FrontEndActionLogicImpl.java

package org.andromda.metafacades.emf.uml22;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import org.andromda.metafacades.uml.EventFacade;
import org.andromda.metafacades.uml.FrontEndActionState;
import org.andromda.metafacades.uml.FrontEndActivityGraph;
import org.andromda.metafacades.uml.FrontEndController;
import org.andromda.metafacades.uml.FrontEndControllerOperation;
import org.andromda.metafacades.uml.FrontEndEvent;
import org.andromda.metafacades.uml.FrontEndFinalState;
import org.andromda.metafacades.uml.FrontEndForward;
import org.andromda.metafacades.uml.FrontEndParameter;
import org.andromda.metafacades.uml.FrontEndUseCase;
import org.andromda.metafacades.uml.FrontEndView;
import org.andromda.metafacades.uml.ModelElementFacade;
import org.andromda.metafacades.uml.OperationFacade;
import org.andromda.metafacades.uml.ParameterFacade;
import org.andromda.metafacades.uml.PseudostateFacade;
import org.andromda.metafacades.uml.StateVertexFacade;
import org.andromda.metafacades.uml.TransitionFacade;
import org.andromda.metafacades.uml.UseCaseFacade;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang.StringUtils;

/**
 * MetafacadeLogic implementation for
 * org.andromda.metafacades.uml.FrontEndAction.
 *
 * @see org.andromda.metafacades.uml.FrontEndAction
 */
