001package org.andromda.metafacades.uml;
002
003import java.util.ArrayList;
004import java.util.Collection;
005import java.util.Date;
006import java.util.HashMap;
007import java.util.Iterator;
008import java.util.List;
009import java.util.Map;
010import java.util.regex.Matcher;
011import java.util.regex.Pattern;
012import org.andromda.core.metafacade.MetafacadeConstants;
013import org.apache.commons.collections.CollectionUtils;
014import org.apache.commons.collections.Predicate;
015import org.apache.commons.lang.BooleanUtils;
016import org.apache.commons.lang.ObjectUtils;
017import org.apache.commons.lang.StringUtils;
018import org.apache.commons.lang.time.FastDateFormat;
019import org.apache.log4j.Logger;
020
021/**
022 * Contains utilities that are common to the UML metafacades.
023 *
024 * @author Chad Brandon
025 * @author Bob Fields
026 */
027public class UMLMetafacadeUtils
028{
029    /**
030     * The logger instance.
031     */
032    private static final Logger logger = Logger.getLogger(UMLMetafacadeUtils.class);
033
034    /**
035     * Returns true or false depending on whether or not this Classifier or any of its specializations is of the given
036     * type having the specified <code>typeName</code>
037     * @param classifier
038     * @param typeName the name of the type (i.e. datatype::Collection)
039     * @return true/false
040     */
041    public static boolean isType(ClassifierFacade classifier, String typeName)
042    {
043        boolean isType = false;
044        if (classifier != null && typeName != null)
045        {
046            final String type = StringUtils.trimToEmpty(typeName);
047            String name = StringUtils.trimToEmpty(classifier.getFullyQualifiedName(true));
048            isType = name.equals(type);
049            // if this isn't a type defined by typeName, see if we can find any
050            // types that inherit from the type.
051            if (!isType)
052            {
053                isType = CollectionUtils.find(classifier.getAllGeneralizations(), new Predicate()
054                {
055                    public boolean evaluate(Object object)
056                    {
057                        String name = StringUtils.trimToEmpty(
058                                ((ModelElementFacade)object).getFullyQualifiedName(true));
059                        return name.equals(type);
060                    }
061                }) != null;
062            }
063            if (!isType)
064            {
065                // Match=true if the classifier name is in a different package than datatype::, i.e. PrimitiveTypes::
066                // or the name is the same. Allows using Java, UML Standard types instead of AndroMDA types
067                String lastType = typeName;
068                if (!StringUtils.isBlank(StringUtils.substringAfterLast(typeName, ":")))
069                {
070                    lastType = StringUtils.substringAfterLast(typeName, ":");
071                }
072                String classifierType = classifier.getFullyQualifiedName();
073                if (!StringUtils.isBlank(StringUtils.substringAfterLast(classifierType, ":")))
074                {
075                    classifierType = StringUtils.substringAfterLast(classifierType, ":");
076                }
077                // If FQN class name is the same as the mapped implementation Class Name
078                name = StringUtils.trimToEmpty(classifier.getFullyQualifiedName(true));
079                // IgnoreCase allows primitive and wrapped types to both return true
080                if (lastType.equalsIgnoreCase(classifierType)
081                    || lastType.equalsIgnoreCase(name) || lastType.equalsIgnoreCase(classifier.getFullyQualifiedName()))
082                {
083                    isType = true;
084                }
085            }
086        }
087        return isType;
088    }
089
090    // TODO: Move this to an external configuration. Distinguish between Java, C# reserved words.
091    private static List<String> reservedWords = new ArrayList<String>();
092    private static void populateReservedWords()
093    {
094        synchronized (reservedWords)
095        {
096            if (reservedWords.isEmpty())
097            {
098                reservedWords.add("abstract");
099                reservedWords.add("as");
100                reservedWords.add("assert");
101                reservedWords.add("auto");
102                reservedWords.add("bool");
103                reservedWords.add("boolean");
104                reservedWords.add("break");
105                reservedWords.add("byte");
106                reservedWords.add("case");
107                reservedWords.add("catch");
108                reservedWords.add("char");
109                reservedWords.add("checked");
110                reservedWords.add("class");
111                reservedWords.add("const");
112                reservedWords.add("continue");
113                reservedWords.add("decimal");
114                reservedWords.add("default");
115                reservedWords.add("delegate");
116                reservedWords.add("delete");
117                reservedWords.add("deprecated");
118                reservedWords.add("do");
119                reservedWords.add("double");
120                reservedWords.add("else");
121                reservedWords.add("enum");
122                reservedWords.add("event");
123                reservedWords.add("explicit");
124                reservedWords.add("export");
125                reservedWords.add("extends");
126                reservedWords.add("extern");
127                reservedWords.add("false");
128                reservedWords.add("final");
129                reservedWords.add("finally");
130                reservedWords.add("fixed");
131                reservedWords.add("float");
132                reservedWords.add("foreach");
133                reservedWords.add("for");
134                reservedWords.add("function");
135                reservedWords.add("goto");
136                reservedWords.add("if");
137                reservedWords.add("implements");
138                reservedWords.add("implicit");
139                reservedWords.add("import");
140                reservedWords.add("in");
141                reservedWords.add("inline");
142                reservedWords.add("instanceof");
143                reservedWords.add("int");
144                reservedWords.add("interface");
145                reservedWords.add("internal");
146                reservedWords.add("is");
147                reservedWords.add("lock");
148                reservedWords.add("long");
149                reservedWords.add("namespace");
150                reservedWords.add("native");
151                reservedWords.add("new");
152                reservedWords.add("null");
153                reservedWords.add("object");
154                reservedWords.add("operator");
155                reservedWords.add("out");
156                reservedWords.add("override");
157                reservedWords.add("package");
158                reservedWords.add("params");
159                reservedWords.add("private");
160                reservedWords.add("property");
161                reservedWords.add("protected");
162                reservedWords.add("public");
163                reservedWords.add("readonly");
164                reservedWords.add("ref");
165                reservedWords.add("register");
166                reservedWords.add("return");
167                reservedWords.add("sbyte");
168                reservedWords.add("sealed");
169                reservedWords.add("short");
170                reservedWords.add("signed");
171                reservedWords.add("sizeof");
172                reservedWords.add("static");
173                reservedWords.add("strictfp");
174                reservedWords.add("shring");
175                reservedWords.add("struct");
176                reservedWords.add("super");
177                reservedWords.add("switch");
178                reservedWords.add("synchronized");
179                reservedWords.add("this");
180                reservedWords.add("thread");
181                reservedWords.add("throw");
182                reservedWords.add("throws");
183                reservedWords.add("transient");
184                reservedWords.add("true");
185                reservedWords.add("try");
186                reservedWords.add("typedef");
187                reservedWords.add("typeof");
188                reservedWords.add("uint");
189                reservedWords.add("ulong");
190                reservedWords.add("unchecked");
191                reservedWords.add("union");
192                reservedWords.add("unsafe");
193                reservedWords.add("unsigned");
194                reservedWords.add("ushort");
195                reservedWords.add("using");
196                reservedWords.add("virtual");
197                reservedWords.add("union");
198                reservedWords.add("unsigned");
199                reservedWords.add("uuid");
200                reservedWords.add("var");
201                reservedWords.add("void");
202                reservedWords.add("volatile");
203                reservedWords.add("while");
204            }
205        }
206    }
207
208    /**
209     * Returns true if the value is a reserved keyword in Java or C#, or cannot be used as a name
210     * @param name the String to check if a keyword
211     * @return true/false
212     */
213    public static boolean isReservedWord(String name)
214    {
215        boolean reserved = false;
216        populateReservedWords();
217        if (StringUtils.isNotBlank(name) && reservedWords.contains(name.toLowerCase()))
218        {
219            reserved = true;
220        }
221        return reserved;
222    }
223
224    /**
225     * Gets the getter prefix for a getter operation given the <code>type</code>.
226     *
227     * @param type the type from which to determine the prefix.
228     * @return the getter prefix.
229     */
230    public static String getGetterPrefix(final ClassifierFacade type)
231    {
232        return type != null && type.isBooleanType() && type.isPrimitive() ? "is" : "get";
233    }
234
235    /**
236     * Gets the getter prefix for a getter operation given the <code>type</code>,
237     * taking multiplicity into account for booleans
238     *
239     * @param type the type from which to determine the prefix.
240     * @param lowerBound If > 0 then type is not optional, thus primitive isBoolean()
241     * @return the getter prefix.
242     */
243    public static String getGetterPrefix(final ClassifierFacade type, int lowerBound)
244    {
245        // Automatically converted to primitive type or wrapped type based on lowerBound
246        return type != null && type.isBooleanType() && (lowerBound > 0 || type.isPrimitive()) ? "is" : "get";
247    }
248
249    /**
250     * Returns true if the passed in constraint <code>expression</code> is of type <code>kind</code>, false otherwise.
251     *
252     * @param expression the expression to check.
253     * @param kind       the constraint kind (i.e. <em>inv</em>,<em>pre</em>, <em>body</em>, etc).
254     * @return true/false
255     */
256    public static boolean isConstraintKind(String expression, String kind)
257    {
258        Pattern pattern = Pattern.compile(".*\\s*" + StringUtils.trimToEmpty(kind) + "\\s*\\w*\\s*:.*", Pattern.DOTALL);
259        Matcher matcher = pattern.matcher(StringUtils.trimToEmpty(expression));
260        return matcher.matches();
261    }
262
263    // TODO extract the mappings into the configurable metafacade namespace in UMLProfile
264    private static Map<String, String> implCollection = new HashMap<String, String>();
265    /**
266     * Transforms the declared type to implementation type for a declared Collection.
267     * Default: Collection=LinkedList, List=ArrayList, Set=HashSet, SortedSet=TreeSet.
268     * Retains the generics and package in the template declaration, if any
269     *
270     * @param input the declared Collection type to be transformed into an implementation type
271     * @return the Collection implementation declaration.
272     */
273    public static String getImplCollection(final String input)
274    {
275        synchronized (implCollection)
276        {
277            // Populate collection map based on profile.xml settings
278            // TODO Use mapped implementation type instead of model types
279            if (implCollection.isEmpty())
280            {
281                // Put all mappings into Map, removing the initial 'datatype::'
282                //implCollection.put("List", "ArrayList");
283                //implCollection.put("Set", "HashSet");
284                //implCollection.put("SortedSet", "TreeSet");
285                //implCollection.put("Map", "HashMap");
286                //implCollection.put("SortedMap", "TreeMap");
287                implCollection.put(UMLProfile.COLLECTION_TYPE_NAME.substring(
288                        UMLProfile.COLLECTION_TYPE_NAME.lastIndexOf(':')+1),
289                    UMLProfile.COLLECTION_IMPL_TYPE_NAME.substring(
290                        UMLProfile.COLLECTION_IMPL_TYPE_NAME.lastIndexOf(':')+1));
291                implCollection.put(UMLProfile.LIST_TYPE_NAME.substring(
292                        UMLProfile.LIST_TYPE_NAME.lastIndexOf(':')+1),
293                    UMLProfile.LIST_IMPL_TYPE_NAME.substring(
294                        UMLProfile.LIST_IMPL_TYPE_NAME.lastIndexOf(':')+1));
295                implCollection.put(UMLProfile.MAP_TYPE_NAME.substring(
296                        UMLProfile.MAP_TYPE_NAME.lastIndexOf(':')+1),
297                    UMLProfile.MAP_IMPL_TYPE_NAME.substring(
298                        UMLProfile.MAP_IMPL_TYPE_NAME.lastIndexOf(':')+1));
299                implCollection.put(UMLProfile.ORDERED_MAP_TYPE_NAME.substring(
300                        UMLProfile.ORDERED_MAP_TYPE_NAME.lastIndexOf(':')+1),
301                    UMLProfile.ORDERED_MAP_IMPL_TYPE_NAME.substring(
302                        UMLProfile.ORDERED_MAP_IMPL_TYPE_NAME.lastIndexOf(':')+1));
303                implCollection.put(UMLProfile.ORDERED_SET_TYPE_NAME.substring(
304                        UMLProfile.ORDERED_SET_TYPE_NAME.lastIndexOf(':')+1),
305                    UMLProfile.ORDERED_SET_IMPL_TYPE_NAME.substring(
306                        UMLProfile.ORDERED_SET_IMPL_TYPE_NAME.lastIndexOf(':')+1));
307                implCollection.put(UMLProfile.SET_TYPE_NAME.substring(
308                        UMLProfile.SET_TYPE_NAME.lastIndexOf(':')+1),
309                    UMLProfile.SET_IMPL_TYPE_NAME.substring(
310                        UMLProfile.SET_IMPL_TYPE_NAME.lastIndexOf(':')+1));
311            }
312        }
313        String collectionImpl = input;
314        // No transformation if no package on fullyQualifiedName
315        if (input.indexOf('.') > 0)
316        {
317            String collectionType = null;
318            String genericType = null;
319            String pkg = null;
320            if (input.indexOf('<') > 0)
321            {
322                collectionType = input.substring(0, input.indexOf('<'));
323                genericType = input.substring(input.indexOf('<'));
324                if (genericType.startsWith("<? extends "))
325                {
326                    // Implementation collection type cannot declare 'extends'
327                    genericType = '<' + genericType.substring(11);
328                }
329            }
330            else
331            {
332                collectionType = input;
333                genericType = "";
334            }
335            if (collectionType.indexOf('.') > 0)
336            {
337                pkg = collectionType.substring(0, collectionType.lastIndexOf('.')+1);
338                collectionType = collectionType.substring(collectionType.lastIndexOf('.')+1);
339            }
340            else
341            {
342                pkg = "java.util.";
343                logger.warn("UMLMetafacadeUtils pkg not found for " + collectionType);
344            }
345            String implType = implCollection.get(collectionType);
346            if (implType == null)
347            {
348                logger.warn("UMLMetafacadeUtils colectionImpl not found for " + collectionType);
349                collectionImpl = pkg + "ArrayList" + genericType;
350            }
351            else
352            {
353                //logger.info("UMLMetafacadeUtils translated from " + collectionType + " to " + implType);
354                collectionImpl = pkg + implType + genericType;
355            }
356        }
357        return collectionImpl;
358    }
359
360    /**
361     * Determines if the class/package should be generated. Will not be generated if it has any
362     * stereotypes: documentation, docOnly, Future, Ignore, analysis, perspective,
363     * or any invalid package identifier characters ` ~!@#%^&*()-+={}[]:;<>,?/|
364     * @param mef ModelElementFacade class to check for stereotypes.
365     * @return false if it has any Stereotypes DocOnly, Future, Ignore configured in UMLProfile
366     */
367    public static boolean shouldOutput(ModelElementFacade mef)
368    {
369        boolean rtn = true;
370        if (mef!=null)
371        {
372            try
373            {
374                PackageFacade pkg = (PackageFacade) mef.getPackage();
375                if (mef instanceof PackageFacade)
376                {
377                    pkg = (PackageFacade) mef;
378                }
379                if (mef.hasStereotype(UMLProfile.STEREOTYPE_DOC_ONLY) ||
380                    mef.hasStereotype(UMLProfile.STEREOTYPE_FUTURE) ||
381                    mef.hasStereotype(UMLProfile.STEREOTYPE_IGNORE))
382                {
383                    rtn = false;
384                }
385                if (pkg != null &&
386                    ( pkg.hasStereotype(UMLProfile.STEREOTYPE_DOC_ONLY) ||
387                    pkg.hasStereotype(UMLProfile.STEREOTYPE_FUTURE) ||
388                    pkg.hasStereotype(UMLProfile.STEREOTYPE_IGNORE) ||
389                    pkg.hasStereotype("analysis") ||
390                    pkg.hasStereotype("perspective") ||
391                    // Verify package does not have any Java disallowed characters
392                    StringUtils.containsAny(pkg.getName(), " `~!@#%^&*()-+={}[]:;<>,?/|") ||
393                            "PrimitiveTypes".equals(pkg.getName()) ||
394                            "datatype".equals(pkg.getName())))
395                {
396                    rtn = false;
397                }
398            }
399            catch (Exception ex)
400            {
401                // Output=true anyway just in case we want this output
402                logger.error("UMLMetafacadeUtils.shouldOutput for " + mef.toString() + ' ' + ex.getClass().getName() + ": "+ ex.getMessage());
403            }
404        }
405        return rtn;
406    }
407    /**
408     * Get the classname without the package name and without additional template<> parameters.
409     *
410     * @param facade
411     * @param enableTemplating
412     * @return getNameWithoutPackage
413     */
414    // TODO This should really be a method on ModelElementFacade
415    public static String getClassDeclaration(ModelElementFacade facade, boolean enableTemplating)
416    {
417        return UMLMetafacadeUtils.getClassDeclaration(facade, facade.getName(), enableTemplating);
418    }
419
420    private static final String namespaceScopeOperator = ".";
421    private static final String COMMA = ", ";
422    private static final String LT = "<";
423    private static final String GT = ">";
424    /**
425     * Get the classname without the package name and without additional template<> parameters.
426     *
427     * @param facade
428     * @param className Class name to use in the class declaration, overrides facade.getName()
429     * @param enableTemplating Whether template declaration should be created.
430     * @return getNameWithoutPackage
431     */
432    // TODO This should really be a method on ModelElementFacade
433    public static String getClassDeclaration(ModelElementFacade facade, String className, boolean enableTemplating)
434    {
435        if (StringUtils.isBlank(className))
436    {
437            className = facade.getName();
438        }
439        String fullName = StringUtils.trimToEmpty(className);
440        final String packageName = facade.getPackageName(true);
441        final String metafacadeNamespaceScopeOperator = MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR;
442        if (StringUtils.isNotBlank(packageName))
443        {
444            fullName = packageName + metafacadeNamespaceScopeOperator + fullName;
445        }
446            final TypeMappings languageMappings = facade.getLanguageMappings();
447            if (languageMappings != null)
448            {
449                fullName = StringUtils.trimToEmpty(languageMappings.getTo(fullName));
450
451                // now replace the metafacade scope operators
452                // with the mapped scope operators
453                fullName = StringUtils.replace(
454                        fullName,
455                        metafacadeNamespaceScopeOperator,
456                        namespaceScopeOperator);
457            }
458        // remove the package qualifier
459        if (fullName.indexOf('.')>-1)
460        {
461            fullName = fullName.substring(fullName.lastIndexOf('.')+1);
462        }
463
464        if (facade.isTemplateParametersPresent() && enableTemplating)
465        {
466            // we'll be constructing the parameter list in this buffer
467            final StringBuilder buffer = new StringBuilder();
468
469            // add the name we've constructed so far
470            buffer.append(fullName);
471
472            // start the parameter list
473            buffer.append(LT);
474
475            // loop over the parameters, we are so to have at least one (see
476            // outer condition)
477            int size = facade.getTemplateParameters().size();
478            int i = 1;
479            for (TemplateParameterFacade parameter : facade.getTemplateParameters())
480            {
481                if (parameter != null)
482                {
483                    /*String name = parameter.getValidationName();
484                    if (name==null && parameter.getParameter() != null)
485                    {
486                        name = parameter.getParameter().getName();
487                    }
488                    buffer.append(name);*/
489                    buffer.append(parameter.getName());
490                    if (i < size)
491                    {
492                        buffer.append(COMMA);
493                        i++;
494                    }
495                }
496            }
497
498            // we're finished listing the parameters
499            buffer.append(GT);
500
501            // we have constructed the full name in the buffer
502            fullName = buffer.toString();
503        }
504
505        return fullName;
506    }
507
508    private static final String QUESTION = "?";
509    /**
510     * Get the generic template<?, ?> declaration.
511     *
512     * @param facade
513     * @param enableTemplating
514     * @return getGenericTemplate
515     */
516    // TODO This should really be a method on ModelElementFacade
517    public static String getGenericTemplate(ModelElementFacade facade, boolean enableTemplating)
518    {
519        String fullName = "";
520        if (facade != null && facade.isTemplateParametersPresent() && enableTemplating)
521        {
522            // we'll be constructing the parameter list in this buffer
523            final StringBuilder buffer = new StringBuilder();
524
525            // start the parameter list
526            buffer.append(LT);
527
528            // loop over the parameters, we are so to have at least one (see
529            // outer condition)
530           for (Iterator<TemplateParameterFacade> parameterIterator =
531               facade.getTemplateParameters().iterator(); parameterIterator.hasNext();)
532           {
533                parameterIterator.next();
534                buffer.append(QUESTION);
535                if (parameterIterator.hasNext())
536                {
537                    buffer.append(COMMA);
538                }
539            }
540
541            // we're finished listing the parameters
542            buffer.append(GT);
543
544            // we have constructed the full name in the buffer
545            fullName = buffer.toString();
546        }
547
548        return fullName;
549    }
550
551    /**
552     * Get the fully-qualified classname without the additional template<> parameters.
553     *
554     * @param facade
555     * @return getFQNameWithoutTemplate
556     */
557    // TODO This should really be a method on ModelElementFacade
558    public static String getFQNameWithoutTemplate(ModelElementFacade facade)
559    {
560        String fullName = StringUtils.trimToEmpty(facade.getName());
561        final String packageName = facade.getPackageName(true);
562        final String metafacadeNamespaceScopeOperator = MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR;
563        if (StringUtils.isNotBlank(packageName))
564        {
565            fullName = packageName + metafacadeNamespaceScopeOperator + fullName;
566        }
567        final TypeMappings languageMappings = facade.getLanguageMappings();
568        if (languageMappings != null)
569        {
570            fullName = StringUtils.trimToEmpty(languageMappings.getTo(fullName));
571            fullName = StringUtils.replace(
572                fullName,
573                metafacadeNamespaceScopeOperator,
574                namespaceScopeOperator);
575        }
576        return fullName;
577    }
578
579    /**
580     * Returns the number of methods without stereotypes or with SimpleClass stereotype. .
581     * @param mef ModelElementFacade class to check for stereotypes.
582     * @param outletFile Name of output file currently being processed. How do we get this in template?
583     * @param refOutput Should .ref files be output?
584     * @return false if it has any Stereotypes DocOnly, Future, Ignore configured in UMLProfile
585     */
586    public static boolean shouldOutput(ModelElementFacade mef, String outletFile, boolean refOutput)
587    {
588        boolean rtn = true;
589        if (outletFile==null)
590        {
591            return rtn;
592        }
593        if (outletFile.endsWith(".ref") && !refOutput)
594        {
595            rtn = false;
596        }
597        else
598        {
599            rtn = shouldOutput(mef);
600        }
601        return rtn;
602    }
603
604    /**
605     * Supplies a result for type = <new value>; initialization for all types
606     * @param facade Type to create default object for
607     * @return Constructor String with facade name
608     */
609    public String createConstructor(ModelElementFacade facade)
610    {
611        return createConstructor(facade, false);
612    }
613
614    /**
615     * Supplies a result for type = <new value>; initialization for all types
616     * @param facade Type to create default object for
617     * @param useMany Return constructor with multiplicity type instead of underlying type
618     * @return Constructor String with facade name
619     */
620    public String createConstructor(ModelElementFacade facade, boolean useMany)
621    {
622        return createConstructor(facade, useMany, null);
623    }
624
625    /**
626     * Supplies a result for type = <new value>; initialization for all types
627     * @param facade Type to create default object for
628     * @param useMany Return constructor with multiplicity type instead of underlying type
629     * @param parent Object containing this facade, which may have an attribute named dependency to a different type
630     * @return Constructor String with facade name
631     */
632    public String createConstructor(ModelElementFacade facade, boolean useMany, ModelElementFacade parent)
633    {
634        if (facade==null)
635        {
636            return "facade was null";
637        }
638        String rtn = "";
639        String toString = "";
640        ClassifierFacade type = null;
641        String typeName = facade.getFullyQualifiedName();
642        String name = facade.getName();
643        String defaultValue = "";
644        // TODO: Default collection type from properties
645        String collectionType = "java.util.ArrayList";
646        Boolean isMany = null;
647        boolean isEnumeration = false;
648        int maxLength = 9999;
649        /*if (parent != null)
650        {
651            // See if a named dependency exists with the same facadeName
652            for (final DependencyFacade dependency : parent.getSourceDependencies())
653            {
654                if (dependency.getName().equals(facade.getName()) && dependency instanceof DependencyFacade)
655                {
656                    facade = ((DependencyFacade)dependency).getTargetElement();
657                    toString = ".toString()";
658                    break;
659                }
660            }
661        }*/
662        try {
663            if (logger.isDebugEnabled())
664            {
665                logger.debug("name=" + name + " typeName=" + typeName + " useMany=" + useMany + " facade=" + facade + " parent=" + parent);
666            }
667            // Use persistence or validation annotations to limit the created value length
668            String length = (String)facade.findTaggedValue("andromda_persistence_column_length");
669            if (length != null && length.length()>0 && StringUtils.isNumeric(length))
670            {
671                maxLength = Integer.parseInt(length);
672            }
673            else
674            {
675                length = (String)facade.findTaggedValue("andromda_persistence_column_precision");
676                if (length != null && length.length()>0 && StringUtils.isNumeric(length))
677                {
678                    maxLength = Integer.parseInt(length);
679                }
680                else
681                {
682                    length = (String)facade.findTaggedValue("andromda_validation_length");
683                    if (length != null && length.length()>0 && StringUtils.isNumeric(length))
684                    {
685                        maxLength = Integer.parseInt(length);
686                    }
687                    else
688                    {
689                        length = (String)facade.findTaggedValue("andromda_validation_precision");
690                        if (length != null && length.length()>0 && StringUtils.isNumeric(length))
691                        {
692                            maxLength = Integer.parseInt(length);
693                        }
694                    }
695                }
696            }
697            if (facade instanceof ClassifierFacade)
698            {
699                ClassifierFacade classifier = (ClassifierFacade) facade;
700                type = classifier;
701                typeName = classifier.getFullyQualifiedName();
702            }
703            if (facade instanceof AttributeFacade)
704            {
705                AttributeFacade attr = (AttributeFacade) facade;
706                defaultValue = attr.getDefaultValue();
707                type = attr.getType();
708                if (useMany)
709                {
710                    typeName = attr.getGetterSetterTypeName();
711                }
712                else
713                {
714                    typeName = type.getFullyQualifiedName();
715                }
716                if (attr.getUpper()>1 || attr.getUpper()==-1)
717                {
718                    isMany = true;
719                }
720            }
721            else if (facade instanceof ParameterFacade)
722            {
723                ParameterFacade attr = (ParameterFacade) facade;
724                defaultValue = attr.getDefaultValue();
725                type = attr.getType();
726                typeName = type.getFullyQualifiedName();
727                if (type.isEnumeration())
728                {
729                    facade = type;
730                }
731                else if (useMany)
732                {
733                    typeName = collectionType + '<' + type.getFullyQualifiedName() + '>';
734                }
735                else
736                {
737                    typeName = type.getFullyQualifiedName();
738                }
739                if (attr.getUpper()>1 || attr.getUpper()==-1)
740                {
741                    isMany = true;
742                }
743            }
744            if (facade instanceof AssociationEndFacade)
745            {
746                AssociationEndFacade attr = (AssociationEndFacade) facade;
747                type = attr.getType();
748                if (useMany)
749                {
750                    typeName = attr.getGetterSetterTypeName();
751                }
752                else
753                {
754                    typeName = type.getFullyQualifiedName();
755                }
756                if (attr.getUpper()>1 || attr.getUpper()==-1)
757                {
758                    isMany = true;
759                }
760                facade = attr.getType();
761            }
762            // TODO: Make this work for attribute types other than String.
763            if (parent != null && StringUtils.isEmpty(defaultValue) && ("String".equals(typeName) || "java.lang.String".equals(typeName)))
764            {
765                // See if a named dependency exists with the same facadeName
766                for (final DependencyFacade dependency : parent.getSourceDependencies())
767                {
768                    if (dependency.getName().equals(facade.getName()))
769                    {
770                        facade = dependency.getTargetElement();
771                        // DependencyFacade type comes back empty for UML2::Integer
772                        // Need to get metaObject Name property and verify it is not null.
773                        if (facade instanceof ClassifierFacade)
774                        {
775                            type = (ClassifierFacade) facade;
776                        }
777                        typeName = facade.getFullyQualifiedName();
778                        toString = ".toString()";
779                        if (logger.isDebugEnabled())
780                        {
781                            logger.debug(parent + " " + facade + " = "
782                                    + dependency + " type=" + type + " typeName="
783                                    + typeName);
784                        }
785                        break;
786                    }
787                }
788            }
789            if (type instanceof EnumerationFacade)
790            {
791                EnumerationFacade enumer = (EnumerationFacade) type;
792                //type = enumer.getLiteralType().getFullyQualifiedName();
793                Collection<AttributeFacade> literals = enumer.getLiterals();
794                if (StringUtils.isEmpty(defaultValue) && !literals.isEmpty())
795                {
796                    // Just get the first enumeration literal
797                    Object literal = literals.iterator().next();
798                    if (literal instanceof EnumerationLiteralFacade)
799                    {
800                        EnumerationLiteralFacade enumLiteral = (EnumerationLiteralFacade) literal;
801                        defaultValue = enumLiteral.getValue();
802                    }
803                    else if (literal instanceof AttributeFacade)
804                    {
805                        AttributeFacade attrib = (AttributeFacade) literal;
806                        defaultValue = attrib.getEnumerationValue();
807                        if (defaultValue==null)
808                        {
809                            defaultValue = attrib.getDefaultValue();
810                        }
811                    }
812                    // Literal value is always a String. Remove quotes if part of default (i.e. class attribute).
813                    defaultValue = StringUtils.remove(defaultValue, "\"");
814                    defaultValue = enumer.getFullyQualifiedName() + ".fromValue(\"" + defaultValue + "\")";
815                }
816                else
817                {
818                    defaultValue = enumer.getName() + '.' + defaultValue;
819                }
820                isEnumeration = true;
821                if (logger.isDebugEnabled())
822                {
823                    logger.debug("EnumerationFacade=" + facade + " type=" + type + " literals=" + literals.size() + " default=" + defaultValue);
824                }
825            }
826            if (type != null && type.findTaggedValue("andromda_persistence_lob_type")!=null)
827            {
828                typeName = String.valueOf(type.findTaggedValue("andromda_persistence_lob_type"));
829                // LOB Types have a different datatype than the underlying declared type
830            }
831            if (useMany && (isMany==null || isMany.booleanValue()) && !typeName.endsWith("[]"))
832            {
833                typeName = UMLMetafacadeUtils.getImplCollection(typeName);
834                if (!typeName.startsWith("java.util") && type != null)
835                {
836                    if (type.equals("java.util.Collection") || typeName.equals("java.util.List"))
837                    {
838                        rtn = "new " + collectionType + "<" + typeName + ">()";
839                    }
840                    else if (typeName.equals("java.util.Set"))
841                    {
842                        rtn = "new java.util.HashSet<" + typeName + ">()";
843                    }
844                    else if (typeName.equals("java.util.Map"))
845                    {
846                        rtn = "new java.util.HashMap<" + typeName + ">()";
847                    }
848                    else
849                    {
850                        rtn = "new " + collectionType + '<' + typeName + ">()";
851                    }
852                }
853                else
854                {
855                    // Assume array or type Collection<type>
856                    rtn = "new " + typeName + "()";
857                }
858            }
859            else if ("String".equals(typeName) || "java.lang.String".equals(typeName))
860            {
861                if (defaultValue != null && defaultValue.trim().length() > 0)
862                {
863                    if (defaultValue.startsWith("\"") || defaultValue.startsWith("'"))
864                    {
865                        defaultValue = defaultValue.substring(1, defaultValue.length()-1);
866                    }
867                    if (defaultValue.endsWith("\"") || defaultValue.endsWith("'"))
868                    {
869                        defaultValue = defaultValue.substring(0, defaultValue.length()-2);
870                    }
871                    if (defaultValue.trim().length() > maxLength)
872                    {
873                        logger.warn("Attribute default for " + facade.getFullyQualifiedName() + " is longer than max column length " + maxLength);
874                        defaultValue = defaultValue.substring(0, maxLength-1);
875                    }
876                }
877                rtn = '\"' + (StringUtils.isNotBlank(defaultValue) ? defaultValue : name) + '\"';
878            }
879            else if ("Boolean".equals(typeName) || "java.lang.Boolean".equals(typeName))
880            {
881                rtn = (StringUtils.isNotBlank(defaultValue) ? "Boolean." + defaultValue.toUpperCase() : "Boolean.TRUE");
882            }
883            else if ("boolean".equals(typeName))
884            {
885                rtn = (StringUtils.isNotBlank(defaultValue) ? defaultValue : "true");
886            }
887            else if ("int".equals(typeName) || "short".equals(typeName) || "long".equals(typeName)
888                    || "byte".equals(typeName) || "float".equals(typeName) || "double".equals(typeName))
889            {
890                rtn = (StringUtils.isNotBlank(defaultValue) ? defaultValue : "1");
891            }
892            else if ("java.util.Date".equals(typeName))
893            {
894                rtn = "new " + typeName + "()";
895            }
896            else if ("java.sql.Timestamp".equals(typeName))
897            {
898                rtn = "new java.sql.Timestamp(System.currentTimeMillis())";
899            }
900            else if ("java.util.Calendar".equals(typeName))
901            {
902                rtn = "java.util.Calendar.getInstance()";
903            }
904            else if ("org.joda.time.LocalTime".equals(typeName))
905            {
906                rtn = "new org.joda.time.LocalTime(1, 1)";
907            }
908            else if ("char".equals(typeName))
909            {
910                rtn = "'" + (StringUtils.isNotEmpty(defaultValue) ? defaultValue : name.substring(0, 1)) + "'";
911            }
912            else if ("Character".equals(typeName))
913            {
914                rtn = "new Character('" + (StringUtils.isNotEmpty(defaultValue) ? "new Character(" + defaultValue : name.substring(0, 1)) + "')";
915            }
916            else if ("Byte".equals(typeName) || "java.lang.Byte".equals(typeName))
917            {
918                rtn = "new Byte(\"" + facade.getName() + "\")";
919            }
920            else if ("Short".equals(typeName) || "java.lang.Short".equals(typeName)
921                    || "Integer".equals(typeName) || "java.lang.Integer".equals(typeName)
922                    || "Long".equals(typeName) || "java.lang.Long".equals(typeName)
923                    || "Float".equals(typeName) || "java.lang.Float".equals(typeName)
924                    || "Double".equals(typeName) || "java.lang.Double".equals(typeName)
925                    || "java.math.BigDecimal".equals(typeName))
926            {
927                rtn = (!StringUtils.isEmpty(defaultValue) ? typeName + ".valueOf(" + defaultValue + ")" : typeName + ".valueOf(1)");
928            }
929            else if ("java.math.BigInteger".equals(typeName))
930            {
931                rtn = (!StringUtils.isEmpty(defaultValue) ? "java.math.BigInteger.valueOf(" + defaultValue + ')' : "java.math.BigInteger.valueOf(1)");
932            }
933            else if ("byte[]".equals(typeName))
934            {
935                rtn = (StringUtils.isNotBlank(defaultValue) ? defaultValue : '\"' + name + '\"') + ".getBytes()";
936            }
937            else if ("char[]".equals(typeName))
938            {
939                String value = StringUtils.isNotBlank(defaultValue) ? defaultValue : name;
940                if (!value.startsWith("\""))
941                {
942                    value = "\"" + value;
943                }
944                if (!value.endsWith("\""))
945                {
946                    value = value + "\"";
947                }
948                rtn = value + ".toCharArray()";
949            }
950            else if ("String[]".equals(typeName))
951            {
952                rtn = "new String[] { " + (StringUtils.isNotBlank(defaultValue) ? defaultValue : '\"' + name + '\"') + " }";
953            }
954            else if (isEnumeration)
955            {
956                if (useMany)
957                {
958                    rtn = collectionType + '<' + defaultValue + '>';
959                }
960                else
961                {
962                    rtn = defaultValue;
963                }
964            }
965            else if (!StringUtils.isEmpty(defaultValue))
966            {
967                rtn = "new " + typeName + '(' + defaultValue + ')';
968            }
969            else if (type != null && type.hasStereotype("EmbeddedValue"))
970            {
971                // EmbeddedValue classes will always be abstract with Impl generated classes.
972                rtn = "new " + typeName + "Impl()";
973            }
974            else if (type instanceof GeneralizableElementFacade)
975            {
976                // If type has a descendant with name <typeName>Impl, assume typeNameImpl must be instantiated instead of typeName
977                if (typeName.endsWith("[]"))
978                {
979                    rtn = "{ new " + typeName.substring(0, typeName.length()-2) + "() }";
980                }
981                else
982                {
983                    rtn = "new " + typeName + "()";
984                }
985                //if (facade instanceof ClassifierFacade)
986                //{
987                    //ClassifierFacade classifier = (ClassifierFacade)facade;
988                    // If type is abstract, choose Impl descendant if exists, or the last descendant
989                    if (type.isAbstract())
990                    {
991                        // Can't instantiate abstract class - pick some descendant
992                        for (GeneralizableElementFacade spec : type.getSpecializations())
993                        {
994                            if (spec.getName().equals(type.getName() + "Impl"))
995                            {
996                                rtn = '(' + type.getName() + ")new " + typeName + "Impl()";
997                                break;
998                            }
999                            rtn = '(' + type.getName() + ")new " + spec.getFullyQualifiedName() + "()";
1000                        }
1001                    }
1002                //}
1003                GeneralizableElementFacade generalization = (GeneralizableElementFacade)type;
1004                for (GeneralizableElementFacade spec : generalization.getSpecializations())
1005                {
1006                    if (spec.getName().equals(type.getName() + "Impl"))
1007                    {
1008                        rtn = '(' + type.getName() + ")new " + spec.getFullyQualifiedName() + "Impl()";
1009                    }
1010                }
1011            }
1012            else if (typeName.endsWith("[]"))
1013            {
1014                rtn = "new " + typeName + " { new " + typeName.substring(0, typeName.length()-2) + "() }";
1015            }
1016            else
1017            {
1018                rtn = "new " + typeName + "()";
1019            }
1020            rtn = StringUtils.replace(rtn, "java.util.Collection", "java.util.ArrayList") + toString;
1021            rtn = StringUtils.replace(rtn, "java.util.Set", "java.util.HashSet") + toString;
1022            if (logger.isDebugEnabled())
1023            {
1024                logger.debug("facade=" + facade + " facadeName=" + facade.getName() + " type=" + type + " typeName=" + typeName + " name=" + name + " isMany=" + isMany + " defaultValue=" + defaultValue + " rtn=" + rtn);
1025            }
1026        } catch (RuntimeException e) {
1027            logger.error(e + " facade=" + facade + " facadeName=" + facade.getName() + " parent=" + parent + " type=" + type + " typeName=" + typeName + " name=" + name + " isMany=" + isMany + " defaultValue=" + defaultValue);
1028            e.printStackTrace();
1029        }
1030        return rtn;
1031    }
1032
1033    /**
1034     * TODO Reference this logic from AssociationEnd
1035     * Determine if this association end owns the relationship. i.e. if the associationEnd property
1036     * belonging to the Entity on the opposite end owns the relationship. Based on tagged value,
1037     * multiplicity, aggregation/composition. If all else fails, the longest name owns the
1038     * association, or else the alphabetically first name. One side of a relationship must
1039     * always own the association and be created and deleted first.
1040     * @param associationEnd the association end
1041     * @return true if the associationEnd (property of the entity on the other end) is owned by the entity on the other end
1042     */
1043    public static boolean isOwningEnd(AssociationEndFacade associationEnd)
1044    {
1045        boolean owning = false;
1046        AssociationEndFacade otherEnd = associationEnd.getOtherEnd();
1047        //String assoc = ((Entity)otherEnd.getValidationOwner()).getName() + "." + associationEnd.getName() + " -> " + associationEnd.getType().getName() + " ";
1048        if (BooleanUtils.toBoolean(
1049                ObjectUtils.toString(otherEnd.findTaggedValue(
1050                    "andromda_persistence_associationEnd_primary"))))
1051        {
1052            owning = true;
1053        }
1054        // See if this end or the other end is tagged as the association owner
1055        else if (BooleanUtils.toBoolean(
1056            ObjectUtils.toString(associationEnd.findTaggedValue(
1057                "andromda_persistence_associationEnd_primary"))))
1058        {
1059            owning = false;
1060        }
1061        // Navigable side always owns the relationship
1062        else if (associationEnd.isNavigable() && !otherEnd.isNavigable())
1063        {
1064            owning = true;
1065            //LOGGER.info("Owning=true: " + assoc + "nav=" + associationEnd.isNavigable() + " Onav=" + otherEnd.isNavigable());
1066        }
1067        else if (!associationEnd.isNavigable() && otherEnd.isNavigable())
1068        {
1069            owning = false;
1070            //LOGGER.info("Owning=false: " + assoc + "nav=" + associationEnd.isNavigable() + " Onav=" + otherEnd.isNavigable());
1071        }
1072        // Other side: aggregation/composition side does not own the bidirectional relationship
1073        else if (otherEnd.isAggregation() || otherEnd.isComposition())
1074        {
1075            owning = false;
1076            //LOGGER.info("Owning=true: " + assoc + "Oagg=" + otherEnd.isAggregation() + " Ocomp=" + otherEnd.isComposition());
1077        }
1078        else if (associationEnd.isAggregation() || associationEnd.isComposition())
1079        {
1080            owning = true;
1081            //LOGGER.info("Owning=false: " + assoc + "Oagg=" + associationEnd.isAggregation() + " Ocomp=" + otherEnd.isComposition());
1082        }
1083        // The many side of 1:M owns the bidirectional relationship
1084        else if (!associationEnd.isMany() && otherEnd.isMany())
1085        {
1086            owning = true;
1087            //LOGGER.info("Owning=true: " + assoc + "many=" + associationEnd.isMany() + " Omany=" + otherEnd.isMany());
1088        }
1089        // Other side: the many side of 1:M owns the bidirectional relationship if no composition/aggregation
1090        else if (associationEnd.isMany() && !otherEnd.isMany())
1091        {
1092            owning = false;
1093            //LOGGER.info("Owning=false: " + assoc + "many=" + associationEnd.isMany() + " Omany=" + otherEnd.isMany());
1094        }
1095        // The optional side of 1:1 or M:M owns the bidirectional relationship
1096        else if (associationEnd.getLower() > 0 && otherEnd.getLower() == 0)
1097        {
1098            owning = true;
1099            //LOGGER.info("Owning=true: " + assoc + "many=" + associationEnd.isMany() + " Omany=" + otherEnd.isMany());
1100        }
1101        // If bidirectional 1:1 or M:M, choose the side with the longest type name because it typically indicates a composition relationship
1102        /*else if (this.getOtherEnd().getType().getName().length()
1103                > this.getType().getName().length())*/
1104        else if (associationEnd.getName().length()
1105            < otherEnd.getName().length())
1106        {
1107            owning = (associationEnd.getName().length() < otherEnd.getName().length());
1108            //LOGGER.info("Owning=true: " + assoc + "endLength=" + associationEnd.getName().length() + " Olength=" + otherEnd.getName().length());
1109        }
1110        // If length is the same, alphabetically earliest is the owner
1111        else if (associationEnd.getName().compareTo(
1112            otherEnd.getName()) < 0)
1113        {
1114            owning = (associationEnd.getName().compareTo(otherEnd.getName()) < 0);
1115            //LOGGER.info("Owning=true: " + assoc + "name=" + associationEnd.getName() + " < OName=" + otherEnd.getName());
1116        }
1117        /*LOGGER.info(((Entity)associationEnd.getOtherEnd().getValidationOwner()).getName()
1118            + "." + associationEnd.getName() +" IsOwningEnd=" + owning + " for "
1119            + ((Entity)associationEnd.getOtherEnd().getValidationOwner()).getName()
1120            + " OName=" + otherEnd.getName() + " Aggregation=" + associationEnd.isAggregation()
1121            + " Composition=" + associationEnd.isComposition() + " Navigable=" + associationEnd.isNavigable()
1122            + " !Navigable=" + !otherEnd.isNavigable() + " Many=" + associationEnd.isMany()
1123            + " OMany=" + otherEnd.isMany() + " Upper=" + associationEnd.getUpper()
1124            + " OUpper=" + otherEnd.getUpper() + " OAggregation=" + otherEnd.isAggregation()
1125            + " OComposition=" + otherEnd.isComposition() + " ONavigable=" + otherEnd.isNavigable()
1126            + " otherEnd=" + otherEnd.getFullyQualifiedName());*/
1127        return owning;
1128    }
1129
1130    private static FastDateFormat df = FastDateFormat.getInstance("MM/dd/yyyy HH:mm:ss");
1131
1132    /**
1133     * Returns the current Date in the specified format.
1134     *
1135     * @param format The format for the output date
1136     * @return the current date in the specified format.
1137     */
1138    public static String getDate(String format)
1139    {
1140        if (df == null || !format.equals(df.getPattern()))
1141        {
1142            df = FastDateFormat.getInstance(format);
1143        }
1144        return df.format(new Date());
1145    }
1146
1147    /**
1148     * Returns the current Date in the specified format.
1149     *
1150     * @return the current date with the default format .
1151     */
1152    public static String getDate()
1153    {
1154        return df.format(new Date());
1155    }
1156}