1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.tools.plugin.extractor.annotations.scanner.visitors;
20
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.stream.Collectors;
30 import java.util.stream.Stream;
31
32 import org.apache.maven.tools.plugin.extractor.annotations.scanner.MojoAnnotatedClass;
33 import org.apache.maven.tools.plugin.extractor.annotations.scanner.MojoAnnotationsScanner;
34 import org.codehaus.plexus.util.StringUtils;
35 import org.objectweb.asm.AnnotationVisitor;
36 import org.objectweb.asm.ClassVisitor;
37 import org.objectweb.asm.FieldVisitor;
38 import org.objectweb.asm.MethodVisitor;
39 import org.objectweb.asm.Opcodes;
40 import org.objectweb.asm.Type;
41 import org.objectweb.asm.signature.SignatureReader;
42 import org.objectweb.asm.util.TraceSignatureVisitor;
43
44 import static org.apache.maven.tools.plugin.extractor.annotations.scanner.DefaultMojoAnnotationsScanner.PARAMETER_V3;
45 import static org.apache.maven.tools.plugin.extractor.annotations.scanner.DefaultMojoAnnotationsScanner.PARAMETER_V4;
46
47
48
49
50
51
52
53 public class MojoClassVisitor extends ClassVisitor {
54 private MojoAnnotatedClass mojoAnnotatedClass;
55
56 private Map<String, MojoAnnotationVisitor> annotationVisitorMap = new HashMap<>();
57
58 private List<MojoFieldVisitor> fieldVisitors = new ArrayList<>();
59
60 private List<MojoMethodVisitor> methodVisitors = new ArrayList<>();
61
62 private int version;
63
64 public MojoClassVisitor() {
65 super(Opcodes.ASM9);
66 }
67
68 public MojoAnnotatedClass getMojoAnnotatedClass() {
69 return mojoAnnotatedClass;
70 }
71
72 public int getVersion() {
73 return version;
74 }
75
76 public MojoAnnotationVisitor getAnnotationVisitor(Class<?> annotation) {
77 return getAnnotationVisitor(annotation.getName());
78 }
79
80 public MojoAnnotationVisitor getAnnotationVisitor(String name) {
81 return annotationVisitorMap.get(name);
82 }
83
84 public List<MojoFieldVisitor> findFieldWithAnnotation(Class<?> annotation) {
85 return findFieldWithAnnotation(Collections.singleton(annotation.getName()));
86 }
87
88 public List<MojoFieldVisitor> findFieldWithAnnotation(Set<String> annotationClassNames) {
89 List<MojoFieldVisitor> mojoFieldVisitors = new ArrayList<>();
90
91 for (MojoFieldVisitor mojoFieldVisitor : this.fieldVisitors) {
92 Map<String, MojoAnnotationVisitor> filedVisitorMap = mojoFieldVisitor.getAnnotationVisitorMap();
93 if (filedVisitorMap.keySet().stream().anyMatch(annotationClassNames::contains)) {
94 mojoFieldVisitors.add(mojoFieldVisitor);
95 }
96 }
97
98 return mojoFieldVisitors;
99 }
100
101 public List<MojoParameterVisitor> findParameterVisitors() {
102 return findParameterVisitors(new HashSet<>(Arrays.asList(PARAMETER_V3, PARAMETER_V4)));
103 }
104
105 public List<MojoParameterVisitor> findParameterVisitors(Set<String> annotationClassNames) {
106 return Stream.concat(
107 findFieldWithAnnotation(annotationClassNames).stream(),
108 methodVisitors.stream().filter(method -> method.getAnnotationVisitorMap().keySet().stream()
109 .anyMatch(annotationClassNames::contains)))
110 .collect(Collectors.toList());
111 }
112
113 @Override
114 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
115 this.version = version;
116 mojoAnnotatedClass = new MojoAnnotatedClass();
117 mojoAnnotatedClass.setClassName(Type.getObjectType(name).getClassName());
118 if (superName != null) {
119 mojoAnnotatedClass.setParentClassName(Type.getObjectType(superName).getClassName());
120 }
121 }
122
123 @Override
124 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
125 String annotationClassName = Type.getType(desc).getClassName();
126 if (!MojoAnnotationsScanner.CLASS_LEVEL_ANNOTATIONS.contains(annotationClassName)) {
127 return null;
128 }
129 if (annotationClassName.startsWith(MojoAnnotationsScanner.V4_API_ANNOTATIONS_PACKAGE)) {
130 mojoAnnotatedClass.setV4Api(true);
131 }
132 MojoAnnotationVisitor mojoAnnotationVisitor = new MojoAnnotationVisitor(annotationClassName);
133 annotationVisitorMap.put(annotationClassName, mojoAnnotationVisitor);
134 return mojoAnnotationVisitor;
135 }
136
137 @Override
138 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
139 List<String> typeParameters = extractTypeParameters(access, signature, true);
140 MojoFieldVisitor mojoFieldVisitor =
141 new MojoFieldVisitor(name, Type.getType(desc).getClassName(), typeParameters);
142 fieldVisitors.add(mojoFieldVisitor);
143 return mojoFieldVisitor;
144 }
145
146
147
148
149
150
151
152
153
154
155 private List<String> extractTypeParameters(int access, String signature, boolean isField) {
156 if (signature == null || signature.isEmpty()) {
157 return Collections.emptyList();
158 }
159 TraceSignatureVisitor traceSignatureVisitor = new TraceSignatureVisitor(access);
160 SignatureReader signatureReader = new SignatureReader(signature);
161 if (isField) {
162 signatureReader.acceptType(traceSignatureVisitor);
163 } else {
164 signatureReader.accept(traceSignatureVisitor);
165 }
166 String declaration = traceSignatureVisitor.getDeclaration();
167 int startTypeParameters = declaration.indexOf('<');
168 if (startTypeParameters == -1) {
169 return Collections.emptyList();
170 }
171 String typeParameters = declaration.substring(startTypeParameters + 1, declaration.lastIndexOf('>'));
172 return Arrays.asList(typeParameters.split(", "));
173 }
174
175 @Override
176 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
177 if ((access & Opcodes.ACC_PUBLIC) != Opcodes.ACC_PUBLIC
178 || (access & Opcodes.ACC_STATIC) == Opcodes.ACC_STATIC) {
179 return null;
180 }
181
182 if (name.length() < 4 || !(name.startsWith("add") || name.startsWith("set"))) {
183 return null;
184 }
185
186 Type type = Type.getType(desc);
187
188 if ("void".equals(type.getReturnType().getClassName()) && type.getArgumentTypes().length == 1) {
189 String fieldName = StringUtils.lowercaseFirstLetter(name.substring(3));
190 String className = type.getArgumentTypes()[0].getClassName();
191 List<String> typeParameters = extractTypeParameters(access, signature, false);
192
193 MojoMethodVisitor mojoMethodVisitor = new MojoMethodVisitor(fieldName, className, typeParameters);
194 methodVisitors.add(mojoMethodVisitor);
195 return mojoMethodVisitor;
196 }
197
198 return null;
199 }
200 }