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.transform.inlining.weaver;
9   
10  import java.util.Iterator;
11  import java.util.Set;
12  import java.util.HashSet;
13  import java.util.List;
14  
15  import org.objectweb.asm.*;
16  import org.codehaus.aspectwerkz.transform.Context;
17  import org.codehaus.aspectwerkz.transform.TransformationConstants;
18  import org.codehaus.aspectwerkz.transform.TransformationUtil;
19  import org.codehaus.aspectwerkz.transform.inlining.ContextImpl;
20  import org.codehaus.aspectwerkz.definition.SystemDefinition;
21  import org.codehaus.aspectwerkz.definition.InterfaceIntroductionDefinition;
22  import org.codehaus.aspectwerkz.definition.MixinDefinition;
23  import org.codehaus.aspectwerkz.expression.ExpressionContext;
24  import org.codehaus.aspectwerkz.expression.PointcutType;
25  import org.codehaus.aspectwerkz.reflect.ClassInfo;
26  import org.codehaus.aspectwerkz.reflect.ClassInfoHelper;
27  
28  /***
29   * Adds an interface to the target class.
30   *
31   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
32   * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
33   */
34  public class AddInterfaceVisitor extends ClassAdapter implements TransformationConstants {
35  
36      private final static String ADVISABLE_MIXIN_IMPL_NAME = "org.codehaus.aspectwerkz.intercept.AdvisableImpl";
37  
38      private final ContextImpl m_ctx;
39      private final ClassInfo m_classInfo;
40  
41      /***
42       * Creates a new add interface class adapter.
43       *
44       * @param cv
45       * @param classInfo
46       * @param ctx
47       */
48      public AddInterfaceVisitor(final ClassVisitor cv,
49                                 final ClassInfo classInfo,
50                                 final Context ctx) {
51          super(cv);
52          m_classInfo = classInfo;
53          m_ctx = (ContextImpl) ctx;
54      }
55  
56      /***
57       * Visits the class.
58       *
59       * @param access
60       * @param name
61       * @param superName
62       * @param interfaces
63       * @param sourceFile
64       */
65      public void visit(final int version,
66                        final int access,
67                        final String name,
68                        final String superName,
69                        final String[] interfaces,
70                        final String sourceFile) {
71          ExpressionContext ctx = new ExpressionContext(PointcutType.WITHIN, m_classInfo, m_classInfo);
72          if (classFilter(m_classInfo, ctx, m_ctx.getDefinitions())) {
73              super.visit(version, access, name, superName, interfaces, sourceFile);
74              return;
75          }
76  
77          // javaclass names of interface to have
78          // use a Set to avoid doublons
79          final Set interfacesToAdd = new HashSet();
80  
81          // already there interface javaclass names
82          for (int i = 0; i < interfaces.length; i++) {
83              interfacesToAdd.add(interfaces[i].replace('/', '.'));
84          }
85  
86          // add new ones
87          final Set systemDefinitions = m_ctx.getDefinitions();
88          for (Iterator it = systemDefinitions.iterator(); it.hasNext();) {
89              SystemDefinition systemDefinition = (SystemDefinition) it.next();
90              final List interfaceIntroDefs = systemDefinition.getInterfaceIntroductionDefinitions(ctx);
91              for (Iterator it2 = interfaceIntroDefs.iterator(); it2.hasNext();) {
92                  final InterfaceIntroductionDefinition interfaceIntroDef = (InterfaceIntroductionDefinition) it2.next();
93                  interfacesToAdd.addAll(interfaceIntroDef.getInterfaceClassNames());
94              }
95              final List mixinDefinitions = systemDefinition.getMixinDefinitions(ctx);
96              for (Iterator it2 = mixinDefinitions.iterator(); it2.hasNext();) {
97                  final MixinDefinition mixinDef = (MixinDefinition) it2.next();
98                  if (ADVISABLE_MIXIN_IMPL_NAME.equals(mixinDef.getMixinImpl().getName())) {
99                      // mark it as made advisable
100                     m_ctx.markMadeAdvisable();
101                 }
102                 final List interfaceList = mixinDef.getInterfaceClassNames();
103                 for (Iterator it3 = interfaceList.iterator(); it3.hasNext();) {
104                     interfacesToAdd.add(((String) it3.next()));
105                 }
106             }
107         }
108 
109         if (ClassInfoHelper.hasMethodClash(interfacesToAdd, m_ctx.getLoader())) {
110             super.visit(version, access, name, superName, interfaces, sourceFile);
111             return;
112         }
113 
114         int i = 0;
115         final String[] newInterfaceArray = new String[interfacesToAdd.size()];
116         for (Iterator it = interfacesToAdd.iterator(); it.hasNext();) {
117             newInterfaceArray[i++] = (String) it.next();
118         }
119 
120         for (int j = 0; j < newInterfaceArray.length; j++) {
121             newInterfaceArray[j] = newInterfaceArray[j].replace('.', '/');
122 
123         }
124         super.visit(version, access, name, superName, newInterfaceArray, sourceFile);
125         m_ctx.markAsAdvised();
126     }
127 
128     /***
129      * Filters the classes to be transformed.
130      *
131      * @param classInfo   the class to filter
132      * @param ctx         the context
133      * @param definitions a set with the definitions
134      * @return boolean true if the method should be filtered away
135      */
136     public static boolean classFilter(final ClassInfo classInfo,
137                                       final ExpressionContext ctx,
138                                       final Set definitions) {
139         for (Iterator it = definitions.iterator(); it.hasNext();) {
140             SystemDefinition systemDef = (SystemDefinition) it.next();
141             if (classInfo.isInterface()) {
142                 return true;
143             }
144             String className = classInfo.getName().replace('/', '.');
145             if (systemDef.inExcludePackage(className)) {
146                 return true;
147             }
148             if (!systemDef.inIncludePackage(className)) {
149                 return true;
150             }
151             if (systemDef.hasMixin(ctx) || systemDef.hasIntroducedInterface(ctx)) {
152                 return false;
153             }
154         }
155         return true;
156     }
157 }