/*
 * Copyright (c) 2005-2016 Vincent Vandenschrick. All rights reserved.
 *
 *  This file is part of the Jspresso framework.
 *
 *  Jspresso is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Jspresso is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with Jspresso.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.jspresso.framework.application.action;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.jspresso.framework.action.ActionContextConstants;
import org.jspresso.framework.application.backend.IBackendController;
import org.jspresso.framework.application.frontend.IFrontendController;
import org.jspresso.framework.application.model.Module;
import org.jspresso.framework.binding.ConnectorHelper;
import org.jspresso.framework.binding.ICollectionConnector;
import org.jspresso.framework.binding.ICollectionConnectorProvider;
import org.jspresso.framework.binding.ICompositeValueConnector;
import org.jspresso.framework.binding.IValueConnector;
import org.jspresso.framework.model.descriptor.IModelDescriptor;
import org.jspresso.framework.util.event.IItemSelectable;
import org.jspresso.framework.util.event.ISelectable;
import org.jspresso.framework.util.i18n.ITranslationProvider;
import org.jspresso.framework.view.IView;
import org.jspresso.framework.view.descriptor.IPropertyViewDescriptor;
import org.jspresso.framework.view.descriptor.basic.ViewHelper;

/**
 * Abstract class for all objects that need to manipulate an action context. It
 * contains helper methods that takes the developer away from the standard
 * context internal knowledge. Action developers can (should) use these helper
 * methods (for reference manual readers, give an eye to the linked javadoc).
 *
 * @author Vincent Vandenschrick
 */
public abstract class AbstractActionContextAware {

  /**
   * Gets the (string) action command out of the context. The action command is
   * an arbitrary character string that can be set by the UI component
   * triggering the action. It is stored using the
   * {@code ActionContextConstants.ACTION_COMMAND} standard key.
   *
   * @param context
   *     the action context.
   * @return the (string) action command if it exists in the action context or
   * null.
   */
  protected String getActionCommand(Map<String, Object> context) {
    return (String) context.get(ActionContextConstants.ACTION_COMMAND);
  }

  /**
   * Gets the action parameter out of the context. The action parameter is a
   * general purpose context entry that can be used to pass an arbitrary
   * parameter along the action chain. It is stored using the
   * {@code ActionContextConstants.ACTION_PARAM} standard key.
   *
   * @param <T>
   *     type inference return.
   * @param context
   *     the action context.
   * @return the action parameter if it exists in the action context or null.
   */
  @SuppressWarnings("unchecked")
  protected <T> T getActionParameter(Map<String, Object> context) {
    return (T) context.get(ActionContextConstants.ACTION_PARAM);
  }

  /**
   * Gets the backend controller out of the action context using either :
   * <ul>
   * <li>the context frontend controller if it exists</li>
   * <li>the {@code ActionContextConstants.BACK_CONTROLLER} standard key if
   * the action was triggered without going through a frontend controller (a
   * batch action for instance).</li>
   * </ul>
   *
   * @param context
   *     the action context.
   * @return the backend controller.
   */
  protected IBackendController getBackendController(Map<String, Object> context) {
    IFrontendController<?, ?, ?> frontController = getFrontendController(context);
    if (frontController != null) {
      return frontController.getBackendController();
    }
    return (IBackendController) context.get(ActionContextConstants.BACK_CONTROLLER);
  }

  /**
   * Gets the frontend controller out of the action context using the
   * {@code ActionContextConstants.FRONT_CONTROLLER} standard key. If the
   * action was triggered without going through a frontend controller (a batch
   * action for instance), this method might return null.
   *
   * @param <E>
   *     the actual gui component type used.
   * @param <F>
   *     the actual icon type used.
   * @param <G>
   *     the actual action type used.
   * @param context
   *     the action context.
   * @return the frontend controller.
   */
  @SuppressWarnings("unchecked")
  protected <E, F, G> IFrontendController<E, F, G> getFrontendController(Map<String, Object> context) {
    return (IFrontendController<E, F, G>) context.get(ActionContextConstants.FRONT_CONTROLLER);
  }

  /**
   * Retrieves the locale the action has to use to execute. This method
   * delegates to the context backend controller that in turn will delegate to
   * the session.
   *
   * @param context
   *     the action context.
   * @return the locale the action executes in.
   */
  protected Locale getLocale(Map<String, Object> context) {
    IFrontendController<?, ?, ?> frontController = getFrontendController(context);
    if (frontController != null) {
      return frontController.getLocale();
    }
    return getBackendController(context).getLocale();
  }

