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-2007 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.ClassTree;
045: import com.sun.source.tree.CompilationUnitTree;
046: import com.sun.source.tree.Tree;
047: import com.sun.source.util.TreePath;
048: import com.sun.source.util.Trees;
049: import java.io.IOException;
050: import java.util.ArrayList;
051: import java.util.Collection;
052: import java.util.Collections;
053: import java.util.List;
054: import javax.lang.model.element.Element;
055: import javax.lang.model.element.ElementKind;
056: import javax.lang.model.element.TypeElement;
057: import org.netbeans.api.java.source.CancellableTask;
058: import org.netbeans.api.java.source.CompilationController;
059: import org.netbeans.api.java.source.CompilationInfo;
060: import org.netbeans.api.java.source.ElementHandle;
061: import org.netbeans.api.java.source.JavaSource;
062: import org.netbeans.api.java.source.JavaSource.Phase;
063: import org.netbeans.api.java.source.TreeUtilities;
064: import org.netbeans.modules.junit.TestabilityResult.SkippedClass;
065:
066: /**
067: * Finds all non-annotation top-level classes in a compilation unit.
068: *
069: * @author Marian Petras
070: */
071: final class TopClassFinder {
072:
073: private TopClassFinder() {
074: }
075:
076: /**
077: */
078: private static class TopClassFinderTask implements
079: CancellableTask<CompilationController> {
080: private List<ElementHandle<TypeElement>> topClassElems;
081: private TestabilityJudge judgeOfTestability;
082: private Collection<SkippedClass> nonTestable;
083: private volatile boolean cancelled;
084:
085: private TopClassFinderTask() {
086: this .judgeOfTestability = null;
087: this .nonTestable = null;
088: }
089:
090: private TopClassFinderTask(TestabilityJudge testabilityJudge,
091: Collection<SkippedClass> nonTestable) {
092: if (testabilityJudge == null) {
093: throw new IllegalArgumentException(
094: "judgeOfTestability: null"); //NOI18N
095: }
096: if (nonTestable == null) {
097: throw new IllegalArgumentException("nonTestable: null"); //NOI18N
098: }
099: this .judgeOfTestability = testabilityJudge;
100: this .nonTestable = nonTestable;
101: }
102:
103: public void run(CompilationController controller)
104: throws IOException {
105: controller.toPhase(Phase.ELEMENTS_RESOLVED);
106: if (cancelled) {
107: return;
108: }
109:
110: if (judgeOfTestability == null) {
111: topClassElems = findTopClassElemHandles(controller,
112: controller.getCompilationUnit());
113: } else {
114: topClassElems = findTopClassElemHandles(controller,
115: controller.getCompilationUnit(),
116: judgeOfTestability, nonTestable);
117: }
118: }
119:
120: public void cancel() {
121: cancelled = true;
122: }
123: }
124:
125: /**
126: */
127: static List<ElementHandle<TypeElement>> findTopClasses(
128: JavaSource javaSource) throws IOException {
129: TopClassFinderTask analyzer = new TopClassFinderTask();
130: javaSource.runUserActionTask(analyzer, true);
131: return analyzer.topClassElems;
132: }
133:
134: /**
135: * Finds testable top-level classes, interfaces and enums in a given
136: * Java source.
137: *
138: * @param javaSource source in which testable classes should be found
139: * @param judge {@code TestCreator} that will select testable
140: * top-level classes
141: * (see {@link TestCreator#isClassTestable})
142: * @param nonTestable container where names of found non-testable classes
143: * should be stored
144: * @return handles to testable top-level classes, interfaces and enums
145: * @exception java.lang.IllegalArgumentException
146: * if any of the parameters is {@code null}
147: */
148: static List<ElementHandle<TypeElement>> findTestableTopClasses(
149: JavaSource javaSource, TestabilityJudge testabilityJudge,
150: Collection<SkippedClass> nonTestable) throws IOException {
151: TopClassFinderTask analyzer = new TopClassFinderTask(
152: testabilityJudge, nonTestable);
153: javaSource.runUserActionTask(analyzer, true);
154: return analyzer.topClassElems;
155: }
156:
157: /**
158: *
159: * @return list of top classes, or an empty list of none were found
160: */
161: static List<ClassTree> findTopClasses(
162: CompilationUnitTree compilationUnit, TreeUtilities treeUtils) {
163: List<? extends Tree> typeDecls = compilationUnit.getTypeDecls();
164: if ((typeDecls == null) || typeDecls.isEmpty()) {
165: return Collections.<ClassTree> emptyList();
166: }
167:
168: List<ClassTree> result = new ArrayList<ClassTree>(typeDecls
169: .size());
170:
171: for (Tree typeDecl : typeDecls) {
172: if (typeDecl.getKind() == Tree.Kind.CLASS) {
173: ClassTree clsTree = (ClassTree) typeDecl;
174: if (isTestable(clsTree, treeUtils)) {
175: result.add(clsTree);
176: }
177: }
178: }
179:
180: return result;
181: }
182:
183: /**
184: *
185: * @return list of {@code Element}s representing top classes,
186: * or an empty list of none were found
187: */
188: private static List<TypeElement> findTopClassElems(
189: CompilationInfo compInfo,
190: CompilationUnitTree compilationUnit,
191: TestabilityJudge testabilityJudge,
192: Collection<SkippedClass> nonTestable) {
193: List<? extends Tree> typeDecls = compilationUnit.getTypeDecls();
194: if ((typeDecls == null) || typeDecls.isEmpty()) {
195: return Collections.<TypeElement> emptyList();
196: }
197:
198: List<TypeElement> result = new ArrayList<TypeElement>(typeDecls
199: .size());
200:
201: Trees trees = compInfo.getTrees();
202: for (Tree typeDecl : typeDecls) {
203: if (typeDecl.getKind() == Tree.Kind.CLASS) {
204: Element element = trees.getElement(new TreePath(
205: new TreePath(compilationUnit), typeDecl));
206: TypeElement typeElement = (TypeElement) element;
207: if (testabilityJudge != null) {
208: TestabilityResult testabilityStatus = testabilityJudge
209: .isClassTestable(compInfo, typeElement);
210: if (testabilityStatus.isTestable()) {
211: result.add(typeElement);
212: } else {
213: nonTestable.add(new SkippedClass(typeElement
214: .getQualifiedName().toString(),
215: testabilityStatus));
216: }
217: } else if (isTestable(element)) {
218: result.add(typeElement);
219: }
220: }
221: }
222:
223: return result;
224: }
225:
226: /**
227: *
228: * @return list of handles to {@code Element}s representing top classes,
229: * or an empty list of none were found
230: */
231: private static List<ElementHandle<TypeElement>> findTopClassElemHandles(
232: CompilationInfo compInfo,
233: CompilationUnitTree compilationUnit) {
234: return getElemHandles(findTopClassElems(compInfo,
235: compilationUnit, null, null));
236: }
237:
238: /**
239: *
240: * @return list of handles to {@code Element}s representing top classes,
241: * or an empty list of none were found
242: */
243: private static List<ElementHandle<TypeElement>> findTopClassElemHandles(
244: CompilationInfo compInfo,
245: CompilationUnitTree compilationUnit,
246: TestabilityJudge testabilityJudge,
247: Collection<SkippedClass> nonTestable) {
248: return getElemHandles(findTopClassElems(compInfo,
249: compilationUnit, testabilityJudge, nonTestable));
250: }
251:
252: private static <T extends Element> List<ElementHandle<T>> getElemHandles(
253: List<T> elements) {
254: if (elements == null) {
255: return null;
256: }
257: if (elements.isEmpty()) {
258: return Collections.<ElementHandle<T>> emptyList();
259: }
260:
261: List<ElementHandle<T>> handles = new ArrayList<ElementHandle<T>>(
262: elements.size());
263: for (T element : elements) {
264: handles.add(ElementHandle.<T> create(element));
265: }
266: return handles;
267: }
268:
269: private static boolean isTestable(ClassTree typeDecl,
270: TreeUtilities treeUtils) {
271: return !treeUtils.isAnnotation(typeDecl);
272: }
273:
274: static boolean isTestable(Element typeDeclElement) {
275: ElementKind elemKind = typeDeclElement.getKind();
276: return (elemKind != ElementKind.ANNOTATION_TYPE)
277: && (elemKind.isClass() || elemKind.isInterface());
278: }
279:
280: }
|