001: package org.andromda.core.metafacade;
002:
003: import java.util.ArrayList;
004: import java.util.Collection;
005: import java.util.Iterator;
006: import java.util.LinkedHashMap;
007: import java.util.LinkedHashSet;
008: import java.util.List;
009: import java.util.ListIterator;
010: import java.util.Map;
011:
012: import org.andromda.core.common.ClassUtils;
013: import org.andromda.core.profile.Profile;
014: import org.apache.commons.lang.StringUtils;
015:
016: /**
017: * A meta facade mapping class. This class is a child of {@link MetafacadeMappings}
018: * (that is: instances of this class below to an instance of {@link MetafacadeMappings}).
019: *
020: * @author Chad Brandon
021: */
022: public class MetafacadeMapping {
023: /**
024: * The meta facade for which this mapping applies.
025: */
026: private Class metafacadeClass = null;
027:
028: /**
029: * Gets the metafacadeClass for this mapping.
030: *
031: * @return Returns the metafacadeClass.
032: */
033: public Class getMetafacadeClass() {
034: return metafacadeClass;
035: }
036:
037: /**
038: * Sets the metafacadeClassName for this mapping.
039: *
040: * @param metafacadeClassName The name of the metafaacde class to set.
041: */
042: public void setMetafacadeClassName(final String metafacadeClassName) {
043: try {
044: this .metafacadeClass = ClassUtils.loadClass(StringUtils
045: .trimToEmpty(metafacadeClassName));
046: } catch (final Throwable throwable) {
047: throw new MetafacadeMappingsException(throwable);
048: }
049: }
050:
051: /**
052: * The name of the mapping class for which this mapping applies. The {@link #context},{@link #stereotypes}and this
053: * name make up the identifying key for this mapping.
054: */
055: private String mappingClassName = null;
056:
057: /**
058: * Gets the name of the metaobject class used for this mapping.
059: *
060: * @return Returns the mappingClassName.
061: */
062: protected String getMappingClassName() {
063: // - if we have a mappingClassName defined, we use it
064: if (this .mappingClassName == null
065: || this .mappingClassName.trim().length() == 0) {
066: // - attempt to get the inherited mapping since it doesn't exist on this class
067: this .mappingClassName = MetafacadeUtils
068: .getInheritedMappingClassName(this );
069: }
070: return this .mappingClassName;
071: }
072:
073: /**
074: * Indicates whether or not the mapping class has been present.
075: *
076: * @return whether or not the mapping class is present in this mapping.
077: */
078: final boolean isMappingClassNamePresent() {
079: return this .mappingClassName != null
080: && this .mappingClassName.trim().length() > 0;
081: }
082:
083: /**
084: * The name of the metaobject class to use for this mapping.
085: *
086: * @param mappingClassName The mappingClassName to set.
087: */
088: public void setMappingClassName(final String mappingClassName) {
089: this .mappingClassName = StringUtils
090: .trimToEmpty(mappingClassName);
091: }
092:
093: /**
094: * Whether or not this mapping represents a <code>contextRoot</code>.
095: */
096: private boolean contextRoot = false;
097:
098: /**
099: * <p/>
100: * Gets whether or not this mapping represents a <code>contextRoot</code>, by default a mapping is <strong>NOT
101: * </strong> a contextRoot. You'll want to specify this as true when other metafacades need to be created within the
102: * context of this metafacade. </p>
103: *
104: * @return Returns the contextRoot.
105: */
106: public boolean isContextRoot() {
107: return this .contextRoot;
108: }
109:
110: /**
111: * Sets the name of the <code>contextRoot</code> for this mapping.
112: *
113: * @param contextRoot The contextRoot to set.
114: * @see #isContextRoot()
115: */
116: public void setContextRoot(final boolean contextRoot) {
117: this .contextRoot = contextRoot;
118: }
119:
120: /**
121: * The stereotypes to which this mapping applies (all stereotypes must be present for this mapping to apply).
122: */
123: private final List stereotypes = new ArrayList();
124:
125: /**
126: * Adds a <code>stereotype</code> to the stereotypes.
127: *
128: * @param stereotype
129: */
130: public void addStereotype(final String stereotype) {
131: this .stereotypes.add(stereotype);
132: }
133:
134: /**
135: * Gets the stereotypes which apply to this mapping.
136: *
137: * @return the names of the stereotypes
138: */
139: final List getStereotypes() {
140: for (final ListIterator iterator = this .stereotypes
141: .listIterator(); iterator.hasNext();) {
142: iterator.set(Profile.instance().get(
143: (String) iterator.next()));
144: }
145: return this .stereotypes;
146: }
147:
148: /**
149: * Indicates whether or not this mapping has any stereotypes defined.
150: *
151: * @return true/false
152: */
153: final boolean hasStereotypes() {
154: return !this .stereotypes.isEmpty();
155: }
156:
157: /**
158: * Used to hold references to language mapping classes.
159: */
160: private final Collection propertyReferences = new LinkedHashSet();
161:
162: /**
163: * Adds a mapping property reference. These are used to populate metafacade impl classes with mapping files, etc.
164: * The property reference applies to the given mapping.
165: *
166: * @param reference the name of the reference.
167: * @see MetafacadeMappings#addPropertyReference(String)
168: */
169: public void addPropertyReference(final String reference) {
170: this .propertyReferences.add(reference);
171: }
172:
173: /**
174: * Returns all mapping references for this MetafacadeMapping instance.
175: */
176: public Collection getPropertyReferences() {
177: return this .propertyReferences;
178: }
179:
180: /**
181: * Used to hold the properties that should apply to the mapping element.
182: */
183: private PropertyGroup mappingProperties = null;
184:
185: /**
186: * Adds a mapping property. This are used to narrow the metafacade to which the mapping can apply. The properties
187: * must exist and must evaluate to the specified value if given for the mapping to match.
188: *
189: * @param name the name of the reference.
190: * @param value the default value of the property reference.
191: */
192: public void addMappingProperty(final String name, final String value) {
193: if (value != null) {
194: if (this .mappingProperties == null) {
195: this .mappingProperties = new PropertyGroup();
196:
197: // we add the mapping properties to the mappingPropertyGroups
198: // collection only once
199: this .mappingPropertyGroups.add(this .mappingProperties);
200: }
201: this .mappingProperties
202: .addProperty(new Property(name, value));
203: }
204: }
205:
206: /**
207: * Stores a collection of all property groups added through {@link #addPropertyReferences(java.util.Collection)}. These are
208: * property groups added from other mappings that return true when executing {@link #match(MetafacadeMapping)}.
209: */
210: private final Collection mappingPropertyGroups = new ArrayList();
211:
212: /**
213: * Adds the <code>propertyGroup</code> to the existing mapping property groups within this mapping.
214: *
215: * @param propertyGroup a property group for this mapping
216: */
217: final void addMappingPropertyGroup(final PropertyGroup propertyGroup) {
218: this .mappingPropertyGroups.add(propertyGroup);
219: }
220:
221: /**
222: * Returns all mapping property groups for this MetafacadeMapping instance.
223: */
224: final Collection getMappingPropertyGroups() {
225: return this .mappingPropertyGroups;
226: }
227:
228: /**
229: * Gets the mapping properties associated this this mapping directly (contained within a {@link
230: * PropertyGroup}instance).
231: *
232: * @return the mapping property group.
233: */
234: final PropertyGroup getMappingProperties() {
235: return this .mappingProperties;
236: }
237:
238: /**
239: * Indicates whether or not this mapping contains any mapping properties.
240: *
241: * @return true/false
242: */
243: final boolean hasMappingProperties() {
244: return this .mappingProperties != null
245: && !this .mappingProperties.getProperties().isEmpty();
246: }
247:
248: /**
249: * Adds all <code>propertyReferences</code> to the property references contained in this MetafacadeMapping
250: * instance.
251: *
252: * @param propertyReferences the property references to add.
253: */
254: public void addPropertyReferences(
255: final Collection propertyReferences) {
256: if (propertyReferences != null) {
257: this .propertyReferences.addAll(propertyReferences);
258: }
259: }
260:
261: /**
262: * The context to which this mapping applies.
263: */
264: private String context = "";
265:
266: /**
267: * Sets the context to which this mapping applies.
268: *
269: * @param context The metafacade context name to set.
270: */
271: public void setContext(final String context) {
272: this .context = StringUtils.trimToEmpty(context);
273: }
274:
275: /**
276: * Gets the context to which this mapping applies.
277: *
278: * @return the name of the context
279: */
280: final String getContext() {
281: return this .context;
282: }
283:
284: /**
285: * Indicates whether or not this mapping has a context.
286: *
287: * @return true/false
288: */
289: final boolean hasContext() {
290: return StringUtils.isNotEmpty(this .context);
291: }
292:
293: /**
294: * The "parent" metafacade mappings;
295: */
296: private MetafacadeMappings mappings;
297:
298: /**
299: * Sets the metafacade mappings instance to which this particular mapping belongs. (i.e. the parent) Note, that this
300: * is populated during the call to {@link MetafacadeMappings#addMapping(MetafacadeMapping)}.
301: *
302: * @param mappings the MetacadeMappings instance to which this mapping belongs.
303: */
304: final void setMetafacadeMappings(final MetafacadeMappings mappings) {
305: this .mappings = mappings;
306: }
307:
308: /**
309: * Gets the "parent" MetafacadeMappings instance to which this mapping belongs.
310: *
311: * @return the parent metafacade mappings instance.
312: */
313: final MetafacadeMappings getMetafacadeMappings() {
314: return this .mappings;
315: }
316:
317: /**
318: * Indicates whether or not the <code>mapping</code> matches this mapping. It matches on the following: <ul>
319: * <li>metafacadeClass</li> <li>mappingClassName</li> <li>stereotypes</li> </ul>
320: */
321: final boolean match(final MetafacadeMapping mapping) {
322: boolean match = mapping != null
323: && this .getMetafacadeClass().equals(
324: mapping.getMetafacadeClass())
325: && this .getStereotypes().equals(
326: mapping.getStereotypes())
327: && this .getContext().equals(mapping.getContext());
328:
329: // - if they match and the mappingClassNames are both non-null, verify they match
330: if (match && this .mappingClassName != null
331: && mapping.mappingClassName != null) {
332: match = this .getMappingClassName().equals(
333: mapping.getMappingClassName());
334: }
335: return match;
336: }
337:
338: /**
339: * @see java.lang.Object#toString()
340: */
341: public String toString() {
342: return super .toString() + "[" + this .getMetafacadeClass()
343: + "], mappingClassName[" + this .mappingClassName
344: + "], properties[" + this .getMappingProperties()
345: + "], stereotypes" + this .stereotypes + ", context["
346: + this .context + "], propertiesReferences"
347: + this .getPropertyReferences();
348: }
349:
350: /**
351: * Represents a group of properties. Properties within a group are evaluated within an 'AND' expression.
352: * PropertyGroups are evaluated together as an 'OR' expressions (i.e. you 'OR' property groups together, and 'AND'
353: * properties together).
354: *
355: * @see MetafacadeMappings#addMapping(MetafacadeMapping)
356: */
357: static final class PropertyGroup {
358: private final Map properties = new LinkedHashMap();
359:
360: /**
361: * Adds a property to the internal collection of properties.
362: *
363: * @param property the property to add to this group.
364: */
365: final void addProperty(final Property property) {
366: final String name = property.getName();
367: if (!this .properties.containsKey(name)) {
368: this .properties.put(name, property);
369: }
370: }
371:
372: /**
373: * Gets the currently internal collection of properties.
374: *
375: * @return the properties collection.
376: */
377: final Collection getProperties() {
378: return this .properties.values();
379: }
380:
381: /**
382: * @see java.lang.Object#toString()
383: */
384: public String toString() {
385: final StringBuffer toString = new StringBuffer();
386: char seperator = ':';
387: for (final Iterator iterator = this .getProperties()
388: .iterator(); iterator.hasNext();) {
389: final Property property = (Property) iterator.next();
390: toString.append(property.getName());
391: if (StringUtils.isNotEmpty(property.getValue())) {
392: toString.append(seperator);
393: toString.append(property.getValue());
394: }
395: if (iterator.hasNext()) {
396: toString.append(seperator);
397: }
398: }
399: return toString.toString();
400: }
401: }
402:
403: /**
404: * Stores and provides access to the mapping element's nested <property/>.
405: */
406: static final class Property {
407: private String name;
408: private String value;
409:
410: Property(final String name, final String value) {
411: this .name = StringUtils.trimToEmpty(name);
412: this .value = value;
413: }
414:
415: /**
416: * Gets the value of the <code>name</code> attribute on the <code>property</code> element.
417: *
418: * @return the name
419: */
420: final String getName() {
421: return StringUtils.trimToEmpty(this .name);
422: }
423:
424: /**
425: * Gets the value of the <code>value</code> attribute defined on the <code>property</code> element.
426: *
427: * @return the value
428: */
429: final String getValue() {
430: return StringUtils.trimToEmpty(this.value);
431: }
432: }
433: }
|