001: package org.andromda.core.configuration;
002:
003: import java.io.File;
004: import java.io.IOException;
005: import java.io.Serializable;
006: import java.net.URL;
007: import java.util.ArrayList;
008: import java.util.Collection;
009: import java.util.HashMap;
010: import java.util.Iterator;
011: import java.util.List;
012: import java.util.Map;
013:
014: import org.andromda.core.common.ResourceUtils;
015:
016: /**
017: * Stores the model information for each model that AndroMDA will process.
018: *
019: * @author Chad Brandon
020: */
021: public class Model implements Serializable {
022: /**
023: * Stores whether or not a last modified check
024: * should be performed.
025: */
026: private boolean lastModifiedCheck = false;
027:
028: /**
029: * Whether or not to perform a last modified check on the model.
030: *
031: * @return Returns the lastModifiedCheck.
032: */
033: public boolean isLastModifiedCheck() {
034: return lastModifiedCheck;
035: }
036:
037: /**
038: * Sets whether or not to perform a last modified check when processing the model. If
039: * <code>true</code> the model will be checked for a timestamp before processing occurs.
040: *
041: * @param lastModifiedCheck true/false
042: */
043: public void setLastModifiedCheck(final boolean lastModifiedCheck) {
044: this .lastModifiedCheck = lastModifiedCheck;
045: }
046:
047: /**
048: * Stores the informationj about what packages should and shouldn't
049: * be processed.
050: */
051: private Filters packages = new Filters();
052:
053: /**
054: * Sets the processAll flag on the internal model packages instance
055: * of this model.
056: *
057: * @param processAllPackages whether or not all packages should be processed by default.
058: */
059: public void setProcessAllPackages(final boolean processAllPackages) {
060: packages.setApplyAll(processAllPackages);
061: }
062:
063: /**
064: * Stores the information about what packages should/shouldn't be processed.
065: *
066: * @return Returns the packages.
067: */
068: public Filters getPackages() {
069: return this .packages;
070: }
071:
072: /**
073: * Sets the model packages for this model. This indicates what
074: * packages should and should not be processed from this model.
075: *
076: * @param packages the packages to process.
077: */
078: public void setPackages(final Filters packages) {
079: this .packages = packages;
080: }
081:
082: /**
083: * Stores the informationj about what constraints should and shouldn't
084: * be enforced.
085: */
086: private Filters constraints = new Filters();
087:
088: /**
089: * Sets the applyAll flag on the internal filters instance
090: * of this model.
091: *
092: * @param enforceAllConstraints whether or not all constraints should be enforced by default.
093: */
094: public void setEnforceAllConstraints(
095: final boolean enforceAllConstraints) {
096: this .constraints.setApplyAll(enforceAllConstraints);
097: }
098:
099: /**
100: * Stores the information about what constraints should/shouldn't be enforced.
101: *
102: * @return Returns the constraints instance.
103: */
104: public Filters getConstraints() {
105: return this .constraints;
106: }
107:
108: /**
109: * Sets the constraints for this model. This indicates what
110: * constraints should and should not be processed from this model.
111: *
112: * @param constraints the packages to process.
113: */
114: public void setConstraints(final Filters constraints) {
115: this .constraints = constraints;
116: }
117:
118: /**
119: * The URL to the model.
120: */
121: private List uris = new ArrayList();
122:
123: /**
124: * Caches the urisAsStrings value (so we don't need
125: * to do the conversion more than once).
126: */
127: private String[] urisAsStrings = null;
128:
129: /**
130: * All URIs that make up the model.
131: *
132: * @return Returns the uri.
133: */
134: public String[] getUris() {
135: if (this .urisAsStrings == null) {
136: final int uriNumber = uris.size();
137: this .urisAsStrings = new String[uriNumber];
138: for (int ctr = 0; ctr < uriNumber; ctr++) {
139: urisAsStrings[ctr] = (uris.get(ctr)).toString();
140: }
141: }
142: return this .urisAsStrings;
143: }
144:
145: /**
146: * Adds the location as a URI to one of the model files.
147: *
148: * @param uri the URI to the model.
149: */
150: public void addUri(final String uri) {
151: try {
152: final URL url = ResourceUtils.toURL(uri);
153: if (url == null) {
154: throw new ConfigurationException(
155: "Model could not be loaded from invalid path --> '"
156: + uri + "'");
157: }
158: try {
159: // - Get around the fact the URL won't be released until the JVM
160: // has been terminated, when using the 'jar' uri protocol.
161: url.openConnection().setDefaultUseCaches(false);
162: } catch (final IOException exception) {
163: // - ignore the exception
164: }
165: this .uris.add(url);
166: } catch (final Throwable throwable) {
167: throw new ConfigurationException(throwable);
168: }
169: }
170:
171: /**
172: * Stores the transformations for this Configuration instance.
173: */
174: private final Collection transformations = new ArrayList();
175:
176: /**
177: * Adds a transformation to this configuration instance.
178: *
179: * @param transformation the transformation instance to add.
180: */
181: public void addTransformation(final Transformation transformation) {
182: this .transformations.add(transformation);
183: }
184:
185: /**
186: * Gets the transformations belonging to this configuration.
187: *
188: * @return the array of {@link Transformation} instances.
189: */
190: public Transformation[] getTransformations() {
191: return (Transformation[]) this .transformations
192: .toArray(new Transformation[0]);
193: }
194:
195: /**
196: * The locations in which to search for module.
197: */
198: private final Collection moduleSearchLocations = new ArrayList();
199:
200: /**
201: * Adds a module search location (these are the locations
202: * in which a search for module is performed).
203: *
204: * @param location a location path.
205: * @see #addModuleSearchLocation(String)
206: */
207: public void addModuleSearchLocation(final Location location) {
208: this .moduleSearchLocations.add(location);
209: }
210:
211: /**
212: * Adds a module search location path (a location
213: * without a pattern defined).
214: *
215: * @param path a location path.
216: * @see #addModuleSearchLocation(Location)
217: */
218: public void addModuleSearchLocation(final String path) {
219: if (path != null) {
220: final Location location = new Location();
221: location.setPath(path);
222: this .moduleSearchLocations.add(location);
223: }
224: }
225:
226: /**
227: * The type of model (i.e. uml-1.4, uml-2.0, etc).
228: */
229: private String type;
230:
231: /**
232: * Gets the type of the model (i.e. the type of metamodel this
233: * model is based upon).
234: *
235: * @return Returns the type.
236: */
237: public String getType() {
238: return this .type;
239: }
240:
241: /**
242: * Sets the type of model (i.e. the type of metamodel this model
243: * is based upon).
244: *
245: * @param type The type to set.
246: */
247: public void setType(final String type) {
248: this .type = type;
249: }
250:
251: /**
252: * Gets the module searach locations for this model instance.
253: *
254: * @return the module search locations.
255: * @see #getModuleSearchLocationPaths()
256: */
257: public Location[] getModuleSearchLocations() {
258: return (Location[]) this .moduleSearchLocations
259: .toArray(new Location[0]);
260: }
261:
262: /**
263: * Stores the path for each module search location in this configuration.
264: */
265: private String[] moduleSearchLocationPaths = null;
266:
267: /**
268: * Gets all found module search location paths for this model instance.
269: *
270: * @return the module search location paths.
271: * @see #getModuleSearchLocations()
272: */
273: public String[] getModuleSearchLocationPaths() {
274: if (this .moduleSearchLocationPaths == null) {
275: final Collection paths = new ArrayList();
276: for (final Iterator iterator = this .moduleSearchLocations
277: .iterator(); iterator.hasNext();) {
278: final Location location = (Location) iterator.next();
279: final URL[] resources = location.getResources();
280: final int resourceNumber = resources.length;
281: for (int ctr = 0; ctr < resourceNumber; ctr++) {
282: paths.add(resources[ctr].toString());
283: }
284: paths.add(location.getPath());
285: }
286: this .moduleSearchLocationPaths = (String[]) paths
287: .toArray(new String[0]);
288: }
289: return this .moduleSearchLocationPaths;
290: }
291:
292: /**
293: * Stores all resources including all resources found within the module search locations
294: * as well as a resource for the {@link #getUris()}.
295: */
296: private URL[] moduleSearchLocationResources = null;
297:
298: /**
299: * Gets the accumulation of all files found when combining the contents
300: * of all module search location paths and their patterns by which they
301: * are filtered as well as the model URI.
302: *
303: * @return all module search location files.
304: */
305: public URL[] getModuleSearchLocationResources() {
306: if (this .moduleSearchLocationResources == null) {
307: final Collection allResources = new ArrayList();
308: final Location[] locations = this
309: .getModuleSearchLocations();
310: for (int ctr = 0; ctr < locations.length; ctr++) {
311: final URL[] resources = locations[ctr].getResources();
312: for (int fileCtr = 0; fileCtr < resources.length; fileCtr++) {
313: allResources.add(resources[fileCtr]);
314: }
315: }
316: this .moduleSearchLocationResources = (URL[]) allResources
317: .toArray(new URL[0]);
318: }
319: return this .moduleSearchLocationResources;
320: }
321:
322: /**
323: * Gets the time of the latest modified uri of the model as a <code>long</code>.
324: * If it can not be determined <code>0</code> is returned.
325: *
326: * @return the time this model was last modified
327: */
328: public long getLastModified() {
329: long lastModifiedTime = 0;
330: for (final Iterator iterator = this .uris.iterator(); iterator
331: .hasNext();) {
332: final URL url = (URL) iterator.next();
333: final long modifiedTime = ResourceUtils
334: .getLastModifiedTime(url);
335: if (modifiedTime > lastModifiedTime) {
336: lastModifiedTime = modifiedTime;
337: }
338: }
339: return lastModifiedTime;
340: }
341:
342: /**
343: * @see java.lang.Object#toString()
344: */
345: public String toString() {
346: String toString = super .toString();
347: final String key = this .getKey();
348: if (key != null && key.trim().length() > 0) {
349: toString = key;
350: }
351: return toString;
352: }
353:
354: /**
355: * Stores the last modified times for each model at the time
356: * {@link #isChanged()} is called.
357: */
358: private static final Map modelModifiedTimes = new HashMap();
359:
360: /**
361: * The unique key that identifies this model.
362: */
363: private String key = null;
364:
365: /**
366: * Creates the unique key that identifies this model
367: * (its made up of a list of all the uris for this model
368: * concatinated).
369: *
370: * @return the unique key
371: */
372: private String getKey() {
373: if (this .key == null || this .key.trim().length() == 0) {
374: final StringBuffer buffer = new StringBuffer();
375: for (final Iterator iterator = this .uris.iterator(); iterator
376: .hasNext();) {
377: final URL uri = (URL) iterator.next();
378: buffer.append(new File(uri.getFile()));
379: if (iterator.hasNext()) {
380: buffer.append(", ");
381: }
382: }
383: this .key = buffer.toString();
384: }
385: return this .key;
386: }
387:
388: /**
389: * The repository to which this model belongs.
390: */
391: private Repository repository;
392:
393: /**
394: * Gets the repository to which this model belongs.
395: *
396: * @return the repository to which this model belongs.
397: */
398: public Repository getRepository() {
399: return this .repository;
400: }
401:
402: /**
403: * Sets the repository to which this model belongs.
404: *
405: * @param repository the repository configuration to which this model belongs.
406: */
407: void setRepository(final Repository repository) {
408: this .repository = repository;
409: }
410:
411: /**
412: * Indicates whether or not the given <code>model</code>
413: * has changed since the previous call to this method.
414: *
415: * @return true/false
416: */
417: public boolean isChanged() {
418: boolean changed = this .getUris().length > 0;
419: if (changed) {
420: final Object modelKey = this .getKey();
421: Map lastModifiedTimes = (Map) modelModifiedTimes
422: .get(modelKey);
423:
424: // - load up the last modified times (from the model and all its modules)
425: // if they haven't been loaded yet
426: if (lastModifiedTimes != null) {
427: final long modelLastModified = ((Long) lastModifiedTimes
428: .get(modelKey)).longValue();
429: changed = this .getLastModified() > modelLastModified;
430: if (!changed) {
431: // - check to see if any of the modules have changed if the model hasn't changed
432: final URL[] resources = this
433: .getModuleSearchLocationResources();
434: for (int ctr = 0; ctr < resources.length; ctr++) {
435: final URL resource = resources[ctr];
436: final Long lastModified = (Long) lastModifiedTimes
437: .get(resource);
438: if (lastModified != null) {
439: // - when we find the first modified module, break out
440: if (ResourceUtils
441: .getLastModifiedTime(resource) > lastModified
442: .longValue()) {
443: changed = true;
444: break;
445: }
446: }
447: }
448: }
449: }
450:
451: // - if our model (or modules) have changed re-load the last modified times
452: if (changed) {
453: this .loadLastModifiedTimes();
454: }
455: }
456: return changed;
457: }
458:
459: /**
460: * Loads (or re-loads) the last modified times from the
461: * {@link #getUris()} and the modules found on the module search path.
462: */
463: private void loadLastModifiedTimes() {
464: final Object modelKey = this .getKey();
465: Map lastModifiedTimes = (Map) modelModifiedTimes.get(modelKey);
466: if (lastModifiedTimes == null) {
467: lastModifiedTimes = new HashMap();
468: } else {
469: lastModifiedTimes.clear();
470: }
471: final URL[] resources = this .getModuleSearchLocationResources();
472: for (int ctr = 0; ctr < resources.length; ctr++) {
473: final URL resource = resources[ctr];
474: lastModifiedTimes.put(resource, new Long(ResourceUtils
475: .getLastModifiedTime(resource)));
476: }
477:
478: // - add the model key last so it overwrites any invalid ones
479: // we might have picked up from adding the module search location files.
480: lastModifiedTimes.put(modelKey,
481: new Long(this .getLastModified()));
482: modelModifiedTimes.put(modelKey, lastModifiedTimes);
483: }
484:
485: /**
486: * Clears out the current last modified times.
487: */
488: static void clearLastModifiedTimes() {
489: modelModifiedTimes.clear();
490: }
491: }
|