View Javadoc
1   package org.andromda.core.metafacade;
2   
3   import java.io.Serializable;
4   import java.util.ArrayList;
5   import java.util.Collection;
6   import java.util.List;
7   import org.apache.commons.lang.StringUtils;
8   import org.apache.commons.lang.builder.ToStringBuilder;
9   import org.apache.log4j.Logger;
10  
11  /**
12   * Base class for all metafacades.
13   *
14   * @author <a href="http://www.mbohlen.de">Matthias Bohlen </a>
15   * @author Chad Brandon
16   * @author Wouter Zoons
17   * @author Bob Fields
18   */
19  public class MetafacadeBase implements Serializable, Comparable
20  {
21      private static final long serialVersionUID = 34L;
22      /**
23       * The meta object which this metafacade wraps.
24       */
25      private Object metaObject;
26  
27      /**
28       * Constructs a new instance of this class with the given <code>metaObject</code>
29       * and <code>context</code>.  The metaObject is the meta model element which
30       * this metafacade insulates. The <code>context</code> is the name of the
31       * context for this metafacade instance.
32       *
33       * @param metaObjectIn the meta object.
34       * @param contextIn the context of this meta object.
35       */
36      public MetafacadeBase(
37          final Object metaObjectIn,
38          final String contextIn)
39      {
40          this.metaObject = metaObjectIn;
41          this.context = contextIn;
42      }
43  
44      /**
45       * Retrieves the <code>owner</code> of this metafacade (for example: an operation owns its parameters, a class owns
46       * its attributes).
47       * <p>
48       * By default <code>null</code> is returned, however this method is overridden by subclasses which have a
49       * <code>parent</code> or <code>owner</code>. This is used to give the model validation messages more context as to
50       * where the validation error occurred. </p>
51       *
52       * @return the owner of this metafacade.
53       */
54      public Object getValidationOwner()
55      {
56          return null;
57      }
58  
59      /**
60       * Retrieves the <code>name</code> of this metafacade used within the validation messages.
61       * <p>
62       * By default <code>null</code> is returned, however this method is overridden by subclasses model elements that do
63       * have a name. </p>
64       *
65       * @return the owner of this metafacade.
66       */
67      public String getValidationName()
68      {
69          return null;
70      }
71  
72      /**
73       * Stores whether or not this metafacade has
74       * been initialized.
75       */
76      private boolean initialized = false;
77  
78      /**
79       * Sets the flag indicating this metafacade has been initialized.
80       */
81      final void setInitialized()
82      {
83          this.initialized = true;
84      }
85  
86      /**
87       * Indicates if this metafacade has been initialized.
88       *
89       * @return true/false
90       */
91      final boolean isInitialized()
92      {
93          return this.initialized;
94      }
95  
96      /**
97       * Validates that this facade's meta object is in a valid state.
98       * <p>
99       * Validate is called during metafacade creation by the factory. In the lifecycle of a metafacade it is validated
100      * only once, this is enforced by the caching within the metafacade factory.</p>
101      *
102      * @param validationMessages any messages generated during validation.
103      */
104     public final void validate(final Collection<ModelValidationMessage> validationMessages)
105     {
106         this.validateInvariants(validationMessages);
107     }
108 
109     /**
110      * <p>
111      * The logic of modeled OCL invariants from derived metafacades will be generated into this method and validation
112      * messages created and collected into the <code>messages</code> collection. This method is called by {@link #validate(Collection validationMessages)}
113      * </p>
114      * By default this method is empty. </p>
115      * @param messages Collection of org.andromda.core.metafacade.ModelValidationMessage
116      */
117     public void validateInvariants(final Collection<ModelValidationMessage> messages)
118     {
119         // By default this does nothing
120     }
121 
122     /**
123      * A lifecycle method, providing the ability for sub classes to take any action after the factory has completely
124      * initialized a metafacade, but before it has been validated for completeness.
125      */
126     public void initialize()
127     {
128         // By default this does nothing
129     }
130 
131     /**
132      * Returns one facade for a particular metaObject. Contacts the MetafacadeFactory to manufacture the proper
133      * metafacade. In certain cases <code>metaObject</code> can also be a metafacade instance; in that case the actual
134      * meta model element is retrieved from the metafacade and a metafacade is constructed from that.
135      *
136      * @param metaObjectIn the underlying meta model element. A metafacade is created for each.
137      * @return MetafacadeBase the facade
138      * @see MetafacadeFactory
139      */
140     protected MetafacadeBase shieldedElement(final Object metaObjectIn)
141     {
142         MetafacadeBase metafacade = null;
143         if (metaObjectIn != null)
144         {
145             final String contextIn = this.getContext();
146             metafacade = MetafacadeFactory.getInstance().createMetafacade(
147                     metaObjectIn,
148                     contextIn);
149 
150             // - The metafacade we've just got may have been found in the cache.
151             //   If so, it can have an arbitrary context (because it's cached).
152             //   We now need to set the context once again, so that all
153             //   other metafacade mappings based on the context work as expected.
154             if(metafacade != null)
155             {
156                 metafacade.resetMetafacadeContext(contextIn);
157             }
158         }
159         return metafacade;
160     }
161 
162     /**
163      * Returns a collection of facades for a collection of metaobjects. Contacts the MetafacadeFactory to manufacture
164      * the proper facades.
165      *
166      * @param metaobjects the objects to decorate
167      * @return Collection of MetafacadeBase-derived objects
168      * @see MetafacadeFactory
169      */
170     protected List shieldedElements(final Collection metaobjects)
171     {
172         final List metafacades = new ArrayList();
173         if (metaobjects != null)
174         {
175             for (final Object metaobject : metaobjects)
176             {
177                 metafacades.add(this.shieldedElement(metaobject));
178             }
179         }
180         return metafacades;
181     }
182 
183     /**
184      * Stores the context for this metafacade
185      */
186     private String context = null;
187 
188     /**
189      * Gets the context for this metafacade.
190      *
191      * @return the context name.
192      */
193     final String getContext()
194     {
195         String contextIn = this.context;
196         if (StringUtils.isBlank(contextIn))
197         {
198             contextIn = this.getMetafacadeName();
199         }
200         return contextIn;
201     }
202 
203     /**
204      * Sets the context for this metafacade. This is used to pass the context along from a metafacade specializing this
205      * metafacade (since we use delegate inheritance between shared and non-shared metafacades), as well as to pass the
206      * context to a metafacade being created within another.
207      *
208      * @param contextIn the metafacade interface name representing the context.
209      * @see MetafacadeMapping#isContextRoot()
210      */
211     public void setMetafacadeContext(final String contextIn)
212     {
213         this.context = contextIn;
214     }
215 
216     /**
217      * Resets the metafacade context after the metafacade was retrieved from the metafacade cache.
218      * DO NOT CALL THIS METHOD BY HAND, it is reserved for use in the MetafacadeFactory.
219      * @see org.andromda.core.metafacade.MetafacadeFactory
220      * @param contextIn the context defined by MetafacadeFactory
221      */
222     public void resetMetafacadeContext(String contextIn)
223     {
224         throw new IllegalStateException("Method resetMetafacadeContext() must be overridden by concrete metafacade class (" + this.getClass().getName() + ")! Please re-generate your metafacades using the new andromda-meta cartridge.");
225     }
226 
227     /**
228      * Stores the namespace for this metafacade
229      */
230     private String metafacadeNamespace = null;
231 
232     /**
233      * Gets the current namespace for this metafacade
234      *
235      * @return String
236      */
237     final String getMetafacadeNamespace()
238     {
239         return this.metafacadeNamespace;
240     }
241 
242     /**
243      * Sets the namespace for this metafacade.
244      *
245      * @param namespaceIn
246      */
247     final void setNamespace(final String namespaceIn)
248     {
249         this.metafacadeNamespace = namespaceIn;
250     }
251 
252     /**
253      * Returns true or false depending on whether the <code>property</code> is registered or not.
254      *
255      * @param property the name of the property to check.
256      * @return true/false on whether or not its registered.
257      */
258     protected boolean isConfiguredProperty(final String property)
259     {
260         return MetafacadeFactory.getInstance().isPropertyRegistered(
261             this,
262             property);
263     }
264 
265     /**
266      * Gets a configured property from the container. Note that the configured property must be registered first.
267      * Needs to be public so that a metafacade reference passed to a utility class can call this method.
268      *
269      * @param property the property name
270      * @return Object the configured property instance (mappings, etc)
271      */
272     public Object getConfiguredProperty(final String property)
273     {
274         return MetafacadeFactory.getInstance().getRegisteredProperty(
275             this,
276             property);
277     }
278 
279     /**
280      * Attempts to set the property with <code>name</code> having the specified <code>value</code> on this metafacade.
281      * @param nameIn
282      * @param value
283      */
284     protected void setProperty(
285         final String nameIn,
286         final Object value)
287     {
288         MetafacadeFactory.getInstance().registerProperty(
289             this.getMetafacadeName(),
290             nameIn,
291             value);
292     }
293 
294     /**
295      * Gets the current meta model object for this metafacade. This is used from {@link MetafacadeFactory} when
296      * attempting to construct a metafacade from a metafacade. This allows us to get the meta object for this metafacade
297      * so that the meta object can be used instead.
298      *
299      * @return the underlying model's meta object instance.
300      */
301     public final Object getMetaObject()
302     {
303         return this.metaObject;
304     }
305 
306     /**
307      * The metafacade logger instance.
308      */
309     protected Logger logger;
310 
311     /**
312      * Package-local setter, called by facade factory. Sets the logger to use inside the facade's code.
313      *
314      * @param loggerIn the logger to set
315      */
316     final void setLogger(final Logger loggerIn)
317     {
318         this.logger = loggerIn;
319     }
320 
321     /**
322      * The flag indicating whether or not this metafacade is a context root.
323      */
324     protected boolean contextRoot = false;
325 
326     /**
327      * Sets whether or not this metafacade represents a contextRoot. If it does represent a context root, then {@link
328      * #getMetafacadeContext()}returns the metafacade interface for this metafacade, otherwise the regular
329      * <code>context</code> is returned.
330      *
331      * @param contextRootIn
332      */
333     final void setContextRoot(final boolean contextRootIn)
334     {
335         this.contextRoot = contextRootIn;
336     }
337 
338     /**
339      * Gets the <code>context</code> for this metafacade. This is either the <code>contextRoot</code> (if one exists),
340      * or the regular <code>context</code>.
341      *
342      * @return the metafacade's context.
343      */
344     public String getMetafacadeContext()
345     {
346         String metafacadeContext = this.getContext();
347         if (this.contextRoot)
348         {
349             metafacadeContext = this.getMetafacadeName();
350         }
351         return metafacadeContext;
352     }
353 
354     /**
355      * Stores the name of the interface for this metafacade
356      */
357     private String metafacadeName = null;
358 
359     /**
360      * Gets the name for this metafacade.
361      *
362      * @return the metafacade's name.
363      */
364     final String getMetafacadeName()
365     {
366         if (this.metafacadeName == null)
367         {
368             this.metafacadeName = MetafacadeImpls.instance().getMetafacadeClass(this.getClass().getName()).getName();
369         }
370         return this.metafacadeName;
371     }
372 
373     /**
374      * @see Object#equals(Object)
375      */
376     @Override
377     public boolean equals(Object object)
378     {
379         boolean equals = false;
380         if (object instanceof MetafacadeBase)
381         {
382             MetafacadeBase that = (MetafacadeBase)object;
383             equals = this.metaObject.equals(that.metaObject);
384         }
385         return equals;
386     }
387 
388     /**
389      * @see Object#hashCode()
390      */
391     @Override
392     public int hashCode()
393     {
394         return this.metaObject.hashCode();
395     }
396 
397     /**
398      * In order to speed up the check for this property (which will happen many times), we cache it :-)
399      */
400     private Boolean metafacadePropertyCachingEnabled = null;
401 
402     /**
403      * A check to verify whether or not to make use of metafacade property caching. This method check if the {@link
404      * MetafacadeProperties#ENABLE_METAFACADE_PROPERTY_CACHING} namespace property has been set, if this is not the case
405      * then the caching will be enabled by default.
406      * @return this.metafacadePropertyCachingEnabled.booleanValue()
407      */
408     public final boolean isMetafacadePropertyCachingEnabled()
409     {
410         if (this.metafacadePropertyCachingEnabled == null)
411         {
412             final String enableCache =
413                 (String)this.getConfiguredProperty(MetafacadeProperties.ENABLE_METAFACADE_PROPERTY_CACHING);
414             this.metafacadePropertyCachingEnabled = Boolean.valueOf(enableCache);
415         }
416         return this.metafacadePropertyCachingEnabled;
417     }
418 
419     /**
420      * The instance of this class as the appropriate metafacade instance.
421      */
422     private MetafacadeBase THIS = null;
423 
424     /**
425      * The metafacade instance of <code>this</code>.  This should be used when
426      * you'd need to check if <code>this</code> was an instance of a given metafacade.
427      * For example: <code>THIS() instanceof SomeMetafacade</code>.
428      *
429      * This <strong>MUST</strong> be used instead of <em>this</em> in order to access the correct
430      * metafacade instance in the hierarchy (since we use delegate inheritance).
431      * @return this.shieldedElement(this.metaObject)
432      */
433     protected final MetafacadeBase THIS()
434     {
435         return this.THIS == null ? this.THIS = this.shieldedElement(this.metaObject) : this.THIS;
436     }
437 
438     /**
439      * @see Object#toString()
440      */
441     @Override
442     public String toString()
443     {
444         return super.toString() + '[' + this.metafacadeName + ": " + this.metaObject.getClass().getName() + ']';
445     }
446 
447     /**
448      * Allow sorting and use in TreeSet. ValidationName is overridden in descendants.
449      * @see Comparable#compareTo(Object)
450      */
451     public int compareTo(Object object)
452     {
453         if (object==null || !(object instanceof MetafacadeBase))
454         {
455             return -1;
456         }
457         MetafacadeBase metafacade = (MetafacadeBase)object;
458         if (metafacade.getValidationName()==null)
459         {
460             return -1;
461         }
462         return metafacade.getValidationName().compareTo(this.getValidationName());
463     }
464 
465     /**
466      * For debug purposes, when we need more than just class and metaclass name
467      * @return String representation of all properties including metaObject info
468      * @see Object#toString()
469      */
470     public String getDebug()
471     {
472         return ToStringBuilder.reflectionToString(this) + '[' + this.metafacadeName + ": " + this.metaObject.getClass().getName() + ": " + ToStringBuilder.reflectionToString(this.metaObject) + ']';
473     }
474 }