public class FrontEndActionLogicImpl
    extends FrontEndActionLogic
{
    private static final long serialVersionUID = -501340062188534083L;

    /**
     * @param metaObject
     * @param context
     */
    public FrontEndActionLogicImpl(
        final Object metaObject,
        final String context)
    {
        super(metaObject, context);
    }

    /*
     * The logger instance.
    private static final Logger LOGGER = Logger.getLogger(FrontEndActionLogicImpl.class);
     */

    /**
     * @see org.andromda.metafacades.uml.FrontEndAction#getInput()
     */
    @Override
    protected Object handleGetInput()
    {
        Object input = null;
        final ModelElementFacade source = this.getSource();
        if (source instanceof PseudostateFacade)
        {
            final PseudostateFacade pseudostate = (PseudostateFacade)source;
            if (pseudostate.isInitialState())
            {
                input = source;
            }
        }
        else
        {
            if (source instanceof FrontEndView)
            {
                input = source;
            }
        }
        return input;
    }

    /**
     * @see org.andromda.metafacades.uml.FrontEndAction#getParameters()
     */
    @Override
    protected List<ParameterFacade> handleGetParameters()
    {
        final EventFacade trigger = this.getTrigger();
        return trigger == null ? Collections.<ParameterFacade>emptyList() : new ArrayList<ParameterFacade>(trigger.getParameters());
    }

    /**
     * @see org.andromda.metafacades.uml.FrontEndAction#findParameter(String)
     */
    @Override
    protected ParameterFacade handleFindParameter(final String name)
    {
        return (ParameterFacade)CollectionUtils.find(
            this.getParameters(),
            new Predicate()
            {
                public boolean evaluate(final Object object)
                {
                    final ParameterFacade parameter = (ParameterFacade)object;
                    return StringUtils.trimToEmpty(parameter.getName()).equals(name);
                }
            });
    }

    /**
     * @see org.andromda.metafacades.uml.FrontEndAction#getDeferredOperations()
     */
    @Override
    protected List<OperationFacade> handleGetDeferredOperations()
    {
        final Collection<OperationFacade> deferredOperations = new LinkedHashSet<OperationFacade>();
        final FrontEndController controller = this.getController();
        if (controller != null)
        {
            final List<FrontEndActionState> actionStates = this.getActionStates();
            for (final FrontEndActionState actionState : actionStates)
            {
                deferredOperations.addAll(actionState.getControllerCalls());
            }

            final List<FrontEndForward> transitions = this.getDecisionTransitions();
            for (final FrontEndForward forward : transitions)
            {
                final FrontEndEvent trigger = forward.getDecisionTrigger();
                if (trigger != null)
                {
                    deferredOperations.add(trigger.getControllerCall());
                }
            }
        }
        return new ArrayList<OperationFacade>(deferredOperations);
    }

    /**
     * @see org.andromda.metafacades.uml.FrontEndAction#getDecisionTransitions()
     */
    @Override
    protected List<TransitionFacade> handleGetDecisionTransitions()
    {
        if (this.decisionTransitions == null)
        {
            this.initializeCollections();
        }
        return new ArrayList<TransitionFacade>(this.decisionTransitions);
    }

    /**
     * @see org.andromda.metafacades.uml.FrontEndAction#getTargetViews()
     */
    @Override
    protected List<StateVertexFacade> handleGetTargetViews()
    {
        final Collection<StateVertexFacade> targetViews = new LinkedHashSet<StateVertexFacade>();
        final Collection<FrontEndForward> forwards = this.getActionForwards();
        for (final FrontEndForward forward : forwards)
        {
            if (forward.isEnteringView())
            {
                targetViews.add(forward.getTarget());
            }
        }
        return new ArrayList<StateVertexFacade>(targetViews);
    }

    /**
     * All action states that make up this action, this includes all possible
     * action states traversed after a decision point too.
     */
    private Collection<FrontEndActionState> actionStates = null;

    /**
     * All transitions leading into either a page or final state that originated
     * from a call to this action.
     */
    private Map<StateVertexFacade, TransitionFacade> actionForwards = null;

    /**
     * All transitions leading into a decision point that originated from a call
     * to this action.
     */
    private Collection<TransitionFacade> decisionTransitions = null;

    /**
     * All transitions that can be traversed when calling this action.
     */
    private Collection<TransitionFacade> transitions = null;

    /**
     * Initializes all action states, action forwards, decision transitions and
     * transitions in one shot, so that they can be queried more efficiently
     * later on.
     */
    private void initializeCollections()
    {
        this.actionStates = new LinkedHashSet<FrontEndActionState>();
        this.actionForwards = new LinkedHashMap<StateVertexFacade, TransitionFacade>();
        this.decisionTransitions = new LinkedHashSet<TransitionFacade>();
        this.transitions = new LinkedHashSet<TransitionFacade>();
        this.collectTransitions(
            (TransitionFacade)this.THIS(),
            this.transitions);
    }

    /**
     * Recursively collects all action states, action forwards, decision
     * transitions and transitions.
     *
     * @param transition
     *            the current transition that is being processed
     * @param processedTransitions
     *            the set of transitions already processed
     */
    private void collectTransitions(
        final TransitionFacade transition,
        final Collection<TransitionFacade> processedTransitions)
    {
        if (processedTransitions.contains(transition))
        {
            return;
        }
        processedTransitions.add(transition);
        final StateVertexFacade target = transition.getTarget();
        if (target instanceof FrontEndView || target instanceof FrontEndFinalState)
        {
            if (!this.actionForwards.containsKey(transition.getTarget()))
            {
                this.actionForwards.put(
                    transition.getTarget(),
                    transition);
            }
        }
        else if (target instanceof PseudostateFacade && ((PseudostateFacade)target).isDecisionPoint())
        {
            this.decisionTransitions.add(transition);
            final Collection<TransitionFacade> outcomes = target.getOutgoings();
            for (final TransitionFacade outcome : outcomes)
            {
                this.collectTransitions(
                        outcome,
                        processedTransitions);
            }
        }
        else if (target instanceof FrontEndActionState)
        {
            this.actionStates.add((FrontEndActionState)target);
            final FrontEndForward forward = ((FrontEndActionState)target).getForward();
            if (forward != null)
            {
                this.collectTransitions(
                    forward,
                    processedTransitions);
            }
        }
        else// all the rest is ignored but outgoing transitions are further
        // processed
        {
            final Collection<TransitionFacade> outcomes = target.getOutgoings();
            for (final TransitionFacade outcome : outcomes)
            {
                this.collectTransitions(
                        outcome,
                        processedTransitions);
            }
        }
    }

    /**
     * @see org.andromda.metafacades.uml.FrontEndAction#getActionStates()
     */
    @Override
    protected List<FrontEndActionState> handleGetActionStates()
    {
        if (this.actionStates == null)
        {
            this.initializeCollections();
        }
        return new ArrayList<FrontEndActionState>(this.actionStates);
    }

    /**
     * @see org.andromda.metafacades.uml.FrontEndAction#getTransitions()
     */
    @Override
    protected List<TransitionFacade> handleGetTransitions()
    {
        if (this.transitions == null)
        {
            this.initializeCollections();
        }
        return new ArrayList<TransitionFacade>(this.transitions);
    }

    /**
     * @see org.andromda.metafacades.uml.FrontEndAction#getActionForwards()
     */
    @Override
    protected List<TransitionFacade> handleGetActionForwards()
    {
        if (this.actionForwards == null)
        {
            this.initializeCollections();
        }
        return new ArrayList<TransitionFacade>(this.actionForwards.values());
    }

    /**
     * @see org.andromda.metafacades.uml.FrontEndAction#getController()
     */
    @Override
    protected Object handleGetController()
    {
        final FrontEndActivityGraph graph = this.getFrontEndActivityGraph();
        return graph == null ? null : graph.getController();
    }

    /**
     * Overridden because actions (transitions) are not directly contained in a
     * UML namespace.
     *
     * @see org.andromda.metafacades.uml.ModelElementFacade#getPackageName()
     */
    @Override
    public String handleGetPackageName()
    {
        String packageName = null;

        final UseCaseFacade useCase = this.getUseCase();
        if (useCase != null)
        {
            packageName = useCase.getPackageName();
        }
        return packageName;
    }

    /**
     * @see org.andromda.metafacades.uml.FrontEndAction#isUseCaseStart()
     */
    @Override
    protected boolean handleIsUseCaseStart()
    {
        final StateVertexFacade source = this.getSource();
        return source instanceof PseudostateFacade && ((PseudostateFacade)source).isInitialState();
    }

    /**
     * @see org.andromda.metafacades.uml.FrontEndAction#getFormFields()
     */
    @Override
    protected List<ParameterFacade> handleGetFormFields()
    {
        final Map<String, ParameterFacade> formFieldMap = new LinkedHashMap<String, ParameterFacade>();

        // - For an action that starts the use case, we need to detect all
        // usecases forwarding to the one
        // belonging to this action if there are any parameters in those
        // transitions we need to have
        // them included in this action's form
        if (this.isUseCaseStart())
        {
            final FrontEndUseCase useCase = this.getUseCase();
            if (useCase != null)
            {
                final Collection finalStates = useCase.getReferencingFinalStates();
                for (final Iterator finalStateIterator = finalStates.iterator(); finalStateIterator.hasNext();)
                {
                    final Object finalStateObject = finalStateIterator.next();

                    // we need to test for the type because a non
                    // struts-use-case final state might accidentally
                    // link to this use-case (for example: the user
                    // temporarily wants to disable code generation
                    // for a specific use-case and is not removing the
                    // final-state to use-case link(s))
                    if (finalStateObject instanceof FrontEndFinalState)
                    {
                        final FrontEndFinalState finalState = (FrontEndFinalState)finalStateObject;
                        final Collection<FrontEndParameter> parameters = finalState.getInterUseCaseParameters();
                        for (final FrontEndParameter parameter : parameters)
                        {
                            formFieldMap.put(
                                    parameter.getName(),
                                    parameter);
                        }
                    }
                }
            }
        }

        // if any action encountered by the execution of the complete
        // action-graph path emits a forward
        // containing one or more parameters they need to be included as a form
        // field too
        final Collection<FrontEndActionState> actionStates = this.getActionStates();
        for (final FrontEndActionState actionState : actionStates)
        {
            final FrontEndForward forward = actionState.getForward();
            if (forward != null)
            {
                final Collection<FrontEndParameter> forwardParameters = forward.getForwardParameters();
                for (final FrontEndParameter forwardParameter : forwardParameters)
                {
                    formFieldMap.put(
                            forwardParameter.getName(),
                            forwardParameter);
                }
            }
        }

        // add page variables for all pages/final-states targeted
        // also add the fields of the target page's actions (for pre-loading)
        final Collection<FrontEndForward> forwards = this.getActionForwards();
        for (final FrontEndForward forward : forwards)
        {
            final StateVertexFacade target = forward.getTarget();
            if (target instanceof FrontEndView)
            {
                final FrontEndView view = (FrontEndView) target;
                final Collection<FrontEndParameter> viewVariables = view.getVariables();
                for (final FrontEndParameter facade : viewVariables)
                {
                    formFieldMap.put(
                            facade.getName(),
                            facade);
                }
                final Collection<FrontEndParameter> allActionParameters = view.getAllFormFields();
                for (final Iterator<FrontEndParameter> actionParameterIterator = allActionParameters.iterator();
                     actionParameterIterator.hasNext();)
                {
                    // - don't allow existing parameters that are tables to be
                    // overwritten (since they take precedence
                    final Object parameter = actionParameterIterator.next();
                    if (parameter instanceof FrontEndParameter)
                    {
                        FrontEndParameter variable = (FrontEndParameter) parameter;
                        final String name = variable.getName();
                        final Object existingParameter = formFieldMap.get(name);
                        if (existingParameter instanceof FrontEndParameter)
                        {
                            final FrontEndParameter existingVariable = (FrontEndParameter) existingParameter;
                            if (existingVariable.isTable())
                            {
                                variable = existingVariable;
                            }
                        }
                        formFieldMap.put(
                                name,
                                variable);
                    }
                }
            }
            else if (target instanceof FrontEndFinalState)
            {
                // only add these if there is no parameter recorded yet with the
                // same name
                final Collection<FrontEndParameter> forwardParameters = forward.getForwardParameters();
                for (final FrontEndParameter facade : forwardParameters)
                {
                    if (!formFieldMap.containsKey(facade.getName()))
                    {
                        formFieldMap.put(
                                facade.getName(),
                                facade);
                    }
                }
            }
        }

        // we do the action parameters in the end because they are allowed to
        // overwrite existing properties
        final Collection<FrontEndParameter> actionParameters = this.getParameters();
        for (final Iterator<FrontEndParameter> parameterIterator = actionParameters.iterator(); parameterIterator.hasNext();)
        {
            final Object parameter = parameterIterator.next();
            if (parameter instanceof FrontEndParameter)
            {
                final FrontEndParameter variable = (FrontEndParameter)parameter;
                formFieldMap.put(
                    variable.getName(),
                    variable);
            }
        }

        // - if we don't have any fields defined on this action and there are no
        // action forwards,
        // take the parameters from the deferred operations (since we would want
        // to stay on the same view)
        if (formFieldMap.isEmpty() && this.getActionForwards().isEmpty())
        {
            for (final FrontEndControllerOperation operation : this.getDeferredOperations())
            {
                if (operation != null)
                {
                    for (ParameterFacade parameter : operation.getArguments())
                    {
                        formFieldMap.put(
                                parameter.getName(),
                                parameter);
                    }
                }
            }
        }
        return new ArrayList<ParameterFacade>(formFieldMap.values());
    }
}