  /**
   * Gets the model this action was triggered on.
   *
   * @param <T>
   *     type inference return.
   * @param context
   *     the action context.
   * @return the model.
   */
  protected <T> T getModel(Map<String, Object> context) {
    return getModel(null, context);
  }

  /**
   * Gets the model this action was triggered on.
   *
   * @param <T>
   *     type inference return.
   * @param viewPermId
   *     the view permId to search for. The view hierarchy is climed to the top and then a chid view with the searched
   *     permId is recursively searched.
   * @param context
   *     the action context.
   * @return the model.
   */
  protected <T> T getModelFromPermId(String viewPermId, Map<String, Object> context) {
    int[] viewPathFromPermId = ViewHelper.getViewPathFromPermId(viewPermId, getView(context));
    return getModel(viewPathFromPermId, context);
  }

  /**
   * Gets the model this action was triggered on.
   *
   * @param <T>
   *     type inference return.
   * @param viewPath
   *     the view index path to follow.
   *     <ul>
   *     <li>A positive integer n means the nth child.</li>
   *     <li>A negative integer -n means the nth parent.</li>
   *     </ul>
   * @param context
   *     the action context.
   * @return the model.
   */
  protected <T> T getModel(int[] viewPath, Map<String, Object> context) {
    IValueConnector modelConnector = getModelConnector(viewPath, context);
    if (modelConnector != null) {
      if (modelConnector instanceof ICompositeValueConnector) {
        return modelConnector.getConnectorValue();
      } else if (modelConnector.getModelProvider() != null) {
        return modelConnector.getModelProvider().getModel();
      }
    }
    return null;
  }

  /**
   * Gets the model connector this action was triggered on. The model connector
   * is the versatile binding structure that adapts the actual model to the
   * Jspresso binding architecture. The actual model is stored in the model
   * connector value. Unless developing very generic actions, this method will
   * rarely be used in favor of the more concrete {@code getXXXModel}
   * context accessors.
   *
   * @param context
   *     the action context.
   * @return the model connector this action was triggered on.
   */
  protected IValueConnector getModelConnector(Map<String, Object> context) {
    return getModelConnector((int[]) null, context);
  }

  /**
   * Gets the model connector this action was triggered on. The model connector
   * is the versatile binding structure that adapts the actual model to the
   * Jspresso binding architecture. The actual model is stored in the model
   * connector value. Unless developing very generic actions, this method will
   * rarely be used in favor of the more concrete {@code getXXXModel}
   * context accessors.
   *
   * @param viewPermId
   *     the view permId to search for. The view hierarchy is climed to the top and then a chid view with the searched
   *     permId is recursively searched.
   * @param context
   *     the action context.
   * @return the model connector this action was triggered on.
   */
  protected IValueConnector getModelConnectorFromPermId(String viewPermId, Map<String, Object> context) {
    int[] viewPathFromPermId = ViewHelper.getViewPathFromPermId(viewPermId, getView(context));
    return getModelConnector(viewPathFromPermId, context);
  }

  /**
   * Gets the model connector this action was triggered on. The model connector
   * is the versatile binding structure that adapts the actual model to the
   * Jspresso binding architecture. The actual model is stored in the model
   * connector value. Unless developing very generic actions, this method will
   * rarely be used in favor of the more concrete {@code getXXXModel}
   * context accessors.
   *
   * @param viewPath
   *     the view index path to follow.
   *     <ul>
   *     <li>A positive integer n means the nth child.</li>
   *     <li>A negative integer -n means the nth parent.</li>
   *     </ul>
   * @param context
   *     the action context.
   * @return the model connector this action was triggered on.
   */
  protected IValueConnector getModelConnector(int[] viewPath, Map<String, Object> context) {
    IValueConnector viewConnector = getViewConnector(viewPath, context);
    if (viewConnector != null) {
      IValueConnector modelConnector = viewConnector.getModelConnector();
      if (modelConnector != null) {
        // This is to deal with double binding
        while (modelConnector.getModelConnector() != null) {
          modelConnector = modelConnector.getModelConnector();
        }
      }
      return modelConnector;
    }
    return null;
  }

