• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package testprogress2;
18 
19 import com.sun.javadoc.AnnotationDesc;
20 import com.sun.javadoc.AnnotationValue;
21 import com.sun.javadoc.ClassDoc;
22 import com.sun.javadoc.ExecutableMemberDoc;
23 import com.sun.javadoc.FieldDoc;
24 import com.sun.javadoc.Parameter;
25 import com.sun.javadoc.ParameterizedType;
26 import com.sun.javadoc.Type;
27 import com.sun.javadoc.TypeVariable;
28 import com.sun.javadoc.AnnotationDesc.ElementValuePair;
29 
30 import testprogress2.TestMethodInformation.Level;
31 
32 /**
33  * holder for a TestTargetNew annotation
34  */
35 public 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 
TestTargetNew(Originator originator)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      */
TestTargetNew(Originator originator, AnnotationDesc ttn, ClassDoc classLevelTargetClass)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 
findMethodSignatureIn(String sig, ClassDoc targetClass)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 
findTargetMethodInSelfAndSupers(String sig, ClassDoc targetClass)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 
parseTargetClassAndMethodSignature(AnnotationDesc targetAnnot, ClassDoc targetClass)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 
equalsSignature(ExecutableMemberDoc mdoc, String refSignature)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 
getLevel()331     public Level getLevel() {
332         return level;
333     }
334 
isHavingProblems()335     public boolean isHavingProblems() {
336         return havingProblems;
337     }
338 
getOriginator()339     public Originator getOriginator() {
340         return originator;
341     }
342 
cloneMe(String extraNote)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 
getTargetMethod()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      */
getTargetClass()366     public ClassDoc getTargetClass() {
367         return targetClass;
368     }
369 
getNotes()370     public String getNotes() {
371         return notes;
372     }
373 }
374