001package org.andromda.cartridges.bpm4struts.metafacades;
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 java.util.TreeMap;
012import javax.swing.tree.DefaultMutableTreeNode;
013import javax.swing.tree.TreeNode;
014import org.andromda.cartridges.bpm4struts.Bpm4StrutsGlobals;
015import org.andromda.cartridges.bpm4struts.Bpm4StrutsProfile;
016import org.andromda.cartridges.bpm4struts.Bpm4StrutsUtils;
017import org.andromda.metafacades.uml.ActionStateFacade;
018import org.andromda.metafacades.uml.ActivityGraphFacade;
019import org.andromda.metafacades.uml.FinalStateFacade;
020import org.andromda.metafacades.uml.FrontEndActivityGraph;
021import org.andromda.metafacades.uml.FrontEndParameter;
022import org.andromda.metafacades.uml.FrontEndUseCase;
023import org.andromda.metafacades.uml.FrontEndView;
024import org.andromda.metafacades.uml.Role;
025import org.andromda.utils.StringUtilsHelper;
026import org.apache.commons.lang.StringUtils;
027
028/**
029 * MetafacadeLogic implementation.
030 *
031 * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCase
032 */
033public class StrutsUseCaseLogicImpl
034    extends StrutsUseCaseLogic
035{
036    private static final long serialVersionUID = 34L;
037    /**
038     * @param metaObject
039     * @param context
040     */
041    public StrutsUseCaseLogicImpl(
042        Object metaObject,
043        String context)
044    {
045        super(metaObject, context);
046    }
047
048    /**
049     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCaseLogic#handleGetTitleKey()
050     */
051    protected String handleGetTitleKey()
052    {
053        return StringUtilsHelper.toResourceMessageKey(normalizeMessages() ? getTitleValue() : getName()) + ".title";
054    }
055
056    /**
057     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCaseLogic#handleGetTitleValue()
058     */
059    protected String handleGetTitleValue()
060    {
061        return StringUtilsHelper.toPhrase(getName());
062    }
063
064    /**
065     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCaseLogic#handleGetOnlineHelpKey()
066     */
067    protected String handleGetOnlineHelpKey()
068    {
069        return StringUtilsHelper.toResourceMessageKey(getName()) + ".online.help";
070    }
071
072    /**
073     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCaseLogic#handleGetOnlineHelpValue()
074     */
075    protected String handleGetOnlineHelpValue()
076    {
077        final String crlf = "<br/>";
078        final StringBuilder buffer = new StringBuilder();
079
080        buffer.append(!this.isDocumentationPresent() ? "No use-case documentation has been specified"
081            : StringUtilsHelper.toResourceMessage(this.getDocumentation("", 64, false)));
082        buffer.append(crlf);
083
084        return StringUtilsHelper.toResourceMessage(buffer.toString());
085    }
086
087    /**
088     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCaseLogic#handleGetActionPath()
089     */
090    protected String handleGetActionPath()
091    {
092        String actionPath = null;
093
094        final StrutsActivityGraph graph = (StrutsActivityGraph)getActivityGraph();
095        if (graph != null)
096        {
097            final StrutsAction action = graph.getFirstAction();
098            if (action != null)
099            {
100                actionPath = action.getActionPath();
101            }
102        }
103        return actionPath;
104    }
105
106    /**
107     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCaseLogic#handleGetActionPathRoot()
108     */
109    protected String handleGetActionPathRoot()
110    {
111        String actionPathRoot = null;
112
113        final StrutsActivityGraph graph = (StrutsActivityGraph)getActivityGraph();
114        if (graph != null)
115        {
116            final StrutsAction action = graph.getFirstAction();
117            if (action != null)
118            {
119                actionPathRoot = action.getActionPathRoot();
120            }
121        }
122        return actionPathRoot;
123    }
124
125    /**
126     * @return isCyclic
127     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCase#isCyclic()
128     */
129    protected boolean handleIsCyclic()
130    {
131        boolean selfTargetting = false;
132        final ActivityGraphFacade graph = getActivityGraph();
133        if (graph != null)
134        {
135            final Collection<FinalStateFacade> finalStates = graph.getFinalStates();
136            for (final Iterator finalStateIterator = finalStates.iterator();
137                 finalStateIterator.hasNext() && !selfTargetting;)
138            {
139                final StrutsFinalState finalState = (StrutsFinalState)finalStateIterator.next();
140                if (this.equals(finalState.getTargetUseCase()))
141                {
142                    selfTargetting = true;
143                }
144            }
145        }
146        return selfTargetting;
147    }
148
149    /**
150     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCaseLogic#handleGetActionRoles()
151     */
152    protected String handleGetActionRoles()
153    {
154        final Collection<Role> users = this.getRoles();
155        final StringBuilder rolesBuffer = new StringBuilder();
156        boolean first = true;
157        for (final Iterator<Role> userIterator = users.iterator(); userIterator.hasNext();)
158        {
159            if (first)
160            {
161                first = false;
162            }
163            else
164            {
165                rolesBuffer.append(',');
166            }
167            final Role role = userIterator.next();
168            rolesBuffer.append(role.getName());
169        }
170        return rolesBuffer.toString();
171    }
172
173    /**
174     * Collections.EMPTY_LIST
175     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCaseLogic#getOperations()
176     */
177    public List getOperations()
178    {
179        return Collections.emptyList();
180    }
181
182    /**
183     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCaseLogic#handleGetPages()
184     */
185    protected List<FrontEndView> handleGetPages()
186    {
187        return this.getViews();
188    }
189
190    /**
191     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCaseLogic#handleGetAllPages()
192     */
193    protected List<StrutsJsp> handleGetAllPages()
194    {
195        final List<StrutsJsp> pagesList = new ArrayList<StrutsJsp>();
196        for (final ActionStateFacade actionState : getModel().getAllActionStates())
197        {
198            if (actionState instanceof StrutsJsp)
199                pagesList.add((StrutsJsp)actionState);
200        }
201        return pagesList;
202    }
203
204    /**
205     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCaseLogic#handleGetFormFields()
206     */
207    protected List handleGetFormFields()
208    {
209        final List formFields = new ArrayList(); // parameter names are supposed to be unique
210
211        for (final StrutsJsp jsp : getPages())
212        {
213            for (final StrutsParameter parameter : jsp.getPageVariables())
214            {
215                formFields.add(parameter);
216            }
217            for (final FrontEndParameter parameter : jsp.getAllActionParameters())
218            {
219                formFields.add(parameter);
220            }
221        }
222        return formFields;
223    }
224
225    /**
226     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCaseLogic#handleIsValidationRequired()
227     */
228    protected boolean handleIsValidationRequired()
229    {
230        for (final StrutsJsp jsp : this.getAllPages())
231        {
232            if (jsp.isValidationRequired())
233            {
234                return true;
235            }
236        }
237        return false;
238    }
239
240    /**
241     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCaseLogic#handleIsApplicationValidationRequired()
242     */
243    protected boolean handleIsApplicationValidationRequired()
244    {
245        for (final FrontEndUseCase useCase : this.getAllUseCases())
246        {
247            if (((StrutsUseCase)useCase).isValidationRequired())
248            {
249                return true;
250            }
251        }
252        return false;
253    }
254
255    /**
256     * Overridden because StrutsAction does not extend FrontEndAction.
257     *
258     * @see org.andromda.metafacades.uml.FrontEndUseCase#getActions()
259     */
260    public List getActions()
261    {
262        final Collection actions = new LinkedHashSet();
263
264        for (final StrutsJsp jsp : getPages())
265        {
266            actions.addAll(jsp.getActions());
267        }
268
269        final StrutsActivityGraph graph = (StrutsActivityGraph)getActivityGraph();
270        if (graph != null)
271        {
272            final StrutsAction action = graph.getFirstAction();
273            if (action != null) actions.add(action);
274        }
275
276        return new ArrayList(actions);
277    }
278
279    /**
280     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCaseLogic#handleGetPageVariables()
281     */
282    protected List<FrontEndParameter>  handleGetPageVariables()
283    {
284        return this.getViewVariables();
285    }
286
287    /**
288     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCaseLogic#handleIsApplicationUseCase()
289     */
290    protected boolean handleIsApplicationUseCase()
291    {
292        return this.isEntryUseCase();
293    }
294
295    /**
296     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCaseLogic#handleGetCssFileName()
297     */
298    protected String handleGetCssFileName()
299    {
300        return this.getPackagePath() + '/' + Bpm4StrutsUtils.toWebFileName(this.getName()) + ".css";
301    }
302
303    /**
304     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCaseLogic#handleGetApplicationHierarchyRoot()
305     */
306    protected TreeNode handleGetApplicationHierarchyRoot()
307    {
308        final UseCaseNode root = new UseCaseNode(this);
309        this.createHierarchy(root);
310        return root;
311    }
312
313    /**
314     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCaseLogic#handleGetHierarchyRoot()
315     */
316    protected TreeNode handleGetHierarchyRoot()
317    {
318        UseCaseNode hierarchy = null;
319
320        final Collection<FrontEndUseCase> allUseCases = this.getAllUseCases();
321        for (final Iterator<FrontEndUseCase> useCaseIterator = allUseCases.iterator(); useCaseIterator.hasNext();)
322        {
323            final StrutsUseCase useCase = (StrutsUseCase)useCaseIterator.next();
324            if (useCase.isApplicationUseCase())
325            {
326                final UseCaseNode root = (UseCaseNode)useCase.getApplicationHierarchyRoot();
327                hierarchy = this.findNode(root, this);
328            }
329        }
330        return hierarchy;
331    }
332
333    /**
334     * Recursively creates a hierarchy of use-cases, starting with the argument use-case as the root. This is primarily
335     * meant to build a set of menu items.
336     */
337    private void createHierarchy(UseCaseNode root)
338    {
339        final StrutsUseCase useCase = (StrutsUseCase)root.getUserObject();
340
341        final FrontEndActivityGraph graph = useCase.getActivityGraph();
342        if (graph != null)
343        {
344            final Collection<FinalStateFacade> finalStates = graph.getFinalStates();
345            for (final Iterator<FinalStateFacade> finalStateIterator = finalStates.iterator(); finalStateIterator.hasNext();)
346            {
347                final StrutsFinalState finalState = (StrutsFinalState)finalStateIterator.next();
348                final StrutsUseCase targetUseCase = (StrutsUseCase)finalState.getTargetUseCase();
349                if (targetUseCase != null)
350                {
351                    final UseCaseNode useCaseNode = new UseCaseNode(targetUseCase);
352                    if (!isNodeAncestor(root, useCaseNode))
353                    {
354                        root.add(useCaseNode);
355                        createHierarchy(useCaseNode);
356                    }
357                }
358            }
359        }
360    }
361
362    /**
363     * <code>true</code> if the argument ancestor node is actually an ancestor of the first node.
364     * <p>
365     * <em>Note: DefaultMutableTreeNode's isNodeAncestor does not work because of its specific impl.</em>
366     */
367    private boolean isNodeAncestor(
368        UseCaseNode node,
369        UseCaseNode ancestorNode)
370    {
371        boolean ancestor = false;
372
373        if (node.getUseCase().equals(ancestorNode.getUseCase()))
374        {
375            ancestor = true;
376        }
377        while (!ancestor && node.getParent() != null)
378        {
379            node = (UseCaseNode)node.getParent();
380            if (this.isNodeAncestor(node, ancestorNode))
381            {
382                ancestor = true;
383            }
384        }
385        return ancestor;
386    }
387
388    /**
389     * Given a root use-case, finds the node in the hierarchy that represent the argument StrutsUseCase node.
390     */
391    private UseCaseNode findNode(
392        UseCaseNode root,
393        StrutsUseCase useCase)
394    {
395        UseCaseNode useCaseNode = null;
396
397        final List<UseCaseNode> nodeList = Collections.list(root.breadthFirstEnumeration());
398        for (final Iterator<UseCaseNode> nodeIterator = nodeList.iterator(); nodeIterator.hasNext() && useCaseNode == null;)
399        {
400            UseCaseNode node = nodeIterator.next();
401            if (useCase.equals(node.getUserObject()))
402            {
403                useCaseNode = node;
404            }
405        }
406        return useCaseNode;
407    }
408
409    /**
410     *
411     */
412    public static final class UseCaseNode
413        extends DefaultMutableTreeNode
414    {
415        /**
416         * Automatically generated variable: serialVersionUID
417         */
418        private static final long serialVersionUID = -4839356733341754931L;
419
420        /**
421         * @param useCase
422         */
423        public UseCaseNode(StrutsUseCase useCase)
424        {
425            super(useCase);
426        }
427
428        /**
429         * @return getUserObject()
430         */
431        public StrutsUseCase getUseCase()
432        {
433            return (StrutsUseCase)getUserObject();
434        }
435    }
436
437    private boolean normalizeMessages()
438    {
439        final String normalizeMessages = (String)getConfiguredProperty(Bpm4StrutsGlobals.PROPERTY_NORMALIZE_MESSAGES);
440        return Boolean.valueOf(normalizeMessages).booleanValue();
441    }
442
443    /**
444     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCaseLogic#handleGetAllMessages()
445     */
446    protected Map handleGetAllMessages()
447    {
448        final boolean normalize = this.normalizeMessages();
449        final Map<String, String> messages = (normalize) ? new TreeMap() : new LinkedHashMap<String, String>();
450
451        if (this.isApplicationUseCase())
452        {
453            final List<FrontEndUseCase> useCases = this.getAllUseCases();
454            for (int i = 0; i < useCases.size(); i++)
455            {
456                // USECASE
457                final StrutsUseCase useCase = (StrutsUseCase)useCases.get(i);
458                messages.put(useCase.getTitleKey(), useCase.getTitleValue());
459                messages.put(useCase.getOnlineHelpKey(), useCase.getOnlineHelpValue());
460
461                final List actions = useCase.getActions();
462                for (int j = 0; j < actions.size(); j++)
463                {
464                    final StrutsAction action = (StrutsAction)actions.get(j);
465
466                    // FORWARDS
467                    for (StrutsForward forward : action.getTransitions())
468                    {
469                        messages.putAll(forward.getSuccessMessages());
470                        messages.putAll(forward.getWarningMessages());
471                    }
472
473                    // EXCEPTION FORWARDS
474                    final List<StrutsExceptionHandler> exceptions = action.getActionExceptions();
475
476                    if (normalize)
477                    {
478                        if (exceptions.isEmpty())
479                        {
480                            if (!action.isUseCaseStart())
481                            {
482                                messages.put(action.getMessageKey() + ".exception", "{0} (java.lang.Exception)");
483                            }
484                        }
485                        else
486                        {
487                            for (final StrutsExceptionHandler exception : exceptions)
488                            {
489                                messages.put(action.getMessageKey() + '.' + exception.getExceptionKey(), "{0}");
490                            }
491                        }
492                    }
493                    else
494                    {
495                        if (exceptions.isEmpty())
496                        {
497                            if (!action.isUseCaseStart())
498                            {
499                                messages.put(action.getMessageKey() + ".exception", "{0} (java.lang.Exception)");
500                            }
501                        }
502                        else
503                        {
504                            for (int l = 0; l < exceptions.size(); l++)
505                            {
506                                final StrutsExceptionHandler exception = exceptions.get(l);
507                                // we construct the key using the action message too because the exception can
508                                // belong to more than one action (therefore it cannot return the correct value
509                                // in .getExceptionKey())
510                                messages.put(action.getMessageKey() + '.' + exception.getExceptionKey(),
511                                    "{0} (" + exception.getExceptionType() + ')');
512                            }
513                        }
514                    }
515
516                    // TRIGGER
517                    final StrutsTrigger trigger = action.getActionTrigger();
518                    if (trigger != null)
519                    {
520                        // only add these when a trigger is present, otherwise it's no use having them
521                        messages.put(action.getOnlineHelpKey(), action.getOnlineHelpValue());
522                        messages.put(action.getDocumentationKey(), action.getDocumentationValue());
523
524                        // the regular trigger messages
525                        messages.put(trigger.getTitleKey(), trigger.getTitleValue());
526                        messages.put(trigger.getNotAllowedTitleKey(), trigger.getNotAllowedTitleValue());
527                        messages.put(trigger.getResetMessageKey(), trigger.getResetMessageValue());
528                        messages.put(trigger.getResetNotAllowedTitleKey(), trigger.getResetNotAllowedTitleValue());
529                        messages.put(trigger.getResetTitleKey(), trigger.getResetTitleValue());
530                        // this one is the same as doing: action.getMessageKey()
531                        messages.put(trigger.getTriggerKey(), trigger.getTriggerValue());
532
533                        // IMAGE LINK
534                        if (action.isImageLink())
535                        {
536                            messages.put(action.getImageMessageKey(), action.getImagePath());
537                        }
538                    }
539                }
540
541                final List pages = useCase.getPages();
542                for (int j = 0; j < pages.size(); j++)
543                {
544                    // PAGE
545                    final StrutsJsp page = (StrutsJsp)pages.get(j);
546                    messages.put(page.getTitleKey(), page.getTitleValue());
547                    messages.put(page.getMessageKey(), page.getMessageValue());
548                    messages.put(page.getOnlineHelpKey(), page.getOnlineHelpValue());
549                    messages.put(page.getDocumentationKey(), page.getDocumentationValue());
550
551                    final List<StrutsParameter> pageVariables = page.getPageVariables();
552                    for (int k = 0; k < pageVariables.size(); k++)
553                    {
554                        // PAGE-VARIABLE
555                        final StrutsParameter parameter = pageVariables.get(k);
556
557                        messages.put(parameter.getMessageKey(), parameter.getMessageValue());
558/*
559                        if (normalize)
560                        {
561                            // the next line is in comment because it's not actually being used
562                            //messages.put(parameter.getTitleKey(), parameter.getTitleValue());
563                            messages.put(parameter.getMessageKey(), parameter.getMessageValue());
564                        }
565                        else
566                        {
567                            // the next line is in comment because it's not actually being used
568                            //messages.put(page.getTitleKey() + '.' + parameter.getTitleKey(), parameter.getTitleValue());
569                            messages.put(page.getTitleKey() + '.' + parameter.getMessageKey(),
570                                    parameter.getMessageValue());
571                        }
572*/
573
574                        // TABLE
575                        if (parameter.isTable())
576                        {
577                            final Collection<String> columnNames = parameter.getTableColumnNames();
578                            for (final Iterator columnNameIterator = columnNames.iterator();
579                                 columnNameIterator.hasNext();)
580                            {
581                                final String columnName = (String)columnNameIterator.next();
582                                messages.put(parameter.getTableColumnMessageKey(columnName),
583                                    parameter.getTableColumnMessageValue(columnName));
584                            }
585                        }
586                    }
587
588                    for (int k = 0; k < actions.size(); k++)
589                    {
590                        // ACTION
591                        final StrutsAction action = (StrutsAction)actions.get(k);
592
593                        // ACTION PARAMETERS
594                        final List parameters = action.getActionParameters();
595                        for (int l = 0; l < parameters.size(); l++)
596                        {
597                            final StrutsParameter parameter = (StrutsParameter)parameters.get(l);
598                            messages.put(parameter.getMessageKey(), parameter.getMessageValue());
599                            messages.put(parameter.getOnlineHelpKey(), parameter.getOnlineHelpValue());
600                            messages.put(parameter.getDocumentationKey(), parameter.getDocumentationValue());
601                            messages.put(parameter.getTitleKey(), parameter.getTitleValue());
602
603                            if (parameter.getValidWhen() != null)
604                            {
605                                // this key needs to be fully qualified since the valid when value can be different
606                                final String completeKeyPrefix = (normalize)
607                                    ? parameter.getMessageKey()
608                                    : useCase.getTitleKey() + '.' +
609                                    page.getMessageKey() + '.' +
610                                    action.getMessageKey() + '.' +
611                                    parameter.getMessageKey();
612                                messages.put(completeKeyPrefix + "_validwhen",
613                                    "{0} is only valid when " + parameter.getValidWhen());
614                            }
615
616                            if (parameter.getOptionCount() > 0)
617                            {
618                                final List optionKeys = parameter.getOptionKeys();
619                                final List optionValues = parameter.getOptionValues();
620
621                                for (int m = 0; m < optionKeys.size(); m++)
622                                {
623                                    messages.put((String)optionKeys.get(m), (String)optionValues.get(m));
624                                    messages.put((String)optionKeys.get(m) + ".title", (String)optionValues.get(m));
625                                }
626                            }
627                        }
628                    }
629                }
630            }
631        }
632
633        return messages;
634    }
635
636    /**
637     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCaseLogic#handleGetOnlineHelpPagePath()
638     */
639    protected String handleGetOnlineHelpPagePath()
640    {
641        final StringBuilder buffer = new StringBuilder();
642
643        if (StringUtils.isNotBlank(this.getPackagePath()))
644        {
645            buffer.append('/');
646            buffer.append(this.getPackagePath());
647        }
648        buffer.append('/');
649        buffer.append(StringUtilsHelper.separate(this.getName(), "-"));
650        buffer.append("_help");
651
652        return buffer.toString();
653    }
654
655    /**
656     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCaseLogic#handleGetOnlineHelpActionPath()
657     */
658    protected String handleGetOnlineHelpActionPath()
659    {
660        final StringBuilder buffer = new StringBuilder();
661
662        if (StringUtils.isNotBlank(this.getPackagePath()))
663        {
664            buffer.append('/');
665            buffer.append(this.getPackagePath());
666        }
667        buffer.append('/');
668        buffer.append(StringUtilsHelper.upperCamelCaseName(this.getName()));
669        buffer.append("Help");
670
671        return buffer.toString();
672    }
673
674    /**
675     * @return Bpm4StrutsProfile.TAGGEDVALUE_ACTION_FORM_KEY
676     * @see org.andromda.cartridges.bpm4struts.metafacades.StrutsUseCase#getFormKey()
677     */
678    protected String handleGetFormKey()
679    {
680        final Object formKeyValue = this.findTaggedValue(Bpm4StrutsProfile.TAGGEDVALUE_ACTION_FORM_KEY);
681        return formKeyValue == null
682            ? Bpm4StrutsProfile.TAGGEDVALUE_ACTION_FORM_DEFAULT_KEY
683            : String.valueOf(formKeyValue);
684    }
685}