1 package org.andromda.core.cartridge.template;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Iterator;
6 import org.andromda.core.common.ClassUtils;
7 import org.andromda.core.common.ExceptionUtils;
8 import org.andromda.core.common.Introspector;
9 import org.andromda.core.metafacade.MetafacadeBase;
10 import org.andromda.core.profile.Profile;
11 import org.apache.commons.lang.StringUtils;
12
13 /**
14 * Represents a single template <modelElement/> nested within the <modelElements/> element. It stores the
15 * actual metafacade instances which match the model element criteria (i.e. stereotype, type, etc) defined by this
16 * instance.
17 *
18 * @author Chad Brandon
19 * @author Bob Fields
20 * @see ModelElements
21 */
22 public class ModelElement
23 {
24 private String stereotype;
25
26 /**
27 * Gets the stereotype of this modelElement.
28 *
29 * @return Returns the stereotype.
30 */
31 public String getStereotype()
32 {
33 return Profile.instance().get(this.stereotype);
34 }
35
36 /**
37 * Returns <code>true</code> or <code>false</code> depending on whether or not this model element has a stereotype
38 * defined.
39 *
40 * @return true/false
41 */
42 public boolean hasStereotype()
43 {
44 return this.stereotype != null;
45 }
46
47 /**
48 * Stores the types defined for this model element.
49 */
50 private final Collection<Type> types = new ArrayList<Type>();
51
52 /**
53 * Gets all types associated with this model element.
54 *
55 * @return the collection of types.
56 */
57 public Collection<Type> getTypes()
58 {
59 return this.types;
60 }
61
62 /**
63 * Returns <code>true</code> or <code>false</code> depending on whether or not this model element has any type
64 * elements defined.
65 *
66 * @return true/false
67 */
68 public boolean hasTypes()
69 {
70 return !this.getTypes().isEmpty();
71 }
72
73 /**
74 * Sets the stereotype of the ModelElement.
75 *
76 * @param stereotype The stereotype to set.
77 */
78 public void setStereotype(final String stereotype)
79 {
80 this.stereotype = stereotype;
81 ExceptionUtils.checkEmpty("stereotype", this.stereotype);
82 }
83
84 /**
85 * Adds the <code>type</code> to the collection of types belonging to this model element.
86 *
87 * @param type the {@link Type}instance.
88 */
89 public void addType(final Type type)
90 {
91 ExceptionUtils.checkNull("type", type);
92 this.types.add(type);
93 }
94
95 /**
96 * Stores the name of the variable for this model element.
97 */
98 private String variable;
99
100 /**
101 * Gets the variable stereotype of this modelElement (this is what is made available to a template during
102 * processing).
103 *
104 * @return Returns the variable.
105 */
106 public String getVariable()
107 {
108 return this.variable;
109 }
110
111 /**
112 * Sets the variable name.
113 *
114 * @param variable The variable to set.
115 */
116 public void setVariable(final String variable)
117 {
118 this.variable = StringUtils.trimToEmpty(variable);
119 }
120
121 /**
122 * The metafacades for this model element.
123 */
124 private Collection<MetafacadeBase> metafacades = new ArrayList<MetafacadeBase>();
125
126 /**
127 * Sets the current metafacades that belong to this ModelElement instance.
128 *
129 * @param metafacades the collection of metafacades
130 */
131 public void setMetafacades(final Collection<MetafacadeBase> metafacades)
132 {
133 ExceptionUtils.checkNull("metafacades", metafacades);
134 this.metafacades = metafacades;
135 this.applyTypeFiltering();
136 }
137
138 /**
139 * Gets the metafacades that belong to this ModelElement instance. These are the actual elements from the model.
140 *
141 * @return the collection of metafacades.
142 */
143 public Collection<MetafacadeBase> getMetafacades()
144 {
145 return this.metafacades;
146 }
147
148 /**
149 * Applies any filtering by any types specified within this model element.
150 */
151 private void applyTypeFiltering()
152 {
153 if (this.hasTypes())
154 {
155 for (final Iterator iterator = this.metafacades.iterator(); iterator.hasNext();)
156 {
157 if (!accept(iterator.next()))
158 {
159 iterator.remove();
160 }
161 }
162 }
163 }
164
165 /**
166 * Checks the <code>object</code> to see whether or not its acceptable. It matches on the types and each type's
167 * properties. <strong>NOTE:</strong> protected visibility to improve performance from within {@link
168 * #applyTypeFiltering()}
169 *
170 * @param metafacade the metafacade to check
171 * @return true/false
172 */
173 private boolean accept(final Object metafacade)
174 {
175 boolean accept = true;
176 for (Type type : this.types)
177 {
178 if (StringUtils.isNotBlank(type.getName()))
179 {
180 try
181 {
182 accept = ClassUtils.loadClass(type.getName()).isAssignableFrom(metafacade.getClass());
183
184 // if the type matches the name, continue
185 if (accept)
186 {
187 for (Type.Property property : type.getProperties())
188 {
189 accept =
190 Introspector.instance().containsValidProperty(
191 metafacade,
192 property.getName(),
193 property.getValue());
194 if (!accept)
195 {
196 // break out of the loop on the first invalid
197 // property since all properties should be valid.
198 break;
199 }
200 }
201 }
202 }
203 catch (final Throwable throwable)
204 {
205 accept = false;
206 }
207 }
208 }
209 return accept;
210 }
211 }