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 java.net.URL;
048: import java.util.Collections;
049: import java.util.logging.Logger;
050: import org.netbeans.api.java.classpath.ClassPath;
051: import org.netbeans.api.java.queries.UnitTestForSourceQuery;
052: import org.netbeans.api.java.source.CompilationInfo;
053: import org.netbeans.api.project.FileOwnerQuery;
054: import org.netbeans.api.project.Project;
055: import org.netbeans.api.project.SourceGroup;
056: import org.netbeans.modules.junit.plugin.JUnitPlugin;
057: import org.netbeans.modules.junit.plugin.JUnitPlugin.CreateTestParam;
058: import org.netbeans.modules.junit.wizards.Utils;
059: import org.openide.DialogDisplayer;
060: import org.openide.ErrorManager;
061: import org.openide.NotifyDescriptor;
062: import org.openide.filesystems.FileObject;
063: import org.openide.filesystems.FileUtil;
064: import org.openide.filesystems.URLMapper;
065: import org.openide.loaders.DataFolder;
066: import org.openide.loaders.DataObject;
067: import org.openide.loaders.DataObjectNotFoundException;
068: import org.openide.nodes.Node;
069: import java.util.ArrayList;
070: import java.util.Collection;
071: import java.util.HashMap;
072: import java.util.List;
073: import java.util.Map;
074: import java.util.Set;
075: import java.util.StringTokenizer;
076: import java.util.logging.Level;
077: import javax.lang.model.element.TypeElement;
078: import org.netbeans.api.java.queries.SourceLevelQuery;
079: import org.openide.util.Utilities;
080:
081: /**
082: *
083: * @author rmatous
084: * @author Marian Petras
085: * @version 1.1
086: */
087: public class TestUtil {
088: static private final String JAVA_SOURCES_SUFFIX = "java";
089: private static final String JAVA_MIME_TYPE = "text/x-java"; //NOI18N
090:
091: static private String getTestClassSuffix() {
092: return JUnitSettings.TEST_CLASSNAME_SUFFIX;
093: }
094:
095: static private String getTestClassPrefix() {
096: return JUnitSettings.TEST_CLASSNAME_PREFIX;
097: }
098:
099: static private String getTestSuiteSuffix() {
100: return JUnitSettings.SUITE_CLASSNAME_SUFFIX;
101: }
102:
103: static private String getTestSuitePrefix() {
104: return JUnitSettings.SUITE_CLASSNAME_PREFIX;
105: }
106:
107: static private String getRootSuiteName() {
108: return JUnitSettings.getDefault().getRootSuiteClassName();
109: }
110:
111: //
112: // test class names
113: //
114: public static String getTestClassFullName(String sourceClassName,
115: String packageName) {
116: String shortTestClassName = getTestClassName(sourceClassName);
117: return ((packageName == null) || (packageName.length() == 0)) ? shortTestClassName
118: : packageName.replace('.', '/') + '/'
119: + shortTestClassName;
120: }
121:
122: public static String getTestClassName(String sourceClassName) {
123: return getTestClassPrefix() + sourceClassName
124: + getTestClassSuffix();
125: }
126:
127: //
128: // suite class names
129: //
130:
131: /**
132: * Converts given package filename to test suite filename, e.g.
133: * "<tt>org/netbeans/foo</tt>" ->
134: * "<tt>org/netbeans/foo/{suite-prefix}Foo{suite-suffix}</tt>"
135: * @param packageFileName package filename in form of "org/netbeans/foo"
136: */
137: public static String convertPackage2SuiteName(String packageFileName) {
138: if (packageFileName.length() == 0) {
139: return getRootSuiteName();
140: } else {
141: int index = packageFileName.lastIndexOf('/');
142: String pkg = index > -1 ? packageFileName
143: .substring(index + 1) : packageFileName;
144: pkg = pkg.substring(0, 1).toUpperCase() + pkg.substring(1);
145: return packageFileName + "/" + getTestSuitePrefix() + pkg
146: + getTestSuiteSuffix();
147: }
148: }
149:
150: /**
151: * Converts given class filename to test filename, e.g.
152: * "<tt>org/netbeans/Foo</tt>"
153: * -> "<tt>org/netbeans/{test-prefix}Foo{test-suffix}</tt>"
154: *
155: * @param classFileName class filename in form of
156: * "<tt>org/netbeans/Foo</tt>",
157: * i.e. without extension, no inner class
158: */
159: public static String convertClass2TestName(String classFileName) {
160: int index = classFileName.lastIndexOf('/');
161: String pkg = index > -1 ? classFileName.substring(0, index)
162: : "";
163: String clazz = index > -1 ? classFileName.substring(index + 1)
164: : classFileName;
165: clazz = clazz.substring(0, 1).toUpperCase()
166: + clazz.substring(1);
167: if (pkg.length() > 0) {
168: pkg += "/";
169: }
170: return pkg + getTestClassPrefix() + clazz
171: + getTestClassSuffix();
172: }
173:
174: /**
175: * Show error message box.
176: */
177: public static void notifyUser(String msg) {
178: notifyUser(msg, NotifyDescriptor.ERROR_MESSAGE);
179: }
180:
181: /**
182: * Show message box of the specified severity.
183: */
184: public static void notifyUser(String msg, int messageType) {
185: NotifyDescriptor descr = new NotifyDescriptor.Message(msg,
186: messageType);
187: DialogDisplayer.getDefault().notify(descr);
188: }
189:
190: // other misc methods
191:
192: static public FileObject getFileObjectFromNode(Node node) {
193: DataObject dO;
194: DataFolder df;
195:
196: dO = node.getCookie(DataObject.class);
197: if (null != dO) {
198: return dO.getPrimaryFile();
199: }
200:
201: df = node.getCookie(DataFolder.class);
202: if (null != df) {
203: return df.getPrimaryFile();
204: }
205:
206: //XXX: retouche
207: // ClassElement ce = (ClassElement) node.getCookie(ClassElement.class);
208: // if (null != ce) {
209: // // find the parent DataObject, which node belongs to
210: // while (null != (node = node.getParentNode())) {
211: // if (null != (dO = (DataObject) node.getCookie(DataObject.class)))
212: // return dO.getPrimaryFile();
213: // }
214: // }
215: return null;
216: }
217:
218: /**
219: */
220: static boolean isJavaFile(FileObject fileObj) {
221: return "java".equals(fileObj.getExt()) //NOI18N
222: || "text/x-java".equals(FileUtil.getMIMEType(fileObj)); //NOI18N
223: }
224:
225: static boolean isClassTest(CompilationInfo compilationInfo,
226: TypeElement classElem) {
227: return isClassImplementingTestInterface(compilationInfo,
228: classElem);
229: }
230:
231: // is JavaClass a Test class?
232: static boolean isClassImplementingTestInterface(
233: CompilationInfo compilationInfo, TypeElement classElem) {
234: String testIfaceFullName = "junit.framework.Test"; //NOI18N
235: TypeElement testIface = compilationInfo.getElements()
236: .getTypeElement(testIfaceFullName);
237:
238: if (testIface == null) {
239: String msg = "junit: TestUtil.isClassImplementingTestInterface(...) " //NOI18N
240: + "could not find TypeElement for " //NOI18N
241: + testIfaceFullName;
242: Logger.getLogger("global").log(Level.WARNING, msg); //NOI18N
243: return false;
244: }
245:
246: return compilationInfo.getTypes().isSubtype(classElem.asType(),
247: testIface.asType());
248: }
249:
250: // is class an exception
251:
252: static boolean isClassException(CompilationInfo compilationInfo,
253: TypeElement classElem) {
254: String throwableFullName = "java.lang.Throwable"; //NOI18N
255: TypeElement throwable = compilationInfo.getElements()
256: .getTypeElement(throwableFullName);
257:
258: if (throwable == null) {
259: String msg = "junit: TestUtil.isClassException(...) " //NOI18N
260: + "could not find TypeElement for " //NOI18N
261: + throwableFullName;
262: Logger.getLogger("global").log(Level.SEVERE, msg); //NOI18N
263: return false;
264: }
265:
266: return compilationInfo.getTypes().isSubtype(classElem.asType(),
267: throwable.asType());
268: }
269:
270: /**
271: * Finds a main class.
272: *
273: * @param compInfo defines scope in which the class is to be found
274: * @param className name of the class to be found
275: * @return the found class; or <code>null</code> if the class was not
276: * found (e.g. because of a broken source file)
277: */
278: public static ClassTree findMainClass(final CompilationInfo compInfo) {
279: final String className = compInfo.getFileObject().getName();
280:
281: CompilationUnitTree compUnitTree = compInfo
282: .getCompilationUnit();
283: String shortClassName = getSimpleName(className);
284: for (Tree typeDecl : compUnitTree.getTypeDecls()) {
285: if (Tree.Kind.CLASS == typeDecl.getKind()) {
286: ClassTree clazz = (ClassTree) typeDecl;
287: if (clazz.getSimpleName().toString().equals(
288: shortClassName)) {
289: return clazz;
290: }
291: }
292: }
293: return null;
294: }
295:
296: /**
297: * Converts filename to the fully qualified name of the main class
298: * residing in the file.<br />
299: * For example: <tt>test/myapp/App.java</tt> --> <tt>test.myapp.App</tt>
300: *
301: * @param filename
302: * @return corresponding package name. Null if the input is not
303: * well formed.
304: */
305: static String fileToClassName(String fileName) {
306: if (fileName.endsWith(".java")) { //NOI18N
307: return (fileName.substring(0, fileName.length() - 5))
308: .replace('/', '.');
309: } else {
310: return null;
311: }
312: }
313:
314: /**
315: * Returns full names of all primary Java classes
316: * withing the specified folder (non-recursive).
317: *
318: * @param packageFolder folder to search
319: * @param classPath classpath to be used for the search
320: * @return list of full names of all primary Java classes
321: * within the specified package
322: */
323: public static List<String> getJavaFileNames(
324: FileObject packageFolder, ClassPath classPath) {
325: FileObject[] children = packageFolder.getChildren();
326: if (children.length == 0) {
327: return Collections.<String> emptyList();
328: }
329:
330: List<String> result = new ArrayList<String>(children.length);
331: for (FileObject child : children) {
332: if (child.isFolder() || child.isVirtual()
333: || !child.getMIMEType().equals(JAVA_MIME_TYPE)) {
334: continue;
335: }
336:
337: DataObject dataObject;
338: try {
339: dataObject = DataObject.find(child);
340: } catch (DataObjectNotFoundException ex) {
341: continue;
342: }
343:
344: //XXX: retouche
345: // Resource rc = JavaModel.getResource(dataObject.getPrimaryFile());
346: // result.add(getMainJavaClass(rc).getName());
347: }
348: return result.isEmpty() ? Collections.<String> emptyList()
349: : result;
350: }
351:
352: //XXX: retouche
353: // public static List filterFeatures(JavaClass cls, Class type) {
354: // LinkedList ret = new LinkedList();
355: // Iterator it = cls.getFeatures().iterator();
356: //
357: // while (it.hasNext()) {
358: // Feature f = (Feature)it.next();
359: // if (type.isAssignableFrom(f.getClass())) ret.add(f);
360: // }
361: // return ret;
362: // }
363:
364: //XXX: retouche
365: // public static Feature getFeatureByName(JavaClass src, Class cls, String name) {
366: // if (!Feature.class.isAssignableFrom(cls)) throw new IllegalArgumentException("cls is not Feature");
367: //
368: // Iterator it = src.getFeatures().iterator();
369: // while (it.hasNext()) {
370: // Object o = it.next();
371: // if (cls.isAssignableFrom(o.getClass())) {
372: // Feature f = (Feature)o;
373: // if (f.getName().equals(name)) return f;
374: // }
375: // }
376: // return null;
377: // }
378:
379: //XXX: retouche
380: // public static JavaClass getClassBySimpleName(JavaClass cls, String name) {
381: // return cls.getInnerClass(name, false);
382: // }
383:
384: static public String createNewName(int i, Set usedNames) {
385: String ret;
386: do {
387: ret = "p" + i++;
388: } while (usedNames.contains(ret));
389: return ret;
390: }
391:
392: //XXX: retouche
393: // static public Parameter cloneParam(Parameter p, JavaModelPackage pkg, int order, Set usedNames) {
394: // String name = p.getName();
395: // if (name == null || name.length()==0 || usedNames.contains(name)) {
396: // name = createNewName(order, usedNames);
397: // }
398: // usedNames.add(name);
399: //
400: //
401: // Parameter ret =
402: // pkg.getParameter().
403: // createParameter(name,
404: // p.getAnnotations(),
405: // p.isFinal(),
406: // null,
407: // 0,//p.getDimCount(),
408: // p.isVarArg());
409: // ret.setType(p.getType());
410: // return ret;
411: // }
412:
413: //XXX: retouche
414: // public static List cloneParams(List params, JavaModelPackage pkg) {
415: // Iterator origParams = params.iterator();
416: // List newParams = new LinkedList();
417: // int o = 0;
418: // HashSet usedNames = new HashSet(params.size()*2);
419: // while (origParams.hasNext()) {
420: // Parameter p = (Parameter)origParams.next();
421: // newParams.add(TestUtil.cloneParam(p, pkg, o++, usedNames));
422: // }
423: // return newParams;
424: // }
425:
426: //XXX: retouche
427: // /**
428: // * Gets collection of types of the parameters passed in in the
429: // * argument. The returned collection has the same size as the
430: // * input collection.
431: // * @param params List<Parameter>
432: // * @return List<Type>
433: // */
434: // static public List getParameterTypes(List params) {
435: // List ret = new ArrayList(params.size());
436: // Iterator it = params.iterator();
437: // while (it.hasNext()) {
438: // ret.add(((Parameter)it.next()).getType());
439: // }
440: // return ret;
441: // }
442:
443: //XXX: retouche
444: // /**
445: // * Gets list of all features within the given class of the given
446: // * class and modifiers.
447: // * @param c the JavaClass to search
448: // * @param cls the Class to search for
449: // * @param modifiers the modifiers to search for
450: // * @param recursive if true, the search descents to superclasses
451: // * and interfaces
452: // * @return List of the collected Features
453: // */
454: // public static List collectFeatures(JavaClass c, Class cls,
455: // int modifiers, boolean recursive) {
456: //
457: // return collectFeatures(c, cls, modifiers, recursive, new LinkedList(), new HashSet());
458: // }
459:
460: //XXX: retouche
461: // private static List collectFeatures(JavaClass c, Class cls,
462: // int modifiers, boolean recursive,
463: // List list, Set visited )
464: // {
465: //
466: // if (!visited.add(c)) return list;
467: // // this class
468: //
469: // int mo = (c.isInterface()) ? Modifier.ABSTRACT : 0;
470: // Iterator it = TestUtil.filterFeatures(c, cls).iterator();
471: // while (it.hasNext()) {
472: // Feature m = (Feature)it.next();
473: // if (((m.getModifiers() | mo) & modifiers) == modifiers) {
474: // list.add(m);
475: // }
476: // }
477: //
478: // if (recursive) {
479: // // super
480: // JavaClass sup = c.getSuperClass();
481: // if (sup != null) collectFeatures(sup, cls, modifiers, recursive, list, visited);
482: //
483: // // interfaces
484: // Iterator ifaces = c.getInterfaces().iterator();
485: // while (ifaces.hasNext()) collectFeatures((JavaClass)ifaces.next(), cls,
486: // modifiers, recursive, list, visited);
487: // }
488: //
489: // return list;
490: // }
491:
492: //XXX: retouche
493: // public static boolean hasMainMethod(JavaClass cls) {
494: //
495: // JavaModelPackage pkg = (JavaModelPackage)cls.refImmediatePackage();
496: // return cls.getMethod("main",
497: // Collections.singletonList(pkg.getArray().resolveArray(TestUtil.getStringType(pkg))),
498: // false) != null;
499: //
500: // }
501:
502: //XXX: retouche
503: // public static Type getStringType(JavaModelPackage pkg) {
504: // return pkg.getType().resolve("java.lang.String");
505: // }
506:
507: //XXX: retouche
508: // public static TypeReference getTypeReference(JavaModelPackage pkg, String name) {
509: // return pkg.getMultipartId().createMultipartId(name, null, Collections.EMPTY_LIST);
510: // }
511:
512: /**
513: * Finds <code>SourceGroup</code>s where a test for the given class
514: * can be created (so that it can be found by the projects infrastructure
515: * when a test for the class is to be opened or run).
516: *
517: * @param fileObject <code>FileObject</code> to find target
518: * <code>SourceGroup</code>(s) for
519: * @return an array of objects - each of them can be either
520: * a <code>SourceGroup</code> for a possible target folder
521: * or simply a <code>FileObject</code> representing a possible
522: * target folder (if <code>SourceGroup</code>) for the folder
523: * was not found);
524: * the returned array may be empty but not <code>null</code>
525: * @author Marian Petras
526: */
527: public static Object[] getTestTargets(FileObject fileObject) {
528:
529: /* .) get project owning the given FileObject: */
530: final Project project = FileOwnerQuery.getOwner(fileObject);
531: if (project == null) {
532: return new Object[0];
533: }
534:
535: SourceGroup sourceGroupOwner = findSourceGroupOwner(fileObject);
536: if (sourceGroupOwner == null) {
537: return new Object[0];
538: }
539:
540: /* .) get URLs of target SourceGroup's roots: */
541: final URL[] rootURLs = UnitTestForSourceQuery
542: .findUnitTests(sourceGroupOwner.getRootFolder());
543: if (rootURLs.length == 0) {
544: return new Object[0];
545: }
546:
547: /* .) convert the URLs to FileObjects: */
548: boolean someSkipped = false;
549: FileObject[] sourceRoots = new FileObject[rootURLs.length];
550: for (int i = 0; i < rootURLs.length; i++) {
551: if ((sourceRoots[i] = URLMapper.findFileObject(rootURLs[i])) == null) {
552: ErrorManager.getDefault().notify(
553: ErrorManager.INFORMATIONAL,
554: new IllegalStateException(
555: "No FileObject found for the following URL: "//NOI18N
556: + rootURLs[i]));
557: someSkipped = true;
558: continue;
559: }
560: if (FileOwnerQuery.getOwner(sourceRoots[i]) != project) {
561: ErrorManager
562: .getDefault()
563: .notify(
564: ErrorManager.INFORMATIONAL,
565: new IllegalStateException(
566: "Source root found by FileOwnerQuery points " //NOI18N
567: + "to a different project for the following URL: " //NOI18N
568: + rootURLs[i]));
569: sourceRoots[i] = null;
570: someSkipped = true;
571: continue;
572: }
573: }
574:
575: if (someSkipped) {
576: FileObject roots[] = skipNulls(sourceRoots,
577: new FileObject[0]);
578: if (roots.length == 0) {
579: return new Object[0];
580: }
581: sourceRoots = roots;
582: }
583:
584: /* .) find SourceGroups corresponding to the FileObjects: */
585: final Object[] targets = new Object[sourceRoots.length];
586: Map<FileObject, SourceGroup> map = getFileObject2SourceGroupMap(project);
587: for (int i = 0; i < sourceRoots.length; i++) {
588: SourceGroup srcGroup = map.get(sourceRoots[i]);
589: targets[i] = srcGroup != null ? srcGroup : sourceRoots[i];
590: }
591: return targets;
592: }
593:
594: /**
595: * Finds a <code>SourceGroup</code> the given file belongs to.
596: * Only Java <code>SourceGroup</code>s are taken into account.
597: *
598: * @param file <code>FileObject</code> whose owning
599: * <code>SourceGroup</code> to be found
600: * @return Java <code>SourceGroup</code> containing the given
601: * file; or <code>null</code> if no such
602: * <code>SourceGroup</code> was found
603: * @author Marian Petras
604: */
605: public static SourceGroup findSourceGroupOwner(FileObject file) {
606: final Project project = FileOwnerQuery.getOwner(file);
607: return findSourceGroupOwner(project, file);
608: }
609:
610: /**
611: * Finds a <code>SourceGroup</code> the given file belongs to.
612: * Only Java <code>SourceGroup</code>s are taken into account.
613: *
614: * @param project the <code>Project</code> the file belongs to
615: * @param file <code>FileObject</code> whose owning
616: * <code>SourceGroup</code> to be found
617: * @return Java <code>SourceGroup</code> containing the given
618: * file; or <code>null</code> if no such
619: * <code>SourceGroup</code> was found
620: */
621:
622: public static SourceGroup findSourceGroupOwner(Project project,
623: FileObject file) {
624: final SourceGroup[] sourceGroups = new Utils(project)
625: .getJavaSourceGroups();
626: for (int i = 0; i < sourceGroups.length; i++) {
627: SourceGroup srcGroup = sourceGroups[i];
628: FileObject root = srcGroup.getRootFolder();
629: if (((file == root) || (FileUtil.isParentOf(root, file)))
630: && srcGroup.contains(file)) {
631: return srcGroup;
632: }
633: }
634: return null;
635: }
636:
637: /**
638: * Finds all <code>SourceGroup</code>s of the given project
639: * containing a class of the given name.
640: *
641: * @param project project to be searched for matching classes
642: * @param className class name pattern
643: * @return unmodifiable collection of <code>SourceGroup</code>s
644: * which contain files corresponding to the given name
645: * (may be empty but not <code>null</code>)
646: * @author Marian Petras
647: */
648: public static Collection<SourceGroup> findSourceGroupOwners(
649: final Project project, final String className) {
650: final SourceGroup[] sourceGroups = new Utils(project)
651: .getJavaSourceGroups();
652: if (sourceGroups.length == 0) {
653: return Collections.<SourceGroup> emptyList();
654: }
655:
656: final String relativePath = className.replace('.', '/')
657: + ".java"; //NOI18N
658:
659: ArrayList<SourceGroup> result = new ArrayList<SourceGroup>(4);
660: for (int i = 0; i < sourceGroups.length; i++) {
661: SourceGroup srcGroup = sourceGroups[i];
662: FileObject root = srcGroup.getRootFolder();
663: FileObject file = root.getFileObject(relativePath);
664: if (file != null && FileUtil.isParentOf(root, file)
665: && srcGroup.contains(file)) {
666: result.add(srcGroup);
667: }
668: }
669: if (result.isEmpty()) {
670: return Collections.<SourceGroup> emptyList();
671: }
672: result.trimToSize();
673: return Collections.unmodifiableList(result);
674: }
675:
676: /**
677: * Creates a copy of the given array, except that <code>null</code> objects
678: * are omitted.
679: * The length of the returned array is (<var>l</var> - <var>n</var>), where
680: * <var>l</var> is length of the passed array and <var>n</var> is number
681: * of <code>null</code> elements of the array. Order of
682: * non-<code>null</code> elements is kept in the returned array.
683: * The returned array is always a new array, even if the passed
684: * array does not contain any <code>null</code> elements.
685: *
686: * @param objs array to copy
687: * @param type an empty array of the correct type to be returned
688: * @return array containing the same objects as the passed array, in the
689: * same order, just with <code>null</code> elements missing
690: * @author Marian Petras
691: */
692: public static <T> T[] skipNulls(final T[] objs, final T[] type) {
693: List<T> resultList = new ArrayList<T>(objs.length);
694:
695: for (int i = 0; i < objs.length; i++) {
696: if (objs[i] != null) {
697: resultList.add(objs[i]);
698: }
699: }
700:
701: return resultList.toArray(type);
702: }
703:
704: /**
705: * Creates a map from folders to <code>SourceGroup</code>s of a given
706: * project.
707: * The map allows to ascertian for a given folder
708: * which <code>SourceGroup</code> it is a root folder of.
709: *
710: * @param project project whose <code>SourceGroup</code>s should be in the
711: * returned map
712: * @return map from containing all <code>SourceGroup</code>s of a given
713: * project, having their root folders as keys
714: * @author Marian Petras
715: */
716: public static Map<FileObject, SourceGroup> getFileObject2SourceGroupMap(
717: Project project) {
718: final SourceGroup[] sourceGroups = new Utils(project)
719: .getJavaSourceGroups();
720:
721: if (sourceGroups.length == 0) {
722: return Collections.<FileObject, SourceGroup> emptyMap();
723: } else if (sourceGroups.length == 1) {
724: return Collections.singletonMap(sourceGroups[0]
725: .getRootFolder(), sourceGroups[0]);
726: } else {
727: Map<FileObject, SourceGroup> map;
728: map = new HashMap<FileObject, SourceGroup>(Math
729: .round(sourceGroups.length * 1.4f + .5f), .75f);
730: for (int i = 0; i < sourceGroups.length; i++) {
731: map.put(sourceGroups[i].getRootFolder(),
732: sourceGroups[i]);
733: }
734: return map;
735: }
736: }
737:
738: // Nice copy of useful methods (Taken from JavaModule)
739: public static boolean isValidPackageName(String str) {
740: if (str.length() > 0 && str.charAt(0) == '.') {
741: return false;
742: }
743: StringTokenizer tukac = new StringTokenizer(str, ".");
744: while (tukac.hasMoreTokens()) {
745: String token = tukac.nextToken();
746: if ("".equals(token)) {
747: return false;
748: }
749: if (!Utilities.isJavaIdentifier(token)) {
750: return false;
751: }
752: }
753: return true;
754: }
755:
756: /**
757: *
758: */
759: public static JUnitPlugin getPluginForProject(final Project project) {
760: Object pluginObj = project.getLookup()
761: .lookup(JUnitPlugin.class);
762: return (pluginObj != null) ? (JUnitPlugin) pluginObj
763: : new DefaultPlugin();
764: }
765:
766: /**
767: * Creates a map of parameters according to the current JUnit module
768: * settings.<br />
769: * Note: The map may not contain all the necessary settings,
770: * i.g. name of a test class is missing.
771: *
772: * @param multipleFiles if {@code true}, the map should contain
773: * also settings need for creation of multiple
774: * tests
775: * @return map of settings to be used by a
776: * {@link org.netbeans.modules.junit.plugin JUnitPlugin}
777: * @see org.netbeans.modules.junit.plugin.JUnitPlugin
778: */
779: public static Map<CreateTestParam, Object> getSettingsMap(
780: boolean multipleFiles) {
781: final JUnitSettings settings = JUnitSettings.getDefault();
782: final Map<CreateTestParam, Object> params = new HashMap<CreateTestParam, Object>(
783: 17);
784:
785: params.put(CreateTestParam.INC_PUBLIC, Boolean.valueOf(settings
786: .isMembersPublic()));
787: params.put(CreateTestParam.INC_PROTECTED, Boolean
788: .valueOf(settings.isMembersProtected()));
789: params.put(CreateTestParam.INC_PKG_PRIVATE, Boolean
790: .valueOf(settings.isMembersPackage()));
791: params.put(CreateTestParam.INC_CODE_HINT, Boolean
792: .valueOf(settings.isBodyComments()));
793: params.put(CreateTestParam.INC_METHOD_BODIES, Boolean
794: .valueOf(settings.isBodyContent()));
795: params.put(CreateTestParam.INC_JAVADOC, Boolean
796: .valueOf(settings.isJavaDoc()));
797:
798: if (multipleFiles) {
799: params.put(CreateTestParam.INC_GENERATE_SUITE, Boolean
800: .valueOf(settings.isGenerateSuiteClasses()));
801: params
802: .put(CreateTestParam.INC_PKG_PRIVATE_CLASS, Boolean
803: .valueOf(settings
804: .isIncludePackagePrivateClasses()));
805: params.put(CreateTestParam.INC_ABSTRACT_CLASS, Boolean
806: .valueOf(settings.isGenerateAbstractImpl()));
807: params.put(CreateTestParam.INC_EXCEPTION_CLASS, Boolean
808: .valueOf(settings.isGenerateExceptionClasses()));
809: }
810:
811: params.put(CreateTestParam.INC_SETUP, Boolean.valueOf(settings
812: .isGenerateSetUp()));
813: params.put(CreateTestParam.INC_TEAR_DOWN, Boolean
814: .valueOf(settings.isGenerateTearDown()));
815:
816: return params;
817: }
818:
819: /**
820: *
821: */
822: static String getPackageName(String fullName) {
823: int i = fullName.lastIndexOf('.');
824: return (i != -1) ? fullName.substring(0, i) : ""; //NOI18N
825: }
826:
827: /**
828: * Gets the last part of a fully qualified Java name.
829: */
830: static String getSimpleName(String fullName) {
831: int lastDotIndex = fullName.lastIndexOf('.');
832: return (lastDotIndex == -1) ? fullName : fullName
833: .substring(lastDotIndex + 1);
834: }
835:
836: private TestUtil() {
837: }
838:
839: /**
840: * Determines source level of the given file.
841: *
842: * @param file file whose source level is to be determined
843: * @return string denoting source level of the given file
844: * (e.g. <code>"1.5"</code>)
845: * or {@code null} if the source level could not be determined
846: */
847: static String getSourceLevel(FileObject file) {
848: ClassPath srcCP = ClassPath
849: .getClassPath(file, ClassPath.SOURCE);
850: if (srcCP == null) {
851: return null;
852: }
853:
854: FileObject srcRoot = srcCP.findOwnerRoot(file);
855: if (srcRoot == null) {
856: return null;
857: }
858:
859: return SourceLevelQuery.getSourceLevel(srcRoot);
860: }
861:
862: /**
863: * Checks whether Java annotations are applicable in the given file.
864: * The result is {@code true} if source level of the given is known
865: * and the source level is 1.5 or higher.
866: *
867: * @param file file to be checked
868: * @return {@code true} if the given file is known to be based on
869: * JDK 1.5 or higher; {@code false} otherwise
870: * @see #getSourceLevel
871: * @see #areAnnotationsSupported(String)
872: */
873: static boolean areAnnotationsSupported(FileObject file) {
874: return areAnnotationsSupported(getSourceLevel(file));
875: }
876:
877: /**
878: * Determines whether Java annotations are supported by the given
879: * Java source level.
880: *
881: * @param sourceLevel Java source level (e.g. <code>"1.5"</code>),
882: * or {@code null} if the source level is unknown
883: * @return {@code true} if the source level is known and Java annotations
884: * are supported in the source level, {@code false} otherwise
885: * @see #areAnnotationsSupported(FileObject)
886: */
887: static boolean areAnnotationsSupported(String sourceLevel) {
888: return (sourceLevel != null)
889: && (sourceLevel.compareTo("1.5") >= 0); //NOI18N
890: }
891:
892: }
|