View Javadoc

1   /***************************************************************************************
2    * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved.                 *
3    * http://aspectwerkz.codehaus.org                                                    *
4    * ---------------------------------------------------------------------------------- *
5    * The software in this package is published under the terms of the LGPL license      *
6    * a copy of which has been included with this distribution in the license.txt file.  *
7    **************************************************************************************/
8   package org.codehaus.aspectwerkz.annotation;
9   
10  
11  import com.thoughtworks.qdox.model.JavaClass;
12  import com.thoughtworks.qdox.model.JavaField;
13  import com.thoughtworks.qdox.model.JavaMethod;
14  import com.thoughtworks.qdox.model.JavaParameter;
15  
16  import org.apache.tools.ant.BuildException;
17  import org.codehaus.aspectwerkz.annotation.instrumentation.AttributeEnhancer;
18  import org.codehaus.aspectwerkz.annotation.instrumentation.asm.AsmAttributeEnhancer;
19  import org.codehaus.aspectwerkz.exception.DefinitionException;
20  import org.codehaus.aspectwerkz.joinpoint.JoinPoint;
21  import org.codehaus.aspectwerkz.joinpoint.StaticJoinPoint;
22  import org.codehaus.aspectwerkz.DeploymentModel;
23  
24  import java.io.BufferedReader;
25  import java.io.File;
26  import java.io.FileInputStream;
27  import java.io.FileReader;
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.net.MalformedURLException;
31  import java.net.URL;
32  import java.net.URLClassLoader;
33  import java.util.ArrayList;
34  import java.util.HashMap;
35  import java.util.Iterator;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.Properties;
39  import java.util.StringTokenizer;
40  
41  /***
42   * <p/>Annotation compiler. <p/>Extracts the annotations from JavaDoc tags and inserts them into the bytecode of the
43   * class.
44   *
45   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
46   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
47   */
48  public class AnnotationC {
49      private static final String COMMAND_LINE_OPTION_DASH = "-";
50      private static final String COMMAND_LINE_OPTION_VERBOSE = "-verbose";
51      private static final String COMMAND_LINE_OPTION_CUSTOM = "-custom";
52      private static final String COMMAND_LINE_OPTION_SRC = "-src";
53      private static final String COMMAND_LINE_OPTION_SRCFILES = "-srcfiles";
54      private static final String COMMAND_LINE_OPTION_SRCINCLUDES = "-srcincludes";
55      private static final String COMMAND_LINE_OPTION_CLASSES = "-classes";
56      private static final String COMMAND_LINE_OPTION_DEST = "-dest";
57  
58      static final String[] SYSTEM_ANNOTATIONS = new String[]{
59          AnnotationConstants.ASPECT, AnnotationConstants.AROUND, AnnotationConstants.BEFORE,
60          AnnotationConstants.AFTER, AnnotationConstants.AFTER_FINALLY,
61          AnnotationConstants.AFTER_RETURNING, AnnotationConstants.AFTER_THROWING,
62          AnnotationConstants.EXPRESSION, AnnotationConstants.INTRODUCE, AnnotationConstants.MIXIN
63      };
64  
65      /***
66       * The annotations properties file define by the user.
67       */
68      public static final Properties ANNOTATION_DEFINITION = new Properties();
69  
70      /***
71       * Verbose logging.
72       */
73      private static boolean s_verbose = false;
74  
75      /***
76       * The class loader.
77       */
78      private static URLClassLoader s_loader;
79  
80      /***
81       * The custom annotations.
82       */
83      private static Map s_customAnnotations = new HashMap();
84      private static final String FILE_SEPARATOR = File.separator;
85  
86      /***
87       * Runs the compiler from the command line.
88       *
89       * @param args
90       */
91      public static void main(String[] args) {
92          if (args.length < 4) {
93              printUsage();
94          }
95          Map commandLineOptions = parseCommandLineOptions(args);
96  
97          String propertiesFilesPath = (String) commandLineOptions.get(COMMAND_LINE_OPTION_CUSTOM);
98          List propertiesFilesList = new ArrayList();
99          if (propertiesFilesPath != null) {
100             StringTokenizer st = new StringTokenizer(propertiesFilesPath, File.pathSeparator);
101             while (st.hasMoreTokens()) {
102                 propertiesFilesList.add(st.nextToken());
103             }
104         }
105         String[] propertiesFiles = (String[]) propertiesFilesList.toArray(new String[0]);
106 
107         compile(
108                 (String) commandLineOptions.get(COMMAND_LINE_OPTION_SRC),
109                 (String) commandLineOptions.get(COMMAND_LINE_OPTION_SRCFILES),
110                 (String) commandLineOptions.get(COMMAND_LINE_OPTION_SRCINCLUDES),
111                 (String) commandLineOptions.get(COMMAND_LINE_OPTION_CLASSES),
112                 (String) commandLineOptions.get(COMMAND_LINE_OPTION_DEST),
113                 propertiesFiles
114         );
115     }
116 
117     /***
118      * Compiles the annotations.
119      *
120      * @param srcDirList
121      * @param srcFileList
122      * @param classPath
123      * @param destDir
124      * @param annotationPropetiesFiles
125      */
126     private static void compile(final String srcDirList,
127                                 final String srcFileList,
128                                 final String srcFileIncludes,
129                                 final String classPath,
130                                 String destDir,
131                                 final String[] annotationPropetiesFiles) {
132         if (srcDirList == null && srcFileList == null && srcFileIncludes == null) {
133             throw new IllegalArgumentException("one of src or srcfiles or srcincludes must be not null");
134         }
135         if ((srcDirList != null && srcFileList != null) ||
136             (srcDirList != null && srcFileIncludes != null) ||
137             (srcFileList != null && srcFileIncludes != null)) { // FIXME: refactor
138             throw new IllegalArgumentException("maximum one of src, srcfiles or srcincludes must be not null");
139         }
140         if (classPath == null) {
141             throw new IllegalArgumentException("class path can not be null");
142         }
143         if (destDir == null) {
144             destDir = classPath;
145         }
146 
147         String[] srcDirs = new String[0];
148         String[] srcFiles = new String[0];
149         if (srcDirList != null) {
150             srcDirs = split(srcDirList, File.pathSeparator);
151         } else if (srcFileList != null) {
152             srcFiles = split(srcFileList, FILE_SEPARATOR);
153         } else {
154             srcFiles = loadSourceList(srcFileIncludes);
155         }
156 
157         compile(s_verbose, srcDirs, srcFiles, split(classPath, File.pathSeparator), destDir, annotationPropetiesFiles);
158     }
159 
160     /***
161      * Compiles the annotations.
162      *
163      * @param verbose
164      * @param srcDirs
165      * @param srcFiles
166      * @param classpath
167      * @param destDir
168      * @param annotationPropertiesFiles
169      */
170     public static void compile(final boolean verbose,
171                                final String[] srcDirs,
172                                final String[] srcFiles,
173                                final String[] classpath,
174                                final String destDir,
175                                final String[] annotationPropertiesFiles) {
176 
177         s_verbose = verbose;
178         URL[] classPath = new URL[classpath.length];
179         try {
180             for (int i = 0; i < classpath.length; i++) {
181                 classPath[i] = new File(classpath[i]).toURL();
182             }
183             s_loader = new URLClassLoader(classPath, AnnotationC.class.getClassLoader());
184         } catch (MalformedURLException e) {
185             String message = "URL [" + classPath + "] is not valid: " + e.toString();
186             logError(message);
187             throw new DefinitionException(message, e);
188         }
189 
190         String destDirToUse = destDir;
191         if (destDir == null) {
192             if (classpath.length != 1) {
193                 throw new DefinitionException("destDir must be specified since classpath is composite");
194             }
195             destDirToUse = classpath[0];
196         }
197 
198         final AnnotationManager manager = new AnnotationManager(s_loader);
199 
200         logInfo("parsing source dirs:");
201         for (int i = 0; i < srcDirs.length; i++) {
202             logInfo("    " + srcDirs[i]);
203         }
204         manager.addSourceTrees(srcDirs);
205 
206         for (int i = 0; i < srcFiles.length; i++) {
207             logInfo("    " + srcFiles[i]);
208             manager.addSource(srcFiles[i]);
209         }
210 
211         doCompile(annotationPropertiesFiles, classPath, manager, destDirToUse);
212     }
213 
214     /***
215      * Compiles the annotations.
216      *
217      * @param annotationPropetiesFiles
218      * @param classPath
219      * @param manager
220      * @param destDir
221      */
222     private static void doCompile(final String[] annotationPropetiesFiles,
223                                   final URL[] classPath,
224                                   final AnnotationManager manager,
225                                   final String destDir) {
226 
227         logInfo("compiling annotations...");
228         logInfo("note: if no output is seen, then nothing is compiled");
229 
230         // register annotations
231         registerSystemAnnotations(manager);
232         registerUserDefinedAnnotations(manager, annotationPropetiesFiles);
233 
234         // get all the classes
235         JavaClass[] classes = manager.getAllClasses();
236         for (int i = 0; i < classes.length; i++) {
237             JavaClass clazz = classes[i];
238             logInfo("class [" + clazz.getFullyQualifiedName() + ']');
239             try {
240                 AttributeEnhancer enhancer = new AsmAttributeEnhancer();
241                 if (enhancer.initialize(clazz.getFullyQualifiedName(), classPath)) {
242                     handleClassAnnotations(manager, enhancer, clazz);
243                     handleInnerClassAnnotations(manager, enhancer, clazz);
244                     JavaMethod[] methods = clazz.getMethods();
245                     for (int j = 0; j < methods.length; j++) {
246                         JavaMethod method = methods[j];
247                         if (method.isConstructor()) {
248                             handleConstructorAnnotations(manager, enhancer, method);
249                         } else {
250                             handleMethodAnnotations(manager, enhancer, method);
251                         }
252                     }
253                     JavaField[] fields = clazz.getFields();
254                     for (int j = 0; j < fields.length; j++) {
255                         handleFieldAnnotations(manager, enhancer, fields[j]);
256                     }
257 
258                     // write enhanced class to disk
259                     enhancer.write(destDir);
260                 }
261             } catch (Throwable e) {
262                 e.printStackTrace();
263                 logWarning(
264                         "could not compile annotations for class ["
265                         + clazz.getFullyQualifiedName() + "] due to: " + e.toString()
266                 );
267             }
268         }
269         logInfo("compiled classes written to " + destDir);
270         logInfo("compilation successful");
271     }
272 
273     /***
274      * Handles the class annotations.
275      *
276      * @param manager
277      * @param enhancer
278      * @param clazz
279      */
280     private static void handleClassAnnotations(final AnnotationManager manager,
281                                                final AttributeEnhancer enhancer,
282                                                final JavaClass clazz) {
283         parseCustomClassAnnotations(clazz, manager, enhancer);
284         parseAspectAnnotations(clazz, manager, enhancer);
285         parseMixinAnnotations(clazz, manager, enhancer);
286     }
287 
288     /***
289      * Parses the custom annotations.
290      *
291      * @param clazz
292      * @param manager
293      * @param enhancer
294      */
295     private static void parseCustomClassAnnotations(final JavaClass clazz,
296                                                     final AnnotationManager manager,
297                                                     final AttributeEnhancer enhancer) {
298         for (Iterator it = s_customAnnotations.keySet().iterator(); it.hasNext();) {
299             String annotationName = (String) it.next();
300             Annotation[] customAnnotations = manager.getAnnotations(annotationName, clazz);
301             for (int i = 0; i < customAnnotations.length; i++) {
302                 Annotation customAnnotation = customAnnotations[i];
303                 if (customAnnotation != null) {
304                     enhancer.insertClassAttribute(
305                             new AnnotationInfo(
306                                     annotationName,
307                                     customAnnotation
308                             )
309                     );
310                     logInfo(
311                             "    custom class annotation [" + annotationName + " @ "
312                             + clazz.getFullyQualifiedName() + ']'
313                     );
314                 }
315             }
316         }
317     }
318 
319     /***
320      * Parses the aspect annotations.
321      *
322      * @param clazz
323      * @param manager
324      * @param enhancer
325      */
326     private static void parseAspectAnnotations(final JavaClass clazz,
327                                                final AnnotationManager manager,
328                                                final AttributeEnhancer enhancer) {
329         Annotation[] annotations = manager.getAnnotations(AnnotationConstants.ASPECT, clazz);
330         for (int i = 0; i < annotations.length; i++) {
331             Annotation annotation = annotations[i];
332             if (annotation != null) {
333                 enhancer.insertClassAttribute(
334                         new AnnotationInfo(AnnotationConstants.ASPECT, annotation)
335                 );
336                 String name = ((Aspect) annotation).name();
337                 if (name == null) {
338                     name = clazz.getFullyQualifiedName();
339                 }
340                 String deploymentModel = ((Aspect) annotation).deploymentModel();
341                 if (((Aspect) annotation).value() != null) {
342                     deploymentModel = ((Aspect) annotation).value();
343                 }
344                 logInfo("aspect [" + name + ']');
345                 logInfo("    deployment model [" + deploymentModel + ']');
346             }
347         }
348     }
349 
350     /***
351      * Parses mixin annotations.
352      *
353      * @param clazz
354      * @param manager
355      * @param enhancer
356      */
357     private static void parseMixinAnnotations(final JavaClass clazz,
358                                               final AnnotationManager manager,
359                                               final AttributeEnhancer enhancer) {
360         final Annotation[] introduceAnnotations = manager.getAnnotations(
361                 AnnotationConstants.MIXIN_DOCLET, clazz
362         );
363         final String className = clazz.getFullyQualifiedName();
364         for (int k = 0; k < introduceAnnotations.length; k++) {
365             Annotation introduceAnnotation = introduceAnnotations[k];
366             if (introduceAnnotation != null && introduceAnnotation instanceof Mixin) {
367                 //directly implemented interfaces
368                 JavaClass[] introducedInterfaceClasses = clazz.getImplementedInterfaces();
369                 String[] introducedInterfaceNames = new String[introducedInterfaceClasses.length];
370                 for (int j = 0; j < introducedInterfaceClasses.length; j++) {
371                     introducedInterfaceNames[j] = introducedInterfaceClasses[j].getFullyQualifiedName();
372                     logInfo("    interface introduction [" + introducedInterfaceNames[j] + ']');
373                 }
374                 if (introducedInterfaceNames.length == 0) {
375                     introducedInterfaceNames = enhancer.getNearestInterfacesInHierarchy(className);
376                     if (introducedInterfaceNames.length == 0) {
377                         throw new RuntimeException("no implicit interfaces found for " + className);
378                     }
379                     for (int j = 0; j < introducedInterfaceNames.length; j++) {
380                         logInfo("    interface introduction [" + introducedInterfaceNames[j] + ']');
381                     }
382                 }
383                 String deploymentModel = DeploymentModel.PER_JVM.toString();
384                 if (((Mixin) introduceAnnotation).deploymentModel() != null) {
385                     deploymentModel = ((Mixin) introduceAnnotation).deploymentModel();
386                 }
387                 String expression = ((Mixin) introduceAnnotation).pointcut();
388                 if (expression == null) {
389                     expression = ((Mixin) introduceAnnotation).value();
390                 }
391                 logInfo(
392                         "    mixin introduction [" + clazz.getFullyQualifiedName()
393                         + " :: " + expression +
394                         "] deployment model ["
395                         + deploymentModel + ']'
396                 );
397                 enhancer.insertClassAttribute(
398                         new AnnotationInfo(AnnotationConstants.MIXIN, introduceAnnotation)
399                 );
400             }
401         }
402     }
403 
404     /***
405      * Handles the method annotations.
406      *
407      * @param manager
408      * @param enhancer
409      * @param method
410      */
411     private static void handleMethodAnnotations(final AnnotationManager manager,
412                                                 final AttributeEnhancer enhancer,
413                                                 final JavaMethod method) {
414         // Pointcut with signature
415         Annotation[] expressionAnnotations = manager.getAnnotations(
416                 AnnotationConstants.EXPRESSION_DOCLET, method
417         );
418         for (int i = 0; i < expressionAnnotations.length; i++) {
419             Annotation expressionAnnotation = expressionAnnotations[i];
420             if (expressionAnnotation != null && expressionAnnotation instanceof Expression) {
421                 enhancer.insertMethodAttribute(
422                         method, new AnnotationInfo(
423                                 AnnotationConstants.EXPRESSION,
424                                 expressionAnnotation
425                         )
426                 );
427                 logInfo(
428                         "    pointcut [" + AnnotationC.getShortCallSignature(method) + " :: "
429                         + ((Expression) expressionAnnotation).value() + ']'
430                 );
431             }
432         }
433         Annotation[] aroundAnnotations = manager.getAnnotations(AnnotationConstants.AROUND_DOCLET, method);
434         for (int i = 0; i < aroundAnnotations.length; i++) {
435             Annotation aroundAnnotation = aroundAnnotations[i];
436             if (aroundAnnotation != null && aroundAnnotation instanceof Around) {
437                 enhancer.insertMethodAttribute(
438                         method, new AnnotationInfo(
439                                 AnnotationConstants.AROUND,
440                                 aroundAnnotation
441                         )
442                 );
443                 logInfo(
444                         "    around advice [" + AnnotationC.getShortCallSignature(method) + " :: "
445                         + ((Around) aroundAnnotation).value() + ']'
446                 );
447             }
448         }
449         Annotation[] beforeAnnotations = manager.getAnnotations(AnnotationConstants.BEFORE_DOCLET, method);
450         for (int i = 0; i < beforeAnnotations.length; i++) {
451             Annotation beforeAnnotation = beforeAnnotations[i];
452             if (beforeAnnotation != null && beforeAnnotation instanceof Before) {
453                 enhancer.insertMethodAttribute(
454                         method, new AnnotationInfo(
455                                 AnnotationConstants.BEFORE,
456                                 beforeAnnotation
457                         )
458                 );
459                 logInfo(
460                         "    before [" + AnnotationC.getShortCallSignature(method) + " :: "
461                         + ((Before) beforeAnnotation).value() + ']'
462                 );
463             }
464         }
465         Annotation[] afterAnnotations = manager.getAnnotations(AnnotationConstants.AFTER_DOCLET, method);
466         for (int i = 0; i < afterAnnotations.length; i++) {
467             Annotation afterAnnotation = afterAnnotations[i];
468             if (afterAnnotation != null && afterAnnotation instanceof After) {
469                 enhancer.insertMethodAttribute(
470                         method, new AnnotationInfo(AnnotationConstants.AFTER, afterAnnotation)
471                 );
472                 logInfo(
473                         "    after advice [" + AnnotationC.getShortCallSignature(method) + " :: "
474                         + ((After) afterAnnotation).value() + ']'
475                 );
476             }
477         }
478         Annotation[] afterFinallyAnnotations = manager.getAnnotations(
479                 AnnotationConstants.AFTER_FINALLY_DOCLET, method
480         );
481         for (int i = 0; i < afterFinallyAnnotations.length; i++) {
482             Annotation afterAnnotation = afterFinallyAnnotations[i];
483             if (afterAnnotation != null && afterAnnotation instanceof AfterFinally) {
484                 enhancer.insertMethodAttribute(
485                         method, new AnnotationInfo(AnnotationConstants.AFTER_FINALLY, afterAnnotation)
486                 );
487                 logInfo(
488                         "    after finally advice [" + AnnotationC.getShortCallSignature(method) + " :: "
489                         + ((AfterFinally) afterAnnotation).value() + ']'
490                 );
491             }
492         }
493         Annotation[] afterReturningAnnotations = manager.getAnnotations(
494                 AnnotationConstants.AFTER_RETURNING_DOCLET, method
495         );
496         for (int i = 0; i < afterReturningAnnotations.length; i++) {
497             Annotation afterAnnotation = afterReturningAnnotations[i];
498             if (afterAnnotation != null && afterAnnotation instanceof AfterReturning) {
499                 enhancer.insertMethodAttribute(
500                         method,
501                         new AnnotationInfo(AnnotationConstants.AFTER_RETURNING, afterAnnotation)
502                 );
503                 logInfo(
504                         "    after returning advice [" + AnnotationC.getShortCallSignature(method) + " :: "
505                         +
506                         AspectAnnotationParser.getExpressionElseValue(
507                                 ((AfterReturning) afterAnnotation).value(),
508                                 ((AfterReturning) afterAnnotation).pointcut()
509                         )
510                         + " :: " + ((AfterReturning) afterAnnotation).type() + ']'
511                 );
512             }
513         }
514         Annotation[] afterThrowingAnnotations = manager.getAnnotations(
515                 AnnotationConstants.AFTER_THROWING_DOCLET, method
516         );
517         for (int i = 0; i < afterThrowingAnnotations.length; i++) {
518             Annotation afterAnnotation = afterThrowingAnnotations[i];
519             if (afterAnnotation != null && afterAnnotation instanceof AfterThrowing) {
520                 enhancer.insertMethodAttribute(
521                         method,
522                         new AnnotationInfo(AnnotationConstants.AFTER_THROWING, afterAnnotation)
523                 );
524                 logInfo(
525                         "    after throwing advice [" + AnnotationC.getShortCallSignature(method) + " :: "
526                         +
527                         AspectAnnotationParser.getExpressionElseValue(
528                                 ((AfterThrowing) afterAnnotation).value(),
529                                 ((AfterThrowing) afterAnnotation).pointcut()
530                         )
531                         + " :: " + ((AfterThrowing) afterAnnotation).type() + ']'
532                 );
533             }
534         }
535         for (Iterator it = s_customAnnotations.keySet().iterator(); it.hasNext();) {
536             String annotationName = (String) it.next();
537             Annotation[] customAnnotations = manager.getAnnotations(annotationName, method);
538             for (int i = 0; i < customAnnotations.length; i++) {
539                 Annotation customAnnotation = customAnnotations[i];
540                 if (customAnnotation != null) {
541                     enhancer.insertMethodAttribute(method, new AnnotationInfo(
542                                 annotationName,
543                                 customAnnotation)
544                     );
545                     logInfo(
546                             "    custom method annotation [" + annotationName + " @ "
547                             + method.getParentClass().getName() + '.' + method.getName() +
548                             ']'
549                     );
550                 }
551             }
552         }
553     }
554 
555     /***
556      * Handles the constructor annotations.
557      *
558      * @param manager
559      * @param enhancer
560      * @param constructor
561      */
562     private static void handleConstructorAnnotations(final AnnotationManager manager,
563                                                      final AttributeEnhancer enhancer,
564                                                      final JavaMethod constructor) {
565         for (Iterator it = s_customAnnotations.keySet().iterator(); it.hasNext();) {
566             String annotationName = (String) it.next();
567             Annotation[] customAnnotations = manager.getAnnotations(annotationName, constructor);
568             for (int i = 0; i < customAnnotations.length; i++) {
569                 Annotation customAnnotation = customAnnotations[i];
570                 if (customAnnotation != null) {
571                     enhancer.insertConstructorAttribute(
572                             constructor, new AnnotationInfo(annotationName,
573                                                             customAnnotation)
574                     );
575                     logInfo(
576                             "    custom constructor annotation [" + annotationName + " @ "
577                             + constructor.getParentClass().getName() + '.' +
578                             constructor.getName()
579                             + ']'
580                     );
581                 }
582             }
583         }
584     }
585 
586     /***
587      * Handles the field annotations.
588      *
589      * @param manager
590      * @param enhancer
591      * @param field
592      */
593     private static void handleFieldAnnotations(final AnnotationManager manager,
594                                                final AttributeEnhancer enhancer,
595                                                final JavaField field) {
596 
597         Annotation[] expressionAnnotations = manager.getAnnotations(
598                 AnnotationConstants.EXPRESSION_DOCLET, field
599         );
600         for (int i = 0; i < expressionAnnotations.length; i++) {
601             Annotation expressionAnnotation = expressionAnnotations[i];
602             if (expressionAnnotation != null && expressionAnnotation instanceof Expression) {
603                 enhancer.insertFieldAttribute(
604                         field, new AnnotationInfo(
605                                 AnnotationConstants.EXPRESSION,
606                                 expressionAnnotation
607                         )
608                 );
609                 logInfo(
610                         "    pointcut [" + field.getName() + " :: " + ((Expression) expressionAnnotation).value()
611                         + ']'
612                 );
613             }
614         }
615         Annotation[] introduceAnnotations = manager.getAnnotations(
616                 AnnotationConstants.INTRODUCE_DOCLET, field
617         );
618         for (int i = 0; i < introduceAnnotations.length; i++) {
619             Annotation introduceAnnotation = introduceAnnotations[i];
620             if (introduceAnnotation != null && introduceAnnotation instanceof Introduce) {
621                 enhancer.insertFieldAttribute(
622                         field, new AnnotationInfo(
623                                 AnnotationConstants.INTRODUCE,
624                                 introduceAnnotation
625                         )
626                 );
627                 logInfo(
628                         "    interface introduction [" + field.getName() + " :: "
629                         + ((Introduce) introduceAnnotation).value() + ']'
630                 );
631             }
632         }
633         for (Iterator it = s_customAnnotations.keySet().iterator(); it.hasNext();) {
634             String annotationName = (String) it.next();
635             Annotation[] customAnnotations = manager.getAnnotations(annotationName, field);
636             for (int i = 0; i < customAnnotations.length; i++) {
637                 Annotation customAnnotation = customAnnotations[i];
638                 if (customAnnotation != null) {
639                     enhancer.insertFieldAttribute(
640                             field, new AnnotationInfo(
641                                     annotationName,
642                                     customAnnotation
643                             )
644                     );
645                     logInfo(
646                             "    custom field annotation [" + annotationName + " @ "
647                             + field.getName() + ']'
648                     );
649                 }
650             }
651         }
652     }
653 
654     /***
655      * Handles the inner class annotations.
656      *
657      * @param manager
658      * @param enhancer
659      * @param clazz
660      */
661     private static void handleInnerClassAnnotations(final AnnotationManager manager,
662                                                     final AttributeEnhancer enhancer,
663                                                     final JavaClass clazz) {
664         JavaClass[] innerClasses = clazz.getInnerClasses();
665         for (int i = 0; i < innerClasses.length; i++) {
666             parseMixinAnnotations(innerClasses[i], manager, enhancer);
667         }
668     }
669 
670     /***
671      * Registers the system annotations.
672      *
673      * @param manager the annotations manager
674      */
675     private static void registerSystemAnnotations(final AnnotationManager manager) {
676         manager.registerAnnotationProxy(Aspect.class, AnnotationConstants.ASPECT_DOCLET);
677         manager.registerAnnotationProxy(Around.class, AnnotationConstants.AROUND_DOCLET);
678         manager.registerAnnotationProxy(Before.class, AnnotationConstants.BEFORE_DOCLET);
679         manager.registerAnnotationProxy(After.class, AnnotationConstants.AFTER_DOCLET);
680         manager.registerAnnotationProxy(AfterReturning.class, AnnotationConstants.AFTER_RETURNING_DOCLET);
681         manager.registerAnnotationProxy(AfterThrowing.class, AnnotationConstants.AFTER_THROWING_DOCLET);
682         manager.registerAnnotationProxy(AfterFinally.class, AnnotationConstants.AFTER_FINALLY_DOCLET);
683         manager.registerAnnotationProxy(Expression.class, AnnotationConstants.EXPRESSION_DOCLET);
684         manager.registerAnnotationProxy(Introduce.class, AnnotationConstants.INTRODUCE_DOCLET);
685         manager.registerAnnotationProxy(Mixin.class, AnnotationConstants.MIXIN_DOCLET);
686     }
687 
688     /***
689      * Registers the user defined annotations.
690      *
691      * @param manager         the annotations manager
692      * @param propertiesFiles
693      */
694     private static void registerUserDefinedAnnotations(final AnnotationManager manager,
695                                                        final String[] propertiesFiles) {
696         if (propertiesFiles == null) {
697             return;
698         }
699         InputStream in = null;
700         for (int i = 0; i < propertiesFiles.length; i++) {
701             String propertiesFile = propertiesFiles[i];
702             try {
703                 in = new FileInputStream(propertiesFile);
704                 ANNOTATION_DEFINITION.load(in);
705             } catch (Exception e) {
706                 String message = "custom annotation properties " + propertiesFile + " can not be loaded: " +
707                                  e.toString();
708                 logWarning(message);
709                 throw new DefinitionException(message);
710             } finally {
711                 try {
712                     in.close();
713                 } catch (Exception e) {
714                     ;
715                 }
716             }
717         }
718 
719         for (Iterator it = ANNOTATION_DEFINITION.entrySet().iterator(); it.hasNext();) {
720             Map.Entry entry = (Map.Entry) it.next();
721             String name = ((String) entry.getKey()).trim();
722             String className = ((String) entry.getValue()).trim();
723             Class klass;
724             if (className.equals("")) {
725                 // use default untyped annotation
726                 klass = UntypedAnnotation.class;
727                 className = klass.getName();
728             } else {
729                 try {
730                     klass = Class.forName(className, false, s_loader);
731                 } catch (ClassNotFoundException e) {
732                     String message = className
733                                      +
734                                      " could not be found on system classpath or class path provided as argument to the compiler";
735                     logError(message);
736                     throw new DefinitionException(message);
737                 }
738             }
739             logInfo("register custom annotation [" + name + " :: " + className + ']');
740             manager.registerAnnotationProxy(klass, name);
741             s_customAnnotations.put(name, className);
742         }
743     }
744 
745     /***
746      * Prints the usage.
747      */
748     private static void printUsage() {
749         System.out.println("AspectWerkz (c) 2002-2005 Jonas BonŽr, Alexandre Vasseur");
750         System.out.println(
751                 "usage: java [options...] org.codehaus.aspectwerkz.annotation.AnnotationC [-verbose] -src <path to src dir> | -srcfiles <list of files> | -srcincludes <path to file> -classes <path to classes dir> [-dest <path to destination dir>] [-custom <property file for custom annotations>]"
752         );
753         System.out.println(
754                 "       -src <path to src dir> provides the list of source directories separated by File.pathSeparator"
755         );
756         System.out.println("       -srcpath <list of files> provides a comma separated list of source files");
757         System.out.println(
758                 "       -srcincludes <path to file> provides the path to a file containing the list of source files (one name per line)"
759         );
760         System.out.println(
761                 "       -dest <path to destination dir> is optional, if omitted the compiled classes will be written to the initial directory"
762         );
763         System.out.println(
764                 "       -custom <property file for cutom annotations> is optional, only needed if you have custom annotations you want to compile"
765         );
766         System.out.println("       -verbose activates compilation status information");
767         System.out.println("");
768         System.out.println("Note: only one of -src -srcpath and -srcincludes may be used");
769 
770         System.exit(0);
771     }
772 
773     /***
774      * Parses the command line options.
775      *
776      * @param args the arguments
777      * @return a map with the options
778      */
779     private static Map parseCommandLineOptions(final String[] args) {
780         final Map arguments = new HashMap();
781         try {
782             for (int i = 0; i < args.length; i++) {
783                 if (args[i].equals(COMMAND_LINE_OPTION_VERBOSE)) {
784                     s_verbose = true;
785                 } else if (args[i].startsWith(COMMAND_LINE_OPTION_DASH)) {
786                     String option = args[i++];
787                     String value = args[i];
788                     arguments.put(option, value);
789                 }
790             }
791         } catch (Exception e) {
792             logError("options list to compiler is not valid");
793             System.exit(1);
794         }
795         return arguments;
796     }
797 
798     /***
799      * Logs an INFO message.
800      *
801      * @param message the message
802      */
803     private static void logInfo(final String message) {
804         if (s_verbose) {
805             System.out.println("AnnotationC::INFO - " + message);
806         }
807     }
808 
809     /***
810      * Logs an ERROR message.
811      *
812      * @param message the message
813      */
814     private static void logError(final String message) {
815         if (s_verbose) {
816             System.err.println("AnnotationC::ERROR - " + message);
817         }
818     }
819 
820     /***
821      * Logs an WARNING message.
822      *
823      * @param message the message
824      */
825     private static void logWarning(final String message) {
826         if (s_verbose) {
827             System.err.println("AnnotationC::WARNING - " + message);
828         }
829     }
830 
831     /***
832      * Helper method to get a pretty printable method signature (no FQN class names)
833      *
834      * @param method
835      * @return
836      */
837     private static String getShortCallSignature(final JavaMethod method) {
838         StringBuffer buffer = new StringBuffer(method.getName());
839         buffer.append("(");
840         for (int i = 0; i < method.getParameters().length; i++) {
841             JavaParameter javaParameter = method.getParameters()[i];
842             if (javaParameter.getType().toString().equals(JoinPoint.class.getName())) {
843                 buffer.append("JoinPoint");
844             } else if (javaParameter.getType().toString().equals(StaticJoinPoint.class.getName())) {
845                 buffer.append("StaticJoinPoint");
846             } else {
847                 buffer.append(javaParameter.getType().toString());
848                 buffer.append(" ");
849                 buffer.append(javaParameter.getName());
850             }
851             if (i + 1 < method.getParameters().length) {
852                 buffer.append(", ");
853             }
854         }
855         buffer.append(")");
856         return buffer.toString();
857     }
858 
859     private static String[] split(String str, String sep) {
860         if (str == null || str.length() == 0) {
861             return new String[0];
862         }
863 
864         int start = 0;
865         int idx = str.indexOf(sep, start);
866         int len = sep.length();
867         List strings = new ArrayList();
868 
869         while (idx != -1) {
870             strings.add(str.substring(start, idx));
871             start = idx + len;
872             idx = str.indexOf(sep, start);
873         }
874 
875         strings.add(str.substring(start));
876 
877         return (String[]) strings.toArray(new String[strings.size()]);
878     }
879 
880     /***
881      * Load and solve relative to working directory the list of files.
882      *
883      * @param srcIncludes
884      * @return
885      */
886     private static String[] loadSourceList(final String srcIncludes) {
887         File currentDir = new File(".");
888         List files = new ArrayList();
889         BufferedReader reader = null;
890 
891         try {
892             reader = new BufferedReader(new FileReader(srcIncludes));
893 
894             String line = reader.readLine();
895             File tmpFile = null;
896             while (line != null) {
897                 if (line.length() > 0) {
898                     tmpFile = new File(currentDir, line);
899                     if (!tmpFile.isFile()) {
900                         logWarning("file not found: [" + tmpFile + "]");
901                     } else {
902                         files.add(tmpFile.getAbsolutePath());
903                     }
904                 }
905                 line = reader.readLine();
906             }
907         } catch (IOException ioe) {
908             throw new BuildException(
909                     "an error occured while reading from pattern file: "
910                     + srcIncludes, ioe
911             );
912         } finally {
913             if (null != reader) {
914                 try {
915                     reader.close();
916                 } catch (IOException ioe) {
917                     //Ignore exception
918                 }
919             }
920         }
921 
922         return (String[]) files.toArray(new String[files.size()]);
923     }
924 
925 }