  /**
   * Gets the model connector this action was triggered on following a model path described as a dotted string.
   *
   * @param modelPath
   *     the model path to follow as a dotted string.
   * @param context
   *     the action context.
   * @return the model connector this action was triggered on.
   */
  protected IValueConnector getModelConnector(String modelPath, Map<String, Object> context) {
    IValueConnector modelConnector = getModelConnector(context);
    if (modelConnector instanceof ICollectionConnectorProvider) {
      int[] selectedIndices = getSelectedIndices(context);
      if (selectedIndices.length > 0) {
        modelConnector = ((ICollectionConnectorProvider) modelConnector).getCollectionConnector().getChildConnector(
            selectedIndices[0]);
      }
    }
    if (modelConnector != null) {
      if (modelPath != null) {
        String[] modelSteps = modelPath.split("\\.");
        for (String modelStep : modelSteps) {
          modelConnector = ((ICompositeValueConnector) modelConnector).getChildConnector(modelStep);
        }
      }
      return modelConnector;
    }
    return null;
  }

  /**
   * Retrieves the model descriptor from the context using the
   * {@code ActionContextConstants.MODEL_DESCRIPTOR} standard key. The
   * model descriptor is registered in the action context based on the model of
   * the view to which the action is attached.
   *
   * @param context
   *     the action context.
   * @return the model descriptor this model action was triggered on.
   */
  protected IModelDescriptor getModelDescriptor(Map<String, Object> context) {
    return (IModelDescriptor) context.get(ActionContextConstants.MODEL_DESCRIPTOR);
  }

  /**
   * Gets the model descriptor this action was triggered on.
   *
   * @param viewPermId
   *     the view permId to search for. The view hierarchy is climed to the top and then a chid view with the searched
   *     permId is recursively searched.
   * @param context
   *     the action context.
   * @return the model connector this action was triggered on.
   */
  protected IModelDescriptor getModelDescriptorFromPermId(String viewPermId, Map<String, Object> context) {
    int[] viewPathFromPermId = ViewHelper.getViewPathFromPermId(viewPermId, getView(context));
    return getModelDescriptor(viewPathFromPermId, context);
  }

  /**
   * Gets the model descriptor this action was triggered on.
   *
   * @param viewPath
   *     the view index path to follow.
   *     <ul>
   *     <li>A positive integer n means the nth child.</li>
   *     <li>A negative integer -n means the nth parent.</li>
   *     </ul>
   * @param context
   *     the action context.
   * @return the model connector this action was triggered on.
   */
  protected IModelDescriptor getModelDescriptor(int[] viewPath, Map<String, Object> context) {
    IValueConnector modelConnector = getModelConnector(viewPath, context);
    if (modelConnector != null) {
      return modelConnector.getModelDescriptor();
    }
    return null;
  }

  /**
   * Gets the model descriptor this action was triggered on.
   *
   * @param modelPath
   *     the model path to follow as a dotted string.
   * @param context
   *     the action context.
   * @return the model descriptor this action was triggered on.
   */
  protected IModelDescriptor getModelDescriptor(String modelPath, Map<String, Object> context) {
    IValueConnector modelConnector = getModelConnector(modelPath, context);
    if (modelConnector != null) {
      return modelConnector.getModelDescriptor();
    }
    return null;
  }

  /**
   * Gets the module this action has been executed on. The value is immutable
   * during the action chain and is stored using the
   * {@code ActionContextConstants.MODULE} key.
   *
   * @param context
   *     the action context.
   * @return the module this action executes on.
   */
  protected Module getModule(Map<String, Object> context) {
    return (Module) context.get(ActionContextConstants.MODULE);
  }

  /**
   * Gets the current module from the context. The value might change during the
   * action chain if one of the action navigates the workspace/module. It is
   * stored using the {@code ActionContextConstants.CURRENT_MODULE} key.
   *
   * @param context
   *     the action context.
   * @return the module this action executes on.
   */
  protected Module getCurrentModule(Map<String, Object> context) {
    return (Module) context.get(ActionContextConstants.CURRENT_MODULE);
  }

  /**
   * Gets the parent model this action was triggered on.
   *
   * @param <T>
   *     type inference return.
   * @param context
   *     the action context.
   * @return the parent model.
   */
  protected <T> T getParentModel(Map<String, Object> context) {
    IValueConnector modelConnector = getModelConnector(context);
    if (modelConnector != null) {
      if (modelConnector.getParentConnector() != null) {
        return modelConnector.getParentConnector().getConnectorValue();
      }
    }
    return null;
  }

