001: /*
002: *
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
004: *
005: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
006: *
007: * The contents of this file are subject to the terms of either the GNU
008: * General Public License Version 2 only ("GPL") or the Common
009: * Development and Distribution License("CDDL") (collectively, the
010: * "License"). You may not use this file except in compliance with the
011: * License. You can obtain a copy of the License at
012: * http://www.netbeans.org/cddl-gplv2.html
013: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
014: * specific language governing permissions and limitations under the
015: * License. When distributing the software, include this License Header
016: * Notice in each file and include the License file at
017: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
018: * particular file as subject to the "Classpath" exception as provided
019: * by Sun in the GPL Version 2 section of the License file that
020: * accompanied this code. If applicable, add the following below the
021: * License Header, with the fields enclosed by brackets [] replaced by
022: * your own identifying information:
023: * "Portions Copyrighted [year] [name of copyright owner]"
024: *
025: * Contributor(s):
026: *
027: * The Original Software is NetBeans. The Initial Developer of the Original
028: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
029: * Microsystems, Inc. All Rights Reserved.
030: *
031: * If you wish your version of this file to be governed by only the CDDL
032: * or only the GPL Version 2, indicate your decision by adding
033: * "[Contributor] elects to include this software in this distribution
034: * under the [CDDL or GPL Version 2] license." If you do not indicate a
035: * single choice of license, a recipient has the option to distribute
036: * your version of this file under either the CDDL, the GPL Version 2 or
037: * to extend the choice of license to its licensees as provided above.
038: * However, if you add GPL Version 2 code and therefore, elected the GPL
039: * Version 2 license, then the option applies only if the new code is
040: * made subject to such option by the copyright holder.
041: */
042: /*
043: * TestScanner.java
044: *
045: * Created on April 25, 2001, 3:57 PM
046: */
047:
048: package org.netbeans.xtest.testrunner;
049:
050: import java.io.File;
051: import java.io.FilenameFilter;
052: import java.io.IOException;
053: import java.util.Arrays;
054: import java.util.Vector;
055: import java.util.LinkedList;
056: import java.util.StringTokenizer;
057: import org.netbeans.junit.Filter;
058:
059: /**
060: *
061: * @author <a href="mailto:vitezslav.stejskal@czech.sun.com">Vitezslav Stejskal</a>
062: * @version 1.0
063: */
064: public class TestScanner {
065: private final static int FILE_PATTERN = 0;
066: private final static int TEST_PATTERN = 1;
067: private final static int EXPFAIL_PATTERN = 2;
068: private final static int PATTERNS_COUNT = 3;
069:
070: protected File basedir;
071: protected String basePath;
072: /**
073: * The files that where found and matched at least one includes, and matched
074: * no excludes.
075: */
076: protected Vector filesIncluded;
077: /**
078: * The directories that where found and matched at least one includes, and
079: * matched no excludes.
080: */
081: protected Vector dirsIncluded;
082: /**
083: * The files that where found and matched at least one includes, and also
084: * matched at least one excludes.
085: */
086: protected Vector filesExcluded;
087: /**
088: * The files that where found and matched at least one includes, and also
089: * matched at least one excludes.
090: */
091: protected Vector dirsExcluded;
092: /**
093: * The files that where found and did not match any includes.
094: */
095: protected Vector filesNotIncluded;
096: /**
097: * The directories that where found and did not match any includes.
098: */
099: protected Vector dirsNotIncluded;
100: /**
101: * The patterns for the files that should be included.
102: */
103: protected String[][] includes;
104: /**
105: * The patterns for the files that should be excluded.
106: */
107: protected String[][] excludes;
108: /**
109: * Have the Vectors holding our results been built by a slow scan?
110: */
111: protected boolean haveSlowResults = false;
112:
113: /** Creates new TestScanner */
114: public TestScanner() {
115: }
116:
117: public void addDefaultExcludes() {
118: }
119:
120: public void setBasedir(java.lang.String basedir) {
121: setBasedir(new File(basedir.replace('/', File.separatorChar)
122: .replace('\\', File.separatorChar)));
123: }
124:
125: public void setBasedir(java.io.File basedir) {
126: this .basedir = basedir;
127: }
128:
129: public java.io.File getBasedir() {
130: return basedir;
131: }
132:
133: public void setBasePath(java.lang.String basePath) {
134: this .basePath = basePath;
135: }
136:
137: public java.lang.String getBasePath() {
138: return basePath;
139: }
140:
141: public TestFilter[] getIncludedFiles() {
142: return getFilesAndFilters(filesIncluded);
143: }
144:
145: public TestFilter[] getIncludedDirectories() {
146: return getFilesAndFilters(dirsIncluded);
147: }
148:
149: public TestFilter[] getExcludedFiles() throws IOException {
150: slowScan();
151: return getFilesAndFilters(filesExcluded);
152: }
153:
154: public TestFilter[] getExcludedDirectories() throws IOException {
155: slowScan();
156: return getFilesAndFilters(dirsExcluded);
157: }
158:
159: public TestFilter[] getNotIncludedFiles() throws IOException {
160: slowScan();
161: return getFilesAndFilters(filesNotIncluded);
162: }
163:
164: public TestFilter[] getNotIncludedDirectories() throws IOException {
165: slowScan();
166: return getFilesAndFilters(dirsNotIncluded);
167: }
168:
169: public void setIncludes(java.lang.String[][] includes) {
170: this .includes = readPatterns(includes);
171: }
172:
173: public void setExcludes(java.lang.String[][] excludes) {
174: this .excludes = readPatterns(excludes);
175: }
176:
177: public void scan() throws IOException {
178: if (basedir == null && basePath == null) {
179: throw new IllegalStateException(
180: "Neither basedir nor basepath are set.");
181: }
182: if (basedir != null) {
183: if (!basedir.exists()) {
184: throw new IllegalStateException("basedir "
185: + basedir.getAbsolutePath() + " does not exist");
186: }
187: if (!basedir.isDirectory()) {
188: throw new IllegalStateException("basedir "
189: + basedir.getAbsolutePath()
190: + " is not a directory");
191: }
192: }
193: if (includes == null) {
194: // No includes supplied, so set it to 'matches all'
195: includes = new String[1][PATTERNS_COUNT];
196: includes[0][FILE_PATTERN] = "**";
197: includes[0][TEST_PATTERN] = null;
198: includes[0][EXPFAIL_PATTERN] = null;
199: }
200: if (excludes == null) {
201: excludes = new String[0][0];
202: }
203:
204: filesIncluded = new Vector();
205: filesNotIncluded = new Vector();
206: filesExcluded = new Vector();
207: dirsIncluded = new Vector();
208: dirsNotIncluded = new Vector();
209: dirsExcluded = new Vector();
210:
211: if (basedir != null) {
212: scandir(basedir, "", true);
213: }
214: if (basePath != null) {
215: StringTokenizer t = new StringTokenizer(basePath,
216: File.pathSeparator);
217: while (t.hasMoreTokens()) {
218: File dir = new File(t.nextToken());
219: if (!dir.exists()) {
220: throw new IllegalStateException("dir "
221: + dir.getAbsolutePath() + " does not exist");
222: }
223: if (!dir.isDirectory()) {
224: throw new IllegalStateException("dir "
225: + dir.getAbsolutePath()
226: + " is not a directory");
227: }
228: scandir(dir, "", true);
229: }
230: }
231: }
232:
233: protected void scandir(File dir, String vpath, boolean fast)
234: throws IOException {
235: String[] newfiles = dir.list(new FilenameFilter() {
236: public boolean accept(File dir, String name) {
237: if (name.endsWith(".class"))
238: return true;
239: if (new File(dir, name).isDirectory())
240: return true;
241: else
242: return false;
243: }
244: });
245:
246: if (newfiles == null) {
247: /*
248: * two reasons are mentioned in the API docs for File.list
249: * (1) dir is not a directory. This is impossible as
250: * we wouldn't get here in this case.
251: * (2) an IO error occurred (why doesn't it throw an exception
252: * then???)
253: */
254: throw new IOException("Error scanning directory "
255: + dir.getAbsolutePath());
256: }
257:
258: for (int i = 0; i < newfiles.length; i++) {
259: String name = vpath + newfiles[i];
260: File file = new File(dir, newfiles[i]);
261: if (file.isDirectory()) {
262: TestFilter tf = new TestFilter(name, null);
263:
264: if (isIncluded(name, tf)) {
265: if (!isExcluded(name, tf)) {
266: dirsIncluded.addElement(tf);
267: if (fast) {
268: scandir(file, name + File.separator, fast);
269: }
270: } else {
271: dirsExcluded.addElement(tf);
272: }
273: } else {
274: dirsNotIncluded.addElement(tf);
275: if (fast && couldHoldIncluded(name)) {
276: scandir(file, name + File.separator, fast);
277: }
278: }
279: if (!fast) {
280: scandir(file, name + File.separator, fast);
281: }
282: } else if (file.isFile()) {
283: Filter f = new Filter();
284: TestFilter tf = new TestFilter(name, f);
285:
286: if (isIncluded(name, tf)) {
287: if (!isExcluded(name, tf)) {
288: filesIncluded.addElement(tf);
289: } else {
290: filesExcluded.addElement(tf);
291: }
292: } else {
293: filesNotIncluded.addElement(tf);
294: }
295: }
296: }
297: }
298:
299: /**
300: * Toplevel invocation for the scan.
301: *
302: * <p>Returns immediately if a slow scan has already been requested.
303: */
304: protected void slowScan() throws IOException {
305: if (haveSlowResults) {
306: return;
307: }
308:
309: String[] excl = new String[dirsExcluded.size()];
310: dirsExcluded.copyInto(excl);
311:
312: String[] notIncl = new String[dirsNotIncluded.size()];
313: dirsNotIncluded.copyInto(notIncl);
314:
315: for (int i = 0; i < excl.length; i++) {
316: scandir(new File(basedir, excl[i]), excl[i]
317: + File.separator, false);
318: }
319:
320: for (int i = 0; i < notIncl.length; i++) {
321: if (!couldHoldIncluded(notIncl[i])) {
322: scandir(new File(basedir, notIncl[i]), notIncl[i]
323: + File.separator, false);
324: }
325: }
326:
327: haveSlowResults = true;
328: }
329:
330: /**
331: * @param name file/directory name being scanned
332: * @param filter the filter for test cases, can be null in case of directory
333: * @return -1 means file is not included, >0 means position in include list
334: */
335: protected boolean isIncluded(String name, TestFilter filter) {
336: LinkedList testPatterns = new LinkedList();
337: int position = -1;
338:
339: for (int i = 0; i < includes.length; i++) {
340: if (PatternUtilities.matchPath(includes[i][FILE_PATTERN],
341: name)) {
342: testPatterns.add(new Filter.IncludeExclude(
343: includes[i][TEST_PATTERN],
344: includes[i][EXPFAIL_PATTERN]));
345: if (position == -1)
346: position = i;
347: }
348: }
349:
350: if (0 < testPatterns.size() && null != filter.getFilter()) {
351: Filter.IncludeExclude[] s = (Filter.IncludeExclude[]) testPatterns
352: .toArray(new Filter.IncludeExclude[] {});
353: filter.getFilter().setIncludes(s);
354: filter.setPosition(position);
355: }
356:
357: return 0 < testPatterns.size();
358: }
359:
360: /**
361: * @param name file/directory name being scanned
362: * @param filter the filter for test cases, can be null in case of directory
363: */
364: protected boolean isExcluded(String name, TestFilter filter) {
365: LinkedList testPatterns = new LinkedList();
366: boolean excluded = false;
367:
368: for (int i = 0; i < excludes.length; i++) {
369: if (PatternUtilities.matchPath(excludes[i][FILE_PATTERN],
370: name)) {
371: if (null == excludes[i][TEST_PATTERN]
372: || 0 == excludes[i][TEST_PATTERN].length()
373: || excludes[i][TEST_PATTERN].equals("*")) {
374: excluded = true;
375: }
376: testPatterns.add(new Filter.IncludeExclude(
377: excludes[i][TEST_PATTERN],
378: excludes[i][EXPFAIL_PATTERN]));
379: }
380: }
381:
382: if (0 < testPatterns.size() && null != filter.getFilter()) {
383: Filter.IncludeExclude[] s = (Filter.IncludeExclude[]) testPatterns
384: .toArray(new Filter.IncludeExclude[] {});
385: filter.getFilter().setExcludes(s);
386: }
387: return excluded;
388: }
389:
390: /**
391: * Tests whether a name matches the start of at least one include pattern.
392: *
393: * @param name the name to match
394: * @return <code>true</code> when the name matches against at least one
395: * include file pattern, <code>false</code> otherwise.
396: */
397: protected boolean couldHoldIncluded(String name) {
398: for (int i = 0; i < includes.length; i++) {
399: if (PatternUtilities.matchPatternStart(
400: includes[i][FILE_PATTERN], name)) {
401: return true;
402: }
403: }
404: return false;
405: }
406:
407: private String[][] readPatterns(String[][] patterns) {
408: String[][] pttrn;
409: if (patterns == null) {
410: pttrn = null;
411: } else {
412: pttrn = new String[patterns.length][PATTERNS_COUNT];
413: for (int i = 0; i < patterns.length; i++) {
414: String pattern;
415: String filter;
416: int pos;
417: pattern = patterns[i][0].replace('/',
418: File.separatorChar).replace('\\',
419: File.separatorChar);
420: if (pattern.endsWith(File.separator)) {
421: pattern += "**";
422: }
423: if ((-1 != (pos = pattern.indexOf(".class")) || -1 != (pos = pattern
424: .indexOf(".java")))
425: && -1 != (pos = pattern.indexOf(
426: File.separatorChar, pos))
427: && !pattern.endsWith("**")) {
428: filter = pattern.substring(pos + 1);
429: pattern = pattern.substring(0, pos);
430: } else {
431: filter = "*";
432: }
433:
434: pttrn[i][FILE_PATTERN] = pattern;
435: pttrn[i][TEST_PATTERN] = filter;
436: pttrn[i][EXPFAIL_PATTERN] = patterns[i][1];
437: }
438: }
439:
440: return pttrn;
441: }
442:
443: private TestFilter[] getFilesAndFilters(Vector v) {
444: TestFilter[] entries = (TestFilter[]) v
445: .toArray(new TestFilter[v.size()]);
446: Arrays.sort(entries);
447: return entries;
448: }
449: }
|