1 package org.andromda.core.configuration;
2
3 import java.io.Serializable;
4 import java.net.URL;
5 import java.util.Collection;
6 import java.util.LinkedHashMap;
7 import java.util.Map;
8 import org.andromda.core.common.ExceptionUtils;
9 import org.andromda.core.namespace.NamespaceComponent;
10 import org.andromda.core.namespace.NamespaceRegistry;
11 import org.andromda.core.namespace.PropertyDefinition;
12 import org.apache.log4j.Logger;
13
14 /**
15 * Directory of configurable Namespace objects. Namespace objects are used for configuring AndroMDA
16 * namespaces.
17 *
18 * @author Chad Brandon
19 * @author Bob Fields
20 * @see org.andromda.core.configuration.Namespace
21 */
22 public class Namespaces
23 implements Serializable
24 {
25 private static final long serialVersionUID = 34L;
26
27 /**
28 * The logger instance.
29 */
30 private static final Logger logger = Logger.getLogger(Namespaces.class);
31
32 /**
33 * This is passed as the cartridge name for the {@link #getProperty} method if we wish to use a 'default' Namespace
34 * for Plugins. This is so we don't need to define a specific mapping for each Plugin if we don't want. If a
35 * namespaceName exists with a specific Plugin name, then that will be used instead of the 'default'
36 */
37 public static final String DEFAULT = "default";
38
39 /**
40 * Stores all namespaces.
41 */
42 private final Map<String, Namespace> namespaces = new LinkedHashMap<String, Namespace>();
43
44 /**
45 * The shared instance.
46 */
47 private static Namespaces instance = null;
48
49 /**
50 * Returns the singleton instance of this Namespaces
51 *
52 * @return instance.
53 */
54 public static Namespaces instance()
55 {
56 if (instance == null)
57 {
58 instance = new Namespaces();
59 }
60 return instance;
61 }
62
63 /**
64 * Gets the namespaces registered in this namespaces instance.
65 *
66 * @return all namespaces.
67 */
68 public Collection<Namespace> getNamespaces()
69 {
70 return this.namespaces.values();
71 }
72
73 /**
74 * Adds a namespace to this collection of namespaces.
75 *
76 * @param namespace the Namespace to add to this instance.
77 */
78 public void addNamespace(final Namespace namespace)
79 {
80 this.namespaces.put(
81 namespace.getName(),
82 namespace);
83 }
84
85 /**
86 * Adds all <code>namespaces</code> to this instance.
87 *
88 * @param namespaces the array of namespaces to add.
89 */
90 public void addNamespaces(final Namespace[] namespaces)
91 {
92 if (namespaces != null && namespaces.length > 0)
93 {
94 final int namespaceNumber = namespaces.length;
95 for (int ctr = 0; ctr < namespaceNumber; ctr++)
96 {
97 this.addNamespace(namespaces[ctr]);
98 }
99 }
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 }