1   package com.atlassian.maven.plugins.amps.codegen.annotations.asm;
2   
3   import java.io.InputStream;
4   import java.lang.reflect.Constructor;
5   
6   import com.atlassian.maven.plugins.amps.codegen.annotations.ModuleCreatorClass;
7   import com.atlassian.maven.plugins.amps.codegen.prompter.AbstractModulePrompter;
8   import com.atlassian.maven.plugins.amps.codegen.prompter.PluginModulePrompter;
9   import com.atlassian.maven.plugins.amps.codegen.prompter.PluginModulePrompterRegistry;
10  import com.atlassian.plugins.codegen.annotations.asm.AbstractAnnotationParser;
11  
12  import org.apache.commons.io.IOUtils;
13  import org.apache.commons.lang.ArrayUtils;
14  import org.apache.maven.plugin.logging.Log;
15  import org.codehaus.plexus.components.interactivity.Prompter;
16  import org.objectweb.asm.*;
17  import org.objectweb.asm.commons.EmptyVisitor;
18  
19  /**
20   * @since 3.6
21   */
22  public class ModulePrompterAnnotationParser extends AbstractAnnotationParser
23  {
24      public static final String PROMPTER_PACKAGE = "com.atlassian.maven.plugins.amps.codegen.prompter";
25      private Log log;
26  
27      private PluginModulePrompterRegistry pluginModulePrompterRegistry;
28      private Prompter mavenPrompter;
29  
30      public ModulePrompterAnnotationParser(PluginModulePrompterRegistry pluginModulePrompterRegistry)
31      {
32          this.pluginModulePrompterRegistry = pluginModulePrompterRegistry;
33      }
34  
35      public void parse() throws Exception
36      {
37          ClassLoader oldLoader = Thread.currentThread()
38                  .getContextClassLoader();
39          Thread.currentThread()
40                  .setContextClassLoader(getClass().getClassLoader());
41          parse(PROMPTER_PACKAGE, new PropmpterClassVisitor());
42          Thread.currentThread()
43                  .setContextClassLoader(oldLoader);
44      }
45  
46      public void parse(String basePackage) throws Exception
47      {
48          ClassLoader oldLoader = Thread.currentThread()
49                  .getContextClassLoader();
50          Thread.currentThread()
51                  .setContextClassLoader(getClass().getClassLoader());
52          parse(basePackage, new PropmpterClassVisitor());
53          Thread.currentThread()
54                  .setContextClassLoader(oldLoader);
55      }
56  
57      public class PropmpterClassVisitor extends EmptyVisitor
58      {
59  
60          private String visitedClassname;
61          private boolean isModulePrompter;
62  
63          @Override
64          public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces)
65          {
66              this.visitedClassname = normalize(name);
67              String iface = PluginModulePrompter.class.getName()
68                      .replace('.', '/');
69              this.isModulePrompter = ArrayUtils.contains(interfaces, iface);
70              if (!isModulePrompter)
71              {
72                  this.isModulePrompter = superHasInterface(superName, iface);
73              }
74  
75              Class modulePrompterClass = null;
76              try
77              {
78                  modulePrompterClass = Class.forName(visitedClassname);
79              } catch (ClassNotFoundException e)
80              {
81                  //dumb. we're visiting this class so it has to exist!
82              }
83  
84              if (isModulePrompter && !AbstractModulePrompter.class.isAssignableFrom(modulePrompterClass))
85              {
86                  isModulePrompter = false;
87                  if (null != log)
88                  {
89                      log.warn(visitedClassname + " MUST extend " + AbstractModulePrompter.class.getName() + ". NOT REGISTERED");
90                  }
91              }
92          }
93  
94          private boolean superHasInterface(String superName, String interfaceName)
95          {
96              boolean hasInterface = false;
97  
98              if (normalize(superName).equals("java.lang.Object"))
99              {
100                 return hasInterface;
101             }
102 
103             ClassLoader classLoader = Thread.currentThread()
104                     .getContextClassLoader();
105             String path = superName.replace('.', '/');
106 
107             InputStream is = null;
108             try
109             {
110                 is = classLoader.getResourceAsStream(path + ".class");
111                 if (null != is)
112                 {
113 
114                     ClassReader classReader = new ClassReader(is);
115                     hasInterface = ArrayUtils.contains(classReader.getInterfaces(), interfaceName);
116                     if (!hasInterface)
117                     {
118                         hasInterface = superHasInterface(classReader.getSuperName(), interfaceName);
119                     }
120                 }
121             } catch (Exception e)
122             {
123                 //don't care
124             } finally
125             {
126                 IOUtils.closeQuietly(is);
127             }
128 
129             return hasInterface;
130         }
131 
132         @Override
133         public AnnotationVisitor visitAnnotation(String annotationName, boolean isVisible)
134         {
135             String normalizedName = normalize(annotationName);
136 
137             if (isModulePrompter && ModuleCreatorClass.class.getName()
138                     .equals(normalizedName))
139             {
140                 return new ModuleCreatorClassAnnotationVisitor(normalizedName);
141             }
142 
143             return null;
144         }
145 
146 
147         @Override
148         public MethodVisitor visitMethod(int i, String s, String s1, String s2, String[] strings)
149         {
150             return null;
151         }
152 
153         @Override
154         public FieldVisitor visitField(int i, String s, String s1, String s2, Object o)
155         {
156             return null;
157         }
158 
159         private class ModuleCreatorClassAnnotationVisitor extends EmptyVisitor
160         {
161 
162             private String annotationName;
163 
164             private ModuleCreatorClassAnnotationVisitor(String annotationName)
165             {
166                 this.annotationName = annotationName;
167             }
168 
169             @Override
170             public void visit(String name, Object value)
171             {
172                 super.visit(name, value);
173                 Type creatorType = (Type) value;
174                 String normalizedCreatorName = normalize(creatorType.getClassName());
175 
176                 try
177                 {
178                     Class creatorClass = Class.forName(normalizedCreatorName);
179                     Class modulePrompterClass = Class.forName(visitedClassname);
180                     Class[] argTypes = new Class[]{Prompter.class};
181                     Object[] args = new Object[]{mavenPrompter};
182 
183                     Constructor prompterConstructor = modulePrompterClass.getConstructor(argTypes);
184                     if (null != prompterConstructor)
185                     {
186                         PluginModulePrompter modulePrompter = (PluginModulePrompter) prompterConstructor.newInstance(args);
187                         pluginModulePrompterRegistry.registerModulePrompter(creatorClass, modulePrompter);
188                     }
189 
190                 } catch (Exception e)
191                 {
192                     e.printStackTrace();
193                 }
194             }
195 
196             @Override
197             public void visitEnd()
198             {
199                 super.visitEnd();
200             }
201         }
202     }
203 
204     public Prompter getMavenPrompter()
205     {
206         return mavenPrompter;
207     }
208 
209     public void setMavenPrompter(Prompter mavenPrompter)
210     {
211         this.mavenPrompter = mavenPrompter;
212     }
213 
214     public Log getLog()
215     {
216         return log;
217     }
218 
219     public void setLog(Log log)
220     {
221         this.log = log;
222     }
223 }