001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2008 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.junit;
043:
044: import com.sun.source.tree.AnnotationTree;
045: import com.sun.source.tree.BlockTree;
046: import com.sun.source.tree.ClassTree;
047: import com.sun.source.tree.ExpressionTree;
048: import com.sun.source.tree.MethodTree;
049: import com.sun.source.tree.ModifiersTree;
050: import com.sun.source.tree.StatementTree;
051: import com.sun.source.tree.Tree;
052: import com.sun.source.tree.TypeParameterTree;
053: import com.sun.source.tree.VariableTree;
054: import com.sun.source.util.TreePath;
055: import com.sun.source.util.Trees;
056: import java.util.ArrayList;
057: import java.util.Collections;
058: import java.util.EnumSet;
059: import java.util.Iterator;
060: import java.util.List;
061: import java.util.Map;
062: import java.util.Set;
063: import javax.lang.model.element.AnnotationMirror;
064: import javax.lang.model.element.AnnotationValue;
065: import javax.lang.model.element.Element;
066: import javax.lang.model.element.ElementKind;
067: import javax.lang.model.element.ExecutableElement;
068: import javax.lang.model.element.Modifier;
069: import javax.lang.model.element.Name;
070: import javax.lang.model.element.TypeElement;
071: import javax.lang.model.type.TypeKind;
072: import javax.lang.model.type.TypeMirror;
073: import javax.lang.model.util.Types;
074: import org.netbeans.api.java.source.ElementHandle;
075: import org.netbeans.api.java.source.TreeMaker;
076: import org.netbeans.api.java.source.WorkingCopy;
077: import static javax.lang.model.element.Modifier.PUBLIC;
078: import static javax.lang.model.element.Modifier.PROTECTED;
079: import static javax.lang.model.element.Modifier.PRIVATE;
080: import static javax.lang.model.element.Modifier.STATIC;
081:
082: /**
083: *
084: * @author Marian Petras
085: */
086: final class JUnit4TestGenerator extends AbstractTestGenerator {
087:
088: /** */
089: static final String ANN_BEFORE_CLASS = "org.junit.BeforeClass"; //NOI18N
090: /** */
091: static final String ANN_AFTER_CLASS = "org.junit.AfterClass"; //NOI18N
092: /** */
093: static final String ANN_BEFORE = "org.junit.Before"; //NOI18N
094: /** */
095: static final String ANN_AFTER = "org.junit.After"; //NOI18N
096: /** */
097: static final String ANN_TEST = "org.junit.Test"; //NOI18N
098: /** */
099: private static final String ANN_RUN_WITH = "org.junit.runner.RunWith";//NOI18N
100: /** */
101: private static final String ANN_SUITE = "org.junit.runners.Suite"; //NOI18N
102: /** */
103: private static final String ANN_SUITE_MEMBERS = "SuiteClasses"; //NOI18N
104: /** */
105: private static final String BEFORE_CLASS_METHOD_NAME = "setUpClass";//NOI18N
106: /** */
107: private static final String AFTER_CLASS_METHOD_NAME = "tearDownClass";//NOI18N
108: /** */
109: private static final String BEFORE_METHOD_NAME = "setUp"; //NOI18N
110: /** */
111: private static final String AFTER_METHOD_NAME = "tearDown"; //NOI18N
112:
113: /**
114: */
115: JUnit4TestGenerator(TestGeneratorSetup setup) {
116: super (setup);
117: }
118:
119: /**
120: */
121: JUnit4TestGenerator(TestGeneratorSetup setup,
122: List<ElementHandle<TypeElement>> srcTopClassHandles,
123: List<String> suiteMembers, boolean isNewTestClass) {
124: super (setup, srcTopClassHandles, suiteMembers, isNewTestClass);
125: }
126:
127: /**
128: */
129: protected ClassTree composeNewTestClass(WorkingCopy workingCopy,
130: String name, List<? extends Tree> members) {
131: final TreeMaker maker = workingCopy.getTreeMaker();
132: ModifiersTree modifiers = maker.Modifiers(Collections
133: .<Modifier> singleton(PUBLIC));
134: return maker.Class(modifiers, //modifiers
135: name, //name
136: Collections.<TypeParameterTree> emptyList(),//type params
137: null, //extends
138: Collections.<ExpressionTree> emptyList(), //implements
139: members); //members
140: }
141:
142: /**
143: */
144: protected List<? extends Tree> generateInitMembers(
145: WorkingCopy workingCopy) {
146: if (!setup.isGenerateBefore() && !setup.isGenerateAfter()
147: && !setup.isGenerateBeforeClass()
148: && !setup.isGenerateAfterClass()) {
149: return Collections.<Tree> emptyList();
150: }
151:
152: List<MethodTree> result = new ArrayList<MethodTree>(4);
153: if (setup.isGenerateBeforeClass()) {
154: result.add(generateInitMethod(BEFORE_CLASS_METHOD_NAME,
155: ANN_BEFORE_CLASS, true, workingCopy));
156: }
157: if (setup.isGenerateAfterClass()) {
158: result.add(generateInitMethod(AFTER_CLASS_METHOD_NAME,
159: ANN_AFTER_CLASS, true, workingCopy));
160: }
161: if (setup.isGenerateBefore()) {
162: result.add(generateInitMethod(BEFORE_METHOD_NAME,
163: ANN_BEFORE, false, workingCopy));
164: }
165: if (setup.isGenerateAfter()) {
166: result.add(generateInitMethod(AFTER_METHOD_NAME, ANN_AFTER,
167: false, workingCopy));
168: }
169: return result;
170: }
171:
172: /**
173: */
174: protected ClassTree generateMissingInitMembers(ClassTree tstClass,
175: TreePath tstClassTreePath, WorkingCopy workingCopy) {
176: if (!setup.isGenerateBefore() && !setup.isGenerateAfter()
177: && !setup.isGenerateBeforeClass()
178: && !setup.isGenerateAfterClass()) {
179: return tstClass;
180: }
181:
182: ClassMap classMap = ClassMap.forClass(tstClass,
183: tstClassTreePath, workingCopy.getTrees());
184:
185: if ((!setup.isGenerateBefore() || classMap.containsBefore())
186: && (!setup.isGenerateAfter() || classMap
187: .containsAfter())
188: && (!setup.isGenerateBeforeClass() || classMap
189: .containsBeforeClass())
190: && (!setup.isGenerateAfterClass() || classMap
191: .containsAfterClass())) {
192: return tstClass;
193: }
194:
195: final TreeMaker maker = workingCopy.getTreeMaker();
196:
197: List<? extends Tree> tstMembersOrig = tstClass.getMembers();
198: List<Tree> tstMembers = new ArrayList<Tree>(tstMembersOrig
199: .size() + 4);
200: tstMembers.addAll(tstMembersOrig);
201:
202: generateMissingInitMembers(tstMembers, classMap, workingCopy);
203:
204: ClassTree newClass = maker.Class(tstClass.getModifiers(),
205: tstClass.getSimpleName(), tstClass.getTypeParameters(),
206: tstClass.getExtendsClause(),
207: (List<? extends ExpressionTree>) tstClass
208: .getImplementsClause(), tstMembers);
209: return newClass;
210: }
211:
212: /**
213: */
214: protected boolean generateMissingInitMembers(List<Tree> tstMembers,
215: ClassMap clsMap, WorkingCopy workingCopy) {
216: boolean modified = false;
217:
218: if (setup.isGenerateBeforeClass()
219: && !clsMap.containsBeforeClass()) {
220: int targetIndex;
221: if (clsMap.containsAfterClass()) {
222: targetIndex = clsMap.getAfterClassIndex();
223: } else {
224: int beforeIndex = clsMap.getBeforeIndex();
225: int afterIndex = clsMap.getAfterIndex();
226: if ((beforeIndex != -1) && (afterIndex != -1)) {
227: targetIndex = Math.min(beforeIndex, afterIndex);
228: } else {
229: /*
230: * if (beforeIndex != -1)
231: * targetIndex = beforeIndex;
232: * else if (afterIndex != -1)
233: * targetIndex = afterIndex;
234: * else
235: * targetIndex = -1;
236: */
237: targetIndex = Math.max(beforeIndex, afterIndex);
238: }
239: }
240: addInitMethod(BEFORE_CLASS_METHOD_NAME, ANN_BEFORE_CLASS,
241: true, targetIndex, tstMembers, clsMap, workingCopy);
242: modified = true;
243: }
244: if (setup.isGenerateAfterClass()
245: && !clsMap.containsAfterClass()) {
246: int targetIndex;
247: if (clsMap.containsBeforeClass()) {
248: targetIndex = clsMap.getBeforeClassIndex() + 1;
249: } else {
250: int beforeIndex = clsMap.getBeforeIndex();
251: int afterIndex = clsMap.getAfterIndex();
252: if ((beforeIndex != -1) && (afterIndex != -1)) {
253: targetIndex = Math.min(beforeIndex, afterIndex);
254: } else {
255: targetIndex = Math.max(beforeIndex, afterIndex);
256: }
257: }
258: addInitMethod(AFTER_CLASS_METHOD_NAME, ANN_AFTER_CLASS,
259: true, targetIndex, tstMembers, clsMap, workingCopy);
260: modified = true;
261: }
262: if (setup.isGenerateBefore() && !clsMap.containsBefore()) {
263: int targetIndex;
264: if (clsMap.containsAfter()) {
265: targetIndex = clsMap.getAfterIndex();
266: } else {
267: int beforeClassIndex = clsMap.getBeforeClassIndex();
268: int afterClassIndex = clsMap.getAfterClassIndex();
269:
270: /*
271: * if ((beforeClassIndex != -1) && (afterClassIndex != -1))
272: * targetIndex = Math.max(beforeClassIndex, afterClassIndex) + 1;
273: * else if (beforeClassIndex != -1)
274: * targetIndex = beforeClassIndex + 1;
275: * else if (afterClassIndex != -1)
276: * targetIndex = afterClassIndex + 1;
277: * else
278: * targetIndex = -1
279: */
280: targetIndex = Math.max(beforeClassIndex,
281: afterClassIndex);
282: if (targetIndex != -1) {
283: targetIndex++;
284: }
285: }
286: addInitMethod(BEFORE_METHOD_NAME, ANN_BEFORE, false,
287: targetIndex, tstMembers, clsMap, workingCopy);
288: modified = true;
289: }
290: if (setup.isGenerateAfter() && !clsMap.containsAfter()) {
291: int targetIndex;
292: if (clsMap.containsBefore()) {
293: targetIndex = clsMap.getBeforeIndex() + 1;
294: } else {
295: int beforeClassIndex = clsMap.getBeforeClassIndex();
296: int afterClassIndex = clsMap.getAfterClassIndex();
297: targetIndex = Math.max(beforeClassIndex,
298: afterClassIndex);
299: if (targetIndex != -1) {
300: targetIndex++;
301: }
302: }
303: addInitMethod(AFTER_METHOD_NAME, ANN_AFTER, false,
304: targetIndex, tstMembers, clsMap, workingCopy);
305: modified = true;
306: }
307:
308: return modified;
309: }
310:
311: /**
312: */
313: private void addInitMethod(String methodName,
314: String annotationClassName, boolean isStatic,
315: int targetIndex, List<Tree> clsMembers, ClassMap clsMap,
316: WorkingCopy workingCopy) {
317: MethodTree initMethod = generateInitMethod(methodName,
318: annotationClassName, isStatic, workingCopy);
319:
320: if (targetIndex == -1) {
321: targetIndex = getPlaceForFirstInitMethod(clsMap);
322: }
323:
324: if (targetIndex != -1) {
325: clsMembers.add(targetIndex, initMethod);
326: } else {
327: clsMembers.add(initMethod);
328: }
329: clsMap.addNoArgMethod(methodName, annotationClassName,
330: targetIndex);
331: }
332:
333: /**
334: * Generates a set-up or a tear-down method.
335: * The generated method will have no arguments, void return type
336: * and a declaration that it may throw {@code java.lang.Exception}.
337: * The method will have a declared protected member access.
338: * The method contains call of the corresponding super method, i.e.
339: * {@code super.setUp()} or {@code super.tearDown()}.
340: *
341: * @param methodName name of the method to be created
342: * @return created method
343: * @see http://junit.sourceforge.net/javadoc/junit/framework/TestCase.html
344: * methods {@code setUp()} and {@code tearDown()}
345: */
346: private MethodTree generateInitMethod(String methodName,
347: String annotationClassName, boolean isStatic,
348: WorkingCopy workingCopy) {
349: Set<Modifier> methodModifiers = isStatic ? createModifierSet(
350: PUBLIC, STATIC) : Collections
351: .<Modifier> singleton(PUBLIC);
352: ModifiersTree modifiers = createModifiersTree(
353: annotationClassName, methodModifiers, workingCopy);
354: TreeMaker maker = workingCopy.getTreeMaker();
355: BlockTree methodBody = maker.Block(Collections
356: .<StatementTree> emptyList(), false);
357: MethodTree method = maker.Method(modifiers, // modifiers
358: methodName, // name
359: maker.PrimitiveType(TypeKind.VOID), // return type
360: Collections.<TypeParameterTree> emptyList(), // type params
361: Collections.<VariableTree> emptyList(), // parameters
362: Collections.<ExpressionTree> singletonList(maker
363: .Identifier("Exception")), // throws...//NOI18N
364: methodBody, null); // default value
365: return method;
366: }
367:
368: /**
369: */
370: protected void generateMissingPostInitMethods(
371: TreePath tstClassTreePath, List<Tree> tstMembers,
372: ClassMap clsMap, WorkingCopy workingCopy) {
373: /* no post-init methods */
374: }
375:
376: /**
377: */
378: protected MethodTree composeNewTestMethod(String testMethodName,
379: BlockTree testMethodBody, List<ExpressionTree> throwsList,
380: WorkingCopy workingCopy) {
381: TreeMaker maker = workingCopy.getTreeMaker();
382: return maker.Method(createModifiersTree(ANN_TEST,
383: createModifierSet(PUBLIC), workingCopy),
384: testMethodName, maker.PrimitiveType(TypeKind.VOID),
385: Collections.<TypeParameterTree> emptyList(),
386: Collections.<VariableTree> emptyList(), throwsList,
387: testMethodBody, null); //default value - used by annotations
388: }
389:
390: /**
391: */
392: protected ClassTree finishSuiteClass(ClassTree tstClass,
393: TreePath tstClassTreePath, List<Tree> tstMembers,
394: List<String> suiteMembers, boolean membersChanged,
395: ClassMap classMap, WorkingCopy workingCopy) {
396:
397: ModifiersTree currModifiers = tstClass.getModifiers();
398: ModifiersTree modifiers = fixSuiteClassModifiers(tstClass,
399: tstClassTreePath, currModifiers, suiteMembers,
400: workingCopy);
401: if (!membersChanged) {
402: if (modifiers != currModifiers) {
403: workingCopy.rewrite(currModifiers, modifiers);
404: }
405: return tstClass;
406: }
407:
408: return workingCopy.getTreeMaker().Class(
409: modifiers,
410: tstClass.getSimpleName(),
411: tstClass.getTypeParameters(),
412: tstClass.getExtendsClause(),
413: (List<? extends ExpressionTree>) tstClass
414: .getImplementsClause(), tstMembers);
415: }
416:
417: /**
418: * Keeps or modifies annotations and modifiers of the given suite class.
419: * Modifiers are modified such that the class is public.
420: * The list of annotations is modified such that it contains
421: * the following annotations:
422: * <pre><code>RunWith(Suite.class)
423: * @SuiteRunner.Suite({...})</code></pre>
424: * with members of the suite in place of the <code>{...}</code> list.
425: *
426: * @param tstClass class whose modifiers and anntations are to be modified
427: * @param tstClassTreePath tree path to the class from the compilation unit
428: * @param modifiers current modifiers and annotations
429: * @param suiteMembers list of class names that should be contained
430: * in the test suite
431: * @return {@code ModifiersTree} object containing the modified set
432: * of class modifiers and annotations, or {@code null}
433: * if no modifications were necessary
434: */
435: private ModifiersTree fixSuiteClassModifiers(ClassTree tstClass,
436: TreePath tstClassTreePath, ModifiersTree modifiers,
437: List<String> suiteMembers, WorkingCopy workingCopy) {
438: boolean flagsModified = false;
439:
440: Set<Modifier> currFlags = modifiers.getFlags();
441: Set<Modifier> flags = EnumSet.copyOf(currFlags);
442: flagsModified |= flags.remove(PRIVATE);
443: flagsModified |= flags.remove(PROTECTED);
444: flagsModified |= flags.add(PUBLIC);
445: if (!flagsModified) {
446: flags = currFlags;
447: }
448:
449: boolean annotationListModified = false;
450:
451: List<? extends AnnotationTree> currAnnotations = modifiers
452: .getAnnotations();
453: List<? extends AnnotationTree> annotations;
454: if (currAnnotations.isEmpty()) {
455: List<AnnotationTree> newAnnotations = new ArrayList<AnnotationTree>(
456: 2);
457: newAnnotations
458: .add(createRunWithSuiteAnnotation(workingCopy));
459: newAnnotations.add(createSuiteAnnotation(suiteMembers,
460: workingCopy));
461: annotations = newAnnotations;
462:
463: annotationListModified = true;
464: } else {
465: Trees trees = workingCopy.getTrees();
466: Element classElement = trees.getElement(tstClassTreePath);
467: List<? extends AnnotationMirror> annMirrors = classElement
468: .getAnnotationMirrors();
469: assert annMirrors.size() == currAnnotations.size();
470:
471: int index = -1, runWithIndex = -1, suiteClassesIndex = -1;
472: for (AnnotationMirror annMirror : annMirrors) {
473: index++;
474: Element annElement = annMirror.getAnnotationType()
475: .asElement();
476: assert annElement instanceof TypeElement;
477: TypeElement annTypeElem = (TypeElement) annElement;
478: Name annFullName = annTypeElem.getQualifiedName();
479:
480: if ((runWithIndex == -1)
481: && annFullName.contentEquals(ANN_RUN_WITH)) {
482: runWithIndex = index;
483: } else if ((suiteClassesIndex == -1)
484: && annFullName.contentEquals(ANN_SUITE + '.'
485: + ANN_SUITE_MEMBERS)) {
486: suiteClassesIndex = index;
487: }
488: }
489:
490: AnnotationTree runWithSuiteAnn;
491: if ((runWithIndex == -1)
492: || !checkRunWithSuiteAnnotation(annMirrors
493: .get(runWithIndex), workingCopy)) {
494: runWithSuiteAnn = createRunWithSuiteAnnotation(workingCopy);
495: } else {
496: runWithSuiteAnn = currAnnotations.get(runWithIndex);
497: }
498:
499: AnnotationTree suiteClassesAnn;
500: if ((suiteClassesIndex == -1)
501: || !checkSuiteMembersAnnotation(annMirrors
502: .get(suiteClassesIndex), suiteMembers,
503: workingCopy)) {
504: suiteClassesAnn = createSuiteAnnotation(suiteMembers,
505: workingCopy);
506: } else {
507: suiteClassesAnn = currAnnotations
508: .get(suiteClassesIndex);
509: }
510:
511: if ((runWithIndex != -1) && (suiteClassesIndex != -1)) {
512: if (runWithSuiteAnn != currAnnotations
513: .get(runWithIndex)) {
514: workingCopy.rewrite(currAnnotations
515: .get(runWithIndex), runWithSuiteAnn);
516: }
517: if (suiteClassesAnn != currAnnotations
518: .get(suiteClassesIndex)) {
519: workingCopy.rewrite(currAnnotations
520: .get(suiteClassesIndex), suiteClassesAnn);
521: }
522: annotations = currAnnotations;
523: } else {
524: List<AnnotationTree> newAnnotations = new ArrayList<AnnotationTree>(
525: currAnnotations.size() + 2);
526: if ((runWithIndex == -1) && (suiteClassesIndex == -1)) {
527:
528: /*
529: * put the @RunWith(...) and @Suite.SuiteClasses(...)
530: * annotations in front of other annotations
531: */
532: newAnnotations.add(runWithSuiteAnn);
533: newAnnotations.add(suiteClassesAnn);
534: if (!currAnnotations.isEmpty()) {
535: newAnnotations.addAll(currAnnotations);
536: }
537: } else {
538: newAnnotations.addAll(currAnnotations);
539: if (runWithIndex == -1) {
540: assert suiteClassesIndex != 1;
541:
542: /*
543: * put the @RunWith(...) annotation
544: * just before the Suite.SuiteClasses(...) annotation
545: */
546: newAnnotations.add(suiteClassesIndex,
547: runWithSuiteAnn);
548: } else {
549: assert runWithIndex != -1;
550:
551: /*
552: * put the @Suite.SuiteClasses(...) annotation
553: * just after the @RunWith(...) annotation
554: */
555: newAnnotations.add(runWithIndex + 1,
556: suiteClassesAnn);
557: }
558: }
559: annotations = newAnnotations;
560:
561: annotationListModified = true;
562: }
563: }
564:
565: if (!flagsModified && !annotationListModified) {
566: return modifiers;
567: }
568:
569: return workingCopy.getTreeMaker().Modifiers(flags, annotations);
570: }
571:
572: /**
573: * Checks that the given annotation is of type
574: * <code>{@value #ANN_RUN_WITH}</code> and contains argument
575: * <code>{@value #ANN_SUITE}{@literal .class}</code>.
576: *
577: * @param annMirror annotation to be checked
578: * @return {@code true} if the annotation meets the described criteria,
579: * {@code false} otherwise
580: */
581: private boolean checkRunWithSuiteAnnotation(
582: AnnotationMirror annMirror, WorkingCopy workingCopy) {
583: Map<? extends ExecutableElement, ? extends AnnotationValue> annParams = annMirror
584: .getElementValues();
585:
586: if (annParams.size() != 1) {
587: return false;
588: }
589:
590: AnnotationValue annValue = annParams.values().iterator().next();
591: Name annValueClsName = getAnnotationValueClassName(annValue,
592: workingCopy.getTypes());
593: return annValueClsName != null ? annValueClsName
594: .contentEquals(ANN_SUITE) : false;
595: }
596:
597: /**
598: * Checks that the given annotation is of type
599: * <code>{@value #ANN_SUITE}.{@value #ANN_SUITE_MEMBERS}</code>
600: * and contains the given list of classes as (the only) argument,
601: * in the same order.
602: *
603: * @param annMirror annotation to be checked
604: * @param suiteMembers list of fully qualified class names denoting
605: * content of the test suite
606: * @return {@code true} if the annotation meets the described criteria,
607: * {@code false} otherwise
608: */
609: private boolean checkSuiteMembersAnnotation(
610: AnnotationMirror annMirror, List<String> suiteMembers,
611: WorkingCopy workingCopy) {
612: Map<? extends ExecutableElement, ? extends AnnotationValue> annParams = annMirror
613: .getElementValues();
614:
615: if (annParams.size() != 1) {
616: return false;
617: }
618:
619: AnnotationValue annValue = annParams.values().iterator().next();
620: Object value = annValue.getValue();
621: if (value instanceof java.util.List) {
622: List<? extends AnnotationValue> items = (List<? extends AnnotationValue>) value;
623:
624: if (items.size() != suiteMembers.size()) {
625: return false;
626: }
627:
628: Types types = workingCopy.getTypes();
629: Iterator<String> suiteMembersIt = suiteMembers.iterator();
630: for (AnnotationValue item : items) {
631: Name suiteMemberName = getAnnotationValueClassName(
632: item, types);
633: if (suiteMemberName == null) {
634: return false;
635: }
636: if (!suiteMemberName.contentEquals(suiteMembersIt
637: .next())) {
638: return false;
639: }
640: }
641: return true;
642: }
643:
644: return false;
645: }
646:
647: /**
648: * Returns fully qualified class name of a class given to an annotation
649: * as (the only) argument.
650: *
651: * @param annValue annotation value
652: * @return fully qualified name of a class represented by the given
653: * annotation value, or {@code null} if the annotation value
654: * does not represent a class
655: */
656: private Name getAnnotationValueClassName(AnnotationValue annValue,
657: Types types) {
658: Object value = annValue.getValue();
659: if (value instanceof TypeMirror) {
660: TypeMirror typeMirror = (TypeMirror) value;
661: Element typeElement = types.asElement(typeMirror);
662: if (typeElement.getKind() == ElementKind.CLASS) {
663: return ((TypeElement) typeElement).getQualifiedName();
664: }
665: }
666: return null;
667: }
668:
669: /**
670: * Creates annotation <code>@org.junit.runner.RunWith</code>.
671: *
672: * @return created annotation
673: */
674: private AnnotationTree createRunWithSuiteAnnotation(
675: WorkingCopy workingCopy) {
676: TreeMaker maker = workingCopy.getTreeMaker();
677:
678: /* @RunWith(Suite.class) */
679: return maker.Annotation(getClassIdentifierTree(ANN_RUN_WITH,
680: workingCopy), Collections
681: .<ExpressionTree> singletonList(maker.MemberSelect(
682: getClassIdentifierTree(ANN_SUITE, workingCopy),
683: "class"))); //NOI18N
684: }
685:
686: /**
687: * Creates annotation
688: * <code>@org.junit.runners.Suite.SuiteClasses({...})</code>.
689: *
690: * @param suiteMembers fully qualified names of classes to be included
691: * in the test suite
692: * @param created annotation
693: */
694: private AnnotationTree createSuiteAnnotation(
695: List<String> suiteMembers, WorkingCopy workingCopy) {
696: final TreeMaker maker = workingCopy.getTreeMaker();
697:
698: List<ExpressionTree> suiteMemberExpressions = new ArrayList<ExpressionTree>(
699: suiteMembers.size());
700: for (String suiteMember : suiteMembers) {
701: suiteMemberExpressions.add(maker.MemberSelect(
702: getClassIdentifierTree(suiteMember, workingCopy),
703: "class")); //NOI18N
704: }
705:
706: /* @Suite.SuiteClasses({TestA.class, TestB.class, ...}) */
707: return maker.Annotation(maker.MemberSelect(
708: getClassIdentifierTree(ANN_SUITE, workingCopy),
709: ANN_SUITE_MEMBERS), Collections.singletonList(maker
710: .NewArray(
711: null, //do not generate "new Class[]"
712: Collections.<ExpressionTree> emptyList(),
713: suiteMemberExpressions)));
714: }
715:
716: }
|