View Javadoc
1   package org.andromda.core.metafacade;
2   
3   import java.util.ArrayList;
4   import java.util.Collection;
5   import java.util.HashSet;
6   import java.util.Iterator;
7   import java.util.LinkedHashMap;
8   import java.util.LinkedHashSet;
9   import java.util.List;
10  import java.util.ListIterator;
11  import java.util.Map;
12  import java.util.Set;
13  import org.andromda.core.common.ClassUtils;
14  import org.andromda.core.profile.Profile;
15  import org.apache.commons.lang.StringUtils;
16  
17  /**
18   * A meta facade mapping class. This class is a child of {@link MetafacadeMappings}
19   * (that is: instances of this class below to an instance of {@link MetafacadeMappings}).
20   *
21   * @author Chad Brandon
22   * @author Bob Fields
23   */
24  public class MetafacadeMapping
25  {
26      /**
27       * The meta facade for which this mapping applies.
28       */
29      private Class metafacadeClass = null;
30  
31      /**
32       * Gets the metafacadeClass for this mapping.
33       *
34       * @return Returns the metafacadeClass.
35       */
36      public Class getMetafacadeClass()
37      {
38          return metafacadeClass;
39      }
40  
41      /**
42       * Sets the metafacadeClassName for this mapping.
43       *
44       * @param metafacadeClassName The name of the metafacade class to set.
45       */
46      public void setMetafacadeClassName(final String metafacadeClassName)
47      {
48          try
49          {
50              this.metafacadeClass = ClassUtils.loadClass(StringUtils.trimToEmpty(metafacadeClassName));
51          }
52          catch (final Throwable throwable)
53          {
54              throw new MetafacadeMappingsException(throwable);
55          }
56      }
57  
58      /**
59       * The names of the mapping classes for which this mapping applies. The {@link #context},{@link #stereotypes}and this
60       * names make up the identifying key for this mapping.
61       */
62      private Set<String> mappingClassNames = new HashSet<String>();
63  
64      /**
65       * Gets the names of the metaobject classes used for this mapping.
66       *
67       * @return Returns the mappingClassNames.
68       */
69      protected  Set<String> getMappingClassNames()
70      {
71          // - if we have a mappingClassName defined, we use it
72          if (this.mappingClassNames.isEmpty())
73          {
74              // - attempt to get the inherited mapping since it doesn't exist on this class
75              this.mappingClassNames = MetafacadeUtils.getInheritedMappingClassNames(this);
76          }
77          return this.mappingClassNames;
78      }
79  
80      /**
81       * Indicates whether or not the mapping class has been present.
82       *
83       * @return whether or not the mapping class is present in this mapping.
84       */
85      final boolean isMappingClassNamePresent()
86      {
87          return !this.mappingClassNames.isEmpty();
88      }
89  
90      /**
91       * The name of the metaobject class to use for this mapping.
92       *
93       * @param mappingClassName The mappingClassName to set.
94       */
95      public void setMappingClassName(final String mappingClassName)
96      {
97          if(!StringUtils.isBlank(mappingClassName))
98          {
99              this.mappingClassNames.clear();
100             this.mappingClassNames.add(StringUtils.trimToEmpty(mappingClassName));
101         }
102     }
103 
104     /**
105      * Whether or not this mapping represents a <code>contextRoot</code>.
106      */
107     private boolean contextRoot = false;
108 
109     /**
110      * <p>
111      * Gets whether or not this mapping represents a <code>contextRoot</code>, by default a mapping is <strong>NOT
112      * </strong> a contextRoot. You'll want to specify this as true when other metafacades need to be created within the
113      * context of this metafacade. </p>
114      *
115      * @return Returns the contextRoot.
116      */
117     public boolean isContextRoot()
118     {
119         return this.contextRoot;
120     }
121 
122     /**
123      * Sets the name of the <code>contextRoot</code> for this mapping.
124      *
125      * @param contextRoot The contextRoot to set.
126      * @see #isContextRoot()
127      */
128     public void setContextRoot(final boolean contextRoot)
129     {
130         this.contextRoot = contextRoot;
131     }
132 
133     /**
134      * The stereotypes to which this mapping applies (all stereotypes must be present for this mapping to apply).
135      */
136     private final List<String> stereotypes = new ArrayList<String>();
137 
138     /**
139      * Adds a <code>stereotype</code> to the stereotypes.
140      *
141      * @param stereotype
142      */
143     public void addStereotype(final String stereotype)
144     {
145         this.stereotypes.add(stereotype);
146     }
147 
148     /**
149      * Gets the stereotypes which apply to this mapping.
150      *
151      * @return the names of the stereotypes
152      */
153     final List<String> getStereotypes()
154     {
155         for (final ListIterator<String> iterator = this.stereotypes.listIterator(); iterator.hasNext();)
156         {
157             iterator.set(Profile.instance().get(iterator.next()));
158         }
159         return this.stereotypes;
160     }
161 
162     /**
163      * Indicates whether or not this mapping has any stereotypes defined.
164      *
165      * @return true/false
166      */
167     final boolean hasStereotypes()
168     {
169         return !this.stereotypes.isEmpty();
170     }
171 
172     /**
173      * Used to hold references to language mapping classes.
174      */
175     private final Collection<String> propertyReferences = new LinkedHashSet<String>();
176 
177     /**
178      * Adds a mapping property reference. These are used to populate metafacade impl classes with mapping files, etc.
179      * The property reference applies to the given mapping.
180      *
181      * @param reference the name of the reference.
182      * @see MetafacadeMappings#addPropertyReference(String)
183      */
184     public void addPropertyReference(final String reference)
185     {
186         this.propertyReferences.add(reference);
187     }
188 
189     /**
190      * Returns all mapping references for this MetafacadeMapping instance.
191      * @return this.propertyReferences
192      */
193     public Collection<String> getPropertyReferences()
194     {
195         return this.propertyReferences;
196     }
197 
198     /**
199      * Used to hold the properties that should apply to the mapping element.
200      */
201     private PropertyGroup mappingProperties = null;
202 
203     /**
204      * Adds a mapping property. This are used to narrow the metafacade to which the mapping can apply. The properties
205      * must exist and must evaluate to the specified value if given for the mapping to match.
206      *
207      * @param name the name of the reference.
208      * @param value the default value of the property reference.
209      */
210     public void addMappingProperty(
211         final String name,
212         final String value)
213     {
214         if (value != null)
215         {
216             if (this.mappingProperties == null)
217             {
218                 this.mappingProperties = new PropertyGroup();
219 
220                 // we add the mapping properties to the mappingPropertyGroups
221                 // collection only once
222                 this.mappingPropertyGroups.add(this.mappingProperties);
223             }
224             this.mappingProperties.addProperty(new Property(
225                     name,
226                     value));
227         }
228     }
229 
230     /**
231      * Stores a collection of all property groups added through {@link #addPropertyReferences(java.util.Collection)}. These are
232      * property groups added from other mappings that return true when executing {@link #match(MetafacadeMapping)}.
233      */
234     private final Collection<PropertyGroup> mappingPropertyGroups = new ArrayList<PropertyGroup>();
235 
236     /**
237      * Adds the <code>propertyGroup</code> to the existing mapping property groups within this mapping.
238      *
239      * @param propertyGroup a property group for this mapping
240      */
241     final void addMappingPropertyGroup(final PropertyGroup propertyGroup)
242     {
243         this.mappingPropertyGroups.add(propertyGroup);
244     }
245 
246     /**
247      * Returns all mapping property groups for this MetafacadeMapping instance.
248      * @return this.mappingPropertyGroups
249      */
250     final Collection<PropertyGroup> getMappingPropertyGroups()
251     {
252         return this.mappingPropertyGroups;
253     }
254 
255     /**
256      * Gets the mapping properties associated this this mapping directly (contained within a {@link
257      * PropertyGroup}instance).
258      *
259      * @return the mapping property group.
260      */
261     final PropertyGroup getMappingProperties()
262     {
263         return this.mappingProperties;
264     }
265 
266     /**
267      * Indicates whether or not this mapping contains any mapping properties.
268      *
269      * @return true/false
270      */
271     final boolean hasMappingProperties()
272     {
273         return this.mappingProperties != null && !this.mappingProperties.getProperties().isEmpty();
274     }
275 
276     /**
277      * Adds all <code>propertyReferences</code> to the property references contained in this MetafacadeMapping
278      * instance.
279      *
280      * @param propertyReferences the property references to add.
281      */
282     public void addPropertyReferences(final Collection<String> propertyReferences)
283     {
284         if (propertyReferences != null)
285         {
286             this.propertyReferences.addAll(propertyReferences);
287         }
288     }
289 
290     /**
291      * The context to which this mapping applies.
292      */
293     private String context = "";
294 
295     /**
296      * Sets the context to which this mapping applies.
297      *
298      * @param context The metafacade context name to set.
299      */
300     public void setContext(final String context)
301     {
302         this.context = StringUtils.trimToEmpty(context);
303     }
304 
305     /**
306      * Gets the context to which this mapping applies.
307      *
308      * @return the name of the context
309      */
310     final String getContext()
311     {
312         return this.context;
313     }
314 
315     /**
316      * Indicates whether or not this mapping has a context.
317      *
318      * @return true/false
319      */
320     final boolean hasContext()
321     {
322         return StringUtils.isNotBlank(this.context);
323     }
324 
325     /**
326      * The "parent" metafacade mappings;
327      */
328     private MetafacadeMappings mappings;
329 
330     /**
331      * Sets the metafacade mappings instance to which this particular mapping belongs. (i.e. the parent) Note, that this
332      * is populated during the call to {@link MetafacadeMappings#addMapping(MetafacadeMapping)}.
333      *
334      * @param mappings the MetacadeMappings instance to which this mapping belongs.
335      */
336     final void setMetafacadeMappings(final MetafacadeMappings mappings)
337     {
338         this.mappings = mappings;
339     }
340 
341     /**
342      * Gets the "parent" MetafacadeMappings instance to which this mapping belongs.
343      *
344      * @return the parent metafacade mappings instance.
345      */
346     final MetafacadeMappings getMetafacadeMappings()
347     {
348         return this.mappings;
349     }
350 
351     /**
352      * Indicates whether or not the <code>mapping</code> matches this mapping. It matches on the following: <ul>
353      * <li>metafacadeClass</li> <li>mappingClassName</li> <li>stereotypes</li> </ul>
354      * @param mapping
355      * @return match this.getMappingClassName().equals(mapping.getMappingClassName())
356      */
357     final boolean match(final MetafacadeMapping mapping)
358     {
359         boolean match =
360             mapping != null && this.getMetafacadeClass().equals(mapping.getMetafacadeClass()) &&
361             this.getStereotypes().equals(mapping.getStereotypes()) && this.getContext().equals(mapping.getContext());
362 
363         // - if they match and the mappingClassNames are both non-null, verify they match
364         if (match && !this.mappingClassNames.isEmpty() && mapping != null && !mapping.mappingClassNames.isEmpty())
365         {
366             match = this.getMappingClassNames().equals(mapping.getMappingClassNames());
367         }
368         return match;
369     }
370 
371     /**
372      * @see Object#toString()
373      */
374     public String toString()
375     {
376         return super.toString() + '[' + this.getMetafacadeClass() + "], mappingClassName[" + this.mappingClassNames +
377         "], properties[" + this.getMappingProperties() + "], stereotypes" + this.stereotypes + ", context[" +
378         this.context + "], propertiesReferences" + this.getPropertyReferences();
379     }
380 
381     /**
382      * Represents a group of properties. Properties within a group are evaluated within an 'AND' expression.
383      * PropertyGroups are evaluated together as an 'OR' expressions (i.e. you 'OR' property groups together, and 'AND'
384      * properties together).
385      *
386      * @see MetafacadeMappings#addMapping(MetafacadeMapping)
387      */
388     static final class PropertyGroup
389     {
390         private final Map<String, Property> properties = new LinkedHashMap<String, Property>();
391 
392         /**
393          * Adds a property to the internal collection of properties.
394          *
395          * @param property the property to add to this group.
396          */
397         final void addProperty(final Property property)
398         {
399             final String name = property.getName();
400             if (!this.properties.containsKey(name))
401             {
402                 this.properties.put(
403                     name,
404                     property);
405             }
406         }
407 
408         /**
409          * Gets the currently internal collection of properties.
410          *
411          * @return the properties collection.
412          */
413         final Collection<Property> getProperties()
414         {
415             return this.properties.values();
416         }
417 
418         /**
419          * @see Object#toString()
420          */
421         public String toString()
422         {
423             final StringBuilder toString = new StringBuilder();
424             char separator = ':';
425             for (final Iterator<Property> iterator = this.getProperties().iterator(); iterator.hasNext();)
426             {
427                 final Property property = iterator.next();
428                 toString.append(property.getName());
429                 if (StringUtils.isNotBlank(property.getValue()))
430                 {
431                     toString.append(separator).append(property.getValue());
432                 }
433                 if (iterator.hasNext())
434                 {
435                     toString.append(separator);
436                 }
437             }
438             return toString.toString();
439         }
440     }
441 
442     /**
443      * Stores and provides access to the mapping element's nested &lt;property/&gt;.
444      */
445     static final class Property
446     {
447         private String name;
448         private String value;
449 
450         /**
451          * @param name
452          * @param value
453          */
454         Property(
455             final String name,
456             final String value)
457         {
458             this.name = StringUtils.trimToEmpty(name);
459             this.value = value;
460         }
461 
462         /**
463          * Gets the value of the <code>name</code> attribute on the <code>property</code> element.
464          *
465          * @return the name
466          */
467         final String getName()
468         {
469             return StringUtils.trimToEmpty(this.name);
470         }
471 
472         /**
473          * Gets the value of the <code>value</code> attribute defined on the <code>property</code> element.
474          *
475          * @return the value
476          */
477         final String getValue()
478         {
479             return StringUtils.trimToEmpty(this.value);
480         }
481     }
482 }