  /**
   * Gets the selected indices out of the UI component if it is a collection
   * component (table, list, ...). More accurately, the selected indices are
   * taken from the view connector that adapts the UI component to the Jspresso
   * binding architecture.
   *
   * @param context
   *     the action context.
   * @return the selected indices stored in the action context.
   */
  protected int[] getSelectedIndices(Map<String, Object> context) {
    return getSelectedIndices(null, context);
  }

  /**
   * Gets the selected indices out of the UI component if it is a collection
   * component (table, list, ...). More accurately, the selected indices are
   * taken from the view connector that adapts the UI component to the Jspresso
   * binding architecture.
   *
   * @param viewPermId
   *     the view permId to search for. The view hierarchy is climed to the top and then a chid view with the searched
   *     permId is recursively searched.
   * @param context
   *     the action context.
   * @return the selected indices stored in the action context.
   */
  protected int[] getSelectedIndicesFromPermId(String viewPermId, Map<String, Object> context) {
    int[] viewPathFromPermId = ViewHelper.getViewPathFromPermId(viewPermId, getView(context));
    return getSelectedIndices(viewPathFromPermId, context);
  }

  /**
   * Gets the selected indices out of the UI component if it is a collection
   * component (table, list, ...). More accurately, the selected indices are
   * taken from the view connector that adapts the UI component to the Jspresso
   * binding architecture.
   *
   * @param viewPath
   *     the view index path to follow.
   *     <ul>
   *     <li>A positive integer n means the nth child.</li>
   *     <li>A negative integer -n means the nth parent.</li>
   *     </ul>
   * @param context
   *     the action context.
   * @return the selected indices stored in the action context.
   */
  protected int[] getSelectedIndices(int[] viewPath, Map<String, Object> context) {
    int[] selectedIndices = null;
    IValueConnector selectableConnector = getViewConnector(viewPath, context);
    while (selectableConnector != null && !(selectableConnector instanceof ISelectable)) {
      if (selectableConnector.getParentConnector() != null) {
        selectableConnector = selectableConnector.getParentConnector();
      } else {
        // To cope with double binding
        selectableConnector = selectableConnector.getModelConnector();
      }
    }
    if (selectableConnector != null) {
      selectedIndices = ((ISelectable) selectableConnector).getSelectedIndices();
    }
    return selectedIndices;
  }


  /**
   * This is a versatile helper method that retrieves the selected model either
   * from the 1st selected child connector if the action was triggered on a
   * collection connector or from the connector itself.
   *
   * @param <T>
   *     type inference return.
   * @param context
   *     the action context.
   * @return the selected model.
   */
  protected <T> T getSelectedModel(Map<String, Object> context) {
    return getSelectedModel(null, context);
  }

  /**
   * This is a versatile helper method that retrieves the selected model either
   * from the 1st selected child connector if the action was triggered on a
   * collection connector or from the connector itself.
   *
   * @param <T>
   *     type inference return.
   * @param viewPermId
   *     the view permId to search for. The view hierarchy is climed to the top and then a chid view with the searched
   *     permId is recursively searched.
   * @param context
   *     the action context.
   * @return the selected model.
   */
  protected <T> T getSelectedModelFromPermId(String viewPermId, Map<String, Object> context) {
    int[] viewPathFromPermId = ViewHelper.getViewPathFromPermId(viewPermId, getView(context));
    return getSelectedModel(viewPathFromPermId, context);
  }

  /**
   * This is a versatile helper method that retrieves the selected model either
   * from the 1st selected child connector if the action was triggered on a
   * collection connector or from the connector itself.
   *
   * @param <T>
   *     type inference return.
   * @param viewPath
   *     the view index path to follow.
   *     <ul>
   *     <li>A positive integer n means the nth child.</li>
   *     <li>A negative integer -n means the nth parent.</li>
   *     </ul>
   * @param context
   *     the action context.
   * @return the selected model.
   */
  protected <T> T getSelectedModel(int[] viewPath, Map<String, Object> context) {
    IValueConnector viewConnector = getViewConnector(viewPath, context);
    T selectedModel;
    if (viewConnector instanceof IItemSelectable) {
      selectedModel = ((IItemSelectable) viewConnector).getSelectedItem();
      if (selectedModel instanceof IValueConnector) {
        selectedModel = ((IValueConnector) selectedModel).getConnectorValue();
      }
    } else {
      selectedModel = getModel(viewPath, context);
    }
    return selectedModel;
  }

