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
9 import org.andromda.core.common.ExceptionUtils;
10 import org.andromda.core.namespace.NamespaceComponent;
11 import org.andromda.core.namespace.NamespaceRegistry;
12 import org.andromda.core.namespace.PropertyDefinition;
13 import org.apache.log4j.Logger;
14
15
16 /***
17 * Directory of configurable Namespace objects. Namespace objects are used for configuring AndroMDA
18 * namespaces.
19 *
20 * @author Chad Brandon
21 * @see org.andromda.core.configuration.Namespace
22 */
23 public class Namespaces
24 implements Serializable
25 {
26 /***
27 * The logger instance.
28 */
29 private static final Logger logger = Logger.getLogger(Namespaces.class);
30
31 /***
32 * This is passed as the cartridge name for the {@link #getProperty} method if we wish to use a 'default' Namespace
33 * for Plugins. This is so we don't need to define a specific mapping for each Plugin if we don't want. If a
34 * namespaceName exists with a specific Plugin name, then that will be used instead of the 'default'
35 */
36 public static final String DEFAULT = "default";
37
38 /***
39 * Stores all namespaces.
40 */
41 private final Map namespaces = new LinkedHashMap();
42
43 /***
44 * The shared instance.
45 */
46 private static Namespaces instance = null;
47
48 /***
49 * Returns the singleton instance of this Namespaces
50 *
51 * @return instance.
52 */
53 public static Namespaces instance()
54 {
55 if (instance == null)
56 {
57 instance = new Namespaces();
58 }
59 return instance;
60 }
61
62 /***
63 * Gets the namespaces registered in this namespaces instance.
64 *
65 * @return all namespaces.
66 */
67 public Collection getNamespaces()
68 {
69 return this.namespaces.values();
70 }
71
72 /***
73 * Adds a namespace to this collection of namespaces.
74 *
75 * @param namespace the Namespace to add to this instance.
76 */
77 public void addNamespace(final Namespace namespace)
78 {
79 this.namespaces.put(
80 namespace.getName(),
81 namespace);
82 }
83
84 /***
85 * Adds all <code>namespaces</code> to this instance.
86 *
87 * @param namespaces the array of namespaces to add.
88 */
89 public void addNamespaces(final Namespace[] namespaces)
90 {
91 if (namespaces != null && namespaces.length > 0)
92 {
93 final int namespaceNumber = namespaces.length;
94 for (int ctr = 0; ctr < namespaceNumber; ctr++)
95 {
96 this.addNamespace(namespaces[ctr]);
97 }
98 }
99 }
100
101 /***
102 * Gets the Namespace with the corresponding <code>namespaceName</code>.
103 *
104 * @param namespaceName
105 * @return the found Namespace
106 */
107 public Namespace getNamespace(final String namespaceName)
108 {
109 return (Namespace)namespaces.get(namespaceName);
110 }
111
112 /***
113 * Indicates if the namespace is present within this instance.
114 *
115 * @param namespaceName the name of the namespace.
116 * @return true/false
117 */
118 public boolean namespacePresent(final String namespaceName)
119 {
120 return this.getNamespace(namespaceName) != null;
121 }
122
123 /***
124 * Retrieves a property from the Namespace with the namespaceName. If the <code>ignore</code> attribute of the
125 * Property instance is set to <code>true</code> then lookup of the property will not be attempted and null will
126 * just be returned instead. If the propety is not found and <code>ignore<code> is not <code>true</code> a warning
127 * message is logged.
128 *
129 * @param namespaceName name of the Plugin to which the namespace applies
130 * @param propertyName name of the namespace property to find.
131 * @return String the namespace property value.
132 */
133 public Property getProperty(
134 final String namespaceName,
135 final String propertyName)
136 {
137 return this.getProperty(
138 namespaceName,
139 propertyName,
140 true);
141 }
142
143 /***
144 * Retrieves a property from the Namespace with the namespaceName. If the <code>ignore</code> attribute of the
145 * Property instance is set to <code>true</code> then lookup of the property will not be attempted and null will
146 * just be returned instead.
147 *
148 * @param namespaceName name of the Plugin to which the namespace applies
149 * @param propertyName name of the namespace property to find.
150 * @param showWarning true/false if we'd like to display a warning if the property/namespace can not be found.
151 * @return String the namespace property value.
152 */
153 public Property getProperty(
154 final String namespaceName,
155 final String propertyName,
156 final boolean showWarning)
157 {
158 ExceptionUtils.checkEmpty(
159 "namespaceName",
160 namespaceName);
161 ExceptionUtils.checkEmpty(
162 "propertyName",
163 propertyName);
164
165 Property property = null;
166 final Namespace namespace = (Namespace)namespaces.get(namespaceName);
167 if (namespace != null)
168 {
169 property = namespace.getProperty(propertyName);
170 }
171
172
173
174 Namespace defaultNamespace = null;
175 if (property == null)
176 {
177 if (logger.isDebugEnabled())
178 {
179 logger.debug("no namespace with name '" + namespaceName + "' found, looking for '" + Namespaces.DEFAULT + "'");
180 }
181 defaultNamespace = (Namespace)namespaces.get(Namespaces.DEFAULT);
182 if (defaultNamespace != null)
183 {
184 property = defaultNamespace.getProperty(propertyName);
185 }
186 }
187
188 if (namespace == null && defaultNamespace == null && showWarning)
189 {
190 logger.warn(
191 "WARNING! No '" + DEFAULT + "' or '" + namespaceName + "' namespace found, " +
192 "--> please define a namespace with at least one of these names, if you would like " +
193 "to ignore this message, define the namespace with " + "ignore set to 'true'");
194 }
195 else if (property == null && showWarning)
196 {
197 logger.warn(
198 "WARNING! Namespaces '" + DEFAULT + "' and '" + namespaceName + "' have no property '" + propertyName +
199 "' defined --> please define this property in AT LEAST ONE of these two namespaces. " +
200 " If you want to 'ignore' this message, add the property to the namespace with ignore set to 'true'");
201 }
202 return property;
203 }
204
205 /***
206 * Retrieves all property definitions for the given namespace.
207 *
208 * @param namespaceName the name of the namespace.
209 * @return the list of properties contained in the namespace.
210 */
211 public PropertyDefinition[] getPropertyDefinitions(final String namespaceName)
212 {
213 final NamespaceRegistry registry = this.getRegistry(namespaceName);
214 return registry == null ? new PropertyDefinition[0] : registry.getPropertyDefinitions();
215 }
216
217 /***
218 * Stores the namespace registries
219 */
220 private final Map registries = new LinkedHashMap();
221
222 /***
223 * Gets all available namespace registries (these are namespaces
224 * which have been discovered but are not necessarily configured).
225 *
226 * @return the collection of namespace registries
227 */
228 public Collection getNamespaceRegistries()
229 {
230 return this.registries.values();
231 }
232
233 /***
234 * Adds a namespace registry to this instance. Namespace registries contain
235 * property definitions that are defined within a {@link NamespaceRegistry}
236 * descriptor (used to describe {@link NamespaceComponent}) instances.
237 *
238 * @param registry the {@link NamespaceRegistry} instance to add.
239 */
240 public void addRegistry(final NamespaceRegistry registry)
241 {
242 if (registry != null)
243 {
244
245 this.registries.put(
246 registry.getName(),
247 registry);
248
249
250 if (registry.isShared())
251 {
252 NamespaceRegistry defaultRegistry = this.getRegistry(Namespaces.DEFAULT);
253 if (defaultRegistry == null)
254 {
255 defaultRegistry = registry;
256 }
257 else
258 {
259 defaultRegistry.addPropertyDefinitions(registry.getPropertyDefinitions());
260 }
261 this.registries.put(
262 Namespaces.DEFAULT,
263 defaultRegistry);
264 }
265 }
266 }
267
268 /***
269 * Indicates if the given <code>namespace</code> is
270 * shared or not.
271 *
272 * @param namespace the namespace to check.
273 * @return true/false.
274 */
275 public boolean isShared(final String namespace)
276 {
277 final NamespaceRegistry registry = this.getRegistry(namespace);
278 return registry != null && registry.isShared();
279 }
280
281 /***
282 * Attempts to get the value of a property from the given
283 * <code>namespace</code> with the given <code>name</code> by first attempting
284 * to retrieve it from the namespace and if no property is defined
285 * in the namespace we retrieve the default value (if one is defined).
286 *
287 * @param namespace the namespace for which to retreive the value.
288 * @param name the name of the value to retrieve.
289 * @return the value (or null if one couldn't be retrieved).
290 */
291 public String getPropertyValue(
292 final String namespace,
293 final String name)
294 {
295 final PropertyDefinition definition = this.getPropertyDefinition(
296 namespace,
297 name);
298 if (definition == null)
299 {
300 throw new NamespacesException("Property '" + name + "' is not registered in either the '" + namespace +
301 "' or '" + Namespaces.DEFAULT + "' namespaces");
302 }
303 final String defaultValue = definition.getDefaultValue();
304 boolean warning = defaultValue == null && definition.isRequired();
305 final Property property = this.getProperty(
306 namespace,
307 name,
308 warning);
309 return property != null && !property.isIgnore() ? property.getValue() : defaultValue;
310 }
311
312 /***
313 * Attempts to retrieve the resource root of the namespace. The resource root is the directory
314 * or archive root which contains all namespace resources.
315 *
316 * @param namespace the namespace of which to retrieve the resource.
317 * @return the resource or null if it could not be found.
318 */
319 public URL[] getResourceRoots(final String namespace)
320 {
321 final NamespaceRegistry registry = this.getRegistry(namespace);
322 if (registry == null)
323 {
324 throw new NamespacesException("'" + namespace + "' is not a registered namespace");
325 }
326
327 final URL[] resourceRoots = registry.getResourceRoots();
328 if (resourceRoots == null || resourceRoots.length == 0)
329 {
330 throw new NamespacesException("No resource root(s) could be retrieved for namespace '" + namespace + "'");
331 }
332 return resourceRoots;
333 }
334
335 /***
336 * Indicates whether or not the <code>component</code> is present within the given
337 * <code>namespace</code>
338 * @param namespace the name of the namespace.
339 * @param component the name of the component type.
340 * @return true/false
341 */
342 public boolean isComponentPresent(
343 final String namespace,
344 final String component)
345 {
346 boolean present = false;
347 final NamespaceRegistry registry = this.getRegistry(namespace);
348 if (namespace != null && component != null && registry != null)
349 {
350 final String[] components = registry.getRegisteredComponents();
351 final int numberOfComponents = components.length;
352 for (int ctr = 0; ctr < numberOfComponents; ctr++)
353 {
354 if (component.equals(components[ctr]))
355 {
356 present = true;
357 break;
358 }
359 }
360 }
361 return present;
362 }
363
364 /***
365 * Attempts to get the value of a property from the given
366 * <code>namespace</code> with the given <code>name</code> by first attempting
367 * to retreive it from the namespace and if no property is defined
368 * in the namespace we retrieve the default value (if one is defined).
369 *
370 * @param namespace the namespace for which to retreive the value.
371 * @param name the name of the value to retrieve.
372 * @return the value (or null if one couldn't be retrieved).
373 */
374 private PropertyDefinition getPropertyDefinition(
375 final String namespace,
376 final String name)
377 {
378 final NamespaceRegistry registry = this.getRegistry(namespace);
379 PropertyDefinition definition = null;
380 if (registry != null)
381 {
382 definition = registry.getPropertyDefinition(name);
383 }
384 if (definition == null)
385 {
386 final NamespaceRegistry defaultRegistry = this.getRegistry(Namespaces.DEFAULT);
387 if (defaultRegistry != null)
388 {
389 definition = defaultRegistry.getPropertyDefinition(name);
390 }
391 }
392 return definition;
393 }
394
395 /***
396 * Retrieves the namespace registry for the given namespace, or returns null
397 * if it doesn't exist.
398 *
399 * @param namespace the namespace name.
400 * @return the registry, or null if not found.
401 */
402 public NamespaceRegistry getRegistry(final String namespace)
403 {
404 return (NamespaceRegistry)this.registries.get(namespace);
405 }
406
407 /***
408 * Clears out the current namespaces.
409 */
410 public void clear()
411 {
412 this.namespaces.clear();
413 }
414 }