1   package com.atlassian.plugins.codegen.annotations.asm;
2   
3   import java.io.InputStream;
4   import java.util.ArrayList;
5   import java.util.HashMap;
6   import java.util.List;
7   import java.util.Map;
8   
9   import com.atlassian.plugins.codegen.annotations.*;
10  import com.atlassian.plugins.codegen.modules.PluginModuleCreator;
11  import com.atlassian.plugins.codegen.modules.PluginModuleCreatorRegistry;
12  
13  import org.apache.commons.io.IOUtils;
14  import org.apache.commons.lang.ArrayUtils;
15  import org.apache.commons.lang.StringUtils;
16  import org.objectweb.asm.AnnotationVisitor;
17  import org.objectweb.asm.ClassReader;
18  import org.objectweb.asm.FieldVisitor;
19  import org.objectweb.asm.MethodVisitor;
20  import org.objectweb.asm.commons.EmptyVisitor;
21  
22  /**
23   * @since 3.6
24   */
25  public class ModuleCreatorAnnotationParser extends AbstractAnnotationParser
26  {
27  
28      public static final String MODULE_PACKAGE = "com.atlassian.plugins.codegen.modules";
29      protected static final Map<String, String> annotationProductMap = new HashMap<String, String>();
30  
31      static
32      {
33          annotationProductMap.put(JiraPluginModuleCreator.class.getName(), PluginModuleCreatorRegistry.JIRA);
34          annotationProductMap.put(ConfluencePluginModuleCreator.class.getName(), PluginModuleCreatorRegistry.CONFLUENCE);
35          annotationProductMap.put(BambooPluginModuleCreator.class.getName(), PluginModuleCreatorRegistry.BAMBOO);
36          annotationProductMap.put(CrowdPluginModuleCreator.class.getName(), PluginModuleCreatorRegistry.CROWD);
37          annotationProductMap.put(FeCruPluginModuleCreator.class.getName(), PluginModuleCreatorRegistry.FECRU);
38          annotationProductMap.put(RefAppPluginModuleCreator.class.getName(), PluginModuleCreatorRegistry.REFAPP);
39      }
40  
41      private final PluginModuleCreatorRegistry pluginModuleCreatorRegistry;
42  
43      public ModuleCreatorAnnotationParser(PluginModuleCreatorRegistry pluginModuleCreatorRegistry)
44      {
45          super();
46          this.pluginModuleCreatorRegistry = pluginModuleCreatorRegistry;
47      }
48  
49      public void parse() throws Exception
50      {
51          ClassLoader oldLoader = Thread.currentThread()
52                  .getContextClassLoader();
53          Thread.currentThread()
54                  .setContextClassLoader(getClass().getClassLoader());
55          parse(MODULE_PACKAGE, new ModuleClassVisitor());
56          Thread.currentThread()
57                  .setContextClassLoader(oldLoader);
58      }
59  
60      public void parse(String basePackage) throws Exception
61      {
62          ClassLoader oldLoader = Thread.currentThread()
63                  .getContextClassLoader();
64          Thread.currentThread()
65                  .setContextClassLoader(getClass().getClassLoader());
66          parse(basePackage, new ModuleClassVisitor());
67          Thread.currentThread()
68                  .setContextClassLoader(oldLoader);
69      }
70  
71      public class ModuleClassVisitor extends EmptyVisitor
72      {
73  
74          private String visitedClassname;
75          private boolean isModuleCreator;
76  
77          @Override
78          public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces)
79          {
80              this.visitedClassname = normalize(name);
81              String iface = PluginModuleCreator.class.getName()
82                      .replace('.', '/');
83              this.isModuleCreator = ArrayUtils.contains(interfaces, iface);
84              if (!isModuleCreator)
85              {
86                  this.isModuleCreator = superHasInterface(superName, iface);
87              }
88          }
89  
90          private boolean superHasInterface(String superName, String interfaceName)
91          {
92              boolean hasInterface = false;
93  
94              if (normalize(superName).equals("java.lang.Object"))
95              {
96                  return hasInterface;
97              }
98  
99              ClassLoader classLoader = Thread.currentThread()
100                     .getContextClassLoader();
101             String path = superName.replace('.', '/');
102 
103             InputStream is = null;
104             try
105             {
106                 is = classLoader.getResourceAsStream(path + ".class");
107                 if (null != is)
108                 {
109 
110                     ClassReader classReader = new ClassReader(is);
111                     hasInterface = ArrayUtils.contains(classReader.getInterfaces(), interfaceName);
112                     if (!hasInterface)
113                     {
114                         hasInterface = superHasInterface(classReader.getSuperName(), interfaceName);
115                     }
116                 }
117             } catch (Exception e)
118             {
119                 //don't care
120             } finally
121             {
122                 IOUtils.closeQuietly(is);
123             }
124 
125             return hasInterface;
126         }
127 
128         @Override
129         public AnnotationVisitor visitAnnotation(String annotationName, boolean isVisible)
130         {
131             String normalizedName = normalize(annotationName);
132 
133             if (isModuleCreator && annotationProductMap.containsKey(normalizedName))
134             {
135                 return new ProductCreatorAnnotationVisitor(normalizedName);
136             }
137 
138             if (isModuleCreator && (Dependencies.class.getName()
139                     .equals(normalizedName) || Dependency.class.equals(normalizedName)))
140             {
141                 return new DependenciesAnnotationVisitor(normalizedName);
142             }
143 
144             return null;
145         }
146 
147 
148         @Override
149         public MethodVisitor visitMethod(int i, String s, String s1, String s2, String[] strings)
150         {
151             return null;
152         }
153 
154         @Override
155         public FieldVisitor visitField(int i, String s, String s1, String s2, Object o)
156         {
157             return null;
158         }
159 
160         private class ProductCreatorAnnotationVisitor extends EmptyVisitor
161         {
162 
163             private String annotationName;
164 
165             private ProductCreatorAnnotationVisitor(String annotationName)
166             {
167                 this.annotationName = annotationName;
168             }
169 
170             @Override
171             public void visitEnd()
172             {
173 
174                 super.visitEnd();
175 
176                 String productId = annotationProductMap.get(annotationName);
177                 if (StringUtils.isNotBlank(productId))
178                 {
179                     try
180                     {
181                         PluginModuleCreator creator = (PluginModuleCreator) Class.forName(visitedClassname)
182                                 .newInstance();
183                         pluginModuleCreatorRegistry.registerModuleCreator(productId, creator);
184                     } catch (Exception e)
185                     {
186                         e.printStackTrace();
187                         //just don't register
188                     }
189                 }
190             }
191 
192         }
193 
194         private class DependenciesAnnotationVisitor extends EmptyVisitor
195         {
196 
197             private String annotationName;
198             private List<DependencyDescriptor> dependencies;
199 
200             private DependenciesAnnotationVisitor(String annotationName)
201             {
202                 this.annotationName = annotationName;
203                 this.dependencies = new ArrayList<DependencyDescriptor>();
204             }
205 
206             //Visits nested annotations
207             @Override
208             public AnnotationVisitor visitAnnotation(String name, String annotationName)
209             {
210                 String normalizedName = normalize(annotationName);
211                 return new DependencyAnnotationVisitor(normalizedName, dependencies);
212             }
213 
214             @Override
215             public void visitEnd()
216             {
217 
218                 super.visitEnd();
219 
220                 if (!dependencies.isEmpty())
221                 {
222                     try
223                     {
224                         Class creatorClass = Class.forName(visitedClassname);
225                         pluginModuleCreatorRegistry.registerModuleCreatorDependencies(creatorClass, dependencies);
226                     } catch (Exception e)
227                     {
228                         e.printStackTrace();
229                         //just don't register
230                     }
231                 }
232             }
233 
234         }
235 
236         private class DependencyAnnotationVisitor extends EmptyVisitor
237         {
238             private String annotationName;
239             private List<DependencyDescriptor> dependencies;
240             private DependencyDescriptor descriptor;
241 
242             private DependencyAnnotationVisitor(String annotationName, List<DependencyDescriptor> dependencies)
243             {
244                 this.annotationName = annotationName;
245                 this.dependencies = dependencies;
246                 this.descriptor = new DependencyDescriptor();
247             }
248 
249             //visit a name/value pair
250             @Override
251             public void visit(String name, Object value)
252             {
253                 if (name.equals("groupId"))
254                 {
255                     descriptor.setGroupId((String) value);
256                 } else if (name.equals("artifactId"))
257                 {
258                     descriptor.setArtifactId((String) value);
259                 } else if (name.equals("version"))
260                 {
261                     descriptor.setVersion((String) value);
262                 } else if (name.equals("scope"))
263                 {
264                     descriptor.setScope((String) value);
265                 }
266             }
267 
268             @Override
269             public void visitEnd()
270             {
271                 super.visitEnd();
272                 dependencies.add(descriptor);
273             }
274         }
275 
276     }
277 
278 }