  /**
   * This is a versatile helper method that retrieves the selected models model
   * either from the selected child connectors if the action was triggered on a
   * collection connector or from the connector itself.
   *
   * @param <T>
   *     type inference return.
   * @param context
   *     the action context.
   * @return the list of selected models.
   */
  protected <T> List<T> getSelectedModels(Map<String, Object> context) {
    return getSelectedModels(null, context);
  }

  /**
   * This is a versatile helper method that retrieves the selected models model
   * either from the selected child connectors if the action was triggered on a
   * collection connector or from the connector itself.
   *
   * @param <T>
   *     type inference return.
   * @param viewPermId
   *     the view permId to search for. The view hierarchy is climed to the top and then a chid view with the searched
   *     permId is recursively searched.
   * @param context
   *     the action context.
   * @return the list of selected models.
   */
  protected <T> List<T> getSelectedModelsFromPermId(String viewPermId, Map<String, Object> context) {
    int[] viewPathFromPermId = ViewHelper.getViewPathFromPermId(viewPermId, getView(context));
    return getSelectedModels(viewPathFromPermId, context);
  }

  /**
   * This is a versatile helper method that retrieves the selected models model
   * either from the selected child connectors if the action was triggered on a
   * collection connector or from the connector itself.
   *
   * @param <T>
   *     type inference return.
   * @param viewPath
   *     the view index path to follow.
   *     <ul>
   *     <li>A positive integer n means the nth child.</li>
   *     <li>A negative integer -n means the nth parent.</li>
   *     </ul>
   * @param context
   *     the action context.
   * @return the list of selected models.
   */
  @SuppressWarnings("unchecked")
  protected <T> List<T> getSelectedModels(int[] viewPath, Map<String, Object> context) {
    IValueConnector modelConnector = getModelConnector(viewPath, context);
    if (modelConnector == null) {
      return null;
    }
    List<T> models;
    if (modelConnector instanceof ICollectionConnector) {
      models = new ArrayList<>();
      int[] selectedIndices = getSelectedIndices(viewPath, context);
      if (selectedIndices != null && selectedIndices.length > 0) {
        for (int selectedIndice : selectedIndices) {
          IValueConnector childConnector = ((ICollectionConnector) modelConnector).getChildConnector(selectedIndice);
          if (childConnector != null) {
            models.add((T) childConnector.getConnectorValue());
          }
        }
      }
    } else {
      T model = getSelectedModel(viewPath, context);
      models = Collections.singletonList(model);
    }
    return models;
  }

  /**
   * Gets he application translation provider out of the action context. This
   * method simply delegates to the context backend controller.
   *
   * @param context
   *     the action context.
   * @return the translation provider.
   */
  protected ITranslationProvider getTranslationProvider(Map<String, Object> context) {
    return getBackendController(context);
  }

  /**
   * Sets the action parameter out of the context. The action parameter is a
   * general purpose context entry that can be used to pass an arbitrary
   * parameter along the action chain. It is stored using the
   * {@code ActionContextConstants.ACTION_PARAM} standard key.
   *
   * @param actionParam
   *     the action parameter to set to the context.
   * @param context
   *     the action context.
   */
  protected void setActionParameter(Object actionParam, Map<String, Object> context) {
    context.put(ActionContextConstants.ACTION_PARAM, actionParam);
  }

  /**
   * Sets the selected indices of the UI component if it is a collection
   * component (table, list, ...). More accurately, the selected indices are set
   * to the view connector that adapts the UI component to the Jspresso binding
   * architecture.
   *
   * @param selectedIndices
   *     the selected indices to store in the action context.
   * @param context
   *     the action context.
   */
  protected void setSelectedIndices(int[] selectedIndices, Map<String, Object> context) {
    setSelectedIndices(null, selectedIndices, context);
  }

  /**
   * Sets the selected indices of the UI component if it is a collection
   * component (table, list, ...). More accurately, the selected indices are set
   * to the view connector that adapts the UI component to the Jspresso binding
   * architecture.
   *
   * @param viewPermId
   *     the view permId to search for. The view hierarchy is climed to the top and then a chid view with the searched
   *     permId is recursively searched.
   * @param selectedIndices
   *     the selected indices to store in the action context.
   * @param context
   *     the action context.
   */
  protected void setSelectedIndicesFromPermId(String viewPermId, int[] selectedIndices, Map<String, Object> context) {
    int[] viewPathFromPermId = ViewHelper.getViewPathFromPermId(viewPermId, getView(context));
    setSelectedIndices(viewPathFromPermId, selectedIndices, context);
  }

