Home | History | Annotate | Download | only in testprogress2
      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 
     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 }
    374