blob: 6102614bdad9e167cf8617f792e2d93685e3282f [file] [log] [blame]
The Android Open Source Projectf8057102009-03-15 16:47:16 -07001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package testprogress2;
18
19import com.sun.javadoc.AnnotationDesc;
20import com.sun.javadoc.AnnotationValue;
21import com.sun.javadoc.ClassDoc;
22import com.sun.javadoc.ExecutableMemberDoc;
23import com.sun.javadoc.FieldDoc;
24import com.sun.javadoc.Parameter;
25import com.sun.javadoc.ParameterizedType;
26import com.sun.javadoc.Type;
27import com.sun.javadoc.TypeVariable;
28import com.sun.javadoc.AnnotationDesc.ElementValuePair;
29
30import testprogress2.TestMethodInformation.Level;
31
32/**
33 * holder for a TestTargetNew annotation
34 */
35public class TestTargetNew {
36 private final Originator originator;
37
38 private Level level = null;
39
40 private String notes = null;
41
42 /*
43 * method or constructor of target class
44 */
45 private ExecutableMemberDoc targetMethod = null;
46
47 /*
48 * only set if the target points -only- to a class, not to a method. e.g for
49 * special "!..." targets
50 */
51 private ClassDoc targetClass = null;
52
53 /*
54 * read from annotation, e.g. foobar(java.lang.String)
55 */
56 private String readMethodSignature = null;
57
58 /*
59 * e.g. foobar
60 */
61 private String readMethodName = null;
62
63 /*
64 * read from annotation
65 */
66 private ClassDoc readTargetClass = null;
67
68 private boolean havingProblems = false;
69
70 private TestTargetNew(Originator originator) {
71 this.originator = originator;
72 }
73
74 /**
75 * @param originator the origin (class or method)
76 * @param ttn the annotation (testtargetnew)
77 * @param classLevelTargetClass the default target class as given in the
78 * testtargetclass annotation
79 */
80 public TestTargetNew(Originator originator, AnnotationDesc ttn,
81 ClassDoc classLevelTargetClass) {
82 this.originator = originator;
83 parseTargetClassAndMethodSignature(ttn, classLevelTargetClass);
84 // post: readMethod, readMethodSignature and readTargetClass are now set
85
86 // test for artificial method targets
87 if (readMethodName.startsWith("!")) {
88 targetMethod = null;
89 targetClass = readTargetClass;
90 // level = Level.ADDITIONAL;
91 // notes already set
92 notes = "target: " + readMethodName
93 + (notes != null ? ", " + "notes: " + notes : "");
94
95 } else if (level == Level.TODO) {
96 notes = "TODO :" + notes;
97 havingProblems = true;
98 } else {
99 // prepare method target:
100 // if the signature contains a "." then the prefix is used as a
101 // reference
102 // to an inner class. This is an alternative to using the clazz
103 // attribute in cases where the class is an inner protected class,
104 // because then the inner class is not visible for the compiler at
105 // the
106 // place of the annotation.
107 // e.g. clazz = Certificate.CertificateRep.class does not work,
108 // so we use clazz = Certificate.class (enclosing class), and method
109 // "Certificate.CertificateRep.<methodHere>", e.g.
110 // "CertificateRep.CertificateRep"
111 // to denote the constructor of the inner protected class
112 // CertificateRep
113 // within Certificate
114 int dotPos = readMethodName.lastIndexOf('.');
115 if (dotPos != -1) {
116 String prefixClassName = readMethodName.substring(0, dotPos);
117 readMethodName = readMethodName.substring(dotPos + 1);
118 ClassDoc[] iCs = readTargetClass.innerClasses();
119 for (ClassDoc iC : iCs) {
120 if (iC.name().equals(prefixClassName)) {
121 readTargetClass = iC;
122 break;
123 }
124 }
125 }
126
127 String methodAndSig = readMethodName + readMethodSignature;
128 ExecutableMemberDoc tmeth = findMethodSignatureIn(methodAndSig,
129 readTargetClass);
130 // we need this double test for the note below
131 if (tmeth == null) {
132 // a) wrong signature or
133 // b) a testMethod in a superclass or superinterface, ok also
134 tmeth = findTargetMethodInSelfAndSupers(methodAndSig,
135 readTargetClass);
136 if (tmeth != null) {
137 if (notes == null)
138 notes = "";
139 notes += "- targetmethod (" + tmeth + ") was found in a "
140 + "superclass/superinterface of the target<br>";
141 }
142 }
143 if (tmeth != null) {
144 // found
145 targetMethod = tmeth;
146 } else {
147 havingProblems = true;
148 notes = "From " + originator.asString()
149 + " -> could not resolve " + "targetMethod for class "
150 + readTargetClass + ", " + "annotation was:" + ttn
151 + ", testMethodSig " + "= " + methodAndSig + "<br>";
152 System.err.println(">>> warning: " + notes);
153 }
154 }
155 }
156
157 private ExecutableMemberDoc findMethodSignatureIn(String sig,
158 ClassDoc targetClass) {
159 ExecutableMemberDoc targetMethod = null;
160 // find the matching method in the target class, check all methods
161 for (ExecutableMemberDoc mdoc : targetClass.methods()) {
162 if (equalsSignature(mdoc, sig)) {
163 return mdoc;
164 }
165 }
166 // check constructors, too
167 for (ExecutableMemberDoc mdoc : targetClass.constructors()) {
168 if (equalsSignature(mdoc, sig)) {
169 return mdoc;
170 }
171 }
172 return null;
173 }
174
175 private ExecutableMemberDoc findTargetMethodInSelfAndSupers(String sig,
176 ClassDoc targetClass) {
177 ExecutableMemberDoc mem = findMethodSignatureIn(sig, targetClass);
178 if (mem != null) {
179 return mem;
180 }
181
182 // else visit parent class or parent interface(s)
183 ClassDoc[] ifs = targetClass.interfaces();
184 for (int i = 0; i < ifs.length; i++) {
185 ClassDoc iface = ifs[i];
186 mem = findTargetMethodInSelfAndSupers(sig, iface);
187 if (mem != null) {
188 return mem;
189 }
190 }
191
192 ClassDoc superclass = targetClass.superclass();
193 if (superclass != null) {
194 mem = findTargetMethodInSelfAndSupers(sig, superclass);
195 if (mem != null) {
196 return mem;
197 }
198 }
199 return null;
200 }
201
202 private void parseTargetClassAndMethodSignature(AnnotationDesc targetAnnot,
203 ClassDoc targetClass) {
204 ElementValuePair[] pairs = targetAnnot.elementValues();
205 String methodName = null;
206 String args = "";
207 for (ElementValuePair kval : pairs) {
208 if (kval.element().name().equals("method")) {
209 methodName = (String)kval.value().value();
210 } else if (kval.element().name().equals("clazz")) {
211 // optional: a different target class than the test-class-level
212 // default.
213 Object obj = kval.value().value();
214 if (obj instanceof ClassDoc) {
215 targetClass = (ClassDoc)obj;
216 } else if (obj instanceof ParameterizedType) {
217 targetClass = ((ParameterizedType)obj).asClassDoc();
218 } else {
219 throw new RuntimeException("annotation elem value is of "
220 + "type " + obj.getClass().getName() + " target "
221 + "annotation = " + targetAnnot);
222 }
223 } else if (kval.element().name().equals("args")) {
224 AnnotationValue[] vals = (AnnotationValue[])kval.value()
225 .value();
226 for (int i = 0; i < vals.length; i++) {
227 AnnotationValue arg = vals[i];
228 String argV;
229 // TODO: we should be able to use Type.asClassDoc() here
230 if (arg.value() instanceof ClassDoc) {
231 ClassDoc cd = (ClassDoc)arg.value();
232 argV = cd.qualifiedName();
233 } else { // primitive type or array type
234 // is there a nicer way to do this?
235 argV = arg.toString();
236 }
237 // strip .class out of args since signature does not contain
238 // those
239 if (argV.endsWith(".class")) {
240 argV = argV.substring(0, argV.length() - 6);
241 }
242 args += (i > 0 ? "," : "") + argV;
243 }
244 } else if (kval.element().name().equals("level")) {
245 AnnotationValue lev = kval.value();
246 FieldDoc fd = (FieldDoc)lev.value();
247 String slevel = fd.name();
248
249 try {
250 level = Enum.valueOf(Level.class, slevel);
251 } catch (IllegalArgumentException iae) {
252 throw new RuntimeException("COMPILE ERROR!!! enum "
253 + slevel + " used in targetMethod for class "
254 + "\"+targetClass+\", "
255 + "annotation was:\"+targetAnnot+\", "
256 + "testMethod = \"+methodDoc.toString()");
257 }
258 } else if (kval.element().name().equals("notes")) {
259 notes = (String)kval.value().value();
260 if (notes.equals("")) {
261 notes = null;
262 }
263 }
264 }
265
266 // String refSig = methodName + "(" + args + ")";
267 // both methodName and methodArgs != null because of Annotation
268 // definition
269 this.readTargetClass = targetClass;
270 this.readMethodSignature = "(" + args + ")";
271 this.readMethodName = methodName;
272 }
273
274 private boolean equalsSignature(ExecutableMemberDoc mdoc,
275 String refSignature) {
276 Parameter[] params = mdoc.parameters();
277 String targs = "";
278 for (int i = 0; i < params.length; i++) {
279 Parameter parameter = params[i];
280 // check for generic type types
281 Type ptype = parameter.type();
282
283 TypeVariable typeVar = ptype.asTypeVariable();
284 String ptname;
285 if (typeVar != null) {
286 ptname = "java.lang.Object"; // the default fallback
287 Type[] bounds = typeVar.bounds();
288 if (bounds.length > 0) {
289 ClassDoc typeClass = bounds[0].asClassDoc();
290 ptname = typeClass.qualifiedName();
291 }
292 String dim = ptype.dimension();
293 if (dim != null && dim.length() > 0) {
294 ptname += dim;
295 }
296 } else {
297 // regular var
298 // ptname = parameter.type().qualifiedTypeName();
299 ptname = parameter.type().toString();
300
301 // System.out.println("quali:"+ptname);
302 // ptname = parameter.typeName();
303 // omit type signature
304 ptname = ptname.replaceAll("<.*>", "");
305 }
306 targs += (i > 0 ? "," : "") + ptname;
307 }
308
309 String methodName = mdoc.name();
310 int lastDot = methodName.lastIndexOf('.');
311 if (lastDot != -1) {
312 // we have a inner class constructor
313 // shrink the name to just name the constructor
314 methodName = methodName.substring(lastDot + 1);
315 }
316
317 String testSig = methodName + "(" + targs + ")";
318
319 // return testSig.equals(refSignature);
320 if (testSig.equals(refSignature)) {
321 // System.out.println("match!!!: ref = "+refSignature+",
322 // test = "+testSig);
323 return true;
324 } else {
325 // System.out.println("no match: ref = "+refSignature+",
326 // test = "+testSig);
327 return false;
328 }
329 }
330
331 public Level getLevel() {
332 return level;
333 }
334
335 public boolean isHavingProblems() {
336 return havingProblems;
337 }
338
339 public Originator getOriginator() {
340 return originator;
341 }
342
343 TestTargetNew cloneMe(String extraNote) {
344 TestTargetNew anew = new TestTargetNew(this.originator);
345 anew.level = this.level;
346 anew.notes = this.notes;
347 anew.targetMethod = this.targetMethod;
348 anew.readMethodSignature = this.readMethodSignature;
349 anew.readTargetClass = this.readTargetClass;
350
351 // mark indirectly tested method always as green, independent
352 // of the original status (to better estimate workload)
353 // anew.level = Level.COMPLETE;
354 anew.notes = extraNote + (notes != null ? ", " + notes : "");
355 return anew;
356 }
357
358 public ExecutableMemberDoc getTargetMethod() {
359 return targetMethod;
360 }
361
362 /**
363 * @return the class of the testtargetnew which method starts with "!", null
364 * otherwise
365 */
366 public ClassDoc getTargetClass() {
367 return targetClass;
368 }
369
370 public String getNotes() {
371 return notes;
372 }
373}