View Javadoc
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 }