KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > junit > JUnit4TestGenerator


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.junit;
21
22 import com.sun.source.tree.AnnotationTree;
23 import com.sun.source.tree.BlockTree;
24 import com.sun.source.tree.ClassTree;
25 import com.sun.source.tree.ExpressionTree;
26 import com.sun.source.tree.MethodTree;
27 import com.sun.source.tree.ModifiersTree;
28 import com.sun.source.tree.StatementTree;
29 import com.sun.source.tree.Tree;
30 import com.sun.source.tree.TypeParameterTree;
31 import com.sun.source.tree.VariableTree;
32 import com.sun.source.util.TreePath;
33 import com.sun.source.util.Trees;
34 import java.util.ArrayList JavaDoc;
35 import java.util.Collections JavaDoc;
36 import java.util.EnumSet JavaDoc;
37 import java.util.HashMap JavaDoc;
38 import java.util.Iterator JavaDoc;
39 import java.util.List JavaDoc;
40 import java.util.Map JavaDoc;
41 import java.util.Set JavaDoc;
42 import javax.lang.model.element.AnnotationMirror;
43 import javax.lang.model.element.AnnotationValue;
44 import javax.lang.model.element.Element;
45 import javax.lang.model.element.ElementKind;
46 import javax.lang.model.element.ExecutableElement;
47 import javax.lang.model.element.Modifier;
48 import javax.lang.model.element.Name;
49 import javax.lang.model.element.TypeElement;
50 import javax.lang.model.type.TypeKind;
51 import javax.lang.model.type.TypeMirror;
52 import javax.lang.model.util.Elements;
53 import javax.lang.model.util.Types;
54 import org.netbeans.api.java.source.ElementHandle;
55 import org.netbeans.api.java.source.TreeMaker;
56 import org.netbeans.api.java.source.WorkingCopy;
57 import org.openide.ErrorManager;
58 import static javax.lang.model.element.Modifier.PUBLIC;
59 import static javax.lang.model.element.Modifier.PROTECTED;
60 import static javax.lang.model.element.Modifier.PRIVATE;
61 import static javax.lang.model.element.Modifier.STATIC;
62
63 /**
64  *
65  * @author Marian Petras
66  */