  /**
   * Sets the selected indices of the UI component if it is a collection
   * component (table, list, ...). More accurately, the selected indices are set
   * to the view connector that adapts the UI component to the Jspresso binding
   * architecture.
   *
   * @param viewPath
   *     the view index path to follow.
   *     <ul>
   *     <li>A positive integer n means the nth child.</li>
   *     <li>A negative integer -n means the nth parent.</li>
   *     </ul>
   * @param selectedIndices
   *     the selected indices to store in the action context.
   * @param context
   *     the action context.
   */
  protected void setSelectedIndices(int[] viewPath, int[] selectedIndices, Map<String, Object> context) {
    IValueConnector selectableConnector = getViewConnector(viewPath, context);
    while (selectableConnector != null && !(selectableConnector instanceof ISelectable)) {
      selectableConnector = selectableConnector.getParentConnector();
    }
    if (selectableConnector != null) {
      ((ISelectable) selectableConnector).setSelectedIndices(selectedIndices);
    }
  }

  /**
   * Retrieves the selected models indices out of the model connector if it's a
   * collection connector and set them as selected indices in the action
   * context.
   *
   * @param selectedModels
   *     the list of models to select in the view connector.
   * @param context
   *     the action context.
   */
  protected void setSelectedModels(Collection<?> selectedModels, Map<String, Object> context) {
    setSelectedModels(null, selectedModels, context);
  }

  /**
   * Retrieves the selected models indices out of the model connector if it's a
   * collection connector and set them as selected indices in the action
   * context.
   *
   * @param viewPermId
   *     the view permId to search for. The view hierarchy is climed to the top and then a chid view with the searched
   *     permId is recursively searched.
   * @param selectedModels
   *     the list of models to select in the view connector.
   * @param context
   *     the action context.
   */
  protected void setSelectedModelsFromPermId(String viewPermId, Collection<?> selectedModels,
                                             Map<String, Object> context) {
    int[] viewPathFromPermId = ViewHelper.getViewPathFromPermId(viewPermId, getView(context));
    setSelectedModels(viewPathFromPermId, selectedModels, context);
  }

  /**
   * Retrieves the selected models indices out of the model connector if it's a
   * collection connector and set them as selected indices in the action
   * context.
   *
   * @param viewPath
   *     the view index path to follow.
   *     <ul>
   *     <li>A positive integer n means the nth child.</li>
   *     <li>A negative integer -n means the nth parent.</li>
   *     </ul>
   * @param selectedModels
   *     the list of models to select in the view connector.
   * @param context
   *     the action context.
   */
  protected void setSelectedModels(int[] viewPath, Collection<?> selectedModels, Map<String, Object> context) {
    IValueConnector modelConnector = getModelConnector(viewPath, context);
    if (modelConnector instanceof ICollectionConnector) {
      setSelectedIndices(viewPath, ConnectorHelper.getIndicesOf((ICollectionConnector) modelConnector, selectedModels),
          context);
    }
  }

  /**
   * Gets the property view descriptor out of the context. The property view
   * descriptor is only filled when an action is triggered from a table column
   * or a form field. It contains a reference to the column/field descriptor the
   * action was triggered from.
   *
   * @param context
   *     the action context.
   * @return the property view descriptor.
   */
  protected IPropertyViewDescriptor getPropertyViewDescriptor(Map<String, Object> context) {
    return (IPropertyViewDescriptor) context.get(ActionContextConstants.PROPERTY_VIEW_DESCRIPTOR);
  }

  /**
   * This is a utility method which is able to retrieve the view this action has
   * been executed on from its context. It uses well-known context keys of the
   * action context which are:
   * <ul>
   * <li> {@code ActionContextConstants.VIEW} to get the the view the action
   * executes on.
   * </ul>
   * <p>
   * The returned view mainly serves for acting on the view component the action
   * has to be triggered on.
   *
   * @param <T>
   *     type inference return.
   * @param context
   *     the action context.
   * @return the view this action was triggered on.
   */
  protected <T> IView<T> getView(Map<String, Object> context) {
    return getView(null, context);
  }

