001package org.andromda.core.cartridge.template; 002 003import java.util.ArrayList; 004import java.util.Collection; 005import java.util.Iterator; 006import org.andromda.core.common.ClassUtils; 007import org.andromda.core.common.ExceptionUtils; 008import org.andromda.core.common.Introspector; 009import org.andromda.core.metafacade.MetafacadeBase; 010import org.andromda.core.profile.Profile; 011import org.apache.commons.lang.StringUtils; 012 013/** 014 * Represents a single template <modelElement/> nested within the <modelElements/> element. It stores the 015 * actual metafacade instances which match the model element criteria (i.e. stereotype, type, etc) defined by this 016 * instance. 017 * 018 * @author Chad Brandon 019 * @author Bob Fields 020 * @see ModelElements 021 */ 022public class ModelElement 023{ 024 private String stereotype; 025 026 /** 027 * Gets the stereotype of this modelElement. 028 * 029 * @return Returns the stereotype. 030 */ 031 public String getStereotype() 032 { 033 return Profile.instance().get(this.stereotype); 034 } 035 036 /** 037 * Returns <code>true</code> or <code>false</code> depending on whether or not this model element has a stereotype 038 * defined. 039 * 040 * @return true/false 041 */ 042 public boolean hasStereotype() 043 { 044 return this.stereotype != null; 045 } 046 047 /** 048 * Stores the types defined for this model element. 049 */ 050 private final Collection<Type> types = new ArrayList<Type>(); 051 052 /** 053 * Gets all types associated with this model element. 054 * 055 * @return the collection of types. 056 */ 057 public Collection<Type> getTypes() 058 { 059 return this.types; 060 } 061 062 /** 063 * Returns <code>true</code> or <code>false</code> depending on whether or not this model element has any type 064 * elements defined. 065 * 066 * @return true/false 067 */ 068 public boolean hasTypes() 069 { 070 return !this.getTypes().isEmpty(); 071 } 072 073 /** 074 * Sets the stereotype of the ModelElement. 075 * 076 * @param stereotype The stereotype to set. 077 */ 078 public void setStereotype(final String stereotype) 079 { 080 this.stereotype = stereotype; 081 ExceptionUtils.checkEmpty("stereotype", this.stereotype); 082 } 083 084 /** 085 * Adds the <code>type</code> to the collection of types belonging to this model element. 086 * 087 * @param type the {@link Type}instance. 088 */ 089 public void addType(final Type type) 090 { 091 ExceptionUtils.checkNull("type", type); 092 this.types.add(type); 093 } 094 095 /** 096 * Stores the name of the variable for this model element. 097 */ 098 private String variable; 099 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}