View Javadoc

1   package org.andromda.core.configuration;
2   
3   import java.io.File;
4   import java.io.IOException;
5   import java.io.Serializable;
6   import java.net.URL;
7   import java.util.ArrayList;
8   import java.util.Collection;
9   import java.util.HashMap;
10  import java.util.Iterator;
11  import java.util.List;
12  import java.util.Map;
13  
14  import org.andromda.core.common.ResourceUtils;
15  
16  
17  /***
18   * Stores the model information for each model that AndroMDA will process.
19   *
20   * @author Chad Brandon
21   */
22  public class Model
23      implements Serializable
24  {
25      /***
26       * Stores whether or not a last modified check
27       * should be performed.
28       */
29      private boolean lastModifiedCheck = false;
30  
31      /***
32       * Whether or not to perform a last modified check on the model.
33       *
34       * @return Returns the lastModifiedCheck.
35       */
36      public boolean isLastModifiedCheck()
37      {
38          return lastModifiedCheck;
39      }
40  
41      /***
42       * Sets whether or not to perform a last modified check when processing the model. If
43       * <code>true</code> the model will be checked for a timestamp before processing occurs.
44       *
45       * @param lastModifiedCheck true/false
46       */
47      public void setLastModifiedCheck(final boolean lastModifiedCheck)
48      {
49          this.lastModifiedCheck = lastModifiedCheck;
50      }
51  
52      /***
53       * Stores the informationj about what packages should and shouldn't
54       * be processed.
55       */
56      privateFilters packages = new Filters()/package-summary.html">ong> Filters packages = new Filters();
57  
58      /***
59       * Sets the processAll flag on the internal model packages instance
60       * of this model.
61       *
62       * @param processAllPackages whether or not all packages should be processed by default.
63       */
64      public void setProcessAllPackages(final boolean processAllPackages)
65      {
66          packages.setApplyAll(processAllPackages);
67      }
68  
69      /***
70       * Stores the information about what packages should/shouldn't be processed.
71       *
72       * @return Returns the packages.
73       */
74      public Filters getPackages()
75      {
76          return</strong> this.packages;
77      }
78  
79      /***
80       * Sets the model packages for this model.  This indicates what
81       * packages should and should not be processed from this model.
82       *
83       * @param packages the packages to process.
84       */
85      publicFilters packages)/package-summary.html">ong> void setPackages(final Filters packages)
86      {
87          this.packages = packages;
88      }
89  
90      /***
91       * Stores the informationj about what constraints should and shouldn't
92       * be enforced.
93       */
94      private Filters constraints = new Filters();
95  
96      /***
97       * Sets the applyAll flag on the internal filters instance
98       * of this model.
99       *
100      * @param enforceAllConstraints whether or not all constraints should be enforced by default.
101      */
102     public void setEnforceAllConstraints(final boolean enforceAllConstraints)
103     {
104         this.constraints.setApplyAll(enforceAllConstraints);
105     }
106 
107     /***
108      * Stores the information about what constraints should/shouldn't be enforced.
109      *
110      * @return Returns the constraints instance.
111      */
112     public Filters getConstraints()
113     {
114         return this.constraints;
115     }
116 
117     /***
118      * Sets the constraints for this model.  This indicates what
119      * constraints should and should not be processed from this model.
120      *
121      * @param constraints the packages to process.
122      */
123     public void setConstraints(final Filters constraints)
124     {
125         this.constraints = constraints;
126     }
127 
128     /***
129      * The URL to the model.
130      */
131     private List uris = new ArrayList();
132 
133     /***
134      * Caches the urisAsStrings value (so we don't need
135      * to do the conversion more than once).
136      */
137     private String[] urisAsStrings = null;
138 
139     /***
140      * All URIs that make up the model.
141      *
142      * @return Returns the uri.
143      */
144     public String[] getUris()
145     {
146         if (this.urisAsStrings == null)
147         {
148             final int uriNumber = uris.size();
149             this.urisAsStrings = new String[uriNumber];
150             for (int ctr = 0; ctr < uriNumber; ctr++)
151             {
152                 urisAsStrings[ctr] = (uris.get(ctr)).toString();
153             }
154         }
155         return this.urisAsStrings;
156     }
157 
158     /***
159      * Adds the location as a URI to one of the model files.
160      *
161      * @param uri the URI to the model.
162      */
163     public void addUri(final String uri)
164     {
165         try
166         {
167             final URL url = ResourceUtils.toURL(uri);
168             if (url == null)
169             {
170                 throw new ConfigurationException("Model could not be loaded from invalid path --> '" + uri + "'");
171             }
172             try
173             {
174                 // - Get around the fact the URL won't be released until the JVM
175                 //   has been terminated, when using the 'jar' uri protocol.
176                 url.openConnection().setDefaultUseCaches(false);
177             }
178             catch (final IOException exception)
179             {
180                 // - ignore the exception
181             }
182             this.uris.add(url);
183         }
184         catch (final Throwable throwable)
185         {
186             throw new ConfigurationException(throwable);
187         }
188     }
189 
190     /***
191      * Stores the transformations for this Configuration instance.
192      */
193     private final Collection transformations = new ArrayList();
194 
195     /***
196      * Adds a transformation to this configuration instance.
197      *
198      * @param transformation the transformation instance to add.
199      */
200     public void addTransformation(final Transformation transformation)
201     {
202         this.transformations.add(transformation);
203     }
204 
205     /***
206      * Gets the transformations belonging to this configuration.
207      *
208      * @return the array of {@link Transformation} instances.
209      */
210     public Transformation[] getTransformations()
211     {
212         return (Transformation[])this.transformations.toArray(new Transformation[0]);
213     }
214 
215     /***
216      * The locations in which to search for module.
217      */
218     private final Collection moduleSearchLocations = new ArrayList();
219 
220     /***
221      * Adds a module search location (these are the locations
222      * in which a search for module is performed).
223      *
224      * @param location a location path.
225      * @see #addModuleSearchLocation(String)
226      */
227     public void addModuleSearchLocation(final Location location)
228     {
229         this.moduleSearchLocations.add(location);
230     }
231 
232     /***
233      * Adds a module search location path (a location
234      * without a pattern defined).
235      *
236      * @param path a location path.
237      * @see #addModuleSearchLocation(Location)
238      */
239     public void addModuleSearchLocation(final String path)
240     {
241         if (path != null)
242         {
243             final Location location = new Location();
244             location.setPath(path);
245             this.moduleSearchLocations.add(location);
246         }
247     }
248 
249     /***
250      * The type of model (i.e. uml-1.4, uml-2.0, etc).
251      */
252     private String type;
253 
254     /***
255      * Gets the type of the model (i.e. the type of metamodel this
256      * model is based upon).
257      *
258      * @return Returns the type.
259      */
260     public String getType()
261     {
262         return this.type;
263     }
264 
265     /***
266      * Sets the type of model (i.e. the type of metamodel this model
267      * is based upon).
268      *
269      * @param type The type to set.
270      */
271     public void setType(final String type)
272     {
273         this.type = type;
274     }
275 
276     /***
277      * Gets the module searach locations for this model instance.
278      *
279      * @return the module search locations.
280      * @see #getModuleSearchLocationPaths()
281      */
282     public Location[] getModuleSearchLocations()
283     {
284         return (Location[])this.moduleSearchLocations.toArray(new Location[0]);
285     }
286 
287     /***
288      * Stores the path for each module search location in this configuration.
289      */
290     private String[] moduleSearchLocationPaths = null;
291 
292     /***
293      * Gets all found module search location paths for this model instance.
294      *
295      * @return the module search location paths.
296      * @see #getModuleSearchLocations()
297      */
298     public String[] getModuleSearchLocationPaths()
299     {
300         if (this.moduleSearchLocationPaths == null)
301         {
302             final Collection paths = new ArrayList();
303             for (final Iterator iterator = this.moduleSearchLocations.iterator(); iterator.hasNext();)
304             {
305                 final Location location = (Location)iterator.next();
306                 final URL[] resources = location.getResources();
307                 final int resourceNumber = resources.length;
308                 for (int ctr = 0; ctr < resourceNumber; ctr++)
309                 {
310                     paths.add(resources[ctr].toString());
311                 }
312                 paths.add(location.getPath());
313             }
314             this.moduleSearchLocationPaths = (String[])paths.toArray(new String[0]);
315         }
316         return this.moduleSearchLocationPaths;
317     }
318 
319     /***
320      * Stores all resources including all resources found within the module search locations
321      * as well as a resource for the {@link #getUris()}.
322      */
323     private URL[] moduleSearchLocationResources = null;
324 
325     /***
326      * Gets the accumulation of all files found when combining the contents
327      * of all module search location paths and their patterns by which they
328      * are filtered as well as the model URI.
329      *
330      * @return all module search location files.
331      */
332     public URL[] getModuleSearchLocationResources()
333     {
334         if (this.moduleSearchLocationResources == null)
335         {
336             final Collection allResources = new ArrayList();
337             final Location[] locations = this.getModuleSearchLocations();
338             for (int ctr = 0; ctr < locations.length; ctr++)
339             {
340                 final URL[] resources = locations[ctr].getResources();
341                 for (int fileCtr = 0; fileCtr < resources.length; fileCtr++)
342                 {
343                     allResources.add(resources[fileCtr]);
344                 }
345             }
346             this.moduleSearchLocationResources = (URL[])allResources.toArray(new URL[0]);
347         }
348         return this.moduleSearchLocationResources;
349     }
350 
351     /***
352      * Gets the time of the latest modified uri of the model as a <code>long</code>.
353      * If it can not be determined <code>0</code> is returned.
354      *
355      * @return the time this model was last modified
356      */
357     public long getLastModified()
358     {
359         long lastModifiedTime = 0;
360         for (final Iterator iterator = this.uris.iterator(); iterator.hasNext();)
361         {
362             final URL url = (URL)iterator.next();
363             final long modifiedTime = ResourceUtils.getLastModifiedTime(url);
364             if (modifiedTime > lastModifiedTime)
365             {
366                 lastModifiedTime = modifiedTime;
367             }
368         }
369         return lastModifiedTime;
370     }
371 
372     /***
373      * @see java.lang.Object#toString()
374      */
375     public String toString()
376     {
377         String toString = super.toString();
378         final String key = this.getKey();
379         if (key != null && key.trim().length() > 0)
380         {
381             toString = key;
382         }
383         return toString;
384     }
385 
386     /***
387      * Stores the last modified times for each model at the time
388      * {@link #isChanged()} is called.
389      */
390     private static final Map modelModifiedTimes = new HashMap();
391 
392     /***
393      * The unique key that identifies this model.
394      */
395     private String key = null;
396 
397     /***
398      * Creates the unique key that identifies this model
399      * (its made up of a list of all the uris for this model
400      * concatinated).
401      *
402      * @return the unique key
403      */
404     private String getKey()
405     {
406         if (this.key == null || this.key.trim().length() == 0)
407         {
408             final StringBuffer buffer = new StringBuffer();
409             for (final Iterator iterator = this.uris.iterator(); iterator.hasNext();)
410             {
411                 final URL uri = (URL)iterator.next();
412                 buffer.append(new File(uri.getFile()));
413                 if (iterator.hasNext())
414                 {
415                     buffer.append(", ");
416                 }
417             }
418             this.key = buffer.toString();
419         }
420         return this.key;
421     }
422 
423     /***
424      * The repository to which this model belongs.
425      */
426     private Repository repository;
427 
428     /***
429      * Gets the repository to which this model belongs.
430      *
431      * @return the repository to which this model belongs.
432      */
433     public Repository getRepository()
434     {
435         return this.repository;
436     }
437 
438     /***
439      * Sets the repository to which this model belongs.
440      *
441      * @param repository the repository configuration to which this model belongs.
442      */
443     void setRepository(final Repository repository)
444     {
445         this.repository = repository;
446     }
447 
448     /***
449      * Indicates whether or not the given <code>model</code>
450      * has changed since the previous call to this method.
451      *
452      * @return true/false
453      */
454     public boolean isChanged()
455     {
456         boolean changed = this.getUris().length > 0;
457         if (changed)
458         {
459             final Object modelKey = this.getKey();
460             Map lastModifiedTimes = (Map)modelModifiedTimes.get(modelKey);
461 
462             // - load up the last modified times (from the model and all its modules)
463             //   if they haven't been loaded yet
464             if (lastModifiedTimes != null)
465             {
466                 final long modelLastModified = ((Long)lastModifiedTimes.get(modelKey)).longValue();
467                 changed = this.getLastModified() > modelLastModified;
468                 if (!changed)
469                 {
470                     // - check to see if any of the modules have changed if the model hasn't changed
471                     final URL[] resources = this.getModuleSearchLocationResources();
472                     for (int ctr = 0; ctr < resources.length; ctr++)
473                     {
474                         final URL resource = resources[ctr];
475                         final Long lastModified = (Long)lastModifiedTimes.get(resource);
476                         if (lastModified != null)
477                         {
478                             // - when we find the first modified module, break out
479                             if (ResourceUtils.getLastModifiedTime(resource) > lastModified.longValue())
480                             {
481                                 changed = true;
482                                 break;
483                             }
484                         }
485                     }
486                 }
487             }
488 
489             // - if our model (or modules) have changed re-load the last modified times
490             if (changed)
491             {
492                 this.loadLastModifiedTimes();
493             }
494         }
495         return changed;
496     }
497 
498     /***
499      * Loads (or re-loads) the last modified times from the
500      * {@link #getUris()} and the modules found on the module search path.
501      */
502     private void loadLastModifiedTimes()
503     {
504         final Object modelKey = this.getKey();
505         Map lastModifiedTimes = (Map)modelModifiedTimes.get(modelKey);
506         if (lastModifiedTimes == null)
507         {
508             lastModifiedTimes = new HashMap();
509         }
510         else
511         {
512             lastModifiedTimes.clear();
513         }
514         final URL[] resources = this.getModuleSearchLocationResources();
515         for (int ctr = 0; ctr < resources.length; ctr++)
516         {
517             final URL resource = resources[ctr];
518             lastModifiedTimes.put(
519                 resource,
520                 new Long(ResourceUtils.getLastModifiedTime(resource)));
521         }
522 
523         // - add the model key last so it overwrites any invalid ones
524         //   we might have picked up from adding the module search location files.
525         lastModifiedTimes.put(
526             modelKey,
527             new Long(this.getLastModified()));
528         modelModifiedTimes.put(
529             modelKey,
530             lastModifiedTimes);
531     }
532 
533     /***
534      * Clears out the current last modified times.
535      */
536     static void clearLastModifiedTimes()
537     {
538         modelModifiedTimes.clear();
539     }
540 }