001package org.andromda.cartridges.jsf2.metafacades;
002
003import java.util.ArrayList;
004import java.util.Collection;
005import java.util.HashMap;
006import java.util.HashSet;
007import java.util.Iterator;
008import java.util.LinkedHashMap;
009import java.util.LinkedHashSet;
010import java.util.List;
011import java.util.Map;
012import java.util.Set;
013import org.andromda.cartridges.jsf2.JSFGlobals;
014import org.andromda.cartridges.jsf2.JSFProfile;
015import org.andromda.cartridges.jsf2.JSFUtils;
016import org.andromda.metafacades.uml.AssociationEndFacade;
017import org.andromda.metafacades.uml.AttributeFacade;
018import org.andromda.metafacades.uml.ClassifierFacade;
019import org.andromda.metafacades.uml.EventFacade;
020import org.andromda.metafacades.uml.FrontEndAction;
021import org.andromda.metafacades.uml.FrontEndActivityGraph;
022import org.andromda.metafacades.uml.FrontEndForward;
023import org.andromda.metafacades.uml.FrontEndParameter;
024import org.andromda.metafacades.uml.FrontEndView;
025import org.andromda.metafacades.uml.ModelElementFacade;
026import org.andromda.metafacades.uml.TransitionFacade;
027import org.andromda.metafacades.uml.UseCaseFacade;
028import org.andromda.utils.StringUtilsHelper;
029import org.apache.commons.collections.CollectionUtils;
030import org.apache.commons.collections.Predicate;
031import org.apache.commons.lang.ObjectUtils;
032import org.apache.commons.lang.StringUtils;
033
034/**
035 * MetafacadeLogic implementation for org.andromda.cartridges.jsf2.metafacades.JSFParameter.
036 *
037 * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter
038 */
039public class JSFParameterLogicImpl
040    extends JSFParameterLogic
041{
042    private static final long serialVersionUID = 34L;
043    /**
044     * @param metaObject
045     * @param context
046     */
047    public JSFParameterLogicImpl(
048        Object metaObject,
049        String context)
050    {
051        super(metaObject, context);
052    }
053
054    /**
055     * Overridden to make sure it's not an inputTable.
056     *
057     * @see org.andromda.metafacades.uml.FrontEndParameter#isTable()
058     */
059    public boolean isTable()
060    {
061        return (super.isTable() || this.isPageableTable()) && !this.isSelectable()
062            && !this.isInputTable() && !this.isInputHidden();
063    }
064
065    /**
066     * @return isPageableTable
067     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#isPageableTable()
068     */
069    protected boolean handleIsPageableTable()
070    {
071        final Object value = this.findTaggedValue(JSFProfile.TAGGEDVALUE_TABLE_PAGEABLE);
072        return Boolean.valueOf(ObjectUtils.toString(value)).booleanValue();
073    }
074
075    /**
076     * @return messageKey
077     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getMessageKey()
078     */
079    protected String handleGetMessageKey()
080    {
081        final StringBuilder messageKey = new StringBuilder();
082
083        if (!this.isNormalizeMessages())
084        {
085            if (this.isActionParameter())
086            {
087                final JSFAction action = (JSFAction)this.getAction();
088                if (action != null)
089                {
090                    messageKey.append(action.getMessageKey());
091                    messageKey.append('.');
092                }
093            }
094            else
095            {
096                final JSFView view = (JSFView)this.getView();
097                if (view != null)
098                {
099                    messageKey.append(view.getMessageKey());
100                    messageKey.append('.');
101                }
102            }
103            messageKey.append("param.");
104        }
105
106        messageKey.append(StringUtilsHelper.toResourceMessageKey(super.getName()));
107        return messageKey.toString();
108    }
109
110    /**
111     * @return getMessageKey() + '.' + JSFGlobals.DOCUMENTATION_MESSAGE_KEY_SUFFIX
112     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getDocumentationKey()
113     */
114    protected String handleGetDocumentationKey()
115    {
116        return getMessageKey() + '.' + JSFGlobals.DOCUMENTATION_MESSAGE_KEY_SUFFIX;
117    }
118
119    /**
120     * @return documentationValue
121     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getDocumentationValue()
122     */
123    protected String handleGetDocumentationValue()
124    {
125        final String value = StringUtilsHelper.toResourceMessage(this.getDocumentation(
126                    "",
127                    64,
128                    false));
129        return value == null ? "" : value;
130    }
131
132    /**
133     * Indicates whether or not we should normalize messages.
134     *
135     * @return true/false
136     */
137    private boolean isNormalizeMessages()
138    {
139        final String normalizeMessages = (String)getConfiguredProperty(JSFGlobals.NORMALIZE_MESSAGES);
140        return Boolean.valueOf(normalizeMessages).booleanValue();
141    }
142
143    /**
144     * @return messageValue
145     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getMessageValue()
146     */
147    protected String handleGetMessageValue()
148    {
149        return StringUtilsHelper.toPhrase(super.getName()); // the actual name is used for displaying
150    }
151
152    /**
153     * @param columnName
154     * @return tableColumnMessageKey
155     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getTableColumnMessageKey(String)
156     */
157    protected String handleGetTableColumnMessageKey(final String columnName)
158    {
159        StringBuilder messageKey = new StringBuilder();
160        if (!this.isNormalizeMessages())
161        {
162            final JSFView view = (JSFView)this.getView();
163            if (view != null)
164            {
165                messageKey.append(this.getMessageKey());
166                messageKey.append('.');
167            }
168        }
169        messageKey.append(StringUtilsHelper.toResourceMessageKey(columnName));
170        return messageKey.toString();
171    }
172
173    /**
174     * @param columnName
175     * @return StringUtilsHelper.toPhrase(columnName)
176     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getTableColumnMessageValue(String)
177     */
178    protected String handleGetTableColumnMessageValue(final String columnName)
179    {
180        return StringUtilsHelper.toPhrase(columnName);
181    }
182
183    /**
184     * @return getTableActions(true)
185     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getTableHyperlinkActions()
186     */
187    protected List<JSFAction> handleGetTableHyperlinkActions()
188    {
189        return this.getTableActions(true);
190    }
191
192    private class ActionFilter implements Predicate
193    {
194        final private boolean hyperlink;
195        public ActionFilter(boolean hyperlink)
196        {
197            this.hyperlink = hyperlink;
198        }
199        
200        @Override
201        public boolean evaluate(Object action) 
202        {
203            return ((JSFAction)action).isHyperlink() == this.hyperlink;
204        }
205    }
206    
207    /**
208     * If this is a table this method returns all those actions that are declared to work
209     * on this table.
210     *
211     * @param hyperlink denotes on which type of actions to filter
212     */
213    private List<JSFAction> getTableActions(boolean hyperlink)
214    {
215        final List<JSFAction> actions = new ArrayList<JSFAction>(super.getTableActions());
216        CollectionUtils.filter(actions, new ActionFilter(hyperlink));
217        return actions;
218    }
219
220    /**
221     * @return getTableActions(false)
222     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getTableFormActions()
223     */
224    protected List<JSFAction> handleGetTableFormActions()
225    {
226        return this.getTableActions(false);
227    }
228
229    /**
230     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getTableActions()
231     */
232    protected List<JSFAction> handleGetTableActions() {
233        final Set<JSFAction> actions = new LinkedHashSet<JSFAction>();
234        final String name = StringUtils.trimToNull(getName());
235        if (name != null && isTable())
236        {
237            final JSFView view = (JSFView)this.getView();
238
239            final Collection<UseCaseFacade> allUseCases = getModel().getAllUseCases();
240            for (final UseCaseFacade useCase : allUseCases)
241            {
242                if (useCase instanceof JSFUseCase)
243                {
244                    final FrontEndActivityGraph graph = ((JSFUseCase)useCase).getActivityGraph();
245                    if (graph != null)
246                    {
247                        final Collection<TransitionFacade> transitions = graph.getTransitions();
248                        for (final TransitionFacade transition : transitions)
249                        {
250                            if (transition.getSource().equals(view) && transition instanceof JSFAction)
251                            {
252                                final JSFAction action = (JSFAction)transition;
253                                if (action.isTableLink() && name.equals(action.getTableLinkName()))
254                                {
255                                    actions.add(action);
256                                }
257                            }
258                        }
259                    }
260                }
261            }
262        }
263        return new ArrayList<JSFAction>(actions);
264    }
265
266    /**
267     * @see org.andromda.metafacades.uml.FrontEndParameter#getTableColumns()
268     */
269    // TODO tableColumns can be either String or JSFParameter. Should use a single return type in Collection.
270    public Collection getTableColumns()
271    {
272        final Collection tableColumns = super.getTableColumns();
273        if (tableColumns.isEmpty())
274        {
275            // try to preserve the order of the elements encountered
276            //final Map<String, JSFParameter> tableColumnsMap = new LinkedHashMap<String, JSFParameter>();
277            final Map tableColumnsMap = new LinkedHashMap();
278
279            // order is important
280            final List<JSFAction> actions = new ArrayList<JSFAction>();
281
282            // all table actions need the exact same parameters, just not always all of them
283            actions.addAll(this.getTableFormActions());
284
285            // if there are any actions that are hyperlinks then their parameters get priority
286            // the user should not have modeled it that way (constraints will warn him/her)
287            actions.addAll(this.getTableHyperlinkActions());
288
289            for (final JSFAction action : actions)
290            {
291                for (final FrontEndParameter actionParameter : action.getParameters())
292                {
293                    if (actionParameter instanceof JSFParameter)
294                    {
295                        final JSFParameter parameter = (JSFParameter)actionParameter;
296                        final String parameterName = parameter.getName();
297                        if (parameterName != null)
298                        {
299                            // never overwrite column specific table links
300                            // the hyperlink table links working on a real column get priority
301                            final Object existingObject = tableColumnsMap.get(parameterName);
302                            if (existingObject instanceof JSFParameter)
303                            {
304                                if (action.isHyperlink() && parameterName.equals(action.getTableLinkColumnName()))
305                                {
306                                    tableColumnsMap.put(
307                                        parameterName,
308                                        parameter);
309                                }
310                            }
311                        }
312                    }
313                }
314            }
315
316            // for any missing parameters we just add the name of the column
317            for (final String columnName : this.getTableColumnNames())
318            {
319                if (!tableColumnsMap.containsKey(columnName))
320                {
321                    tableColumnsMap.put(
322                        columnName,
323                        columnName);
324                }
325            }
326
327            // return everything in the same order as it has been modeled (using the table tagged value)
328            for (final String columnObject : this.getTableColumnNames())
329            {
330                tableColumns.add(tableColumnsMap.get(columnObject));
331            }
332        }
333        return tableColumns;
334    }
335
336    /**
337     * @return the default date format pattern as defined using the configured property
338     */
339    private String getDefaultDateFormat()
340    {
341        return (String)this.getConfiguredProperty(JSFGlobals.PROPERTY_DEFAULT_DATEFORMAT);
342    }
343
344    /**
345     * @return format
346     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getFormat()
347     */
348    protected String handleGetFormat()
349    {
350        return JSFUtils.getFormat(
351            (ModelElementFacade)this.THIS(),
352            this.getType(),
353            this.getDefaultDateFormat(),
354            this.getDefaultTimeFormat());
355    }
356
357    /**
358     * @return the default time format pattern as defined using the configured property
359     */
360    private String getDefaultTimeFormat()
361    {
362        return (String)this.getConfiguredProperty(JSFGlobals.PROPERTY_DEFAULT_TIMEFORMAT);
363    }
364
365    /**
366     * @return JSFUtils.isStrictDateFormat((ModelElementFacade)this.THIS())
367     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#isStrictDateFormat()
368     */
369    protected boolean handleIsStrictDateFormat()
370    {
371        return JSFUtils.isStrictDateFormat((ModelElementFacade)this.THIS());
372    }
373
374    /**
375     * @return dateFormatter
376     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getDateFormatter()
377     */
378    protected String handleGetDateFormatter()
379    {
380        final ClassifierFacade type = this.getType();
381        return type != null && type.isDateType() ? this.getName() + "DateFormatter" : null;
382    }
383
384    /**
385     * @return timeFormatter
386     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getTimeFormatter()
387     */
388    protected String handleGetTimeFormatter()
389    {
390        final ClassifierFacade type = this.getType();
391        return type != null && type.isTimeType() ? this.getName() + "TimeFormatter" : null;
392    }
393
394    /**
395     * Gets the current value of the specified input type (or an empty string
396     * if one isn't specified).
397     *
398     * @return the input type name.
399     */
400    private String getInputType()
401    {
402        return ObjectUtils.toString(this.findTaggedValue(JSFProfile.TAGGEDVALUE_INPUT_TYPE)).trim();
403    }
404
405    /**
406     * Indicates whether or not this parameter is of the given input type.
407     *
408     * @param inputType the name of the input type to check for.
409     * @return true/false
410     */
411    private boolean isInputType(final String inputType)
412    {
413        return inputType.equalsIgnoreCase(this.getInputType());
414    }
415
416    /**
417     * @return isInputType(JSFGlobals.INPUT_TEXTAREA)
418     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#isInputTextarea()
419     */
420    protected boolean handleIsInputTextarea()
421    {
422        return this.isInputType(JSFGlobals.INPUT_TEXTAREA);
423    }
424
425    /**
426     * @return isInputType(JSFGlobals.INPUT_SELECT)
427     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#isInputSelect()
428     */
429    protected boolean handleIsInputSelect()
430    {
431        return this.isInputType(JSFGlobals.INPUT_SELECT);
432    }
433
434    /**
435     * @return isInputType(JSFGlobals.INPUT_PASSWORD)
436     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#isInputSecret()
437     */
438    protected boolean handleIsInputSecret()
439    {
440        return this.isInputType(JSFGlobals.INPUT_PASSWORD);
441    }
442
443    /**
444     * @return isInputType(JSFGlobals.INPUT_HIDDEN)
445     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#isInputHidden()
446     */
447    protected boolean handleIsInputHidden()
448    {
449        return this.isInputType(JSFGlobals.INPUT_HIDDEN);
450    }
451
452    /**
453     * @return isInputType(JSFGlobals.PLAIN_TEXT)
454     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#isPlaintext()
455     */
456    protected boolean handleIsPlaintext()
457    {
458        return this.isInputType(JSFGlobals.PLAIN_TEXT);
459    }
460
461    /**
462     * @return isInputTable
463     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#isInputTable()
464     */
465    protected boolean handleIsInputTable()
466    {
467        return this.getInputTableIdentifierColumns().length() > 0 || this.isInputType(JSFGlobals.INPUT_TABLE);
468    }
469
470    /**
471     * @return isInputType(JSFGlobals.INPUT_RADIO)
472     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#isInputRadio()
473     */
474    protected boolean handleIsInputRadio()
475    {
476        return this.isInputType(JSFGlobals.INPUT_RADIO);
477    }
478
479    /**
480     * @return isInputType(JSFGlobals.INPUT_TEXT)
481     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#isInputText()
482     */
483    protected boolean handleIsInputText()
484    {
485        return this.isInputType(JSFGlobals.INPUT_TEXT);
486    }
487
488    /**
489     * @return isInputType(JSFGlobals.INPUT_MULTIBOX)
490     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#isInputMultibox()
491     */
492    protected boolean handleIsInputMultibox()
493    {
494        return this.isInputType(JSFGlobals.INPUT_MULTIBOX);
495    }
496
497    /**
498     * @return isInputCheckbox
499     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#isInputCheckbox()
500     */
501    protected boolean handleIsInputCheckbox()
502    {
503        boolean checkbox = this.isInputType(JSFGlobals.INPUT_CHECKBOX);
504        if (!checkbox && this.getInputType().length() == 0)
505        {
506            final ClassifierFacade type = this.getType();
507            checkbox = type != null ? type.isBooleanType() : false;
508        }
509        return checkbox;
510    }
511
512    /**
513     * @return isInputFile
514     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#isInputFile()
515     */
516    protected boolean handleIsInputFile()
517    {
518        boolean file = false;
519        ClassifierFacade type = getType();
520        if (type != null)
521        {
522            file = type.isFileType();
523        }
524        return file;
525    }
526
527    /**
528     * @return backingListName
529     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getBackingListName()
530     */
531    protected String handleGetBackingListName()
532    {
533        return ObjectUtils.toString(this.getConfiguredProperty(JSFGlobals.BACKING_LIST_PATTERN)).replaceAll(
534            "\\{0\\}",
535            this.getName());
536    }
537
538    /**
539     * @return backingValueName
540     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getBackingValueName()
541     */
542    protected String handleGetBackingValueName()
543    {
544        return ObjectUtils.toString(this.getConfiguredProperty(JSFGlobals.BACKING_VALUE_PATTERN)).replaceAll(
545            "\\{0\\}",
546            this.getName());
547    }
548
549    /**
550     * @return valueListName
551     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getValueListName()
552     */
553    protected String handleGetValueListName()
554    {
555        return ObjectUtils.toString(this.getConfiguredProperty(JSFGlobals.VALUE_LIST_PATTERN)).replaceAll(
556            "\\{0\\}",
557            this.getName());
558    }
559
560    /**
561     * @return labelListName
562     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getLabelListName()
563     */
564    protected String handleGetLabelListName()
565    {
566        return ObjectUtils.toString(this.getConfiguredProperty(JSFGlobals.LABEL_LIST_PATTERN)).replaceAll(
567            "\\{0\\}",
568            this.getName());
569    }
570
571    /**
572     * @return isSelectable
573     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#isSelectable()
574     */
575    protected boolean handleIsSelectable()
576    {
577        boolean selectable = false;
578        if (this.isActionParameter())
579        {
580            selectable = this.isInputMultibox() || this.isInputSelect() || this.isInputRadio();
581            final ClassifierFacade type = this.getType();
582
583            if (!selectable && type != null)
584            {
585                final String name = this.getName();
586                final String typeName = type.getFullyQualifiedName();
587
588                // - if the parameter is not selectable but on a targetting page it IS selectable we must
589                //   allow the user to set the backing list too
590                final Collection<FrontEndView> views = this.getAction().getTargetViews();
591                for (final Iterator<FrontEndView> iterator = views.iterator(); iterator.hasNext() && !selectable;)
592                {
593                    final Collection<FrontEndParameter> parameters = iterator.next().getAllActionParameters();
594                    for (final Iterator<FrontEndParameter> parameterIterator = parameters.iterator();
595                        parameterIterator.hasNext() && !selectable;)
596                    {
597                        final Object object = parameterIterator.next();
598                        if (object instanceof JSFParameter)
599                        {
600                            final JSFParameter parameter = (JSFParameter)object;
601                            final String parameterName = parameter.getName();
602                            final ClassifierFacade parameterType = parameter.getType();
603                            if (parameterType != null)
604                            {
605                                final String parameterTypeName = parameterType.getFullyQualifiedName();
606                                if (name.equals(parameterName) && typeName.equals(parameterTypeName))
607                                {
608                                    selectable =
609                                        parameter.isInputMultibox() || parameter.isInputSelect() ||
610                                        parameter.isInputRadio();
611                                }
612                            }
613                        }
614                    }
615                }
616            }
617        }
618        else if (this.isControllerOperationArgument())
619        {
620            final String name = this.getName();
621            final Collection actions = this.getControllerOperation().getDeferringActions();
622            for (final Iterator actionIterator = actions.iterator(); actionIterator.hasNext();)
623            {
624                final JSFAction action = (JSFAction)actionIterator.next();
625                final Collection<FrontEndParameter>  formFields = action.getFormFields();
626                for (final Iterator<FrontEndParameter>  fieldIterator = formFields.iterator();
627                    fieldIterator.hasNext() && !selectable;)
628                {
629                    final Object object = fieldIterator.next();
630                    if (object instanceof JSFParameter)
631                    {
632                        final JSFParameter parameter = (JSFParameter)object;
633                        if (!parameter.equals(this))
634                        {
635                            if (name.equals(parameter.getName()))
636                            {
637                                selectable = parameter.isSelectable();
638                            }
639                        }
640                    }
641                }
642            }
643        }
644        return selectable;
645    }
646
647    /**
648     * Stores the initial value of each type.
649     */
650    private final Map<String, String> initialValues = new HashMap<String, String>();
651
652    /**
653     * @return constructDummyArray()
654     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getValueListDummyValue()
655     */
656    protected String handleGetValueListDummyValue()
657    {
658        return this.constructDummyArray();
659    }
660
661    /**
662     * @return dummyValue
663     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getDummyValue()
664     */
665    protected String handleGetDummyValue()
666    {
667        final ClassifierFacade type = this.getType();
668        final String typeName = type != null ? type.getFullyQualifiedName() : "";
669        String initialValue = null;
670        if (type != null)
671        {
672            if (type.isSetType())
673            {
674                initialValue =
675                    "new java.util.LinkedHashSet(java.util.Arrays.asList(" + this.constructDummyArray() + "))";
676            }
677            else if (type.isCollectionType())
678            {
679                initialValue = "java.util.Arrays.asList(" + this.constructDummyArray() + ")";
680            }
681            else if (type.isArrayType())
682            {
683                initialValue = this.constructDummyArray();
684            }
685            final String name = this.getName() != null ? this.getName() : "";
686            if (this.initialValues.isEmpty())
687            {
688                initialValues.put(
689                    boolean.class.getName(),
690                    "false");
691                initialValues.put(
692                    int.class.getName(),
693                    "(int)" + name.hashCode());
694                initialValues.put(
695                    long.class.getName(),
696                    "(long)" + name.hashCode());
697                initialValues.put(
698                    short.class.getName(),
699                    "(short)" + name.hashCode());
700                initialValues.put(
701                    byte.class.getName(),
702                    "(byte)" + name.hashCode());
703                initialValues.put(
704                    float.class.getName(),
705                    "(float)" + name.hashCode());
706                initialValues.put(
707                    double.class.getName(),
708                    "(double)" + name.hashCode());
709                initialValues.put(
710                    char.class.getName(),
711                    "(char)" + name.hashCode());
712
713                initialValues.put(
714                    String.class.getName(),
715                    "\"" + name + "-test" + "\"");
716                initialValues.put(
717                    java.util.Date.class.getName(),
718                    "new java.util.Date()");
719                initialValues.put(
720                    java.sql.Date.class.getName(),
721                    "new java.util.Date()");
722                initialValues.put(
723                    java.sql.Timestamp.class.getName(),
724                    "new java.util.Date()");
725
726                initialValues.put(
727                    Integer.class.getName(),
728                    "new Integer((int)" + name.hashCode() + ")");
729                initialValues.put(
730                    Boolean.class.getName(),
731                    "Boolean.FALSE");
732                initialValues.put(
733                    Long.class.getName(),
734                    "new Long((long)" + name.hashCode() + ")");
735                initialValues.put(
736                    Character.class.getName(),
737                    "new Character(char)" + name.hashCode() + ")");
738                initialValues.put(
739                    Float.class.getName(),
740                    "new Float((float)" + name.hashCode() / hashCode() + ")");
741                initialValues.put(
742                    Double.class.getName(),
743                    "new Double((double)" + name.hashCode() / hashCode() + ")");
744                initialValues.put(
745                    Short.class.getName(),
746                    "new Short((short)" + name.hashCode() + ")");
747                initialValues.put(
748                    Byte.class.getName(),
749                    "new Byte((byte)" + name.hashCode() + ")");
750            }
751            if (initialValue == null)
752            {
753                initialValue = this.initialValues.get(typeName);
754            }
755        }
756        if (initialValue == null)
757        {
758            initialValue = "null";
759        }
760        return initialValue;
761    }
762
763    /**
764     * Constructs a string representing an array initialization in Java.
765     *
766     * @return A String representing Java code for the initialization of an array.
767     */
768    private String constructDummyArray()
769    {
770        return JSFUtils.constructDummyArrayDeclaration(
771            this.getName(),
772            JSFGlobals.DUMMY_ARRAY_COUNT);
773    }
774
775    /**
776     * @return getName() + "SortColumn"
777     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getTableSortColumnProperty()
778     */
779    protected String handleGetTableSortColumnProperty()
780    {
781        return this.getName() + "SortColumn";
782    }
783
784    /**
785     * @return getName() + "SortAscending"
786     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getTableSortAscendingProperty()
787     */
788    protected String handleGetTableSortAscendingProperty()
789    {
790        return this.getName() + "SortAscending";
791    }
792
793    /**
794     * @return getName() + "Set"
795     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getFormAttributeSetProperty()
796     */
797    protected String handleGetFormAttributeSetProperty()
798    {
799        return this.getName() + "Set";
800    }
801
802    //TODO remove after 3.4 release
803    /**
804     * Hack to keep the compatibility with Andromda 3.4-SNAPSHOT
805     */
806    /**
807     * @see org.andromda.metafacades.uml.FrontEndParameter#getView()
808     */
809    public FrontEndView getView()
810    {
811        Object view = null;
812        final EventFacade event = this.getEvent();
813        if (event != null)
814        {
815            final TransitionFacade transition = event.getTransition();
816            if (transition instanceof JSFActionLogicImpl)
817            {
818                final JSFActionLogicImpl action = (JSFActionLogicImpl)transition;
819                view = action.getInput();
820            }
821            else if (transition instanceof FrontEndForward)
822            {
823                final FrontEndForward forward = (FrontEndForward)transition;
824                if (forward.isEnteringView())
825                {
826                    view = forward.getTarget();
827                }
828            }
829        }
830        return (FrontEndView)view;
831    }
832
833    /**
834     * @return validationRequired
835     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#isValidationRequired()
836     */
837    protected boolean handleIsValidationRequired()
838    {
839        boolean required = !this.getValidatorTypes().isEmpty();
840        if (!required)
841        {
842            // - look for any attributes
843            for (final Iterator<JSFAttribute> iterator = this.getAttributes().iterator(); iterator.hasNext();)
844            {
845                required = !iterator.next().getValidatorTypes().isEmpty();
846                if (required)
847                {
848                    break;
849                }
850            }
851
852            // - look for any table columns
853            if (!required)
854            {
855                for (final Iterator iterator = this.getTableColumns().iterator(); iterator.hasNext();)
856                {
857                    final Object object = iterator.next();
858                    if (object instanceof JSFAttribute)
859                    {
860                        final JSFAttribute attribute = (JSFAttribute)object;
861                        required = !attribute.getValidatorTypes().isEmpty();
862                        if (required)
863                        {
864                            break;
865                        }
866                    }
867                }
868            }
869        }
870        return required;
871    }
872
873    /**
874     * @return validatorTypes
875     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getValidatorTypes()
876     */
877    protected Collection handleGetValidatorTypes()
878    {
879        return JSFUtils.getValidatorTypes(
880            (ModelElementFacade)this.THIS(),
881            this.getType());
882    }
883
884    /**
885     * @return JSFUtils.getValidWhen(this)
886     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getValidWhen()
887     */
888    protected String handleGetValidWhen()
889    {
890        return JSFUtils.getValidWhen(this);
891    }
892
893    /**
894     * Overridden to have the same behavior as bpm4struts.
895     *
896     * @see org.andromda.metafacades.uml.ParameterFacade#isRequired()
897     */
898    public boolean isRequired()
899    {
900        if("org.omg.uml.foundation.core".equals(metaObject.getClass().getPackage().getName()))
901        {
902            //if uml 1.4, keep the old behavior (like bpm4struts)
903            final Object value = this.findTaggedValue(JSFProfile.TAGGEDVALUE_INPUT_REQUIRED);
904            return Boolean.valueOf(ObjectUtils.toString(value)).booleanValue();
905        }
906        else
907        {
908            //if >= uml 2, default behavior
909            return super.isRequired();
910        }
911    }
912
913    /**
914     * @return JSFUtils.isReadOnly(this)
915     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#isReadOnly()
916     */
917    protected boolean handleIsReadOnly()
918    {
919        return JSFUtils.isReadOnly(this);
920    }
921
922    /**
923     * @param validatorType
924     * @return validatorArgs
925     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getValidatorArgs(String)
926     */
927    protected Collection handleGetValidatorArgs(final String validatorType)
928    {
929        return JSFUtils.getValidatorArgs(
930            (ModelElementFacade)this.THIS(),
931            validatorType);
932    }
933
934    /**
935     * @return validatorVars
936     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getValidatorVars()
937     */
938    protected Collection handleGetValidatorVars()
939    {
940        return JSFUtils.getValidatorVars(
941            (ModelElementFacade)this.THIS(),
942            this.getType(),
943            null);
944    }
945
946    /**
947     * @return reset
948     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#isReset()
949     */
950    protected boolean handleIsReset()
951    {
952        boolean reset =
953            Boolean.valueOf(ObjectUtils.toString(this.findTaggedValue(JSFProfile.TAGGEDVALUE_INPUT_RESET)))
954                   .booleanValue();
955        if (!reset)
956        {
957            final JSFAction action = (JSFAction)this.getAction();
958            reset = action != null && action.isFormReset();
959        }
960        return reset;
961    }
962
963    /**
964     * @return complex
965     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#isComplex()
966     */
967    protected boolean handleIsComplex()
968    {
969        boolean complex = false;
970        final ClassifierFacade type = this.getType();
971        if (type != null)
972        {
973            complex = !type.getAttributes().isEmpty();
974            if (!complex)
975            {
976                complex = !type.getAssociationEnds().isEmpty();
977            }
978        }
979        return complex;
980    }
981
982    /**
983     * @return attributes
984     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getAttributes()
985     */
986    protected Collection<AttributeFacade> handleGetAttributes()
987    {
988        Collection<AttributeFacade> attributes = null;
989        ClassifierFacade type = this.getType();
990        if (type != null)
991        {
992            if (type.isArrayType())
993            {
994                type = type.getNonArray();
995            }
996            if (type != null)
997            {
998                attributes = type.getAttributes(true);
999            }
1000        }
1001        return attributes == null ? new ArrayList<AttributeFacade>() : attributes;
1002    }
1003
1004    /**
1005     * @return navigableAssociationEnds
1006     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getNavigableAssociationEnds()
1007     */
1008    @Override
1009    protected Collection<AssociationEndFacade> handleGetNavigableAssociationEnds()
1010    {
1011        Collection<AssociationEndFacade> associationEnds = null;
1012        ClassifierFacade type = this.getType();
1013        if (type != null)
1014        {
1015            if (type.isArrayType())
1016            {
1017                type = type.getNonArray();
1018            }
1019            if (type != null)
1020            {
1021                associationEnds = type.getNavigableConnectingEnds();
1022            }
1023        }
1024        return associationEnds == null ? new ArrayList<AssociationEndFacade>() : associationEnds;
1025    }
1026
1027    /**
1028     * @return isEqualValidator
1029     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#isEqualValidator()
1030     */
1031    protected boolean handleIsEqualValidator()
1032    {
1033        final String equal = JSFUtils.getEqual((ModelElementFacade)this.THIS());
1034        return equal != null && equal.trim().length() > 0;
1035    }
1036
1037    /**
1038     * @return isBackingValueRequired
1039     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#isEqualValidator()
1040     */
1041    protected boolean handleIsBackingValueRequired()
1042    {
1043        boolean required = false;
1044        if (this.isActionParameter())
1045        {
1046            required = this.isInputTable();
1047            final ClassifierFacade type = this.getType();
1048
1049            if (!required && type != null)
1050            {
1051                final String name = this.getName();
1052                final String typeName = type.getFullyQualifiedName();
1053
1054                // - if the backing value is not required for this parameter but on
1055                //   a targeting page it IS selectable we must allow the user to set the backing value as well
1056                final Collection<FrontEndView> views = this.getAction().getTargetViews();
1057                for (final Iterator<FrontEndView> iterator = views.iterator(); iterator.hasNext() && !required;)
1058                {
1059                    final Collection<FrontEndParameter> parameters = iterator.next().getAllActionParameters();
1060                    for (final Iterator<FrontEndParameter> parameterIterator = parameters.iterator();
1061                        parameterIterator.hasNext() && !required;)
1062                    {
1063                        final FrontEndParameter object = parameterIterator.next();
1064                        if (object instanceof JSFParameter)
1065                        {
1066                            final JSFParameter parameter = (JSFParameter)object;
1067                            final String parameterName = parameter.getName();
1068                            final ClassifierFacade parameterType = parameter.getType();
1069                            if (parameterType != null)
1070                            {
1071                                final String parameterTypeName = parameterType.getFullyQualifiedName();
1072                                if (name.equals(parameterName) && typeName.equals(parameterTypeName))
1073                                {
1074                                    required = parameter.isInputTable();
1075                                }
1076                            }
1077                        }
1078                    }
1079                }
1080            }
1081        }
1082        else if (this.isControllerOperationArgument())
1083        {
1084            final String name = this.getName();
1085            final Collection<FrontEndAction> actions = this.getControllerOperation().getDeferringActions();
1086            for (final Iterator<FrontEndAction> actionIterator = actions.iterator(); actionIterator.hasNext();)
1087            {
1088                final JSFAction action = (JSFAction)actionIterator.next();
1089                final Collection<FrontEndParameter> formFields = action.getFormFields();
1090                for (final Iterator<FrontEndParameter> fieldIterator = formFields.iterator();
1091                    fieldIterator.hasNext() && !required;)
1092                {
1093                    final Object object = fieldIterator.next();
1094                    if (object instanceof JSFParameter)
1095                    {
1096                        final JSFParameter parameter = (JSFParameter)object;
1097                        if (!parameter.equals(this))
1098                        {
1099                            if (name.equals(parameter.getName()))
1100                            {
1101                                required = parameter.isBackingValueRequired();
1102                            }
1103                        }
1104                    }
1105                }
1106            }
1107        }
1108        return required;
1109    }
1110
1111    /**
1112     * @return findTaggedValue(JSFProfile.TAGGEDVALUE_INPUT_TABLE_IDENTIFIER_COLUMNS)
1113     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getInputTableIdentifierColumns()
1114     */
1115    protected String handleGetInputTableIdentifierColumns()
1116    {
1117        return ObjectUtils.toString(this.findTaggedValue(JSFProfile.TAGGEDVALUE_INPUT_TABLE_IDENTIFIER_COLUMNS)).trim();
1118    }
1119
1120    /**
1121     * @param columnName
1122     * @return tableColumnActions
1123     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getTableColumnActions(String)
1124     */
1125    protected List<JSFAction> handleGetTableColumnActions(final String columnName)
1126    {
1127        final List<JSFAction> columnActions = new ArrayList<JSFAction>();
1128
1129        if (columnName != null)
1130        {
1131            final Set<JSFAction> actions = new LinkedHashSet<JSFAction>(this.getTableHyperlinkActions());
1132            actions.addAll(this.getTableFormActions());
1133            for (final JSFAction action : actions)
1134            {
1135                if (columnName.equals(action.getTableLinkColumnName()))
1136                {
1137                    columnActions.add(action);
1138                }
1139            }
1140        }
1141
1142        return columnActions;
1143    }
1144
1145    /**
1146     * @return maxLength
1147     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getMaxLength()
1148     */
1149    protected String handleGetMaxLength()
1150    {
1151        final Collection<Collection> vars=getValidatorVars();
1152        if(vars == null)
1153        {
1154            return null;
1155        }
1156        for(Iterator<Collection> it=vars.iterator(); it.hasNext();)
1157        {
1158            final Object[] values=(it.next()).toArray();
1159            if("maxlength".equals(values[0]))
1160            {
1161                return values[1].toString();
1162            }
1163        }
1164        return null;
1165    }
1166
1167    //to be used in the range validator: "range - 1000" or "range 20 -".
1168    /** - */
1169    static final String UNDEFINED_BOUND="-";
1170    /** javax.validation.constraints.NotNull */
1171    static final String AN_REQUIRED = "@javax.validation.constraints.NotNull";
1172    /** org.hibernate.validator.constraints.URL */
1173    static final String AN_URL = "@org.hibernate.validator.constraints.URL";
1174    /** org.apache.myfaces.extensions.validator.baseval.annotation.LongRange */
1175    static final String AN_LONG_RANGE = "@org.apache.myfaces.extensions.validator.baseval.annotation.LongRange";
1176    /** org.apache.myfaces.extensions.validator.baseval.annotation.DoubleRange */
1177    static final String AN_DOUBLE_RANGE = "@org.apache.myfaces.extensions.validator.baseval.annotation.DoubleRange";
1178    /** org.hibernate.validator.constraints.Email */
1179    static final String AN_EMAIL = "@org.hibernate.validator.constraints.Email";
1180    /** org.hibernate.validator.constraints.CreditCardNumber */
1181    static final String AN_CREDIT_CARD = "@org.hibernate.validator.constraints.CreditCardNumber";
1182    /** javax.validation.constraints.Size */
1183    static final String AN_LENGTH = "@javax.validation.constraints.Size";
1184    /** org.apache.myfaces.extensions.validator.baseval.annotation.Pattern */
1185    static final String AN_PATTERN = "@org.apache.myfaces.extensions.validator.baseval.annotation.Pattern";
1186    /** org.apache.myfaces.extensions.validator.crossval.annotation.Equals */
1187    static final String AN_EQUALS = "@org.apache.myfaces.extensions.validator.crossval.annotation.Equals";
1188
1189    /**
1190     * @return the annotations
1191     * @see org.andromda.cartridges.jsf2.metafacades.JSFParameter#getMaxLength()
1192     */
1193    @Override
1194    protected Collection<String> handleGetAnnotations()
1195    {
1196        final Collection<String> result=new HashSet<String>();
1197        boolean requiredAdded=false;
1198        for(String vt: (Collection<String>)getValidatorTypes())
1199        {
1200            if(vt.startsWith("@")) //add the annotation
1201            {
1202                result.add(vt);
1203            }
1204            if(JSFUtils.VT_REQUIRED.equals(vt))
1205            {
1206                requiredAdded=true;
1207                result.add(AN_REQUIRED);
1208            }
1209            else if(JSFUtils.VT_URL.equals(vt))
1210            {
1211                result.add(AN_URL);
1212            }
1213            else if(JSFUtils.VT_INT_RANGE.equals(vt))
1214            {
1215                final StringBuilder sb=new StringBuilder(AN_LONG_RANGE+"(");
1216                final String format = JSFUtils.getInputFormat((ModelElementFacade)this.THIS());
1217                final String rangeStart = JSFUtils.getRangeStart(format);
1218                boolean addComma=false;
1219                if(StringUtils.isNotBlank(rangeStart) && !rangeStart.equals(UNDEFINED_BOUND))
1220                {
1221                    sb.append("minimum="+rangeStart);
1222                    addComma=true;
1223                }
1224                final String rangeEnd = JSFUtils.getRangeEnd(format);
1225                if(StringUtils.isNotBlank(rangeEnd) && !rangeEnd.equals(UNDEFINED_BOUND))
1226                {
1227                    if(addComma)
1228                    {
1229                        sb.append(",");
1230                    }
1231                    sb.append("maximum="+rangeEnd);
1232                }
1233                sb.append(")");
1234                result.add(sb.toString());
1235            }
1236            else if(JSFUtils.VT_FLOAT_RANGE.equals(vt) || JSFUtils.VT_DOUBLE_RANGE.equals(vt))
1237            {
1238                final StringBuilder sb=new StringBuilder(AN_DOUBLE_RANGE+"(");
1239                final String format = JSFUtils.getInputFormat(((ModelElementFacade)this.THIS()));
1240                final String rangeStart = JSFUtils.getRangeStart(format);
1241                boolean addComma=false;
1242                if(StringUtils.isNotBlank(rangeStart) && !rangeStart.equals(UNDEFINED_BOUND))
1243                {
1244                    sb.append("minimum="+rangeStart);
1245                    addComma=true;
1246                }
1247                final String rangeEnd = JSFUtils.getRangeEnd(format);
1248                if(StringUtils.isNotBlank(rangeEnd) && !rangeEnd.equals(UNDEFINED_BOUND))
1249                {
1250                    if(addComma)
1251                    {
1252                        sb.append(",");
1253                    }
1254                    sb.append("maximum="+rangeEnd);
1255                }
1256                sb.append(")");
1257                result.add(sb.toString());
1258            }
1259            else if(JSFUtils.VT_EMAIL.equals(vt))
1260            {
1261                result.add(AN_EMAIL);
1262            }
1263            else if(JSFUtils.VT_CREDIT_CARD.equals(vt))
1264            {
1265                result.add(AN_CREDIT_CARD);
1266            }
1267            else if(JSFUtils.VT_MIN_LENGTH.equals(vt) || JSFUtils.VT_MAX_LENGTH.equals(vt))
1268            {
1269                final StringBuilder sb=new StringBuilder(AN_LENGTH+"(");
1270                final Collection formats = this.findTaggedValues(JSFProfile.TAGGEDVALUE_INPUT_FORMAT);
1271                boolean addComma=false;
1272                for (final Iterator formatIterator = formats.iterator(); formatIterator.hasNext();)
1273                {
1274                    final String additionalFormat = String.valueOf(formatIterator.next());
1275                    if (JSFUtils.isMinLengthFormat(additionalFormat))
1276                    {
1277                        if(addComma)
1278                        {
1279                            sb.append(",");
1280                        }
1281                        sb.append("min=");
1282                        sb.append(JSFUtils.getMinLengthValue(additionalFormat));
1283                        addComma=true;
1284                    }
1285                    else if (JSFUtils.isMaxLengthFormat(additionalFormat))
1286                    {
1287                        if(addComma)
1288                        {
1289                            sb.append(",");
1290                        }
1291                        sb.append("max=");
1292                        sb.append(JSFUtils.getMinLengthValue(additionalFormat));
1293                        addComma=true;
1294                    }
1295                }
1296                sb.append(")");
1297                result.add(sb.toString());
1298            }
1299            else if(JSFUtils.VT_MASK.equals(vt))
1300            {
1301                final Collection formats = this.findTaggedValues(JSFProfile.TAGGEDVALUE_INPUT_FORMAT);
1302                for (final Iterator formatIterator = formats.iterator(); formatIterator.hasNext();)
1303                {
1304                    final String additionalFormat = String.valueOf(formatIterator.next());
1305                    if (JSFUtils.isPatternFormat(additionalFormat))
1306                    {
1307                        result.add(AN_PATTERN+"(\""+JSFUtils.getPatternValue(additionalFormat)+"\")");
1308                    }
1309                }
1310            }
1311            else if(JSFUtils.VT_VALID_WHEN.equals(vt))
1312            {
1313                result.add("");
1314            }
1315            else if(JSFUtils.VT_EQUAL.equals(vt))
1316            {
1317                result.add(AN_EQUALS+"(\""+JSFUtils.getEqual((ModelElementFacade)this.THIS())+"\")");
1318            }
1319        }
1320        if(!requiredAdded && getLower() > 0)
1321        {
1322            result.add(AN_REQUIRED);
1323        }
1324        return result;
1325    }
1326}