001package org.andromda.core.configuration; 002 003import java.io.Serializable; 004import java.net.URL; 005import java.util.Collection; 006import java.util.LinkedHashMap; 007import java.util.Map; 008import org.andromda.core.common.ExceptionUtils; 009import org.andromda.core.namespace.NamespaceComponent; 010import org.andromda.core.namespace.NamespaceRegistry; 011import org.andromda.core.namespace.PropertyDefinition; 012import org.apache.log4j.Logger; 013 014/** 015 * Directory of configurable Namespace objects. Namespace objects are used for configuring AndroMDA 016 * namespaces. 017 * 018 * @author Chad Brandon 019 * @author Bob Fields 020 * @see org.andromda.core.configuration.Namespace 021 */ 022public class Namespaces 023 implements Serializable 024{ 025 private static final long serialVersionUID = 34L; 026 027 /** 028 * The logger instance. 029 */ 030 private static final Logger logger = Logger.getLogger(Namespaces.class); 031 032 /** 033 * This is passed as the cartridge name for the {@link #getProperty} method if we wish to use a 'default' Namespace 034 * for Plugins. This is so we don't need to define a specific mapping for each Plugin if we don't want. If a 035 * namespaceName exists with a specific Plugin name, then that will be used instead of the 'default' 036 */ 037 public static final String DEFAULT = "default"; 038 039 /** 040 * Stores all namespaces. 041 */ 042 private final Map<String, Namespace> namespaces = new LinkedHashMap<String, Namespace>(); 043 044 /** 045 * The shared instance. 046 */ 047 private static Namespaces instance = null; 048 049 /** 050 * Returns the singleton instance of this Namespaces 051 * 052 * @return instance. 053 */ 054 public static Namespaces instance() 055 { 056 if (instance == null) 057 { 058 instance = new Namespaces(); 059 } 060 return instance; 061 } 062 063 /** 064 * Gets the namespaces registered in this namespaces instance. 065 * 066 * @return all namespaces. 067 */ 068 public Collection<Namespace> getNamespaces() 069 { 070 return this.namespaces.values(); 071 } 072 073 /** 074 * Adds a namespace to this collection of namespaces. 075 * 076 * @param namespace the Namespace to add to this instance. 077 */ 078 public void addNamespace(final Namespace namespace) 079 { 080 this.namespaces.put( 081 namespace.getName(), 082 namespace); 083 } 084 085 /** 086 * Adds all <code>namespaces</code> to this instance. 087 * 088 * @param namespaces the array of namespaces to add. 089 */ 090 public void addNamespaces(final Namespace[] namespaces) 091 { 092 if (namespaces != null && namespaces.length > 0) 093 { 094 final int namespaceNumber = namespaces.length; 095 for (int ctr = 0; ctr < namespaceNumber; ctr++) 096 { 097 this.addNamespace(namespaces[ctr]); 098 } 099 } 100 } 101 102 /** 103 * Gets the Namespace with the corresponding <code>namespaceName</code>. 104 * 105 * @param namespaceName 106 * @return the found Namespace 107 */ 108 public Namespace getNamespace(final String namespaceName) 109 { 110 return namespaces.get(namespaceName); 111 } 112 113 /** 114 * Indicates if the namespace is present within this instance. 115 * 116 * @param namespaceName the name of the namespace. 117 * @return true/false 118 */ 119 public boolean namespacePresent(final String namespaceName) 120 { 121 return this.getNamespace(namespaceName) != null; 122 } 123 124 /** 125 * Retrieves a property from the Namespace with the namespaceName. If the <code>ignore</code> attribute of the 126 * Property instance is set to <code>true</code> then lookup of the property will not be attempted and null will 127 * just be returned instead. If the property is not found and <code>ignore</code> is not <code>true</code> a warning 128 * message is logged. 129 * 130 * @param namespaceName name of the Plugin to which the namespace applies 131 * @param propertyName name of the namespace property to find. 132 * @return String the namespace property value. 133 */ 134 public Property getProperty( 135 final String namespaceName, 136 final String propertyName) 137 { 138 final Collection<Property> properties = this.getProperties( 139 namespaceName, 140 propertyName); 141 return properties == null || properties.isEmpty() ? 142 null : properties.iterator().next(); 143 } 144 145 /** 146 * Retrieves a property from the Namespace with the namespaceName. If the <code>ignore</code> attribute of the 147 * Property instance is set to <code>true</code> then lookup of the property will not be attempted and null will 148 * just be returned instead. If the property is not found and <code>ignore</code> is not <code>true</code> a warning 149 * message is logged. 150 * 151 * @param namespaceName name of the Plugin to which the namespace applies 152 * @param propertyName name of the namespace property to find. 153 * @return String the namespace property value. 154 */ 155 public Collection<Property> getProperties( 156 final String namespaceName, 157 final String propertyName) 158 { 159 return this.getProperties( 160 namespaceName, 161 propertyName, 162 true); 163 } 164 165 /** 166 * Retrieves a property from the Namespace with the namespaceName. If the <code>ignore</code> attribute of the 167 * Property instance is set to <code>true</code> then lookup of the property will not be attempted and null will 168 * just be returned instead. 169 * 170 * @param namespaceName name of the Plugin to which the namespace applies 171 * @param propertyName name of the namespace property to find. 172 * @param showWarning true/false if we'd like to display a warning if the property/namespace can not be found. 173 * @return the collection of properties. 174 */ 175 public Property getProperty( 176 final String namespaceName, 177 final String propertyName, 178 final boolean showWarning) 179 { 180 final Collection<Property> properties = this.getProperties( 181 namespaceName, 182 propertyName, 183 showWarning); 184 return properties == null || properties.isEmpty() ? 185 null : properties.iterator().next(); 186 } 187 188 /** 189 * Retrieves a property from the Namespace with the namespaceName. If the <code>ignore</code> attribute of the 190 * Property instance is set to <code>true</code> then lookup of the property will not be attempted and null will 191 * just be returned instead. 192 * 193 * @param namespaceName name of the Plugin to which the namespace applies 194 * @param propertyName name of the namespace property to find. 195 * @param showWarning true/false if we'd like to display a warning if the property/namespace can not be found. 196 * @return the collection of properties. 197 */ 198 public Collection<Property> getProperties( 199 final String namespaceName, 200 final String propertyName, 201 final boolean showWarning) 202 { 203 ExceptionUtils.checkEmpty( 204 "namespaceName", 205 namespaceName); 206 ExceptionUtils.checkEmpty( 207 "propertyName", 208 propertyName); 209 210 Collection<Property> property = null; 211 final Namespace namespace = this.namespaces.get(namespaceName); 212 if (namespace != null) 213 { 214 property = namespace.getProperties(propertyName); 215 } 216 217 // - since we couldn't find a Namespace for the specified cartridge, 218 // try to lookup the default 219 Namespace defaultNamespace = null; 220 if (property == null) 221 { 222 /*if (logger.isDebugEnabled()) 223 { 224 logger.debug("no namespace with name '" + namespaceName + "' found, looking for '" + Namespaces.DEFAULT + '\''); 225 }*/ 226 defaultNamespace = this.namespaces.get(Namespaces.DEFAULT); 227 if (defaultNamespace != null) 228 { 229 property = defaultNamespace.getProperties(propertyName); 230 } 231 } 232 233 if (namespace == null && defaultNamespace == null && showWarning) 234 { 235 logger.warn( 236 "WARNING! No '" + DEFAULT + "' or '" + namespaceName + "' namespace found, " + 237 "--> please define a namespace with at least one of these names, if you would like " + 238 "to ignore this message, define the namespace with " + "ignore set to 'true'"); 239 } 240 else if (property == null && showWarning) 241 { 242 logger.warn( 243 "WARNING! Namespaces '" + DEFAULT + "' and '" + namespaceName + "' have no property '" + propertyName + 244 "' defined --> please define this property in AT LEAST ONE of these two namespaces. " + 245 " If you want to 'ignore' this message, add the property to the namespace with ignore set to 'true'"); 246 } 247 return property; 248 } 249 250 /** 251 * Retrieves all property definitions for the given namespace. 252 * 253 * @param namespaceName the name of the namespace. 254 * @return the list of properties contained in the namespace. 255 */ 256 public PropertyDefinition[] getPropertyDefinitions(final String namespaceName) 257 { 258 final NamespaceRegistry registry = this.getRegistry(namespaceName); 259 return registry == null ? new PropertyDefinition[0] : registry.getPropertyDefinitions(); 260 } 261 262 /** 263 * Stores the namespace registries 264 */ 265 private final Map<String, NamespaceRegistry> registries = new LinkedHashMap<String, NamespaceRegistry>(); 266 267 /** 268 * Gets all available namespace registries (these are namespaces 269 * which have been discovered but are not necessarily configured). 270 * 271 * @return the collection of namespace registries 272 */ 273 public Collection<NamespaceRegistry> getNamespaceRegistries() 274 { 275 return this.registries.values(); 276 } 277 278 /** 279 * Adds a namespace registry to this instance. Namespace registries contain 280 * property definitions that are defined within a {@link NamespaceRegistry} 281 * descriptor (used to describe {@link NamespaceComponent}) instances. 282 * 283 * @param registry the {@link NamespaceRegistry} instance to add. 284 */ 285 public void addRegistry(final NamespaceRegistry registry) 286 { 287 if (registry != null) 288 { 289 // - first add the registry directly under its own name 290 this.registries.put( 291 registry.getName(), 292 registry); 293 294 // - if the registry is shared, we add the registry to the default namespace as well 295 if (registry.isShared()) 296 { 297 NamespaceRegistry defaultRegistry = this.getRegistry(Namespaces.DEFAULT); 298 if (defaultRegistry == null) 299 { 300 defaultRegistry = registry; 301 } 302 else 303 { 304 defaultRegistry.addPropertyDefinitions(registry.getPropertyDefinitions()); 305 } 306 this.registries.put( 307 Namespaces.DEFAULT, 308 defaultRegistry); 309 } 310 } 311 } 312 313 /** 314 * Indicates if the given <code>namespace</code> is 315 * shared or not. 316 * 317 * @param namespace the namespace to check. 318 * @return true/false. 319 */ 320 public boolean isShared(final String namespace) 321 { 322 final NamespaceRegistry registry = this.getRegistry(namespace); 323 return registry != null && registry.isShared(); 324 } 325 326 /** 327 * Attempts to get the value of a property from the given 328 * <code>namespace</code> with the given <code>name</code> by first attempting 329 * to retrieve it from the namespace and if no property is defined 330 * in the namespace we retrieve the default value (if one is defined). 331 * 332 * @param namespace the namespace for which to retreive the value. 333 * @param name the name of the value to retrieve. 334 * @return the value (or null if one couldn't be retrieved). 335 */ 336 public String getPropertyValue( 337 final String namespace, 338 final String name) 339 { 340 final PropertyDefinition definition = this.getPropertyDefinition( 341 namespace, 342 name); 343 if (definition == null) 344 { 345 throw new NamespacesException("Property '" + name + "' is not registered in either the '" + namespace + 346 "' or '" + Namespaces.DEFAULT + "' namespaces"); 347 } 348 final String defaultValue = definition.getDefaultValue(); 349 boolean warning = defaultValue == null && definition.isRequired(); 350 final Property property = this.getProperty( 351 namespace, 352 name, 353 warning); 354 return property != null && !property.isIgnore() ? property.getValue() : defaultValue; 355 } 356 357 /** 358 * Attempts to retrieve the resource root of the namespace. The resource root is the directory 359 * or archive root which contains all namespace resources. 360 * 361 * @param namespace the namespace of which to retrieve the resource. 362 * @return the resource or null if it could not be found. 363 */ 364 public URL[] getResourceRoots(final String namespace) 365 { 366 final NamespaceRegistry registry = this.getRegistry(namespace); 367 if (registry == null) 368 { 369 throw new NamespacesException('\'' + namespace + "' is not a registered namespace"); 370 } 371 372 final URL[] resourceRoots = registry.getResourceRoots(); 373 if (resourceRoots == null || resourceRoots.length == 0) 374 { 375 throw new NamespacesException("No resource root(s) could be retrieved for namespace '" + namespace + '\''); 376 } 377 return resourceRoots; 378 } 379 380 /** 381 * Indicates whether or not the <code>component</code> is present within the given 382 * <code>namespace</code> 383 * @param namespace the name of the namespace. 384 * @param component the name of the component type. 385 * @return true/false 386 */ 387 public boolean isComponentPresent( 388 final String namespace, 389 final String component) 390 { 391 boolean present = false; 392 final NamespaceRegistry registry = this.getRegistry(namespace); 393 if (namespace != null && component != null && registry != null) 394 { 395 final String[] components = registry.getRegisteredComponents(); 396 final int numberOfComponents = components.length; 397 for (int ctr = 0; ctr < numberOfComponents; ctr++) 398 { 399 if (component.equals(components[ctr])) 400 { 401 present = true; 402 break; 403 } 404 } 405 } 406 return present; 407 } 408 409 /** 410 * Attempts to get the value of a property from the given 411 * <code>namespace</code> with the given <code>name</code> by first attempting 412 * to retreive it from the namespace and if no property is defined 413 * in the namespace we retrieve the default value (if one is defined). 414 * 415 * @param namespace the namespace for which to retreive the value. 416 * @param name the name of the value to retrieve. 417 * @return the value (or null if one couldn't be retrieved). 418 */ 419 private PropertyDefinition getPropertyDefinition( 420 final String namespace, 421 final String name) 422 { 423 final NamespaceRegistry registry = this.getRegistry(namespace); 424 PropertyDefinition definition = null; 425 if (registry != null) 426 { 427 definition = registry.getPropertyDefinition(name); 428 } 429 if (definition == null) 430 { 431 final NamespaceRegistry defaultRegistry = this.getRegistry(Namespaces.DEFAULT); 432 if (defaultRegistry != null) 433 { 434 definition = defaultRegistry.getPropertyDefinition(name); 435 } 436 } 437 return definition; 438 } 439 440 /** 441 * Retrieves the namespace registry for the given namespace, or returns null 442 * if it doesn't exist. 443 * 444 * @param namespace the namespace name. 445 * @return the registry, or null if not found. 446 */ 447 public NamespaceRegistry getRegistry(final String namespace) 448 { 449 return this.registries.get(namespace); 450 } 451 452 /** 453 * Clears out the current namespaces. 454 */ 455 public void clear() 456 { 457 this.namespaces.clear(); 458 } 459}