67 public class JUnit4TestGenerator extends AbstractTestGenerator {
68     
69     /** */
70     static final String JavaDoc ANN_BEFORE_CLASS = "org.junit.BeforeClass"; //NOI18N
71
/** */
72     static final String JavaDoc ANN_AFTER_CLASS = "org.junit.AfterClass"; //NOI18N
73
/** */
74     static final String JavaDoc ANN_BEFORE = "org.junit.Before"; //NOI18N
75
/** */
76     static final String JavaDoc ANN_AFTER = "org.junit.After"; //NOI18N
77
/** */
78     static final String JavaDoc ANN_TEST = "org.junit.Test"; //NOI18N
79
/** */
80     private static final String JavaDoc ANN_RUN_WITH = "org.junit.runner.RunWith";//NOI18N
81
/** */
82     private static final String JavaDoc ANN_SUITE = "org.junit.runners.Suite"; //NOI18N
83
/** */
84     private static final String JavaDoc ANN_SUITE_MEMBERS = "SuiteClasses"; //NOI18N
85
/** */
86     private static final String JavaDoc BEFORE_CLASS_METHOD_NAME = "setUpClass";//NOI18N
87
/** */
88     private static final String JavaDoc AFTER_CLASS_METHOD_NAME = "tearDownClass";//NOI18N
89
/** */
90     private static final String JavaDoc BEFORE_METHOD_NAME = "setUp"; //NOI18N
91
/** */
92     private static final String JavaDoc AFTER_METHOD_NAME = "tearDown"; //NOI18N
93

94     /**
95      */

96     JUnit4TestGenerator(TestGeneratorSetup setup) {
97         super(setup);
98     }
99     
100     /**
101      */

102     JUnit4TestGenerator(TestGeneratorSetup setup,
103                         List JavaDoc<ElementHandle<TypeElement>> srcTopClassHandles,
104                         List JavaDoc<String JavaDoc>suiteMembers,
105                         boolean isNewTestClass) {
106         super(setup, srcTopClassHandles, suiteMembers, isNewTestClass);
107     }
108     
109     
110     /**
111      */

112     protected ClassTree composeNewTestClass(WorkingCopy workingCopy,
113                                             String JavaDoc name,
114                                             List JavaDoc<? extends Tree> members) {
115         final TreeMaker maker = workingCopy.getTreeMaker();
116         ModifiersTree modifiers = maker.Modifiers(
117                                       Collections.<Modifier>singleton(PUBLIC));
118         return maker.Class(
119                     modifiers, //modifiers
120
name, //name
121
Collections.<TypeParameterTree>emptyList(),//type params
122
null, //extends
123
Collections.<ExpressionTree>emptyList(), //implements
124
members); //members
125
}
126     
127     /**
128      */

129     protected List JavaDoc<? extends Tree> generateInitMembers(WorkingCopy workingCopy) {
130         if (!setup.isGenerateBefore() && !setup.isGenerateAfter()
131                 && !setup.isGenerateBeforeClass() && !setup.isGenerateAfterClass()) {
132             return Collections.<Tree>emptyList();
133         }
134
135         List JavaDoc<MethodTree> result = new ArrayList JavaDoc<MethodTree>(4);
136         if (setup.isGenerateBeforeClass()) {
137             result.add(
138                     generateInitMethod(BEFORE_CLASS_METHOD_NAME, ANN_BEFORE_CLASS, true, workingCopy));
139         }
140         if (setup.isGenerateAfterClass()) {
141             result.add(
142                     generateInitMethod(AFTER_CLASS_METHOD_NAME, ANN_AFTER_CLASS, true, workingCopy));
143         }
144         if (setup.isGenerateBefore()) {
145             result.add(
146                     generateInitMethod(BEFORE_METHOD_NAME, ANN_BEFORE, false, workingCopy));
147         }
148         if (setup.isGenerateAfter()) {
149             result.add(
150                     generateInitMethod(AFTER_METHOD_NAME, ANN_AFTER, false, workingCopy));
151         }
152         return result;
153     }
154
155     /**
156      */

157     protected ClassTree generateMissingInitMembers(ClassTree tstClass,
158                                                    TreePath tstClassTreePath,
159                                                    WorkingCopy workingCopy) {
160         if (!setup.isGenerateBefore() && !setup.isGenerateAfter()
161                 && !setup.isGenerateBeforeClass() && !setup.isGenerateAfterClass()) {
162             return tstClass;
163         }
164
165         ClassMap classMap = ClassMap.forClass(tstClass, tstClassTreePath,
166                                               workingCopy.getTrees());
167
168         if ((!setup.isGenerateBefore() || classMap.containsBefore())
169                 && (!setup.isGenerateAfter() || classMap.containsAfter())
170                 && (!setup.isGenerateBeforeClass() || classMap.containsBeforeClass())
171                 && (!setup.isGenerateAfterClass() || classMap.containsAfterClass())) {
172             return tstClass;
173         }
174
175         final TreeMaker maker = workingCopy.getTreeMaker();
176
177         List JavaDoc<? extends Tree> tstMembersOrig = tstClass.getMembers();
178         List JavaDoc<Tree> tstMembers = new ArrayList JavaDoc<Tree>(tstMembersOrig.size() + 4);
179         tstMembers.addAll(tstMembersOrig);
180
181         generateMissingInitMembers(tstMembers, classMap, workingCopy);
182
183         ClassTree newClass = maker.Class(
184                 tstClass.getModifiers(),
185                 tstClass.getSimpleName(),
186                 tstClass.getTypeParameters(),
187                 tstClass.getExtendsClause(),
188                 (List JavaDoc<? extends ExpressionTree>) tstClass.getImplementsClause(),
189                 tstMembers);
190         return newClass;
191     }
192     
193     /**
194      */

195     protected boolean generateMissingInitMembers(List JavaDoc<Tree> tstMembers,
196                                                ClassMap clsMap,
197                                                WorkingCopy workingCopy) {
198         boolean modified = false;
199         
200         if (setup.isGenerateBeforeClass() && !clsMap.containsBeforeClass()) {
201             int targetIndex;
202             if (clsMap.containsAfterClass()) {
203                 targetIndex = clsMap.getAfterClassIndex();
204             } else {
205                 int beforeIndex = clsMap.getBeforeIndex();
206                 int afterIndex = clsMap.getAfterIndex();
207                 if ((beforeIndex != -1) && (afterIndex != -1)) {
208                     targetIndex = Math.min(beforeIndex, afterIndex);
209                 } else {
210                     /*
211                      * if (beforeIndex != -1)
212                      * targetIndex = beforeIndex;
213                      * else if (afterIndex != -1)
214                      * targetIndex = afterIndex;
215                      * else
216                      * targetIndex = -1;
217                      */

218                     targetIndex = Math.max(beforeIndex, afterIndex);
219                 }
220             }
221             addInitMethod(BEFORE_CLASS_METHOD_NAME,
222                           ANN_BEFORE_CLASS,
223                           true,
224                           targetIndex,
225                           tstMembers,
226                           clsMap,
227                           workingCopy);
228             modified = true;
229         }
230         if (setup.isGenerateAfterClass() && !clsMap.containsAfterClass()) {
231             int targetIndex;
232             if (clsMap.containsBeforeClass()) {
233                 targetIndex = clsMap.getBeforeClassIndex() + 1;
234             } else {
235                 int beforeIndex = clsMap.getBeforeIndex();
236                 int afterIndex = clsMap.getAfterIndex();
237                 if ((beforeIndex != -1) && (afterIndex != -1)) {
238                     targetIndex = Math.min(beforeIndex, afterIndex);
239                 } else {
240                     targetIndex = Math.max(beforeIndex, afterIndex);
241                 }
242             }
243             addInitMethod(AFTER_CLASS_METHOD_NAME,
244                           ANN_AFTER_CLASS,
245                           true,
246                           targetIndex,
247                           tstMembers,
248                           clsMap,
249                           workingCopy);
250             modified = true;
251         }
252         if (setup.isGenerateBefore() && !clsMap.containsBefore()) {
253             int targetIndex;
254             if (clsMap.containsAfter()) {
255                 targetIndex = clsMap.getAfterIndex();
256             } else {
257                 int beforeClassIndex = clsMap.getBeforeClassIndex();
258                 int afterClassIndex = clsMap.getAfterClassIndex();
259                 
260                 /*
261                  * if ((beforeClassIndex != -1) && (afterClassIndex != -1))
262                  * targetIndex = Math.max(beforeClassIndex, afterClassIndex) + 1;
263                  * else if (beforeClassIndex != -1)
264                  * targetIndex = beforeClassIndex + 1;
265                  * else if (afterClassIndex != -1)
266                  * targetIndex = afterClassIndex + 1;
267                  * else
268                  * targetIndex = -1
269                  */

270                 targetIndex = Math.max(beforeClassIndex, afterClassIndex);
271                 if (targetIndex != -1) {
272                     targetIndex++;
273                 }
274             }
275             addInitMethod(BEFORE_METHOD_NAME,
276                           ANN_BEFORE,
277                           false,
278                           targetIndex,
279                           tstMembers,
280                           clsMap,
281                           workingCopy);
282             modified = true;
283         }
284         if (setup.isGenerateAfter() && !clsMap.containsAfter()) {
285             int targetIndex;
286             if (clsMap.containsBefore()) {
287                 targetIndex = clsMap.getBeforeIndex() + 1;
288             } else {
289                 int beforeClassIndex = clsMap.getBeforeClassIndex();
290                 int afterClassIndex = clsMap.getAfterClassIndex();
291                 targetIndex = Math.max(beforeClassIndex, afterClassIndex);
292                 if (targetIndex != -1) {
293                     targetIndex++;
294                 }
295             }
296             addInitMethod(AFTER_METHOD_NAME,
297                           ANN_AFTER,
298                           false,
299                           targetIndex,
300                           tstMembers,
301                           clsMap,
302                           workingCopy);
303             modified = true;
304         }
305         
306         return modified;
307     }
308
309     /**
310      */

311     private void addInitMethod(String JavaDoc methodName,
312                                String JavaDoc annotationClassName,
313                                boolean isStatic,
314                                int targetIndex,
315                                List JavaDoc<Tree> clsMembers,
316                                ClassMap clsMap,
317                                WorkingCopy workingCopy) {
318         MethodTree initMethod = generateInitMethod(methodName,
319                                                    annotationClassName,
320                                                    isStatic,
321                                                    workingCopy);
322         
323         if (targetIndex == -1) {
324             targetIndex = getPlaceForFirstInitMethod(clsMap);
325         }
326         
327         if (targetIndex != -1) {
328             clsMembers.add(targetIndex, initMethod);
329         } else {
330             clsMembers.add(initMethod);
331         }
332         clsMap.addNoArgMethod(methodName, annotationClassName, targetIndex);
333     }
334
335     /**
336      * Generates a set-up or a tear-down method.
337      * The generated method will have no arguments, void return type
338      * and a declaration that it may throw {@code java.lang.Exception}.
339      * The method will have a declared protected member access.
340      * The method contains call of the corresponding super method, i.e.
341      * {@code super.setUp()} or {@code super.tearDown()}.
342      *
343      * @param methodName name of the method to be created
344      * @return created method
345      * @see http://junit.sourceforge.net/javadoc/junit/framework/TestCase.html
346      * methods {@code setUp()} and {@code tearDown()}
347      */

348     private MethodTree generateInitMethod(String JavaDoc methodName,
349                                           String JavaDoc annotationClassName,
350                                           boolean isStatic,
351                                           WorkingCopy workingCopy) {
352         Set JavaDoc<Modifier> methodModifiers
353                 = isStatic ? createModifierSet(PUBLIC, STATIC)
354                            : Collections.<Modifier>singleton(PUBLIC);
355         ModifiersTree modifiers = createModifiersTree(annotationClassName,
356                                                       methodModifiers,
357                                                       workingCopy);
358         TreeMaker maker = workingCopy.getTreeMaker();
359         BlockTree methodBody = maker.Block(
360                 Collections.<StatementTree>emptyList(),
361                 false);
362         MethodTree method = maker.Method(
363                 modifiers, // modifiers
364
methodName, // name
365
maker.PrimitiveType(TypeKind.VOID), // return type
366
Collections.<TypeParameterTree>emptyList(), // type params
367
Collections.<VariableTree>emptyList(), // parameters
368
Collections.<ExpressionTree>singletonList(
369                         maker.Identifier("Exception")), // throws...//NOI18N
370
methodBody,
371                 null); // default value
372
return method;
373     }
374     
375     /**
376      */

377     protected void generateMissingPostInitMethods(TreePath tstClassTreePath,
378                                                   List JavaDoc<Tree> tstMembers,
379                                                   ClassMap clsMap,
380                                                   WorkingCopy workingCopy) {
381         /* no post-init methods */
382     }
383     
384     /**
385      */

386     protected String JavaDoc createTestMethodName(String JavaDoc smName) {
387         return smName;
388     }
389     
390     /**
391      */

392     protected MethodTree composeNewTestMethod(String JavaDoc testMethodName,
393                                               BlockTree testMethodBody,
394                                               List JavaDoc<ExpressionTree> throwsList,
395                                               WorkingCopy workingCopy) {
396         TreeMaker maker = workingCopy.getTreeMaker();
397         return maker.Method(
398                 createModifiersTree(ANN_TEST,
399                                     createModifierSet(PUBLIC),
400                                     workingCopy),
401                 testMethodName,
402                 maker.PrimitiveType(TypeKind.VOID),
403                 Collections.<TypeParameterTree>emptyList(),
404                 Collections.<VariableTree>emptyList(),
405                 throwsList,
406                 testMethodBody,
407                 null); //default value - used by annotations
408
}
409     
410     /**
411      */

412     protected ClassTree finishSuiteClass(ClassTree tstClass,
413                                          TreePath tstClassTreePath,
414                                          List JavaDoc<Tree> tstMembers,
415                                          List JavaDoc<String JavaDoc> suiteMembers,
416                                          boolean membersChanged,
417                                          ClassMap classMap,
418                                          WorkingCopy workingCopy) {
419
420         ModifiersTree currModifiers = tstClass.getModifiers();
421         ModifiersTree modifiers = fixSuiteClassModifiers(tstClass,
422                                                          tstClassTreePath,
423                                                          currModifiers,
424                                                          suiteMembers,
425                                                          workingCopy);
426         if (!membersChanged) {
427             if (modifiers != currModifiers) {
428                 workingCopy.rewrite(currModifiers, modifiers);
429             }
430             return tstClass;
431         }
432
433         return workingCopy.getTreeMaker().Class(
434                 modifiers,
435                 tstClass.getSimpleName(),
436                 tstClass.getTypeParameters(),
437                 tstClass.getExtendsClause(),
438                 (List JavaDoc<? extends ExpressionTree>) tstClass.getImplementsClause(),
439                 tstMembers);
440     }
441     
442     /**
443      * Keeps or modifies annotations and modifiers of the given suite class.
444      * Modifiers are modified such that the class is public.
445      * The list of annotations is modified such that it contains
446      * the following annotations:
447      * <pre><code>RunWith(Suite.class)
448      * @SuiteRunner.Suite({...})</code></pre>
449      * with members of the suite in place of the <code>{...}</code> list.
450      *
451      * @param tstClass class whose modifiers and anntations are to be modified
452      * @param tstClassTreePath tree path to the class from the compilation unit
453      * @param modifiers current modifiers and annotations
454      * @param suiteMembers list of class names that should be contained
455      * in the test suite
456      * @return {@code ModifiersTree} object containing the modified set
457      * of class modifiers and annotations, or {@code null}
458      * if no modifications were necessary
459      */

460     private ModifiersTree fixSuiteClassModifiers(ClassTree tstClass,
461                                                  TreePath tstClassTreePath,
462                                                  ModifiersTree modifiers,
463                                                  List JavaDoc<String JavaDoc> suiteMembers,
464                                                  WorkingCopy workingCopy) {
465         boolean flagsModified = false;
466         
467         Set JavaDoc<Modifier> currFlags = modifiers.getFlags();
468         Set JavaDoc<Modifier> flags = EnumSet.copyOf(currFlags);
469         flagsModified |= flags.remove(PRIVATE);
470         flagsModified |= flags.remove(PROTECTED);
471         flagsModified |= flags.add(PUBLIC);
472         if (!flagsModified) {
473             flags = currFlags;
474         }
475         
476         
477         boolean annotationListModified = false;
478         
479         List JavaDoc<? extends AnnotationTree> currAnnotations = modifiers.getAnnotations();
480         List JavaDoc<? extends AnnotationTree> annotations;
481         if (currAnnotations.isEmpty()) {
482             List JavaDoc<AnnotationTree> newAnnotations = new ArrayList JavaDoc<AnnotationTree>(2);
483             newAnnotations.add(createRunWithSuiteAnnotation(workingCopy));
484             newAnnotations.add(createSuiteAnnotation(suiteMembers, workingCopy));
485             annotations = newAnnotations;
486             
487             annotationListModified = true;
488         } else {
489             Trees trees = workingCopy.getTrees();
490             Element classElement = trees.getElement(tstClassTreePath);
491             List JavaDoc<? extends AnnotationMirror> annMirrors
492                     = classElement.getAnnotationMirrors();
493             assert annMirrors.size() == currAnnotations.size();
494             
495             
496             int index = -1, runWithIndex = -1, suiteClassesIndex = -1;
497             for (AnnotationMirror annMirror : annMirrors) {
498                 index++;
499                 Element annElement = annMirror.getAnnotationType().asElement();
500                 assert annElement instanceof TypeElement;
501                 TypeElement annTypeElem = (TypeElement) annElement;
502                 Name annFullName = annTypeElem.getQualifiedName();
503                 
504                 if ((runWithIndex == -1) && annFullName.contentEquals(ANN_RUN_WITH)) {
505                     runWithIndex = index;
506                 } else if ((suiteClassesIndex == -1)
507                         && annFullName.contentEquals(ANN_SUITE + '.' + ANN_SUITE_MEMBERS)) {
508                     suiteClassesIndex = index;
509                 }
510             }
511             
512             AnnotationTree runWithSuiteAnn;
513             if ((runWithIndex == -1) || !checkRunWithSuiteAnnotation(
514                                                 annMirrors.get(runWithIndex),
515                                                 workingCopy)) {
516                 runWithSuiteAnn = createRunWithSuiteAnnotation(workingCopy);
517             } else {
518                 runWithSuiteAnn = currAnnotations.get(runWithIndex);
519             }
520             
521             AnnotationTree suiteClassesAnn;
522             if ((suiteClassesIndex == -1) || !checkSuiteMembersAnnotation(
523                                                       annMirrors.get(suiteClassesIndex),
524                                                       suiteMembers,
525                                                       workingCopy)) {
526                 suiteClassesAnn = createSuiteAnnotation(suiteMembers, workingCopy);
527             } else {
528                 suiteClassesAnn = currAnnotations.get(suiteClassesIndex);
529             }
530             
531             if ((runWithIndex != -1) && (suiteClassesIndex != -1)) {
532                 if (runWithSuiteAnn != currAnnotations.get(runWithIndex)) {
533                     workingCopy.rewrite(
534                             currAnnotations.get(runWithIndex),
535                             runWithSuiteAnn);
536                 }
537                 if (suiteClassesAnn != currAnnotations.get(suiteClassesIndex)) {
538                     workingCopy.rewrite(
539                             currAnnotations.get(suiteClassesIndex),
540                             suiteClassesAnn);
541                 }
542                 annotations = currAnnotations;
543             } else {
544                 List JavaDoc<AnnotationTree> newAnnotations
545                         = new ArrayList JavaDoc<AnnotationTree>(currAnnotations.size() + 2);
546                 if ((runWithIndex == -1) && (suiteClassesIndex == -1)) {
547                     
548                     /*
549                      * put the @RunWith(...) and @Suite.SuiteClasses(...)
550                      * annotations in front of other annotations
551                      */

552                     newAnnotations.add(runWithSuiteAnn);
553                     newAnnotations.add(suiteClassesAnn);
554                     if (!currAnnotations.isEmpty()) {
555                         newAnnotations.addAll(currAnnotations);
556                     }
557                 } else {
558                     newAnnotations.addAll(currAnnotations);
559                     if (runWithIndex == -1) {
560                         assert suiteClassesIndex != 1;
561                         
562                         /*
563                          * put the @RunWith(...) annotation
564                          * just before the Suite.SuiteClasses(...) annotation
565                          */

566                         newAnnotations.add(suiteClassesIndex, runWithSuiteAnn);
567                     } else {
568                         assert runWithIndex != -1;
569                         
570                         /*
571                          * put the @Suite.SuiteClasses(...) annotation
572                          * just after the @RunWith(...) annotation
573                          */

574                         newAnnotations.add(runWithIndex + 1, suiteClassesAnn);
575                     }
576                 }
577                 annotations = newAnnotations;
578                 
579                 annotationListModified = true;
580             }
581         }
582         
583         if (!flagsModified && !annotationListModified) {
584             return modifiers;
585         }
586         
587         return workingCopy.getTreeMaker().Modifiers(flags, annotations);
588     }
589     
590     /**
591      * Checks that the given annotation is of type
592      * <code>{@value #ANN_RUN_WITH}</code> and contains argument
593      * <code>{@value #ANN_SUITE}{@literal .class}</code>.
594      *
595      * @param annMirror annotation to be checked
596      * @return {@code true} if the annotation meets the described criteria,
597      * {@code false} otherwise
598      */

599     private boolean checkRunWithSuiteAnnotation(AnnotationMirror annMirror,
600                                                 WorkingCopy workingCopy) {
601         Map JavaDoc<? extends ExecutableElement,? extends AnnotationValue> annParams
602                 = annMirror.getElementValues();
603         
604         if (annParams.size() != 1) {
605             return false;
606         }
607         
608         AnnotationValue annValue = annParams.values().iterator().next();
609         Name annValueClsName = getAnnotationValueClassName(annValue,
610                                                            workingCopy.getTypes());
611         return annValueClsName != null
612                ? annValueClsName.contentEquals(ANN_SUITE)
613                : false;
614     }
615     
616     /**
617      * Checks that the given annotation is of type
618      * <code>{@value #ANN_SUITE}.{@value #ANN_SUITE_MEMBERS}</code>
619      * and contains the given list of classes as (the only) argument,
620      * in the same order.
621      *
622      * @param annMirror annotation to be checked
623      * @param suiteMembers list of fully qualified class names denoting
624      * content of the test suite
625      * @return {@code true} if the annotation meets the described criteria,
626      * {@code false} otherwise
627      */

628     private boolean checkSuiteMembersAnnotation(AnnotationMirror annMirror,
629                                                 List JavaDoc<String JavaDoc> suiteMembers,
630                                                 WorkingCopy workingCopy) {
631         Map JavaDoc<? extends ExecutableElement,? extends AnnotationValue> annParams
632                 = annMirror.getElementValues();
633         
634         if (annParams.size() != 1) {
635             return false;
636         }
637         
638         AnnotationValue annValue = annParams.values().iterator().next();
639         Object JavaDoc value = annValue.getValue();
640         if (value instanceof java.util.List JavaDoc) {
641             List JavaDoc<? extends AnnotationValue> items
642                     = (List JavaDoc<? extends AnnotationValue>) value;
643             
644             if (items.size() != suiteMembers.size()) {
645                 return false;
646             }
647             
648             Types types = workingCopy.getTypes();
649             Iterator JavaDoc<String JavaDoc> suiteMembersIt = suiteMembers.iterator();
650             for (AnnotationValue item : items) {
651                 Name suiteMemberName = getAnnotationValueClassName(item, types);
652                 if (suiteMemberName == null) {
653                     return false;
654                 }
655                 if (!suiteMemberName.contentEquals(suiteMembersIt.next())) {
656                     return false;
657                 }
658             }
659             return true;
660         }
661         
662         return false;
663     }
664     
665     /**
666      * Returns fully qualified class name of a class given to an annotation
667      * as (the only) argument.
668      *
669      * @param annValue annotation value
670      * @return fully qualified name of a class represented by the given
671      * annotation value, or {@code null} if the annotation value
672      * does not represent a class
673      */

674     private Name getAnnotationValueClassName(AnnotationValue annValue,
675                                              Types types) {
676         Object JavaDoc value = annValue.getValue();
677         if (value instanceof TypeMirror) {
678             TypeMirror typeMirror = (TypeMirror) value;
679             Element typeElement = types.asElement(typeMirror);
680             if (typeElement.getKind() == ElementKind.CLASS) {
681                 return ((TypeElement) typeElement).getQualifiedName();
682             }
683         }
684         return null;
685     }
686     
687     /**
688      * Creates annotation <code>@org.junit.runner.RunWith</code>.
689      *
690      * @return created annotation
691      */

692     private AnnotationTree createRunWithSuiteAnnotation(
693                                                 WorkingCopy workingCopy) {
694         TreeMaker maker = workingCopy.getTreeMaker();
695         
696         /* @RunWith(Suite.class) */
697         return maker.Annotation(
698                 getClassIdentifierTree(ANN_RUN_WITH, workingCopy),
699                 Collections.<ExpressionTree>singletonList(
700                         maker.MemberSelect(
701                                 getClassIdentifierTree(ANN_SUITE, workingCopy),
702                                 "class"))); //NOI18N
703
}
704     
705     /**
706      * Creates annotation
707      * <code>@org.junit.runners.Suite.SuiteClasses({...})</code>.
708      *
709      * @param suiteMembers fully qualified names of classes to be included
710      * in the test suite
711      * @param created annotation
712      */

713     private AnnotationTree createSuiteAnnotation(List JavaDoc<String JavaDoc> suiteMembers,
714                                                  WorkingCopy workingCopy) {
715         final TreeMaker maker = workingCopy.getTreeMaker();
716         
717         List JavaDoc<ExpressionTree> suiteMemberExpressions
718                 = new ArrayList JavaDoc<ExpressionTree>(suiteMembers.size());
719         for (String JavaDoc suiteMember : suiteMembers) {
720             suiteMemberExpressions.add(
721                     maker.MemberSelect(
722                             getClassIdentifierTree(suiteMember, workingCopy),
723                             "class")); //NOI18N
724
}
725         
726         /* @Suite.SuiteClasses({TestA.class, TestB.class, ...}) */
727         return maker.Annotation(
728                 maker.MemberSelect(
729                         getClassIdentifierTree(ANN_SUITE, workingCopy),
730                         ANN_SUITE_MEMBERS),
731                 Collections.singletonList(
732                         maker.Assignment(
733                                 maker.Identifier("value"), //NOI18N
734
maker.NewArray(
735                                         maker.Identifier("Class"), //NOI18N
736
Collections.<ExpressionTree>emptyList(),
737                                         suiteMemberExpressions))));
738     }
739
740     /**
741      */

742     private ModifiersTree createModifiersTree(String JavaDoc annotationClassName,
743                                               Set JavaDoc<Modifier> modifiers,
744                                               WorkingCopy workingCopy) {
745         TreeMaker maker = workingCopy.getTreeMaker();
746         AnnotationTree annotation = maker.Annotation(
747                 getClassIdentifierTree(annotationClassName, workingCopy),
748                 Collections.<ExpressionTree>emptyList());
749         return maker.Modifiers(modifiers,
750                                Collections.<AnnotationTree>singletonList(
751                                                                    annotation));
752     }
753
754     /** */
755     private Map JavaDoc<String JavaDoc, ExpressionTree> classIdentifiers;
756
757     /**
758      */

759     private ExpressionTree getClassIdentifierTree(String JavaDoc className,
760                                         WorkingCopy workingCopy) {
761         ExpressionTree classIdentifier;
762         if (classIdentifiers == null) {
763             classIdentifier = null;
764             classIdentifiers = new HashMap JavaDoc<String JavaDoc, ExpressionTree>(13);
765         } else {
766             classIdentifier = classIdentifiers.get(className);
767         }
768         if (classIdentifier == null) {
769             TypeElement typeElement
770                     = getElemForClassName(className, workingCopy.getElements());
771             TreeMaker maker = workingCopy.getTreeMaker();
772             classIdentifier = (typeElement != null)
773                                ? maker.QualIdent(typeElement)
774                                : maker.Identifier(className);
775             classIdentifiers.put(className, classIdentifier);
776         }
777         return classIdentifier;
778     }
779
780     /**
781      */

782     private static TypeElement getElemForClassName(String JavaDoc className,
783                                                    Elements elements) {
784         TypeElement elem = elements.getTypeElement(className);
785         if (elem == null) {
786             ErrorManager.getDefault().log(
787                     ErrorManager.ERROR,
788                     "Could not find TypeElement for " + className); //NOI18N
789
}
790         return elem;
791     }
792
793 }
794
Popular Tags