View Javadoc

1   /**
2    * - Digitalis Internal Framework v2.0 -
3    *
4    * (C) 2007, Digitalis Informatica.
5    *
6    * Distribuicao e Gestao de Informatica, Lda. Estrada de Paco de Arcos num.9 -
7    * Piso -1 2780-666 Paco de Arcos Telefone: (351) 21 4408990 Fax: (351) 21
8    * 4408999 http://www.digitalis.pt
9    */
10  package pt.digitalis.dif.codegen.util;
11  
12  import java.util.ArrayList;
13  import java.util.HashMap;
14  import java.util.List;
15  import java.util.ListIterator;
16  import java.util.Map;
17  
18  import com.google.inject.Inject;
19  
20  import pt.digitalis.dif.dem.DEMAnnotationLogic;
21  import pt.digitalis.utils.bytecode.holders.AnnotationHolder;
22  import pt.digitalis.utils.bytecode.holders.ClassHolder;
23  import pt.digitalis.utils.inspection.ResourceUtils;
24  import pt.digitalis.utils.inspection.exception.AuxiliaryOperationException;
25  import pt.digitalis.utils.inspection.exception.ResourceNotFoundException;
26  
27  /**
28   * Holds the list of packages that will be searched for Entity classes by the DIFCodeGenerator. Provides methods to add
29   * packages to the list of packages, and to access the list.
30   *
31   * @author Pedro Viegas <a href="mailto:pviegas@digitalis.pt">pviegas@digitalis.pt</a>
32   * @author Rodrigo Gonçalves <a href="mailto:rgoncalves@digitalis.pt">rgoncalves@digitalis.pt</a>
33   * @created Jul 23, 2007
34   */
35  final public class DEMLoaderHelper {
36  
37      /** 'Providers' folder name by convention. */
38      final static public String PROVIDERS_DIR = "providers";
39  
40      /** 'Applications' folder name by convention. */
41      final static public String APPLICATIONS_DIR = "applications";
42  
43      /** 'Services' folder name by convention. */
44      final static public String SERVICES_DIR = "services";
45  
46      /** 'Stages' folder name by convention. */
47      final static public String STAGES_DIR = "stages";
48  
49      /** 'Validators' folder name by convention. */
50      final static public String VALIDATORS_DIR = "validators";
51  
52      /** The list of packages to search. */
53      static private List<String> packagesToSearch = new ArrayList<String>();
54  
55      /** Class path for the DEM Annotations. */
56      final static private String DEM_ANNOTATIONS_CLASS_PATH = "pt.digitalis.dif.dem.annotations";
57  
58      /** Class path for the DEM Entity Annotations. */
59      final static private String DEM_ENTITIES_ANNOTATIONS_CLASS_PATH = DEM_ANNOTATIONS_CLASS_PATH + ".entities";
60  
61      /**
62       * Defines the FQN of the <code>@AnnotationLogicClass</code> annotation.
63       */
64      final static private String ANNOTATION_LOGIC_CLASS = DEM_ANNOTATIONS_CLASS_PATH
65              + ".metaannotations.AnnotationLogicClass";
66  
67      /** The DEM Entity annotations defined on the framework. */
68      static private List<String> DEMEntitiesAnnontations = null;
69  
70      /** The annotationLogic Map */
71      static private Map<String, DEMAnnotationLogic> annotationLogicMap = null;
72  
73      /** Default constructor. Enforce noninstantiability */
74      private DEMLoaderHelper()
75      {}
76  
77      /**
78       * Adds a package to the packageList after validating that this package does not already exist or is contained in an
79       * existing parent package
80       *
81       * @param packageName
82       *            the name of the package to add
83       * @return T if package was added, F otherwise
84       */
85      static public boolean addPackage(String packageName)
86      {
87  
88          if (packagesToSearch == null)
89              packagesToSearch = new ArrayList<String>();
90  
91          if (!packagesToSearch.contains(packageName))
92          {
93              if (!isPackageContained(packageName))
94              {
95  
96                  /*
97                   * Remove from registry all eventual packages that will be contained by the new package
98                   *
99                   * <pre>
100                  *
101                  * IMPLEMENTATION NOTE:
102                  *
103                  * The usual "for" loop form (foreach) for collections uses an internal iterator of type Iterator<E>
104                  * which doesn't support list modifying. Calling remove() on a list being iterated in such a loop raises
105                  * a ConcurrentModificationException. To cope with this limitation a "for" loop with an explicit
106                  * ListIterator<E> must be used and all modifying operations must be done through the iterator.
107                  *
108                  * </pre>
109                  */
110 
111                 for (ListIterator<String> iter = packagesToSearch.listIterator(); iter.hasNext();)
112                 {
113                     String registeredPackage = (String) iter.next();
114 
115                     if (registeredPackage.startsWith(packageName))
116                         iter.remove();
117                 }
118 
119                 // Add the new package to the registry
120                 packagesToSearch.add(packageName);
121 
122                 return true;
123             }
124         }
125 
126         return false;
127     }
128 
129     /**
130      * Verifies if the given package is contained on a previously registered package.
131      *
132      * @param packageName
133      *            the package name
134      * @return T if packageName is contained on any existing package
135      */
136     static private boolean isPackageContained(String packageName)
137     {
138 
139         for (String registeredPackage: packagesToSearch)
140         {
141             if (packageName.startsWith(registeredPackage))
142                 return true;
143         }
144         return false;
145     }
146 
147     /**
148      * Return the list of packages.
149      *
150      * @return the List of packages
151      */
152     static public List<String> getPackageList()
153     {
154         return packagesToSearch;
155     }
156 
157     /**
158      * Makes internal package list eligible for GC when it is no longer needed. Must be called explicitly since this is
159      * a static class with static attributes for temporary data.
160      */
161     static public void cleanUp()
162     {
163         packagesToSearch = null;
164         DEMEntitiesAnnontations = null;
165     }
166 
167     /**
168      * Searches a given package for DEM Entity annotated classes.
169      *
170      * @param packageName
171      *            the package to search for DEM classes
172      * @return a List of CGClassHolder
173      * @throws ResourceNotFoundException
174      *             if package is not found
175      */
176     static public List<ClassHolder> getDEMEntityClassesInPackage(String packageName) throws ResourceNotFoundException
177     {
178 
179         // Get all classes for the given package
180         List<String> packageClasses = ResourceUtils.getClassesInDeepPackage(packageName);
181 
182         // Initialize return object
183         ArrayList<ClassHolder> entityClasses = new ArrayList<ClassHolder>();
184         // Reuse var
185         ClassHolder tempClass;
186 
187         // Add all DEM Entity classes to the return list
188         for (String className: packageClasses)
189         {
190 
191             // Build an holder for the class...
192             try
193             {
194                 tempClass = new ClassHolder(className);
195 
196             }
197             catch (ResourceNotFoundException e)
198             {
199                 tempClass = null;
200             }
201 
202             if (tempClass != null)
203             {
204                 // If it's a DEM Entity add holder to result
205                 if (isDEMAnnotatedClass(tempClass))
206                     entityClasses.add(tempClass);
207                 else
208                     // Else delete it from repository (it won't be needed for enhancement)
209                     tempClass.deleteClassFromRepository();
210             }
211         }
212 
213         return entityClasses;
214     }
215 
216     /**
217      * Tests a class for DEM Entity annotations.
218      *
219      * @param clazz
220      *            the class to search for annotations
221      * @return T if class is a DEM Entity, F otherwise.
222      * @throws ResourceNotFoundException
223      *             if annotations can't be read
224      */
225     static private boolean isDEMAnnotatedClass(ClassHolder clazz) throws ResourceNotFoundException
226     {
227 
228         boolean isDEMAnnotatedClass = false;
229 
230         // Check if any of the DEM Annotations is on the class's annotations
231         for (String annotationName: getDEMEntitiesAnnontations())
232         {
233             // If at least one is present, return immediately
234             if (clazz.containsAnnotation(annotationName))
235             {
236                 isDEMAnnotatedClass = true;
237                 break;
238             }
239         }
240 
241         return isDEMAnnotatedClass;
242     }
243 
244     /**
245      * Builds a map of all DEM annotations with associated logic.
246      *
247      * @return the Map of annotations
248      * @throws ResourceNotFoundException
249      *             if DEM annotations classes can't be found
250      * @throws AuxiliaryOperationException
251      *             if a DEMAnnotationLogic object can't be created
252      */
253     static public Map<String, DEMAnnotationLogic> getAnnotationLogicMap() throws ResourceNotFoundException,
254             AuxiliaryOperationException
255     {
256 
257         if (annotationLogicMap == null)
258         {
259 
260             List<String> demAnnotationsClasses = getDEMAnnotations();
261 
262             annotationLogicMap = new HashMap<String, DEMAnnotationLogic>();
263 
264             // Reuse vars
265             ClassHolder tempAnnotationClass;
266             AnnotationHolder annotationLogicClassAnnotation;
267 
268             // For each class...
269             for (String annotationClassName: demAnnotationsClasses)
270             {
271 
272                 // ...build a holder...
273                 tempAnnotationClass = new ClassHolder(annotationClassName);
274 
275                 // ..get the class that defines the annotation logic
276                 annotationLogicClassAnnotation = tempAnnotationClass.getAnnotations().get(ANNOTATION_LOGIC_CLASS);
277 
278                 // If annotation @AnnotationLogicClass is present...
279                 if (annotationLogicClassAnnotation != null)
280                 {
281 
282                     // Build an object of the appropriate type and put it on the map
283                     annotationLogicMap.put(tempAnnotationClass.getFQName(), DEMAnnotationLogic.makeObject(
284                             annotationLogicClassAnnotation.getMembers().get("value").toString(), tempAnnotationClass));
285 
286                 }
287                 else
288                 {
289                     // Request the construction of a default annotation logic
290                     // object and put it on the map
291                     annotationLogicMap.put(tempAnnotationClass.getFQName(), DEMAnnotationLogic.makeObject(
292                             DEMAnnotationLogic.DEFAULT_DEM_ANNOTATION_LOGIC_CLASS, tempAnnotationClass));
293                 }
294 
295             }
296 
297             // Add the Google Guice inject annotation fot DiF to register injection as present
298             annotationLogicMap.put(Inject.class.getCanonicalName(), DEMAnnotationLogic.makeObject(
299                     DEMAnnotationLogic.DEFAULT_DEM_ANNOTATION_LOGIC_CLASS, new ClassHolder(Inject.class
300                             .getCanonicalName())));
301 
302         }
303 
304         // Return map
305         return annotationLogicMap;
306     }
307 
308     /**
309      * Returns a list with the names of all DEM annotations classes.
310      *
311      * @return the list with all DEM annotations class names
312      * @throws ResourceNotFoundException
313      *             if some needed resource is not found
314      */
315     static public List<String> getDEMAnnotations() throws ResourceNotFoundException
316     {
317         return ResourceUtils.getClassesInDeepPackage(DEM_ANNOTATIONS_CLASS_PATH);
318     }
319 
320     /**
321      * Returns a list with the defined DEM Entity Annotation FQNs for the framework
322      *
323      * @return a list with the defined DEM Entity Annotation FQNs
324      * @throws ResourceNotFoundException
325      *             if DEM annotations classes can't be found
326      */
327     public static List<String> getDEMEntitiesAnnontations() throws ResourceNotFoundException
328     {
329         // Lazy loading
330         if (DEMEntitiesAnnontations == null)
331             try
332             {
333                 try
334                 {
335                     // Fetches the the classes on the framework's DEM Entity
336                     // conventioned package
337                     DEMEntitiesAnnontations = ResourceUtils.getClassesInPackage(DEM_ENTITIES_ANNOTATIONS_CLASS_PATH);
338                 }
339                 catch (ResourceNotFoundException resourceNotFoundException)
340                 {
341                     throw new ResourceNotFoundException("Could not found DEM annotations on "
342                             + DEM_ENTITIES_ANNOTATIONS_CLASS_PATH, resourceNotFoundException);
343                 }
344 
345                 // Treat the situation where no DEM entities are found...
346                 if (DEMEntitiesAnnontations.size() == 0 || DEMEntitiesAnnontations == null)
347                     DEMEntitiesAnnontations = null;
348 
349             }
350             catch (ResourceNotFoundException resourceNotFoundException)
351             {
352                 // The caught exception is already of the correct type and
353                 // provides the needed information to spot the problem.
354                 // Therefore, wrap it up on another exception seems unnecessary.
355                 DEMEntitiesAnnontations = null;
356 
357                 throw resourceNotFoundException;
358             }
359 
360         return DEMEntitiesAnnontations;
361     }
362 }