  /**
   * This is a utility method which is able to retrieve the view this action has
   * been executed on from its context. It uses well-known context keys of the
   * action context which are:
   * <ul>
   * <li> {@code ActionContextConstants.VIEW} to get the the view the action
   * executes on.
   * </ul>
   * <p>
   * The returned view mainly serves for acting on the view component the action
   * has to be triggered on.
   *
   * @param <T>
   *     type inference return.
   * @param viewPermId
   *     the view permId to search for. The view hierarchy is climed to the top and then a chid view with the searched
   *     permId is recursively searched.
   * @param context
   *     the action context.
   * @return the view this action was triggered on.
   */
  @SuppressWarnings("unchecked")
  protected <T> IView<T> getViewFromPermId(String viewPermId, Map<String, Object> context) {
    int[] viewPathFromPermId = ViewHelper.getViewPathFromPermId(viewPermId, getView(context));
    return getView(viewPathFromPermId, context);
  }

  /**
   * This is a utility method which is able to retrieve the view this action has
   * been executed on from its context. It uses well-known context keys of the
   * action context which are:
   * <ul>
   * <li> {@code ActionContextConstants.VIEW} to get the the view the action
   * executes on.
   * </ul>
   * <p>
   * The returned view mainly serves for acting on the view component the action
   * has to be triggered on.
   *
   * @param <T>
   *     type inference return.
   * @param viewPath
   *     the view index path to follow.
   *     <ul>
   *     <li>A positive integer n means the nth child.</li> <li>A negative
   *     integer -n means the nth parent.</li>
   *     </ul>
   * @param context
   *     the action context.
   * @return the view this action was triggered on.
   */
  @SuppressWarnings("unchecked")
  protected <T> IView<T> getView(int[] viewPath, Map<String, Object> context) {
    return ViewHelper.navigate((IView<T>) context.get(ActionContextConstants.VIEW), viewPath);
  }

  /**
   * This is a utility method which is able to retrieve the view connector this
   * action has been executed on from its context. It uses well-known context
   * keys of the action context which are:
   * <ul>
   * <li> {@code ActionContextConstants.VIEW_CONNECTOR} to get the the view
   * value connector the action executes on.
   * </ul>
   * <p>
   * The returned connector mainly serves for acting on the view component the
   * action has to be triggered on.
   *
   * @param context
   *     the action context.
   * @return the value connector this action was triggered on.
   */
  protected IValueConnector getViewConnector(Map<String, Object> context) {
    return getViewConnector(null, context);
  }

  /**
   * This is a utility method which is able to retrieve the view connector this
   * action has been executed on from its context. It uses well-known context
   * keys of the action context which are:
   * <ul>
   * <li> {@code ActionContextConstants.VIEW_CONNECTOR} to get the the view
   * value connector the action executes on.
   * </ul>
   * <p>
   * The returned connector mainly serves for acting on the view component the
   * action has to be triggered on.
   *
   * @param viewPermId
   *     the view permId to search for. The view hierarchy is climed to the top and then a chid view with the searched
   *     permId is recursively searched.
   * @param context
   *     the action context.
   * @return the value connector this action was triggered on.
   */
  protected IValueConnector getViewConnectorFromPermId(String viewPermId, Map<String, Object> context) {
    int[] viewPathFromPermId = ViewHelper.getViewPathFromPermId(viewPermId, getView(context));
    return getViewConnector(viewPathFromPermId, context);
  }

  /**
   * This is a utility method which is able to retrieve the view connector this
   * action has been executed on from its context. It uses well-known context
   * keys of the action context which are:
   * <ul>
   * <li> {@code ActionContextConstants.VIEW_CONNECTOR} to get the the view
   * value connector the action executes on.
   * </ul>
   * <p>
   * The returned connector mainly serves for acting on the view component the
   * action has to be triggered on.
   *
   * @param viewPath
   *     the view index path to follow.
   *     <ul>
   *     <li>A positive integer n means the nth child.</li> <li>A negative
   *     integer -n means the nth parent.</li>
   *     </ul>
   * @param context
   *     the action context.
   * @return the value connector this action was triggered on.
   */
  protected IValueConnector getViewConnector(int[] viewPath, Map<String, Object> context) {
    if (viewPath == null || viewPath.length == 0) {
      return (IValueConnector) context.get(ActionContextConstants.VIEW_CONNECTOR);
    }
    IView<?> view = getView(viewPath, context);
    if (view != null) {
      return view.getConnector();
    }
    return null;
  }

}
