Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
DEMLoaderHelper |
|
| 3.5;3,5 |
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 | 0 | 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 | 0 | 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 | 0 | static private List<String> DEMEntitiesAnnontations = null; |
69 | ||
70 | /** The annotationLogic Map */ | |
71 | 0 | static private Map<String, DEMAnnotationLogic> annotationLogicMap = null; |
72 | ||
73 | /** Default constructor. Enforce noninstantiability */ | |
74 | 0 | private DEMLoaderHelper() |
75 | 0 | {} |
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 | 0 | if (packagesToSearch == null) |
89 | 0 | packagesToSearch = new ArrayList<String>(); |
90 | ||
91 | 0 | if (!packagesToSearch.contains(packageName)) |
92 | { | |
93 | 0 | 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 | 0 | for (ListIterator<String> iter = packagesToSearch.listIterator(); iter.hasNext();) |
112 | { | |
113 | 0 | String registeredPackage = (String) iter.next(); |
114 | ||
115 | 0 | if (registeredPackage.startsWith(packageName)) |
116 | 0 | iter.remove(); |
117 | } | |
118 | ||
119 | // Add the new package to the registry | |
120 | 0 | packagesToSearch.add(packageName); |
121 | ||
122 | 0 | return true; |
123 | } | |
124 | } | |
125 | ||
126 | 0 | 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 | 0 | for (String registeredPackage: packagesToSearch) |
140 | { | |
141 | 0 | if (packageName.startsWith(registeredPackage)) |
142 | 0 | return true; |
143 | } | |
144 | 0 | 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 | 0 | 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 | 0 | packagesToSearch = null; |
164 | 0 | DEMEntitiesAnnontations = null; |
165 | 0 | } |
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 | 0 | List<String> packageClasses = ResourceUtils.getClassesInDeepPackage(packageName); |
181 | ||
182 | // Initialize return object | |
183 | 0 | ArrayList<ClassHolder> entityClasses = new ArrayList<ClassHolder>(); |
184 | // Reuse var | |
185 | ClassHolder tempClass; | |
186 | ||
187 | // Add all DEM Entity classes to the return list | |
188 | 0 | for (String className: packageClasses) |
189 | { | |
190 | ||
191 | // Build an holder for the class... | |
192 | try | |
193 | { | |
194 | 0 | tempClass = new ClassHolder(className); |
195 | ||
196 | } | |
197 | 0 | catch (ResourceNotFoundException e) |
198 | { | |
199 | 0 | tempClass = null; |
200 | } | |
201 | ||
202 | 0 | if (tempClass != null) |
203 | { | |
204 | // If it's a DEM Entity add holder to result | |
205 | 0 | if (isDEMAnnotatedClass(tempClass)) |
206 | 0 | entityClasses.add(tempClass); |
207 | else | |
208 | // Else delete it from repository (it won't be needed for enhancement) | |
209 | 0 | tempClass.deleteClassFromRepository(); |
210 | } | |
211 | } | |
212 | ||
213 | 0 | 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 | 0 | boolean isDEMAnnotatedClass = false; |
229 | ||
230 | // Check if any of the DEM Annotations is on the class's annotations | |
231 | 0 | for (String annotationName: getDEMEntitiesAnnontations()) |
232 | { | |
233 | // If at least one is present, return immediately | |
234 | 0 | if (clazz.containsAnnotation(annotationName)) |
235 | { | |
236 | 0 | isDEMAnnotatedClass = true; |
237 | 0 | break; |
238 | } | |
239 | } | |
240 | ||
241 | 0 | 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 | 0 | if (annotationLogicMap == null) |
258 | { | |
259 | ||
260 | 0 | List<String> demAnnotationsClasses = getDEMAnnotations(); |
261 | ||
262 | 0 | annotationLogicMap = new HashMap<String, DEMAnnotationLogic>(); |
263 | ||
264 | // Reuse vars | |
265 | ClassHolder tempAnnotationClass; | |
266 | AnnotationHolder annotationLogicClassAnnotation; | |
267 | ||
268 | // For each class... | |
269 | 0 | for (String annotationClassName: demAnnotationsClasses) |
270 | { | |
271 | ||
272 | // ...build a holder... | |
273 | 0 | tempAnnotationClass = new ClassHolder(annotationClassName); |
274 | ||
275 | // ..get the class that defines the annotation logic | |
276 | 0 | annotationLogicClassAnnotation = tempAnnotationClass.getAnnotations().get(ANNOTATION_LOGIC_CLASS); |
277 | ||
278 | // If annotation @AnnotationLogicClass is present... | |
279 | 0 | if (annotationLogicClassAnnotation != null) |
280 | { | |
281 | ||
282 | // Build an object of the appropriate type and put it on the map | |
283 | 0 | annotationLogicMap.put(tempAnnotationClass.getFQName(), DEMAnnotationLogic.makeObject( |
284 | 0 | 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 | 0 | annotationLogicMap.put(tempAnnotationClass.getFQName(), DEMAnnotationLogic.makeObject( |
292 | 0 | 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 | 0 | annotationLogicMap.put(Inject.class.getCanonicalName(), DEMAnnotationLogic.makeObject( |
299 | 0 | DEMAnnotationLogic.DEFAULT_DEM_ANNOTATION_LOGIC_CLASS, new ClassHolder(Inject.class |
300 | 0 | .getCanonicalName()))); |
301 | ||
302 | } | |
303 | ||
304 | // Return map | |
305 | 0 | 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 | 0 | 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 | 0 | if (DEMEntitiesAnnontations == null) |
331 | try | |
332 | { | |
333 | try | |
334 | { | |
335 | // Fetches the the classes on the framework's DEM Entity | |
336 | // conventioned package | |
337 | 0 | DEMEntitiesAnnontations = ResourceUtils.getClassesInPackage(DEM_ENTITIES_ANNOTATIONS_CLASS_PATH); |
338 | } | |
339 | 0 | catch (ResourceNotFoundException resourceNotFoundException) |
340 | { | |
341 | 0 | throw new ResourceNotFoundException("Could not found DEM annotations on " |
342 | 0 | + DEM_ENTITIES_ANNOTATIONS_CLASS_PATH, resourceNotFoundException); |
343 | } | |
344 | ||
345 | // Treat the situation where no DEM entities are found... | |
346 | 0 | if (DEMEntitiesAnnontations.size() == 0 || DEMEntitiesAnnontations == null) |
347 | 0 | DEMEntitiesAnnontations = null; |
348 | ||
349 | } | |
350 | 0 | 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 | 0 | DEMEntitiesAnnontations = null; |
356 | ||
357 | 0 | throw resourceNotFoundException; |
358 | } | |
359 | ||
360 | 0 | return DEMEntitiesAnnontations; |
361 | } | |
362 | } |