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 }