001package org.andromda.metafacades.uml14;
002
003import java.util.ArrayList;
004import java.util.Collection;
005import java.util.Collections;
006import java.util.Iterator;
007import java.util.LinkedHashMap;
008import java.util.LinkedHashSet;
009import java.util.List;
010import java.util.Map;
011import org.andromda.metafacades.uml.EventFacade;
012import org.andromda.metafacades.uml.FrontEndActionState;
013import org.andromda.metafacades.uml.FrontEndActivityGraph;
014import org.andromda.metafacades.uml.FrontEndController;
015import org.andromda.metafacades.uml.FrontEndControllerOperation;
016import org.andromda.metafacades.uml.FrontEndEvent;
017import org.andromda.metafacades.uml.FrontEndFinalState;
018import org.andromda.metafacades.uml.FrontEndForward;
019import org.andromda.metafacades.uml.FrontEndParameter;
020import org.andromda.metafacades.uml.FrontEndUseCase;
021import org.andromda.metafacades.uml.FrontEndView;
022import org.andromda.metafacades.uml.ModelElementFacade;
023import org.andromda.metafacades.uml.OperationFacade;
024import org.andromda.metafacades.uml.ParameterFacade;
025import org.andromda.metafacades.uml.PseudostateFacade;
026import org.andromda.metafacades.uml.StateVertexFacade;
027import org.andromda.metafacades.uml.TransitionFacade;
028import org.andromda.metafacades.uml.UseCaseFacade;
029import org.apache.commons.collections.CollectionUtils;
030import org.apache.commons.collections.Predicate;
031import org.apache.commons.lang.StringUtils;
032
033
034/**
035 * MetafacadeLogic implementation for org.andromda.metafacades.uml.FrontEndAction.
036 *
037 * @see org.andromda.metafacades.uml.FrontEndAction
038 * @author Bob Fields
039 */
040public class FrontEndActionLogicImpl
041    extends FrontEndActionLogic
042{
043    private static final long serialVersionUID = -5553731254658712583L;
044
045    /**
046     * @param metaObject
047     * @param context
048     */
049    public FrontEndActionLogicImpl(
050        Object metaObject,
051        String context)
052    {
053        super(metaObject, context);
054    }
055
056    /**
057     * Returns either a PseudostateFacade or FrontEndView
058     * @return input source
059     * @see org.andromda.metafacades.uml.FrontEndAction#getInput()
060     */
061    protected Object handleGetInput()
062    {
063        Object input = null;
064        final ModelElementFacade source = this.getSource();
065        if (source instanceof PseudostateFacade)
066        {
067            final PseudostateFacade pseudostate = (PseudostateFacade)source;
068            if (pseudostate.isInitialState())
069            {
070                input = source;
071            }
072        }
073        else
074        {
075            if (source instanceof FrontEndView)
076            {
077                input = source;
078            }
079        }
080        return input;
081    }
082
083    /**
084     * @see org.andromda.metafacades.uml.FrontEndAction#getParameters()
085     */
086    @Override
087    protected List handleGetParameters()
088    {
089        final EventFacade trigger = this.getTrigger();
090        return trigger == null ? Collections.emptyList() : new ArrayList(trigger.getParameters());
091    }
092
093    /**
094     * @see  org.andromda.metafacades.uml.FrontEndAction#findParameter(String)
095     */
096    @Override
097    protected ParameterFacade handleFindParameter(final String name)
098    {
099        return (ParameterFacade)CollectionUtils.find(
100            this.getParameters(),
101            new Predicate()
102            {
103                public boolean evaluate(Object object)
104                {
105                    final ParameterFacade parameter = (ParameterFacade)object;
106                    return StringUtils.trimToEmpty(parameter.getName()).equals(name);
107                }
108            });
109    }
110
111    /**
112     * @see org.andromda.metafacades.uml.FrontEndAction#getDeferredOperations()
113     */
114    @Override
115    protected List handleGetDeferredOperations()
116    {
117        final Collection deferredOperations = new LinkedHashSet();
118        final FrontEndController controller = this.getController();
119        if (controller != null)
120        {
121            final List actionStates = this.getActionStates();
122            for (int ctr = 0; ctr < actionStates.size(); ctr++)
123            {
124                final FrontEndActionState actionState = (FrontEndActionState)actionStates.get(ctr);
125                deferredOperations.addAll(actionState.getControllerCalls());
126            }
127
128            final List transitions = this.getDecisionTransitions();
129            for (int ctr = 0; ctr < transitions.size(); ctr++)
130            {
131                final FrontEndForward forward = (FrontEndForward)transitions.get(ctr);
132                final FrontEndEvent trigger = forward.getDecisionTrigger();
133                if (trigger != null)
134                {
135                    deferredOperations.add(trigger.getControllerCall());
136                }
137            }
138        }
139        return new ArrayList(deferredOperations);
140    }
141
142    /**
143     * @see org.andromda.metafacades.uml.FrontEndAction#getDecisionTransitions()
144     */
145    @Override
146    protected List handleGetDecisionTransitions()
147    {
148        if (decisionTransitions == null)
149        {
150            initializeCollections();
151        }
152        return new ArrayList(decisionTransitions);
153    }
154
155    /**
156     * @see org.andromda.metafacades.uml.FrontEndAction#getTargetViews()
157     */
158    @Override
159    protected List<StateVertexFacade> handleGetTargetViews()
160    {
161        final Collection<StateVertexFacade> targetViews = new LinkedHashSet<StateVertexFacade>();
162        final Collection<FrontEndForward> forwards = this.getActionForwards();
163        for (final Iterator<FrontEndForward> iterator = forwards.iterator(); iterator.hasNext();)
164        {
165            final FrontEndForward forward = iterator.next();
166            if (forward.isEnteringView())
167            {
168                targetViews.add(forward.getTarget());
169            }
170        }
171        return new ArrayList(targetViews);
172    }
173
174    /**
175     * All action states that make up this action, this includes all possible action states traversed
176     * after a decision point too.
177     */
178    private Collection actionStates = null;
179
180    /**
181     * All transitions leading into either a page or final state that originated from a call to this action.
182     */
183    private Map actionForwards = null;
184
185    /**
186     * All transitions leading into a decision point that originated from a call to this action.
187     */
188    private Collection decisionTransitions = null;
189
190    /**
191     * All transitions that can be traversed when calling this action.
192     */
193    private Collection transitions = null;
194
195    /**
196     * Initializes all action states, action forwards, decision transitions and transitions in one shot, so that they
197     * can be queried more effiencently later on.
198     */
199    private void initializeCollections()
200    {
201        this.actionStates = new LinkedHashSet();
202        this.actionForwards = new LinkedHashMap();
203        this.decisionTransitions = new LinkedHashSet();
204        this.transitions = new LinkedHashSet();
205        this.collectTransitions(
206            (TransitionFacade)this.THIS(),
207            transitions);
208    }
209
210    /**
211     * Recursively collects all action states, action forwards, decision transitions and transitions.
212     *
213     * @param transition the current transition that is being processed
214     * @param processedTransitions the set of transitions already processed
215     */
216    private void collectTransitions(
217        TransitionFacade transition,
218        Collection processedTransitions)
219    {
220        if (processedTransitions.contains(transition))
221        {
222            return;
223        }
224        processedTransitions.add(transition);
225        final StateVertexFacade target = transition.getTarget();
226        if (target instanceof FrontEndView || target instanceof FrontEndFinalState)
227        {
228            if (!this.actionForwards.containsKey(transition.getTarget()))
229            {
230                this.actionForwards.put(
231                    transition.getTarget(),
232                    transition);
233            }
234        }
235        else if (target instanceof PseudostateFacade && ((PseudostateFacade)target).isDecisionPoint())
236        {
237            this.decisionTransitions.add(transition);
238            final Collection<TransitionFacade> outcomes = target.getOutgoings();
239            for (final Iterator<TransitionFacade> iterator = outcomes.iterator(); iterator.hasNext();)
240            {
241                final TransitionFacade outcome = iterator.next();
242                collectTransitions(
243                    outcome,
244                    processedTransitions);
245            }
246        }
247        else if (target instanceof FrontEndActionState)
248        {
249            this.actionStates.add(target);
250            final FrontEndForward forward = ((FrontEndActionState)target).getForward();
251            if (forward != null)
252            {
253                collectTransitions(
254                    forward,
255                    processedTransitions);
256            }
257        }
258        else // all the rest is ignored but outgoing transitions are further processed
259        {
260            final Collection<TransitionFacade> outcomes = target.getOutgoings();
261            for (final Iterator<TransitionFacade> iterator = outcomes.iterator(); iterator.hasNext();)
262            {
263                final TransitionFacade outcome = iterator.next();
264                collectTransitions(
265                    outcome,
266                    processedTransitions);
267            }
268        }
269    }
270
271    /**
272     * @see org.andromda.metafacades.uml.FrontEndAction#getActionStates()
273     */
274    @Override
275    protected List handleGetActionStates()
276    {
277        if (this.actionStates == null)
278        {
279            this.initializeCollections();
280        }
281        return new ArrayList(this.actionStates);
282    }
283
284    /**
285     * @see org.andromda.metafacades.uml.FrontEndAction#getTransitions()
286     */
287    @Override
288    protected List handleGetTransitions()
289    {
290        if (this.transitions == null)
291        {
292            this.initializeCollections();
293        }
294        return new ArrayList(this.transitions);
295    }
296
297    /**
298     * @see org.andromda.metafacades.uml.FrontEndAction#getActionForwards()
299     */
300    @Override
301    protected List handleGetActionForwards()
302    {
303        if (this.actionForwards == null)
304        {
305            this.initializeCollections();
306        }
307        return new ArrayList(this.actionForwards.values());
308    }
309
310    /**
311     * @see org.andromda.metafacades.uml.FrontEndAction#getController()
312     */
313    @Override
314    protected FrontEndController handleGetController()
315    {
316        final FrontEndActivityGraph graph = this.getFrontEndActivityGraph();
317        return graph == null ? null : graph.getController();
318    }
319
320    /**
321     * Overridden because actions (transitions) are not directly contained in a UML namespace.
322     *
323     * @see org.andromda.metafacades.uml.ModelElementFacade#getPackageName()
324     */
325    @Override
326    public String handleGetPackageName()
327    {
328        String packageName = null;
329
330        final UseCaseFacade useCase = this.getUseCase();
331        if (useCase != null)
332        {
333            packageName = useCase.getPackageName();
334        }
335        return packageName;
336    }
337
338    /**
339     * @see org.andromda.metafacades.uml.FrontEndAction#isUseCaseStart()
340     */
341    @Override
342    protected boolean handleIsUseCaseStart()
343    {
344        final StateVertexFacade source = getSource();
345        return source instanceof PseudostateFacade && ((PseudostateFacade)source).isInitialState();
346    }
347
348    /**
349     * @see org.andromda.metafacades.uml.FrontEndAction#getFormFields()
350     */
351    @Override
352    protected List<ParameterFacade> handleGetFormFields()
353    {
354        final Map<String, ParameterFacade> formFieldMap = new LinkedHashMap<String, ParameterFacade>();
355
356        // - For an action that starts the use case, we need to detect all usecases forwarding to the one
357        //   belonging to this action if there are any parameters in those transitions we need to have
358        //   them included in this action's form
359        if (this.isUseCaseStart())
360        {
361            final FrontEndUseCase useCase = this.getUseCase();
362            if (useCase != null)
363            {
364                final Collection<FrontEndFinalState> finalStates = useCase.getReferencingFinalStates();
365                for (final Iterator<FrontEndFinalState> finalStateIterator = finalStates.iterator(); finalStateIterator.hasNext();)
366                {
367                    final Object finalStateObject = finalStateIterator.next();
368
369                    // we need to test for the type because a non struts-use-case final state might accidently
370                    // link to this use-case (for example: the user temporarily wants to disable code generation
371                    // for a specific use-case and is not removing the final-state to use-case link(s))
372                    if (finalStateObject instanceof FrontEndFinalState)
373                    {
374                        final FrontEndFinalState finalState = (FrontEndFinalState)finalStateObject;
375                        final Collection<FrontEndParameter> parameters = finalState.getInterUseCaseParameters();
376                        for (final Iterator<FrontEndParameter> parameterIterator = parameters.iterator(); parameterIterator.hasNext();)
377                        {
378                            final ParameterFacade parameter = (ParameterFacade)parameterIterator.next();
379                            formFieldMap.put(
380                                parameter.getName(),
381                                parameter);
382                        }
383                    }
384                }
385            }
386        }
387
388        // if any action encountered by the execution of the complete action-graph path emits a forward
389        // containing one or more parameters they need to be included as a form field too
390        final Collection actionStates = this.getActionStates();
391        for (final Iterator iterator = actionStates.iterator(); iterator.hasNext();)
392        {
393            final FrontEndActionState actionState = (FrontEndActionState)iterator.next();
394            final FrontEndForward forward = actionState.getForward();
395            if (forward != null)
396            {
397                final Collection<FrontEndParameter> forwardParameters = forward.getForwardParameters();
398                for (final Iterator<FrontEndParameter> parameterIterator = forwardParameters.iterator(); parameterIterator.hasNext();)
399                {
400                    final ParameterFacade forwardParameter = (ParameterFacade)parameterIterator.next();
401                    formFieldMap.put(
402                        forwardParameter.getName(),
403                        forwardParameter);
404                }
405            }
406        }
407
408        // add page variables for all pages/final-states targetted
409        // also add the fields of the target page's actions (for preloading)
410        final Collection forwards = this.getActionForwards();
411        for (final Iterator iterator = forwards.iterator(); iterator.hasNext();)
412        {
413            final FrontEndForward forward = (FrontEndForward)iterator.next();
414            final StateVertexFacade target = forward.getTarget();
415            if (target instanceof FrontEndView)
416            {
417                final FrontEndView view = (FrontEndView)target;
418                final Collection<FrontEndParameter> viewVariables = view.getVariables();
419                for (final Iterator<FrontEndParameter> pageVariableIterator = viewVariables.iterator(); pageVariableIterator.hasNext();)
420                {
421                    final ParameterFacade facade = (ParameterFacade)pageVariableIterator.next();
422                    formFieldMap.put(
423                        facade.getName(),
424                        facade);
425                }
426                final Collection<FrontEndParameter> allActionParameters = view.getAllFormFields();
427                for (final Iterator<FrontEndParameter> actionParameterIterator = allActionParameters.iterator();
428                    actionParameterIterator.hasNext();)
429                {
430                    // - don't allow existing parameters that are tables be overwritten (since they take
431                    //   precedence
432                    final Object parameter = actionParameterIterator.next();
433                    if (parameter instanceof FrontEndParameter)
434                    {
435                        FrontEndParameter variable = (FrontEndParameter)parameter;
436                        final String name = variable.getName();
437                        final Object existingParameter = formFieldMap.get(name);
438                        if (existingParameter instanceof FrontEndParameter)
439                        {
440                            final FrontEndParameter existingVariable = (FrontEndParameter)existingParameter;
441                            if (existingVariable.isTable())
442                            {
443                                variable = existingVariable;
444                            }
445                        }
446                        formFieldMap.put(
447                            name,
448                            variable);
449                    }
450                }
451            }
452            else if (target instanceof FrontEndFinalState)
453            {
454                // only add these if there is no parameter recorded yet with the same name
455                final Collection<FrontEndParameter> forwardParameters = forward.getForwardParameters();
456                for (final Iterator<FrontEndParameter> parameterIterator = forwardParameters.iterator(); parameterIterator.hasNext();)
457                {
458                    final FrontEndParameter facade = parameterIterator.next();
459                    if (!formFieldMap.containsKey(facade.getName()))
460                    {
461                        formFieldMap.put(
462                            facade.getName(),
463                            facade);
464                    }
465                }
466            }
467        }
468
469        // we do the action parameters in the end because they are allowed to overwrite existing properties
470        final Collection<FrontEndParameter> actionParameters = this.getParameters();
471        for (final Iterator<FrontEndParameter> parameterIterator = actionParameters.iterator(); parameterIterator.hasNext();)
472        {
473            final Object parameter = parameterIterator.next();
474            if (parameter instanceof FrontEndParameter)
475            {
476                final FrontEndParameter variable = (FrontEndParameter)parameter;
477                formFieldMap.put(
478                    variable.getName(),
479                    variable);
480            }
481        }
482
483        // - if we don't have any fields defined on this action and there are no action forwards,
484        //   take the parameters from the deferred operations (since we would want to stay on the same view)
485        if (formFieldMap.isEmpty() && this.getActionForwards().isEmpty())
486        {
487            for (final Iterator<FrontEndControllerOperation> iterator = this.getDeferredOperations().iterator(); iterator.hasNext();)
488            {
489                final OperationFacade operation = (OperationFacade)iterator.next();
490                for (final Iterator<ParameterFacade> parameterIterator = operation.getArguments().iterator(); parameterIterator.hasNext();)
491                {
492                    final ParameterFacade parameter = parameterIterator.next();
493                    formFieldMap.put(parameter.getName(), parameter);
494                }
495            }
496        }
497        return new ArrayList<ParameterFacade>(formFieldMap.values());
498    }
499}