public void bindActionHandler(
      final Object fieldInstance,
      final Class<? extends ActionHandler> actionHandlerType,
      final Object[] sources,
      ActionWrapper actionWrapper,
      Object... viewModels)
      throws IllegalAccessException, UnsupportedOperationException {

    Method actionHandler = null;
    Object viewModel = null;
    for (Object viewModelObject : viewModels) {
      actionHandler = findActionHandlerInstance(viewModelObject, actionHandlerType);
      viewModel = viewModelObject;
      if (actionHandler != null) {
        break;
      }
    }

    if (actionHandler == null || viewModel == null) {
      throw new NoSuchElementException(
          "Handler for Action "
              + actionHandlerType
              + " not found: Check annotation, instantiation and type.");
    }

    if (actionWrapper == null) {
      throw new NoSuchElementException(
          "No "
              + ActionWrapper.class.getSimpleName()
              + " for "
              + fieldInstance.getClass()
              + " found.");
    }

    final Method actionHandlerTarget = actionHandler;
    final Object viewModelTarget = viewModel;

    actionWrapper.addActionHandler(
        fieldInstance,
        new ActionHandler() {
          @Override
          public <ActionDataType> void handle(ActionDataType actionData)
              throws UnsupportedOperationException {
            if (circularActionStateUpdatingFields.containsKey(fieldInstance)
                && circularActionStateUpdatingFields.get(fieldInstance)) {
              return;
            }
            if (circularActionStateUpdatingFields.containsKey(fieldInstance)) {
              circularActionStateUpdatingFields.put(fieldInstance, true);
            }

            Object[] sourceData = new Object[sources.length];
            for (int i = 0; i < sources.length; i++) {
              SourceWrapper<?> sourceWrapper = null;
              for (Class<?> key : sourceWrappers.keySet()) {
                if (key.isAssignableFrom(sources[i].getClass())) {
                  sourceWrapper = sourceWrappers.get(key);
                  break;
                }
              }

              if (sourceWrapper == null) {
                throw new NoSuchElementException(
                    "No "
                        + SourceWrapper.class.getSimpleName()
                        + " for "
                        + sources[i].getClass()
                        + " found.");
              }

              sourceData[i] = sourceWrapper.get(sources[i]);
            }

            try {
              if (sourceData.length != 0) {
                actionHandlerTarget.invoke(viewModelTarget, sourceData);
              } else {
                actionHandlerTarget.invoke(viewModelTarget, actionData);
              }
            } catch (IllegalAccessException
                | IllegalArgumentException
                | InvocationTargetException
                | ClassCastException e) {
              throw new UnsupportedOperationException(
                  "Invoking "
                      + ActionHandler.class.getSimpleName()
                      + " "
                      + actionHandlerTarget.getName()
                      + " in "
                      + viewModelTarget.getClass().getSimpleName()
                      + " for "
                      + actionHandlerType.getName()
                      + " event of "
                      + fieldInstance.getClass().getSimpleName()
                      + " failed: check amount and type of parameters.");
            } finally {
              circularActionStateUpdatingFields.put(fieldInstance, false);
            }
          }
        });
  }
 private static void installActionWrapper(
     ActionMap map, String actionName, ActionWrapper wrapper) {
   wrapper.setParent(map.get(actionName));
   map.put(actionName, wrapper);
 }