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 }