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