View Javadoc
1   package org.andromda.metafacades.emf.uml22;
2   
3   import java.util.ArrayList;
4   import java.util.Collection;
5   import java.util.Collections;
6   import java.util.Comparator;
7   import java.util.Iterator;
8   import java.util.LinkedHashSet;
9   import java.util.List;
10  import java.util.Set;
11  import org.andromda.metafacades.uml.AssociationEndFacade;
12  import org.andromda.metafacades.uml.AttributeFacade;
13  import org.andromda.metafacades.uml.ClassifierFacade;
14  import org.andromda.metafacades.uml.DependencyFacade;
15  import org.andromda.metafacades.uml.FilteredCollection;
16  import org.andromda.metafacades.uml.GeneralizableElementFacade;
17  import org.andromda.metafacades.uml.MetafacadeUtils;
18  import org.andromda.metafacades.uml.ModelElementFacade;
19  import org.andromda.metafacades.uml.NameMasker;
20  import org.andromda.metafacades.uml.OperationFacade;
21  import org.andromda.metafacades.uml.PackageFacade;
22  import org.andromda.metafacades.uml.TypeMappings;
23  import org.andromda.metafacades.uml.UMLMetafacadeProperties;
24  import org.andromda.metafacades.uml.UMLMetafacadeUtils;
25  import org.andromda.metafacades.uml.UMLProfile;
26  import org.apache.commons.collections.CollectionUtils;
27  import org.apache.commons.collections.Predicate;
28  import org.apache.commons.collections.Transformer;
29  import org.apache.commons.lang.StringUtils;
30  import org.apache.log4j.Logger;
31  import org.eclipse.uml2.uml.Abstraction;
32  import org.eclipse.uml2.uml.AssociationClass;
33  import org.eclipse.uml2.uml.Class;
34  import org.eclipse.uml2.uml.Classifier;
35  import org.eclipse.uml2.uml.ClassifierTemplateParameter;
36  import org.eclipse.uml2.uml.DataType;
37  import org.eclipse.uml2.uml.Dependency;
38  import org.eclipse.uml2.uml.Element;
39  import org.eclipse.uml2.uml.Enumeration;
40  import org.eclipse.uml2.uml.Interface;
41  import org.eclipse.uml2.uml.NamedElement;
42  import org.eclipse.uml2.uml.Operation;
43  import org.eclipse.uml2.uml.Parameter;
44  import org.eclipse.uml2.uml.PrimitiveType;
45  import org.eclipse.uml2.uml.Property;
46  import org.eclipse.uml2.uml.TemplateParameter;
47  
48  /**
49   * <p>
50   * Represents a Classifier model element
51   * </p>
52   * MetafacadeLogic implementation for
53   * org.andromda.metafacades.uml.ClassifierFacade.
54   *
55   * @see org.andromda.metafacades.uml.ClassifierFacade
56   * @author Bob Fields
57   */
58  public class ClassifierFacadeLogicImpl
59      extends ClassifierFacadeLogic
60  {
61      private static final long serialVersionUID = 34L;
62      /**
63       * Public constructor for ClassifierFacadeLogicImpl
64       * @see org.andromda.metafacades.uml.ClassifierFacade
65       * @param metaObject
66       * @param context
67       */
68      public ClassifierFacadeLogicImpl(
69          final Classifier metaObject,
70          final String context)
71      {
72          super(metaObject, context);
73      }
74  
75      /**
76       * The logger instance.
77       */
78      private static final Logger LOGGER = Logger.getLogger(ClassifierFacadeLogicImpl.class);
79  
80      /**
81       * Overridden to provide name masking.
82       *
83       * @see org.andromda.metafacades.uml.ModelElementFacade#getName()
84       */
85      @Override
86      protected String handleGetName()
87      {
88          String nameMask = null;
89          try
90          {
91              nameMask = String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.CLASSIFIER_NAME_MASK));
92          }
93          catch (Exception ignore)
94          {
95              LOGGER.warn("classifierNameMask not found in " + this.toString());
96              nameMask = "none";
97          }
98          return NameMasker.mask(super.handleGetName(), nameMask);
99      }
100 
101     /**
102      * <p>
103      * Indicates whether or not this classifier represents a primitive
104      * type. If this type has a wrapper then it's primitive, otherwise it isn't.
105      * </p>
106      * @see org.andromda.metafacades.uml.ClassifierFacade#isPrimitive()
107      */
108     @Override
109     protected boolean handleIsPrimitive()
110     {
111         return this.getWrapperMappings() != null &&
112         this.getWrapperMappings().getMappings().containsFrom(this.handleGetFullyQualifiedName());
113     }
114 
115     /**
116      * Indicates whether or not this classifier represents a wrapped primitive type.
117      * @return wrappedPrimitive true/false
118      * @see org.andromda.metafacades.uml.ClassifierFacade#isWrappedPrimitive()
119      */
120     @Override
121     protected boolean handleIsWrappedPrimitive()
122     {
123         // Try both the fully qualified name and the ClassName
124         return this.getWrapperMappings() != null &&
125             (this.getWrapperMappings().getMappings().containsTo(this.handleGetFullyQualifiedName())
126             || this.getWrapperMappings().getMappings().containsTo(this.handleGetName()));
127     }
128 
129     /**
130      * <p>
131      * The attributes from this classifier in the form of an operation
132      * call (this example would be in Java): '(String
133      * attributeOne, String attributeTwo).  If there were no
134      * attributes on the classifier, the result would be an empty '()'.
135      * </p>
136      * @see org.andromda.metafacades.uml.ClassifierFacade#getOperationCallFromAttributes()
137      */
138     @Override
139     protected String handleGetOperationCallFromAttributes()
140     {
141         final StringBuilder call = new StringBuilder("(");
142         String separator = "";
143         for (AttributeFacade attribute : this.getAttributes())
144         {
145             call.append(separator);
146             final String typeName = attribute.getType().getFullyQualifiedName();
147             call.append(typeName);
148             call.append(' ');
149             call.append(attribute.getName());
150             separator = ", ";
151         }
152         call.append(')');
153         return call.toString();
154     }
155 
156     /**
157      * <p>
158      * Indicates if this classifier is 'abstract'.
159      * </p>
160      * @see org.andromda.metafacades.uml.ClassifierFacade#isAbstract()
161      */
162     @Override
163     protected boolean handleIsAbstract()
164     {
165         return this.metaObject.isAbstract();
166     }
167 
168     /**
169      * @see org.andromda.metafacades.uml.ClassifierFacade#getProperties()
170      */
171     @Override
172     protected List<ModelElementFacade> handleGetProperties()
173     {
174         return this.handleGetProperties(false);
175     }
176 
177     /**
178      * @see org.andromda.metafacades.uml.ClassifierFacade#getAllProperties()
179      */
180     @Override
181     public Collection<ModelElementFacade> handleGetAllProperties()
182     {
183         return (Collection<ModelElementFacade>) this.handleGetProperties(true);
184     }
185 
186     /**
187      * Can return either an AttributeFacade or AssociationFacade Collection (UML2 Property)
188      * @see org.andromda.metafacades.uml.ClassifierFacade#getAllRequiredConstructorParameters()
189      */
190     @Override
191     public Collection<ModelElementFacade> handleGetAllRequiredConstructorParameters()
192     {
193         final Collection<ModelElementFacade> allRequiredConstructorParameters = new ArrayList<ModelElementFacade>();
194 
195         final Collection<GeneralizableElementFacade> generalizations = this.getGeneralizations();
196         for (GeneralizableElementFacade parent : generalizations)
197         {
198             if (parent instanceof ClassifierFacade)
199             {
200                 allRequiredConstructorParameters.addAll(
201                     ((ClassifierFacade)parent).getAllRequiredConstructorParameters());
202             }
203         }
204 
205         allRequiredConstructorParameters.addAll(this.getRequiredConstructorParameters());
206 
207         return allRequiredConstructorParameters;
208     }
209 
210     /**
211      * Can return either an AttributeFacade or AssociationFacade Collection (UML2 Property)
212      * @see org.andromda.metafacades.uml.ClassifierFacade#getRequiredConstructorParameters()
213      */
214     @Override
215     public Collection<ModelElementFacade> handleGetRequiredConstructorParameters()
216     {
217         final Collection<ModelElementFacade> requiredConstructorParameters = new ArrayList<ModelElementFacade>();
218 
219         final Collection<? extends ModelElementFacade> properties = this.getProperties();
220         for (final Object property : properties)
221         {
222             if (property instanceof AttributeFacade)
223             {
224                 final AttributeFacade attribute = (AttributeFacade)property;
225                 if (!attribute.isDerived() && (attribute.isRequired() || attribute.isReadOnly()))
226                 {
227                     requiredConstructorParameters.add(attribute);
228                 }
229             }
230             else if (property instanceof AssociationEndFacade)
231             {
232                 final AssociationEndFacade associationEnd = (AssociationEndFacade)property;
233                 if (!associationEnd.isDerived() && (associationEnd.isRequired() || associationEnd.isReadOnly()))
234                 {
235                     requiredConstructorParameters.add(associationEnd);
236                 }
237             }
238         }
239 
240         return requiredConstructorParameters;
241     }
242 
243     /**
244      * <p>
245      * True/false depending on whether or not this classifier
246      * represents a datatype.
247      * </p>
248      * @see org.andromda.metafacades.uml.ClassifierFacade#isDataType()
249      */
250     @Override
251     protected boolean handleIsDataType()
252     {
253         return this.metaObject instanceof DataType;
254     }
255 
256     /**
257      * <p>
258      * True if this classifier represents an array type. False
259      * otherwise.
260      * </p>
261      * @see org.andromda.metafacades.uml.ClassifierFacade#isArrayType()
262      */
263     @Override
264     protected boolean handleIsArrayType()
265     {
266         // try both the mapped name and the implementation name, since byte[] is mapped to Blob
267         final String name =  this.handleGetFullyQualifiedName(true);
268         final String suffix = this.getArraySuffix();
269         return name.endsWith(suffix) || this.handleGetFullyQualifiedName(true).endsWith(suffix);
270     }
271 
272     /*
273      * Gets the array suffix from the configured metafacade properties.
274      *
275      * @return the array suffix.
276     private String getArraySuffix()
277     {
278         // TODO: Private method 'getArraySuffix' also declared in class 'org.andromda.metafacades.emf.uml22.ModelElementFacadeLogicImpl'
279         return String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.ARRAY_NAME_SUFFIX));
280     }
281      */
282 
283     /**
284      * <p>
285      * True if this classifier represents a collection type. False
286      * otherwise.
287      * </p>
288      * @see org.andromda.metafacades.uml.ClassifierFacade#isCollectionType()
289      */
290     @Override
291     protected boolean handleIsCollectionType()
292     {
293         return UMLMetafacadeUtils.isType(
294             this,
295             UMLProfile.COLLECTION_TYPE_NAME);
296     }
297 
298     /**
299      * <p>
300      * The wrapper name for this classifier if a mapped type has a
301      * defined wrapper class (i.e. 'long' maps to 'Long').  If the
302      * classifier doesn't have a wrapper defined for it, this method
303      * will return a null.  Note that wrapper mappings must be defined
304      * for the namespace by defining the 'wrapperMappingsUri', this
305      * property must point to the location of the mappings file which
306      * maps the primitives to wrapper types.
307      * </p>
308      * @see org.andromda.metafacades.uml.ClassifierFacade#getWrapperName()
309      */
310     @Override
311     protected String handleGetWrapperName()
312     {
313         String wrapperName = null;
314         if (this.getWrapperMappings() != null &&
315             this.getWrapperMappings().getMappings().containsFrom(this.handleGetFullyQualifiedName()))
316         {
317             wrapperName = this.getWrapperMappings().getTo(this.handleGetFullyQualifiedName());
318         }
319         return wrapperName;
320     }
321 
322     /**
323      * Gets the mappings from primitive types to wrapper types. Some languages
324      * have primitives (i.e., Java) and some languages don't, so therefore this
325      * property is optional.
326      *
327      * @return the wrapper mappings
328      */
329     protected TypeMappings getWrapperMappings()
330     {
331         final String propertyName = UMLMetafacadeProperties.WRAPPER_MAPPINGS_URI;
332         final Object property = this.getConfiguredProperty(propertyName);
333         TypeMappings mappings = null;
334         if (property instanceof String)
335         {
336             final String uri = (String)property;
337             try
338             {
339                 mappings = TypeMappings.getInstance(uri);
340                 this.setProperty(
341                     propertyName,
342                     mappings);
343             }
344             catch (final Exception ex)
345             {
346                 final String errMsg = "Error getting '" + propertyName + "' --> '" + uri + '\'';
347                 ClassifierFacadeLogicImpl.LOGGER.error(
348                     errMsg, ex);
349 
350                 // don't throw the exception
351             }
352         }
353         else
354         {
355             mappings = (TypeMappings)property;
356         }
357         return mappings;
358     }
359 
360     /**
361      * <p>
362      * True when this classifier is a date type.
363      * </p>
364      * @see org.andromda.metafacades.uml.ClassifierFacade#isDateType()
365      */
366     @Override
367     protected boolean handleIsDateType()
368     {
369         return UMLMetafacadeUtils.isType(
370             this,
371             UMLProfile.DATE_TYPE_NAME);
372     }
373 
374     /**
375      * <p>
376      * True/false depending on whether or not this Classifier
377      * represents an interface.
378      * </p>
379      * @see org.andromda.metafacades.uml.ClassifierFacade#isInterface()
380      */
381     @Override
382     protected boolean handleIsInterface()
383     {
384         return this.metaObject instanceof Interface;
385     }
386 
387     /**
388      * <p>
389      * A String representing the new Constructor value for this classifier type to
390      * be used in a Java environment.
391      * </p>
392      * @return new constructor
393      * @see org.andromda.metafacades.uml.ClassifierFacade#getJavaNullString()
394      */
395     @Override
396     protected String handleGetJavaNewString()
397     {
398         String javaNewString;
399         if (this.isPrimitive())
400         {
401             if (UMLMetafacadeUtils.isType(
402                     this,
403                     UMLProfile.BOOLEAN_TYPE_NAME))
404             {
405                 javaNewString = "false";
406             }
407             else
408             {
409                 javaNewString = "0";
410             }
411         }
412         else if (this.isWrappedPrimitive())
413         {
414             if (UMLMetafacadeUtils.isType(
415                 this,
416                 UMLProfile.BOOLEAN_TYPE_NAME))
417             {
418                 javaNewString = "Boolean.FALSE";
419             }
420             else
421             {
422                 javaNewString = this.handleGetFullyQualifiedName() + ".valueOf(0)";
423             }
424         }
425         else
426         {
427             javaNewString = "new " + this.handleGetFullyQualifiedName() + "()";
428         }
429         return javaNewString;
430     }
431 
432     /**
433      * <p>
434      * A String representing the null-value for this classifier type to
435      * be used in a Java environment.
436      * </p>
437      * @see org.andromda.metafacades.uml.ClassifierFacade#getJavaNullString()
438      */
439     @Override
440     protected String handleGetJavaNullString()
441     {
442         String javaNullString;
443         if (this.isPrimitive())
444         {
445             if (UMLMetafacadeUtils.isType(
446                     this,
447                     UMLProfile.BOOLEAN_TYPE_NAME))
448             {
449                 javaNullString = "false";
450             }
451             else
452             {
453                 javaNullString = "0";
454             }
455         }
456         else
457         {
458             javaNullString = "null";
459         }
460         return javaNullString;
461     }
462 
463     /**
464      * <p>
465      * True if this classifier represents a list type. False otherwise.
466      * </p>
467      * @see org.andromda.metafacades.uml.ClassifierFacade#isListType()
468      */
469     @Override
470     protected boolean handleIsListType()
471     {
472         return UMLMetafacadeUtils.isType(
473             this,
474             UMLProfile.LIST_TYPE_NAME);
475     }
476 
477     /**
478      * <p>
479      * True if this classifier represents a set type. False otherwise.
480      * </p>
481      * @see org.andromda.metafacades.uml.ClassifierFacade#isSetType()
482      */
483     @Override
484     protected boolean handleIsSetType()
485     {
486         return UMLMetafacadeUtils.isType(
487             this,
488             UMLProfile.SET_TYPE_NAME);
489     }
490 
491     /**
492      * <p>
493      * Returns true if this type represents a 'file' type.
494      * </p>
495      * @see org.andromda.metafacades.uml.ClassifierFacade#isFileType()
496      */
497     @Override
498     protected boolean handleIsFileType()
499     {
500         return UMLMetafacadeUtils.isType(
501             this,
502             UMLProfile.FILE_TYPE_NAME);
503     }
504 
505     /**
506      * <p>
507      * Indicates whether or not this classifier represents a Map type.
508      * </p>
509      * @see org.andromda.metafacades.uml.ClassifierFacade#isMapType()
510      */
511     @Override
512     public boolean handleIsMapType()
513     {
514         return UMLMetafacadeUtils.isType(
515             this,
516             UMLProfile.MAP_TYPE_NAME);
517     }
518 
519     /**
520      * <p>
521      * Indicates whether or not this classifier represents a string
522      * type.
523      * </p>
524      * @see org.andromda.metafacades.uml.ClassifierFacade#isStringType()
525      */
526     @Override
527     protected boolean handleIsStringType()
528     {
529         // Allow mapping multiple model types to String type
530         return "String".equals(this.handleGetFullyQualifiedName())
531            || "java.lang.String".equals(this.handleGetFullyQualifiedName())
532            || UMLMetafacadeUtils.isType(
533             this,
534             UMLProfile.STRING_TYPE_NAME);
535     }
536 
537     /**
538      * <p>
539      * True if this classifier is in fact marked as an enumeration.
540      * </p>
541      * @see org.andromda.metafacades.uml.ClassifierFacade#isEnumeration()
542      */
543     @Override
544     protected boolean handleIsEnumeration()
545     {
546         return (this.hasStereotype(UMLProfile.STEREOTYPE_ENUMERATION)) || (this.metaObject instanceof Enumeration);
547     }
548 
549     /**
550      * <p>
551      * The name of the classifier as an array.
552      * </p>
553      * @see org.andromda.metafacades.uml.ClassifierFacade#getArrayName()
554      */
555     @Override
556     protected String handleGetArrayName()
557     {
558         return this.handleGetName() + this.getArraySuffix();
559     }
560 
561     /**
562      * <p>
563      * The fully qualified name of the classifier as an array.
564      * </p>
565      * @see org.andromda.metafacades.uml.ClassifierFacade#getFullyQualifiedArrayName()
566      */
567     @Override
568     protected String handleGetFullyQualifiedArrayName()
569     {
570         return this.handleGetFullyQualifiedName() + this.getArraySuffix();
571     }
572 
573     /**
574      * <p>
575      * Returns the serial version UID of the underlying model element.
576      * </p>
577      * @see org.andromda.metafacades.uml.ClassifierFacade#getSerialVersionUID()
578      */
579     @Override
580     protected long handleGetSerialVersionUID()
581     {
582         if (ClassifierFacadeLogicImpl.LOGGER.isDebugEnabled())
583         {
584             ClassifierFacadeLogicImpl.LOGGER.debug("Starting get serial UID");
585         }
586         long serialVersionUID;
587         final String serialVersionString = UmlUtilities.getSerialVersionUID(this);
588         if (serialVersionString == null)
589         {
590             serialVersionUID = MetafacadeUtils.calculateDefaultSUID(this);
591         }
592         else
593         {
594             serialVersionUID = Long.parseLong(serialVersionString);
595         }
596         if (ClassifierFacadeLogicImpl.LOGGER.isDebugEnabled())
597         {
598             ClassifierFacadeLogicImpl.LOGGER.debug("SerialVersionUID for "
599                 + this.metaObject.getQualifiedName() + " is " + serialVersionUID);
600         }
601         return serialVersionUID;
602     }
603 
604     /**
605      * <p>
606      * Returns true if this type represents a Blob type.
607      * </p>
608      * @see org.andromda.metafacades.uml.ClassifierFacade#isBlobType()
609      */
610     @Override
611     protected boolean handleIsBlobType()
612     {
613         return UMLMetafacadeUtils.isType(
614             this,
615             UMLProfile.BLOB_TYPE_NAME);
616     }
617 
618     /**
619      * @see org.andromda.metafacades.uml.ClassifierFacade#isClobType()
620      */
621     @Override
622     protected boolean handleIsClobType()
623     {
624         return UMLMetafacadeUtils.isType(
625             this,
626             UMLProfile.CLOB_TYPE_NAME);
627     }
628 
629     /**
630      * <p>
631      * Indicates if this type represents a boolean type or not.
632      * </p>
633      * @see org.andromda.metafacades.uml.ClassifierFacade#isBooleanType()
634      */
635     @Override
636     protected boolean handleIsBooleanType()
637     {
638         return UMLMetafacadeUtils.isType(
639             this,
640             UMLProfile.BOOLEAN_TYPE_NAME);
641     }
642 
643     /**
644      * <p>
645      * Indicates if this type represents a char, Character, or java.lang.Character type or not.
646      * </p>
647      * @see org.andromda.metafacades.uml.ClassifierFacade#isCharacterType()
648      */
649     @Override
650     protected boolean handleIsCharacterType()
651     {
652         final String characterType = UMLProfile.CHARACTER_TYPE_NAME;
653         // Check both char and Character by taking the part after datatype::
654         final String charType = characterType.substring(characterType.indexOf(':')+1).substring(0, 4).toLowerCase();
655         return UMLMetafacadeUtils.isType(
656             this,
657             charType) ||
658             UMLMetafacadeUtils.isType(
659                 this,
660                 characterType);
661     }
662 
663     /**
664      * <p>
665      * Indicates whether or not this classifier represents a time type.
666      * </p>
667      * @see org.andromda.metafacades.uml.ClassifierFacade#isTimeType()
668      */
669     @Override
670     protected boolean handleIsTimeType()
671     {
672         return UMLMetafacadeUtils.isType(
673             this,
674             UMLProfile.TIME_TYPE_NAME);
675     }
676 
677     /**
678      * <p>
679      * Indicates whether or not this classifier represents a double type.
680      * </p>
681      * @return isDoubleType
682      * @see org.andromda.metafacades.uml.ClassifierFacade#isDoubleType()
683      */
684     @Override
685     protected boolean handleIsDoubleType()
686     {
687         return UMLMetafacadeUtils.isType(
688             this,
689             UMLProfile.DOUBLE_TYPE_NAME);
690     }
691 
692     /**
693      * <p>
694      * Indicates whether or not this classifier represents a float type.
695      * </p>
696      * @return isFloatType
697      * @see org.andromda.metafacades.uml.ClassifierFacade#isFloatType()
698      */
699     @Override
700     protected boolean handleIsFloatType()
701     {
702         return UMLMetafacadeUtils.isType(
703             this,
704             UMLProfile.FLOAT_TYPE_NAME);
705     }
706 
707     /**
708      * <p>
709      * Indicates whether or not this classifier represents an integer type.
710      * </p>
711      * @return isIntegerType
712      * @see org.andromda.metafacades.uml.ClassifierFacade#isIntegerType()
713      */
714     @Override
715     protected boolean handleIsIntegerType()
716     {
717         final String integerType = UMLProfile.INTEGER_TYPE_NAME;
718         // Check both int and Integer by taking the part after datatype::
719         final String intType = integerType.substring(integerType.lastIndexOf(':')+1).substring(0, 3).toLowerCase();
720         return UMLMetafacadeUtils.isType(
721             this,
722             intType) ||
723             UMLMetafacadeUtils.isType(
724                 this,
725                 integerType);
726     }
727 
728     /**
729      * <p>
730      * Indicates whether or not this classifier represents a long type.
731      * </p>
732      * @return isLongType
733      * @see org.andromda.metafacades.uml.ClassifierFacade#isLongType()
734      */
735     @Override
736     protected boolean handleIsLongType()
737     {
738         return UMLMetafacadeUtils.isType(
739             this,
740             UMLProfile.LONG_TYPE_NAME);
741     }
742 
743     /**
744      * @see org.andromda.metafacades.uml.ClassifierFacade#getAttributes(boolean)
745      */
746     @Override
747     protected List<AttributeFacade> handleGetAttributes(final boolean follow)
748     {
749         return this.shieldedElements(UmlUtilities.getAttributes(
750             this.metaObject,
751             follow));
752     }
753 
754     /**
755      * @see org.andromda.metafacades.uml.ClassifierFacade#findAttribute(String)
756      */
757     @Override
758     protected AttributeFacade handleFindAttribute(final String name)
759     {
760         return (AttributeFacade)CollectionUtils.find(
761             this.getAttributes(true),
762             new Predicate()
763             {
764                 public boolean evaluate(final Object object)
765                 {
766                     final AttributeFacade attribute = (AttributeFacade)object;
767                     return StringUtils.trimToEmpty(attribute.getName()).equals(name);
768                 }
769             });
770     }
771 
772     /**
773      * Can return either an AttributeFacade or AssociationFacade Collection (UML2 Property)
774      * @see org.andromda.metafacades.uml.ClassifierFacade#getProperties(boolean)
775      */
776     @Override
777     protected List<ModelElementFacade> handleGetProperties(final boolean follow)
778     {
779         final List<ModelElementFacade> properties = new ArrayList<ModelElementFacade>();
780         if (follow && !this.getGeneralizations().isEmpty())
781         {
782             for (Object generalization : this.getGeneralizations())
783             {
784                 if (generalization instanceof ClassifierFacade)
785                 {
786                     properties.addAll(((ClassifierFacade)generalization).getAllProperties());
787                 }
788             }
789         }
790         properties.addAll(this.getAttributes(false));
791         properties.addAll(this.getNavigableConnectingEnds(false));
792         return properties;
793     }
794 
795     /**
796      * @see org.andromda.metafacades.emf.uml22.ClassifierFacadeLogic#handleGetOperations()
797      */
798     @Override
799     protected List<Operation> handleGetOperations()
800     {
801         final List<Operation> operations;
802         if (this.metaObject instanceof Class)
803         {
804             operations = ((Class)this.metaObject).getOwnedOperations();
805         }
806         else if (this.metaObject instanceof Interface)
807         {
808             operations = ((Interface)this.metaObject).getOwnedOperations();
809         }
810         else
811         {
812             operations = Collections.emptyList();
813         }
814         //Collections.sort(operations, new OperationComparator());
815 
816         return operations;
817     }
818 
819     /**
820      * Note: if this instance represents an actual class we resolve any realized interfaces recursively, in case this
821      * instance represents an interface we return only the owned operations.
822      *
823      * @see org.andromda.metafacades.uml.ClassifierFacade#getOperations()
824      */
825     @Override
826     protected Collection<Operation> handleGetImplementationOperations()
827     {
828         final Collection<Operation> operations;
829 
830         if (this.metaObject instanceof Class)
831         {
832             operations = new LinkedHashSet<Operation>(((Class)this.metaObject).getOwnedOperations());
833 
834             final Collection<Dependency> dependencies = new FilteredCollection(this.metaObject.getClientDependencies())
835             {
836                 /** serialVersionUID = 1L */
837                 private static final long serialVersionUID = 1L;
838 
839                 @Override
840                 public boolean evaluate(final Object object)
841                 {
842                     return object instanceof Abstraction;
843                 }
844             };
845 
846             for (Dependency dependency : dependencies)
847             {
848                 final List<NamedElement> suppliers = ((Abstraction)dependency).getSuppliers();
849                 for (NamedElement supplier : suppliers)
850                 {
851                     if (supplier instanceof Interface)
852                     {
853                         operations.addAll(resolveInterfaceOperationsRecursively((Interface) supplier));
854                     }
855                 }
856             }
857         }
858         else if (this.metaObject instanceof Interface)
859         {
860             operations = new LinkedHashSet<Operation>(((Interface)this.metaObject).getOwnedOperations());
861         }
862         else
863         {
864             operations = Collections.emptyList();
865         }
866 
867         return operations;
868     }
869 
870     private static Collection<Operation> resolveInterfaceOperationsRecursively(final Interface classifier)
871     {
872         // preserve ordering
873         final Collection<Operation> operations = new LinkedHashSet<Operation>(classifier.getOwnedOperations());
874 
875         final List<Classifier> generals = classifier.getGenerals();
876         for (final Classifier generalObject : generals)
877         {
878             if (generalObject instanceof Interface)
879             {
880                 operations.addAll(resolveInterfaceOperationsRecursively((Interface) generalObject));
881             }
882         }
883 
884         return operations;
885     }
886 
887     /**
888      * @see org.andromda.metafacades.uml.ClassifierFacade#getAttributes()
889      */
890     @Override
891     protected List<Property> handleGetAttributes()
892     {
893         return UmlUtilities.getAttributes(this.metaObject, false);
894     }
895 
896     /**
897      * @see org.andromda.metafacades.uml.ClassifierFacade#getAssociationEnds()
898      */
899     @Override
900     protected List<AssociationEndFacade> handleGetAssociationEnds()
901     {
902         return this.shieldedElements(UmlUtilities.getAssociationEnds(this.metaObject, false));
903     }
904 
905     /**
906      * @return Owner of this Classifier. Used to distinguish between a regular class
907      * and a TemplateParameter Class/Interface/Type
908      * @see org.andromda.metafacades.uml.ClassifierFacade#getAttributes(boolean)
909      */
910     protected Element getOwner()
911     {
912         return this.metaObject.getOwner();
913     }
914 
915     /**
916      * @see org.andromda.metafacades.uml.ClassifierFacade#getNonArray()
917      */
918     @Override
919     protected ClassifierFacade handleGetNonArray()
920     {
921         ClassifierFacade nonArrayType = (ClassifierFacade)this.THIS();
922 
923         final String arraySuffix = this.getArraySuffix();
924 
925         if (this.handleGetFullyQualifiedName().contains(arraySuffix))
926         {
927             final PackageFacade packageFacade = this.getRootPackage();
928             final String fullQualifiedName = this.handleGetFullyQualifiedName(true);
929 
930             if (ClassifierFacadeLogicImpl.LOGGER.isDebugEnabled())
931             {
932                 ClassifierFacadeLogicImpl.LOGGER.debug(
933                     "Looking for non-array type of element " + fullQualifiedName + " with array suffix " + arraySuffix +
934                     ", root: " + packageFacade);
935                 ClassifierFacadeLogicImpl.LOGGER.debug("Metaobject: " + this.metaObject + " its model is : " + this.metaObject.getModel());
936             }
937             nonArrayType =
938                 (ClassifierFacade)packageFacade.findModelElement(StringUtils.replace(
939                         fullQualifiedName,
940                         arraySuffix,
941                         ""));
942         }
943         return nonArrayType;
944     }
945 
946     /**
947      * @see org.andromda.metafacades.uml.ClassifierFacade#getArray()
948      */
949     @Override
950     protected ClassifierFacade handleGetArray()
951     {
952         ClassifierFacade arrayType = (ClassifierFacade)this.THIS();
953         if (this.metaObject instanceof PrimitiveType)
954         {
955             String name = this.handleGetFullyQualifiedName(true);
956             if (!name.contains(this.getArraySuffix()))
957             {
958                 name += this.getArraySuffix();
959                 final PackageFacade pkg = this.getRootPackage();
960                 if (pkg!=null)
961                 {
962                     arrayType = (ClassifierFacade)this.shieldedElement(this.getRootPackage().findModelElement(name));
963                 }
964             }
965         }
966         else
967         {
968             arrayType = null;
969         }
970         return arrayType;
971     }
972 
973     /**
974      * @see org.andromda.metafacades.uml.ClassifierFacade#getStaticAttributes()
975      */
976     @Override
977     protected Collection<AttributeFacade> handleGetStaticAttributes()
978     {
979         final Collection<AttributeFacade> attributes = this.getAttributes();
980         CollectionUtils.filter(
981             attributes,
982             new Predicate()
983             {
984                 public boolean evaluate(final Object object)
985                 {
986                     return object != null && ((AttributeFacade)object).isStatic();
987                 }
988             });
989 
990         return attributes;
991     }
992 
993     /**
994      * @see org.andromda.metafacades.uml.ClassifierFacade#getInstanceAttributes()
995      */
996     @Override
997     protected Collection<AttributeFacade> handleGetInstanceAttributes()
998     {
999         final Collection<AttributeFacade> attributes = this.getAttributes();
1000         CollectionUtils.filter(
1001             attributes,
1002             new Predicate()
1003             {
1004                 public boolean evaluate(final Object object)
1005                 {
1006                     return object != null && !((AttributeFacade)object).isStatic();
1007                 }
1008             });
1009         return attributes;
1010     }
1011 
1012     /**
1013      * @see org.andromda.metafacades.uml.ClassifierFacade#getStaticOperations()
1014      */
1015     @Override
1016     protected List<OperationFacade> handleGetStaticOperations()
1017     {
1018         final List<OperationFacade> operations = new ArrayList<OperationFacade>();
1019         for (OperationFacade operation : this.getOperations())
1020         {
1021             if (operation.isStatic())
1022             {
1023                 operations.add(operation);
1024             }
1025         }
1026         return operations;
1027     }
1028 
1029     /**
1030      * @see org.andromda.metafacades.uml.ClassifierFacade#getInstanceOperations()
1031      */
1032     @Override
1033     protected List<OperationFacade> handleGetInstanceOperations()
1034     {
1035         return this.getStaticOperations();
1036     }
1037 
1038     /**
1039      * @see org.andromda.metafacades.uml.ClassifierFacade#getAbstractions()
1040      */
1041     @Override
1042     protected Collection<Abstraction> handleGetAbstractions()
1043     {
1044         final Collection<Abstraction> abstractions = new ArrayList<Abstraction>();
1045         for (Dependency dependency : this.metaObject.getClientDependencies())
1046         {
1047             if (dependency instanceof Abstraction)
1048             {
1049                 abstractions.add((Abstraction) dependency);
1050             }
1051         }
1052         return abstractions;
1053     }
1054 
1055     /**
1056      * @see org.andromda.metafacades.uml.ClassifierFacade#getNavigableConnectingEnds()
1057      */
1058     @Override
1059     protected Collection<AssociationEndFacade> handleGetNavigableConnectingEnds()
1060     {
1061         final Collection<AssociationEndFacade> connectingEnds =
1062             new ArrayList<AssociationEndFacade>(this.getAssociationEnds());
1063         CollectionUtils.transform(
1064             connectingEnds,
1065             new Transformer()
1066             {
1067                 public Object transform(final Object object)
1068                 {
1069                     if (object == null) return null;
1070                     return ((AssociationEndFacade)object).getOtherEnd();
1071                 }
1072             });
1073         CollectionUtils.filter(
1074             connectingEnds,
1075             new Predicate()
1076             {
1077                 public boolean evaluate(final Object object)
1078                 {
1079                     return object != null && ((AssociationEndFacade)object).isNavigable();
1080                 }
1081             });
1082         return connectingEnds;
1083     }
1084 
1085     /**
1086      * @see org.andromda.metafacades.uml.ClassifierFacade#getNavigableConnectingEnds(boolean)
1087      */
1088     @Override
1089     protected List<AssociationEndFacade> handleGetNavigableConnectingEnds(final boolean follow)
1090     {
1091         final List<AssociationEndFacade> connectingEnds = this.shieldedElements(UmlUtilities.getAssociationEnds(
1092                     this.metaObject,
1093                     follow));
1094         CollectionUtils.transform(
1095             connectingEnds,
1096             new Transformer()
1097             {
1098                 public Object transform(final Object object)
1099                 {
1100                     if (object == null) return null;
1101                     return ((AssociationEndFacade)object).getOtherEnd();
1102                 }
1103             });
1104         CollectionUtils.filter(
1105             connectingEnds,
1106             new Predicate()
1107             {
1108                 public boolean evaluate(final Object object)
1109                 {
1110                     return object != null && ((AssociationEndFacade)object).isNavigable();
1111                 }
1112             });
1113         if (ClassifierFacadeLogicImpl.LOGGER.isDebugEnabled())
1114         {
1115             ClassifierFacadeLogicImpl.LOGGER.debug("handleGetNavigableConnectingEnds "
1116                 + this.metaObject.getQualifiedName() + ' ' + connectingEnds.size());
1117         }
1118         return connectingEnds;
1119     }
1120 
1121     /**
1122      * @see org.andromda.metafacades.emf.uml22.ClassifierFacadeLogic#handleIsLeaf()
1123      */
1124     @Override
1125     protected boolean handleIsLeaf()
1126     {
1127         return this.metaObject.isLeaf();
1128     }
1129 
1130     /**
1131      * @see org.andromda.metafacades.emf.uml22.ClassifierFacadeLogic#handleGetInterfaceAbstractions()
1132      */
1133     @Override
1134     protected Collection<ClassifierFacade> handleGetInterfaceAbstractions()
1135     {
1136         final Collection<ClassifierFacade> interfaceAbstractions = new LinkedHashSet<ClassifierFacade>();
1137         if (this.getAbstractions() != null)
1138         {
1139             // TODO Changing from Iterator to for: causes ClassCastException casting DependencyFacadeLogicImpl to ClassifierFacade
1140             // TODO Change to handleGetAbstractions, look for target end of type Interface (not Classifier)
1141             for (final Iterator<ClassifierFacade> abstractionIterator = this.getAbstractions().iterator(); abstractionIterator.hasNext();)
1142             {
1143                 final Object obj = abstractionIterator.next();
1144                 try
1145                 {
1146                     final DependencyFacade abstraction = (DependencyFacade)obj;
1147                     final ModelElementFacade element = abstraction.getTargetElement();
1148 
1149                     if (element instanceof ClassifierFacade)
1150                     {
1151                         final ClassifierFacade classifier = (ClassifierFacade)element;
1152                         if (classifier.isInterface())
1153                         {
1154                             interfaceAbstractions.add(classifier);
1155                         }
1156                     }
1157                 }
1158                 catch (Exception e)
1159                 {
1160                     LOGGER.warn("ClassifierFacade.handleGetInterfaceAbstractions " + obj + ' ' + e.getMessage());
1161                 }
1162             }
1163         }
1164 
1165         return interfaceAbstractions;
1166     }
1167 
1168     /**
1169      * @see org.andromda.metafacades.emf.uml22.ClassifierFacadeLogic#handleGetImplementedInterfaceList()
1170      */
1171     @Override
1172     protected String handleGetImplementedInterfaceList()
1173     {
1174         final String interfaceList;
1175 
1176         final Collection<ClassifierFacade> interfaces = this.getInterfaceAbstractions();
1177         if (interfaces.isEmpty())
1178         {
1179             interfaceList = "";
1180         }
1181         else
1182         {
1183             final StringBuilder list = new StringBuilder();
1184             for (final Iterator<ClassifierFacade> iterator = interfaces.iterator(); iterator.hasNext();)
1185             {
1186                 final ClassifierFacade element = iterator.next();
1187                 list.append(element.getBindedFullyQualifiedName(this));
1188                 if (iterator.hasNext())
1189                 {
1190                     list.append(", ");
1191                 }
1192             }
1193             interfaceList = list.toString();
1194         }
1195 
1196         return interfaceList;
1197     }
1198 
1199 /*    protected Object handleFindTaggedValue(final String tagName, final boolean follow)
1200     {
1201         return null;
1202     }
1203 
1204     protected boolean handleIsBindingDependenciesPresent()
1205     {
1206         return false;
1207     }*/
1208 
1209     /**
1210      * @see org.andromda.metafacades.emf.uml22.ModelElementFacadeLogicImpl#handleIsTemplateParametersPresent()
1211      */
1212     protected boolean handleIsTemplateParametersPresent()
1213     {
1214         if (this.metaObject.getOwnedTemplateSignature()==null
1215             || this.metaObject.getOwnedTemplateSignature().getOwnedParameters()==null)
1216         {
1217             return false;
1218         }
1219         return !this.metaObject.getOwnedTemplateSignature().getOwnedParameters().isEmpty();
1220     }
1221 
1222     /** Not implemented
1223      * @see org.andromda.metafacades.emf.uml22.ModelElementFacadeLogicImpl#handleCopyTaggedValues(org.andromda.metafacades.uml.ModelElementFacade)
1224      */
1225     protected void handleCopyTaggedValues(final ModelElementFacade element)
1226     {
1227         // Not implemented
1228     }
1229 
1230     /**
1231      * @see org.andromda.metafacades.emf.uml22.ModelElementFacadeLogicImpl#handleGetTemplateParameter(String)
1232      */
1233     protected DataType handleGetTemplateParameter(final String parameterName)
1234     {
1235         if (this.metaObject.getOwnedTemplateSignature()==null
1236                 || this.metaObject.getOwnedTemplateSignature().getOwnedParameters()==null)
1237         {
1238             return null;
1239         }
1240 
1241         for (TemplateParameter param : this.metaObject.getOwnedTemplateSignature().getOwnedParameters())
1242         {
1243             final DataType element = (DataType)((ClassifierTemplateParameter)param).getOwnedParameteredElement();
1244             if (element.getName().equals(parameterName))
1245             {
1246                 return element;
1247             }
1248         }
1249         return null;
1250     }
1251 
1252     /**
1253      * @see org.andromda.metafacades.emf.uml22.ModelElementFacadeLogic#handleGetTemplateParameters()
1254      */
1255     protected Collection<TemplateParameter> handleGetTemplateParameters()
1256     {
1257         if (this.metaObject.getOwnedTemplateSignature()==null
1258                 || this.metaObject.getOwnedTemplateSignature().getOwnedParameters()==null)
1259         {
1260             return new ArrayList<TemplateParameter>();
1261         }
1262         return this.metaObject.getOwnedTemplateSignature().getOwnedParameters();
1263     }
1264 
1265     /**
1266      * @see org.andromda.metafacades.emf.uml22.ClassifierFacadeLogic#handleIsAssociationClass()
1267      */
1268     @Override
1269     protected boolean handleIsAssociationClass()
1270     {
1271         return AssociationClass.class.isAssignableFrom(this.metaObject.getClass());
1272     }
1273 
1274     /**
1275      * @see org.andromda.metafacades.emf.uml22.ClassifierFacadeLogic#handleGetAssociatedClasses()
1276      */
1277     @Override
1278     protected Collection<ClassifierFacade> handleGetAssociatedClasses()
1279     {
1280         final Set<ClassifierFacade> associatedClasses = new LinkedHashSet<ClassifierFacade>();
1281 
1282         final List<AssociationEndFacade> associationEnds = this.getAssociationEnds();
1283         for (final AssociationEndFacade associationEndFacade : associationEnds)
1284         {
1285             associatedClasses.add(associationEndFacade.getOtherEnd().getType());
1286         }
1287 
1288         return associatedClasses;
1289     }
1290 
1291     /**
1292      * @see org.andromda.metafacades.emf.uml22.ClassifierFacadeLogic#handleGetAllAssociatedClasses()
1293      */
1294     @Override
1295     protected Set<ClassifierFacade> handleGetAllAssociatedClasses()
1296     {
1297         final Set<ClassifierFacade> associatedClasses = new LinkedHashSet<ClassifierFacade>();
1298         associatedClasses.addAll(this.getAssociatedClasses());
1299         for (final GeneralizableElementFacade gen : this.getGeneralizations())
1300         {
1301             final ClassifierFacade parent = (ClassifierFacade)gen;
1302             associatedClasses.addAll(parent.getAllAssociatedClasses());
1303         }
1304 
1305         return associatedClasses;
1306     }
1307 
1308     /**
1309      * @see org.andromda.metafacades.emf.uml22.ClassifierFacadeLogic#handleGetSuperClass()
1310      */
1311     @Override
1312     protected ClassifierFacade handleGetSuperClass()
1313     {
1314         final GeneralizableElementFacade superClass = this.getGeneralization();
1315         return (ClassifierFacade)(superClass instanceof ClassifierFacade ? superClass : null);
1316     }
1317 
1318     /**
1319      * @see org.andromda.metafacades.emf.uml22.ClassifierFacadeLogic#handleIsEmbeddedValue()
1320      */
1321     @Override
1322     protected boolean handleIsEmbeddedValue()
1323     {
1324         return this.hasStereotype(UMLProfile.STEREOTYPE_EMBEDDED_VALUE);
1325     }
1326 
1327     // Sort Operations by name, then by number of parameters, then by parameter names
1328     @SuppressWarnings("unused")
1329     private static class OperationComparator implements Comparator<Operation>
1330     {
1331         private static final long serialVersionUID = 1L;
1332         public int compare(final Operation operation1, final Operation operation2)
1333         {
1334             int rtn = operation1.getName().compareTo(operation2.getName());
1335             if (rtn == 0)
1336             {
1337                 rtn = operation1.getOwnedParameters().size() - operation1.getOwnedParameters().size();
1338                 if (rtn == 0)
1339                 {
1340                     int index = 0;
1341                     for (Parameter parameter : operation1.getOwnedParameters())
1342                     {
1343                         rtn = parameter.getName().compareTo(operation2.getOwnedParameters().get(index).getName());
1344                         if (rtn != 0)
1345                         {
1346                             break;
1347                         }
1348                         index++;
1349                     }
1350                 }
1351             }
1352             return rtn;
1353         }
1354     }
1355 }