001: package org.andromda.core.namespace;
002:
003: import java.io.InputStream;
004:
005: import java.net.URL;
006:
007: import java.text.Collator;
008:
009: import java.util.ArrayList;
010: import java.util.Collection;
011: import java.util.Collections;
012: import java.util.Comparator;
013: import java.util.HashMap;
014: import java.util.Iterator;
015: import java.util.LinkedHashMap;
016: import java.util.List;
017: import java.util.Map;
018:
019: import org.andromda.core.common.AndroMDALogger;
020: import org.andromda.core.common.ComponentContainer;
021: import org.andromda.core.common.Merger;
022: import org.andromda.core.common.ResourceFinder;
023: import org.andromda.core.common.ResourceUtils;
024: import org.andromda.core.common.XmlObjectFactory;
025: import org.andromda.core.configuration.Namespaces;
026: import org.andromda.core.profile.Profile;
027: import org.apache.commons.lang.StringUtils;
028:
029: /**
030: * The registry for namespace components. Namespace components are components
031: * that reside within a namespace and can be configured by a namespace.
032: *
033: * @author Chad Brandon
034: */
035: public class NamespaceComponents {
036: /**
037: * The shared registry instance.
038: */
039: private static NamespaceComponents instance;
040:
041: /**
042: * Gets the shared instance of this registry.
043: *
044: * @return the shared registry instance.
045: */
046: public static final NamespaceComponents instance() {
047: if (instance == null) {
048: final XmlObjectFactory factory = XmlObjectFactory
049: .getInstance(NamespaceComponents.class);
050: instance = (NamespaceComponents) factory
051: .getObject(ResourceUtils
052: .getResource(CONFIGURATION_URI));
053: }
054: return instance;
055: }
056:
057: /**
058: * The URI to the descriptor for this instance.
059: */
060: private static final String CONFIGURATION_URI = "META-INF/andromda/namespace-components.xml";
061:
062: /**
063: * This class should not be instantiated through this constructor, it is
064: * only here to allow construction by the {@link XmlObjectFactory}. The
065: * instance of this class should be retrieved through the call to
066: * {@link #instance()}.
067: */
068: public NamespaceComponents() {
069: }
070:
071: /**
072: * Discovers all namespaces found on the classpath.
073: */
074: public void discover() {
075: AndroMDALogger.info("- discovering namespaces -");
076:
077: final XmlObjectFactory registryFactory = XmlObjectFactory
078: .getInstance(NamespaceRegistry.class);
079: final ComponentContainer container = ComponentContainer
080: .instance();
081:
082: // - discover all registries and sort them by name
083: final Map registryMap = this .discoverAllRegistries();
084: final List registries = new ArrayList(registryMap.keySet());
085: Collections.sort(registries, new NamespaceRegistryComparator());
086: for (final Iterator iterator = registries.iterator(); iterator
087: .hasNext();) {
088: NamespaceRegistry registry = (NamespaceRegistry) iterator
089: .next();
090: final URL resource = (URL) registryMap.get(registry);
091: final String registryName = registry.getName();
092:
093: // - only register if we haven't yet registered the namespace resource
094: if (!this .registeredNamespaceResources.contains(resource)) {
095: final Namespaces namespaces = Namespaces.instance();
096: final String namespace = registry.isShared() ? Namespaces.DEFAULT
097: : registry.getName();
098:
099: // - first merge on the namespace registry descriptor (if needed)
100: final Merger merger = Merger.instance();
101: boolean requiresMerge = merger.requiresMerge(namespace);
102: if (requiresMerge) {
103: registry = (NamespaceRegistry) registryFactory
104: .getObject(merger
105: .getMergedString(ResourceUtils
106: .getContents(resource),
107: namespace), resource);
108: }
109:
110: // - add the resource root
111: registry.addResourceRoot(this
112: .getNamespaceResourceRoot(resource));
113:
114: // - only log the fact we've found the namespace registry, if we haven't done it yet
115: if (!this .registeredRegistries.contains(registryName)) {
116: AndroMDALogger.info("found namespace --> '"
117: + registryName + "'");
118: this .registeredRegistries.add(registryName);
119: }
120:
121: final NamespaceRegistry existingRegistry = namespaces
122: .getRegistry(registryName);
123: if (existingRegistry != null) {
124: // - if we already have an existing registry with the same name, copy
125: // over any resources.
126: registry.copy(existingRegistry);
127: }
128:
129: // - add the registry to the namespaces instance
130: namespaces.addRegistry(registry);
131: final String[] components = registry
132: .getRegisteredComponents();
133: final int componentNumber = components.length;
134: for (int componentCtr = 0; componentCtr < componentNumber; componentCtr++) {
135: final String componentName = components[componentCtr];
136: final Component component = this
137: .getComponent(componentName);
138: if (component == null) {
139: throw new NamespaceComponentsException(
140: "'"
141: + componentName
142: + "' is not a valid namespace component");
143: }
144:
145: // - add any paths defined within the registry
146: component.addPaths(registry.getPaths(component
147: .getName()));
148: if (!container.isRegisteredByNamespace(
149: registryName, component.getType())) {
150: AndroMDALogger
151: .info(" + registering component '"
152: + componentName + "'");
153: final XmlObjectFactory componentFactory = XmlObjectFactory
154: .getInstance(component.getType());
155: final URL componentResource = this
156: .getNamespaceResource(registry
157: .getResourceRoots(), component
158: .getPaths());
159: if (componentResource == null) {
160: throw new NamespaceComponentsException(
161: "'"
162: + componentName
163: + "' is not a valid component within namespace '"
164: + namespace
165: + "' (the "
166: + componentName
167: + "'s descriptor can not be found)");
168: }
169: NamespaceComponent namespaceComponent = (NamespaceComponent) componentFactory
170: .getObject(componentResource);
171:
172: // - now perform a merge of the descriptor (if we require one)
173: if (requiresMerge) {
174: namespaceComponent = (NamespaceComponent) componentFactory
175: .getObject(merger
176: .getMergedString(
177: ResourceUtils
178: .getContents(componentResource),
179: namespace));
180: }
181:
182: namespaceComponent.setNamespace(registryName);
183: namespaceComponent
184: .setResource(componentResource);
185: container.registerComponentByNamespace(
186: registryName, component.getType(),
187: namespaceComponent);
188: }
189: }
190: }
191: this .registeredNamespaceResources.add(resource);
192: }
193:
194: // - initialize the profile
195: Profile.instance().initialize();
196: }
197:
198: /**
199: * Discovers all registries and loads them into a map with the registry as the key
200: * and the resource that configured the registry as the value.
201: *
202: * @return the registries in a Map
203: */
204: private Map discoverAllRegistries() {
205: final Map registries = new HashMap();
206: final URL[] resources = ResourceFinder.findResources(this
207: .getPath());
208: final XmlObjectFactory registryFactory = XmlObjectFactory
209: .getInstance(NamespaceRegistry.class);
210: if (resources != null && resources.length > 0) {
211: final int numberOfResources = resources.length;
212: for (int ctr = 0; ctr < numberOfResources; ctr++) {
213: final URL resource = resources[ctr];
214: final NamespaceRegistry registry = (NamespaceRegistry) registryFactory
215: .getObject(resource);
216: registries.put(registry, resource);
217: }
218: }
219: return registries;
220: }
221:
222: /**
223: * Keeps track of the namespaces resources that have been already registered.
224: */
225: private Collection registeredNamespaceResources = new ArrayList();
226:
227: /**
228: * Keeps track of the namespace registries that have been registered.
229: */
230: private Collection registeredRegistries = new ArrayList();
231:
232: /**
233: * Attempts to retrieve a resource relative to the given
234: * <code>resourceRoots</code> by computing the complete path from the given
235: * relative <code>path</code>. Retrieves the first valid one found.
236: *
237: * @param resourceRoots the resourceRoots from which to perform search.
238: * @param paths the relative paths to check.
239: * @return the resource found or null if invalid.
240: */
241: private URL getNamespaceResource(final URL[] resourceRoots,
242: final String[] paths) {
243: URL namespaceResource = null;
244: if (resourceRoots != null) {
245: final int numberOfResourceRoots = resourceRoots.length;
246: for (int ctr = 0; ctr < numberOfResourceRoots; ctr++) {
247: final URL resource = resourceRoots[ctr];
248: final int pathNumber = paths.length;
249: for (int ctr2 = 0; ctr2 < pathNumber; ctr2++) {
250: final String path = paths[ctr2];
251: InputStream stream = null;
252: try {
253: namespaceResource = new URL(ResourceUtils
254: .normalizePath(resource + path));
255: stream = namespaceResource.openStream();
256: stream.close();
257: } catch (final Throwable throwable) {
258: namespaceResource = null;
259: } finally {
260: stream = null;
261: }
262:
263: // - break if we've found one
264: if (namespaceResource != null) {
265: break;
266: }
267: }
268:
269: // - break if we've found one
270: if (namespaceResource != null) {
271: break;
272: }
273: }
274: }
275: return namespaceResource;
276: }
277:
278: /**
279: * Attempts to retrieve the resource root of the namespace; that is the
280: * directory (whether it be a regular directory or achive root) which this
281: * namespace spans.
282: *
283: * @param resource the resource from which to retrieve the root.
284: * @return the namespace root, or null if could not be found.
285: */
286: private URL getNamespaceResourceRoot(final URL resource) {
287: final String resourcePath = resource != null ? resource
288: .toString().replace('\\', '/') : null;
289: return ResourceUtils.toURL(StringUtils.replace(resourcePath,
290: this .path, ""));
291: }
292:
293: /**
294: * The path to search for the namespace descriptor.
295: */
296: private String path;
297:
298: /**
299: * Gets the path to the namespace registry descriptor.
300: *
301: * @return The path to a namespace registry descriptor.
302: */
303: public String getPath() {
304: return this .path;
305: }
306:
307: /**
308: * Sets the path to the namespace registry descriptor.
309: *
310: * @param path The path to a namespace registry descriptor.
311: */
312: public void setPath(String path) {
313: this .path = path;
314: }
315:
316: /**
317: * Stores the actual component definitions for this namespace registry.
318: */
319: private final Map components = new LinkedHashMap();
320:
321: /**
322: * Adds a new component to this namespace registry.
323: *
324: * @param component the component to add to this namespace registry.
325: */
326: public void addComponent(final Component component) {
327: if (component != null) {
328: this .components.put(component.getName(), component);
329: }
330: }
331:
332: /**
333: * Shuts down this component registry and reclaims any resources used.
334: */
335: public void shutdown() {
336: this .components.clear();
337: this .registeredNamespaceResources.clear();
338: this .registeredRegistries.clear();
339: instance = null;
340: }
341:
342: /**
343: * Retrieves a component by name (or returns null if one can not be found).
344: *
345: * @param name the name of the component to retrieve.
346: * @return the component instance or null.
347: */
348: private Component getComponent(final String name) {
349: return (Component) this .components.get(name);
350: }
351:
352: /**
353: * Used to sort namespace registries by name.
354: */
355: private final static class NamespaceRegistryComparator implements
356: Comparator {
357: private final Collator collator = Collator.getInstance();
358:
359: NamespaceRegistryComparator() {
360: collator.setStrength(Collator.PRIMARY);
361: }
362:
363: public int compare(final Object objectA, final Object objectB) {
364: final NamespaceRegistry a = (NamespaceRegistry) objectA;
365: final NamespaceRegistry b = (NamespaceRegistry) objectB;
366: return collator.compare(a.getName(), b.getName());
367: }
368: }
369: }
|