You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

DirectoryScanner.java 33 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 1999 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowlegement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowlegement may appear in the software itself,
  24. * if and wherever such third-party acknowlegements normally appear.
  25. *
  26. * 4. The names "The Jakarta Project", "Ant", and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Group.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. package org.apache.tools.ant;
  55. import java.io.*;
  56. import java.util.*;
  57. /**
  58. * Class for scanning a directory for files/directories that match a certain
  59. * criteria.
  60. * <p>
  61. * These criteria consist of a set of include and exclude patterns. With these
  62. * patterns, you can select which files you want to have included, and which
  63. * files you want to have excluded.
  64. * <p>
  65. * The idea is simple. A given directory is recursively scanned for all files
  66. * and directories. Each file/directory is matched against a set of include
  67. * and exclude patterns. Only files/directories that match at least one
  68. * pattern of the include pattern list, and don't match a pattern of the
  69. * exclude pattern list will be placed in the list of files/directories found.
  70. * <p>
  71. * When no list of include patterns is supplied, "**" will be used, which
  72. * means that everything will be matched. When no list of exclude patterns is
  73. * supplied, an empty list is used, such that nothing will be excluded.
  74. * <p>
  75. * The pattern matching is done as follows:
  76. * The name to be matched is split up in path segments. A path segment is the
  77. * name of a directory or file, which is bounded by
  78. * <code>File.separator</code> ('/' under UNIX, '\' under Windows).
  79. * E.g. "abc/def/ghi/xyz.java" is split up in the segments "abc", "def", "ghi"
  80. * and "xyz.java".
  81. * The same is done for the pattern against which should be matched.
  82. * <p>
  83. * Then the segments of the name and the pattern will be matched against each
  84. * other. When '**' is used for a path segment in the pattern, then it matches
  85. * zero or more path segments of the name.
  86. * <p>
  87. * There are special case regarding the use of <code>File.separator</code>s at
  88. * the beginningof the pattern and the string to match:<br>
  89. * When a pattern starts with a <code>File.separator</code>, the string
  90. * to match must also start with a <code>File.separator</code>.
  91. * When a pattern does not start with a <code>File.separator</code>, the
  92. * string to match may not start with a <code>File.separator</code>.
  93. * When one of these rules is not obeyed, the string will not
  94. * match.
  95. * <p>
  96. * When a name path segment is matched against a pattern path segment, the
  97. * following special characters can be used:
  98. * '*' matches zero or more characters,
  99. * '?' matches one character.
  100. * <p>
  101. * Examples:
  102. * <p>
  103. * "**\*.class" matches all .class files/dirs in a directory tree.
  104. * <p>
  105. * "test\a??.java" matches all files/dirs which start with an 'a', then two
  106. * more characters and then ".java", in a directory called test.
  107. * <p>
  108. * "**" matches everything in a directory tree.
  109. * <p>
  110. * "**\test\**\XYZ*" matches all files/dirs that start with "XYZ" and where
  111. * there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123").
  112. * <p>
  113. * Example of usage:
  114. * <pre>
  115. * String[] includes = {"**\\*.class"};
  116. * String[] excludes = {"modules\\*\\**"};
  117. * ds.setIncludes(includes);
  118. * ds.setExcludes(excludes);
  119. * ds.setBasedir(new File("test"));
  120. * ds.scan();
  121. *
  122. * System.out.println("FILES:");
  123. * String[] files = ds.getIncludedFiles();
  124. * for (int i = 0; i < files.length;i++) {
  125. * System.out.println(files[i]);
  126. * }
  127. * </pre>
  128. * This will scan a directory called test for .class files, but excludes all
  129. * .class files in all directories under a directory called "modules"
  130. *
  131. * @author Arnout J. Kuiper <a href="mailto:ajkuiper@wxs.nl">ajkuiper@wxs.nl</a>
  132. */
  133. public class DirectoryScanner implements FileScanner {
  134. /**
  135. * Patterns that should be excluded by default.
  136. *
  137. * @see #addDefaultExcludes()
  138. */
  139. protected final static String[] DEFAULTEXCLUDES = {
  140. "**/*~",
  141. "**/#*#",
  142. "**/.#*",
  143. "**/%*%",
  144. "**/CVS",
  145. "**/CVS/**",
  146. "**/.cvsignore",
  147. "**/SCCS",
  148. "**/SCCS/**",
  149. "**/vssver.scc"
  150. };
  151. /**
  152. * The base directory which should be scanned.
  153. */
  154. protected File basedir;
  155. /**
  156. * The patterns for the files that should be included.
  157. */
  158. protected String[] includes;
  159. /**
  160. * The patterns for the files that should be excluded.
  161. */
  162. protected String[] excludes;
  163. /**
  164. * The files that where found and matched at least one includes, and matched
  165. * no excludes.
  166. */
  167. protected Vector filesIncluded;
  168. /**
  169. * The files that where found and did not match any includes.
  170. */
  171. protected Vector filesNotIncluded;
  172. /**
  173. * The files that where found and matched at least one includes, and also
  174. * matched at least one excludes.
  175. */
  176. protected Vector filesExcluded;
  177. /**
  178. * The directories that where found and matched at least one includes, and
  179. * matched no excludes.
  180. */
  181. protected Vector dirsIncluded;
  182. /**
  183. * The directories that where found and did not match any includes.
  184. */
  185. protected Vector dirsNotIncluded;
  186. /**
  187. * The files that where found and matched at least one includes, and also
  188. * matched at least one excludes.
  189. */
  190. protected Vector dirsExcluded;
  191. /**
  192. * Have the Vectors holding our results been built by a slow scan?
  193. */
  194. protected boolean haveSlowResults = false;
  195. /**
  196. * Constructor.
  197. */
  198. public DirectoryScanner() {
  199. }
  200. /**
  201. * Does the path match the start of this pattern up to the first "**".
  202. +
  203. * <p>This is not a general purpose test and should only be used if you
  204. * can live with false positives.</p>
  205. *
  206. * <p><code>pattern=**\\a</code> and <code>str=b</code> will yield true.
  207. *
  208. * @param pattern the (non-null) pattern to match against
  209. * @param str the (non-null) string (path) to match
  210. */
  211. protected static boolean matchPatternStart(String pattern, String str) {
  212. // When str starts with a File.separator, pattern has to start with a
  213. // File.separator.
  214. // When pattern starts with a File.separator, str has to start with a
  215. // File.separator.
  216. if (str.startsWith(File.separator) !=
  217. pattern.startsWith(File.separator)) {
  218. return false;
  219. }
  220. Vector patDirs = new Vector();
  221. StringTokenizer st = new StringTokenizer(pattern,File.separator);
  222. while (st.hasMoreTokens()) {
  223. patDirs.addElement(st.nextToken());
  224. }
  225. Vector strDirs = new Vector();
  226. st = new StringTokenizer(str,File.separator);
  227. while (st.hasMoreTokens()) {
  228. strDirs.addElement(st.nextToken());
  229. }
  230. int patIdxStart = 0;
  231. int patIdxEnd = patDirs.size()-1;
  232. int strIdxStart = 0;
  233. int strIdxEnd = strDirs.size()-1;
  234. // up to first '**'
  235. while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
  236. String patDir = (String)patDirs.elementAt(patIdxStart);
  237. if (patDir.equals("**")) {
  238. break;
  239. }
  240. if (!match(patDir,(String)strDirs.elementAt(strIdxStart))) {
  241. return false;
  242. }
  243. patIdxStart++;
  244. strIdxStart++;
  245. }
  246. if (strIdxStart > strIdxEnd) {
  247. // String is exhausted
  248. return true;
  249. } else if (patIdxStart > patIdxEnd) {
  250. // String not exhausted, but pattern is. Failure.
  251. return false;
  252. } else {
  253. // pattern now holds ** while string is not exhausted
  254. // this will generate false positives but we can live with that.
  255. return true;
  256. }
  257. }
  258. /**
  259. * Matches a path against a pattern.
  260. *
  261. * @param pattern the (non-null) pattern to match against
  262. * @param str the (non-null) string (path) to match
  263. *
  264. * @return <code>true</code> when the pattern matches against the string.
  265. * <code>false</code> otherwise.
  266. */
  267. protected static boolean matchPath(String pattern, String str) {
  268. // When str starts with a File.separator, pattern has to start with a
  269. // File.separator.
  270. // When pattern starts with a File.separator, str has to start with a
  271. // File.separator.
  272. if (str.startsWith(File.separator) !=
  273. pattern.startsWith(File.separator)) {
  274. return false;
  275. }
  276. Vector patDirs = new Vector();
  277. StringTokenizer st = new StringTokenizer(pattern,File.separator);
  278. while (st.hasMoreTokens()) {
  279. patDirs.addElement(st.nextToken());
  280. }
  281. Vector strDirs = new Vector();
  282. st = new StringTokenizer(str,File.separator);
  283. while (st.hasMoreTokens()) {
  284. strDirs.addElement(st.nextToken());
  285. }
  286. int patIdxStart = 0;
  287. int patIdxEnd = patDirs.size()-1;
  288. int strIdxStart = 0;
  289. int strIdxEnd = strDirs.size()-1;
  290. // up to first '**'
  291. while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
  292. String patDir = (String)patDirs.elementAt(patIdxStart);
  293. if (patDir.equals("**")) {
  294. break;
  295. }
  296. if (!match(patDir,(String)strDirs.elementAt(strIdxStart))) {
  297. return false;
  298. }
  299. patIdxStart++;
  300. strIdxStart++;
  301. }
  302. if (strIdxStart > strIdxEnd) {
  303. // String is exhausted
  304. for (int i = patIdxStart; i <= patIdxEnd; i++) {
  305. if (!patDirs.elementAt(i).equals("**")) {
  306. return false;
  307. }
  308. }
  309. return true;
  310. } else {
  311. if (patIdxStart > patIdxEnd) {
  312. // String not exhausted, but pattern is. Failure.
  313. return false;
  314. }
  315. }
  316. // up to last '**'
  317. while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
  318. String patDir = (String)patDirs.elementAt(patIdxEnd);
  319. if (patDir.equals("**")) {
  320. break;
  321. }
  322. if (!match(patDir,(String)strDirs.elementAt(strIdxEnd))) {
  323. return false;
  324. }
  325. patIdxEnd--;
  326. strIdxEnd--;
  327. }
  328. if (strIdxStart > strIdxEnd) {
  329. // String is exhausted
  330. for (int i = patIdxStart; i <= patIdxEnd; i++) {
  331. if (!patDirs.elementAt(i).equals("**")) {
  332. return false;
  333. }
  334. }
  335. return true;
  336. }
  337. while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
  338. int patIdxTmp = -1;
  339. for (int i = patIdxStart+1; i <= patIdxEnd; i++) {
  340. if (patDirs.elementAt(i).equals("**")) {
  341. patIdxTmp = i;
  342. break;
  343. }
  344. }
  345. if (patIdxTmp == patIdxStart+1) {
  346. // '**/**' situation, so skip one
  347. patIdxStart++;
  348. continue;
  349. }
  350. // Find the pattern between padIdxStart & padIdxTmp in str between
  351. // strIdxStart & strIdxEnd
  352. int patLength = (patIdxTmp-patIdxStart-1);
  353. int strLength = (strIdxEnd-strIdxStart+1);
  354. int foundIdx = -1;
  355. strLoop:
  356. for (int i = 0; i <= strLength - patLength; i++) {
  357. for (int j = 0; j < patLength; j++) {
  358. String subPat = (String)patDirs.elementAt(patIdxStart+j+1);
  359. String subStr = (String)strDirs.elementAt(strIdxStart+i+j);
  360. if (!match(subPat,subStr)) {
  361. continue strLoop;
  362. }
  363. }
  364. foundIdx = strIdxStart+i;
  365. break;
  366. }
  367. if (foundIdx == -1) {
  368. return false;
  369. }
  370. patIdxStart = patIdxTmp;
  371. strIdxStart = foundIdx+patLength;
  372. }
  373. for (int i = patIdxStart; i <= patIdxEnd; i++) {
  374. if (!patDirs.elementAt(i).equals("**")) {
  375. return false;
  376. }
  377. }
  378. return true;
  379. }
  380. /**
  381. * Matches a string against a pattern. The pattern contains two special
  382. * characters:
  383. * '*' which means zero or more characters,
  384. * '?' which means one and only one character.
  385. *
  386. * @param pattern the (non-null) pattern to match against
  387. * @param str the (non-null) string that must be matched against the
  388. * pattern
  389. *
  390. * @return <code>true</code> when the string matches against the pattern,
  391. * <code>false</code> otherwise.
  392. */
  393. protected static boolean match(String pattern, String str) {
  394. char[] patArr = pattern.toCharArray();
  395. char[] strArr = str.toCharArray();
  396. int patIdxStart = 0;
  397. int patIdxEnd = patArr.length-1;
  398. int strIdxStart = 0;
  399. int strIdxEnd = strArr.length-1;
  400. char ch;
  401. boolean containsStar = false;
  402. for (int i = 0; i < patArr.length; i++) {
  403. if (patArr[i] == '*') {
  404. containsStar = true;
  405. break;
  406. }
  407. }
  408. if (!containsStar) {
  409. // No '*'s, so we make a shortcut
  410. if (patIdxEnd != strIdxEnd) {
  411. return false; // Pattern and string do not have the same size
  412. }
  413. for (int i = 0; i <= patIdxEnd; i++) {
  414. ch = patArr[i];
  415. if (ch != '?' && ch != strArr[i]) {
  416. return false; // Character mismatch
  417. }
  418. }
  419. return true; // String matches against pattern
  420. }
  421. if (patIdxEnd == 0) {
  422. return true; // Pattern contains only '*', which matches anything
  423. }
  424. // Process characters before first star
  425. while((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
  426. if (ch != '?' && ch != strArr[strIdxStart]) {
  427. return false;
  428. }
  429. patIdxStart++;
  430. strIdxStart++;
  431. }
  432. if (strIdxStart > strIdxEnd) {
  433. // All characters in the string are used. Check if only '*'s are
  434. // left in the pattern. If so, we succeeded. Otherwise failure.
  435. for (int i = patIdxStart; i <= patIdxEnd; i++) {
  436. if (patArr[i] != '*') {
  437. return false;
  438. }
  439. }
  440. return true;
  441. }
  442. // Process characters after last star
  443. while((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
  444. if (ch != '?' && ch != strArr[strIdxEnd]) {
  445. return false;
  446. }
  447. patIdxEnd--;
  448. strIdxEnd--;
  449. }
  450. if (strIdxStart > strIdxEnd) {
  451. // All characters in the string are used. Check if only '*'s are
  452. // left in the pattern. If so, we succeeded. Otherwise failure.
  453. for (int i = patIdxStart; i <= patIdxEnd; i++) {
  454. if (patArr[i] != '*') {
  455. return false;
  456. }
  457. }
  458. return true;
  459. }
  460. // process pattern between stars. padIdxStart and patIdxEnd point
  461. // always to a '*'.
  462. while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
  463. int patIdxTmp = -1;
  464. for (int i = patIdxStart+1; i <= patIdxEnd; i++) {
  465. if (patArr[i] == '*') {
  466. patIdxTmp = i;
  467. break;
  468. }
  469. }
  470. if (patIdxTmp == patIdxStart+1) {
  471. // Two stars next to each other, skip the first one.
  472. patIdxStart++;
  473. continue;
  474. }
  475. // Find the pattern between padIdxStart & padIdxTmp in str between
  476. // strIdxStart & strIdxEnd
  477. int patLength = (patIdxTmp-patIdxStart-1);
  478. int strLength = (strIdxEnd-strIdxStart+1);
  479. int foundIdx = -1;
  480. strLoop:
  481. for (int i = 0; i <= strLength - patLength; i++) {
  482. for (int j = 0; j < patLength; j++) {
  483. ch = patArr[patIdxStart+j+1];
  484. if (ch != '?' && ch != strArr[strIdxStart+i+j]) {
  485. continue strLoop;
  486. }
  487. }
  488. foundIdx = strIdxStart+i;
  489. break;
  490. }
  491. if (foundIdx == -1) {
  492. return false;
  493. }
  494. patIdxStart = patIdxTmp;
  495. strIdxStart = foundIdx+patLength;
  496. }
  497. // All characters in the string are used. Check if only '*'s are left
  498. // in the pattern. If so, we succeeded. Otherwise failure.
  499. for (int i = patIdxStart; i <= patIdxEnd; i++) {
  500. if (patArr[i] != '*') {
  501. return false;
  502. }
  503. }
  504. return true;
  505. }
  506. /**
  507. * Sets the basedir for scanning. This is the directory that is scanned
  508. * recursively. All '/' and '\' characters are replaced by
  509. * <code>File.separatorChar</code>. So the separator used need not match
  510. * <code>File.separatorChar</code>.
  511. *
  512. * @param basedir the (non-null) basedir for scanning
  513. */
  514. public void setBasedir(String basedir) {
  515. setBasedir(new File(basedir.replace('/',File.separatorChar).replace('\\',File.separatorChar)));
  516. }
  517. /**
  518. * Sets the basedir for scanning. This is the directory that is scanned
  519. * recursively.
  520. *
  521. * @param basedir the basedir for scanning
  522. */
  523. public void setBasedir(File basedir) {
  524. this.basedir = basedir;
  525. }
  526. /**
  527. * Gets the basedir that is used for scanning. This is the directory that
  528. * is scanned recursively.
  529. *
  530. * @return the basedir that is used for scanning
  531. */
  532. public File getBasedir() {
  533. return basedir;
  534. }
  535. /**
  536. * Sets the set of include patterns to use. All '/' and '\' characters are
  537. * replaced by <code>File.separatorChar</code>. So the separator used need
  538. * not match <code>File.separatorChar</code>.
  539. * <p>
  540. * When a pattern ends with a '/' or '\', "**" is appended.
  541. *
  542. * @param includes list of include patterns
  543. */
  544. public void setIncludes(String[] includes) {
  545. if (includes == null) {
  546. this.includes = null;
  547. } else {
  548. this.includes = new String[includes.length];
  549. for (int i = 0; i < includes.length; i++) {
  550. String pattern;
  551. pattern = includes[i].replace('/',File.separatorChar).replace('\\',File.separatorChar);
  552. if (pattern.endsWith(File.separator)) {
  553. pattern += "**";
  554. }
  555. this.includes[i] = pattern;
  556. }
  557. }
  558. }
  559. /**
  560. * Sets the set of exclude patterns to use. All '/' and '\' characters are
  561. * replaced by <code>File.separatorChar</code>. So the separator used need
  562. * not match <code>File.separatorChar</code>.
  563. * <p>
  564. * When a pattern ends with a '/' or '\', "**" is appended.
  565. *
  566. * @param excludes list of exclude patterns
  567. */
  568. public void setExcludes(String[] excludes) {
  569. if (excludes == null) {
  570. this.excludes = null;
  571. } else {
  572. this.excludes = new String[excludes.length];
  573. for (int i = 0; i < excludes.length; i++) {
  574. String pattern;
  575. pattern = excludes[i].replace('/',File.separatorChar).replace('\\',File.separatorChar);
  576. if (pattern.endsWith(File.separator)) {
  577. pattern += "**";
  578. }
  579. this.excludes[i] = pattern;
  580. }
  581. }
  582. }
  583. /**
  584. * Scans the base directory for files that match at least one include
  585. * pattern, and don't match any exclude patterns.
  586. *
  587. * @exception IllegalStateException when basedir was set incorrecly
  588. */
  589. public void scan() {
  590. if (basedir == null) {
  591. throw new IllegalStateException("No basedir set");
  592. }
  593. if (!basedir.exists()) {
  594. throw new IllegalStateException("basedir " + basedir
  595. + " does not exist");
  596. }
  597. if (!basedir.isDirectory()) {
  598. throw new IllegalStateException("basedir " + basedir
  599. + " is not a directory");
  600. }
  601. if (includes == null) {
  602. // No includes supplied, so set it to 'matches all'
  603. includes = new String[1];
  604. includes[0] = "**";
  605. }
  606. if (excludes == null) {
  607. excludes = new String[0];
  608. }
  609. filesIncluded = new Vector();
  610. filesNotIncluded = new Vector();
  611. filesExcluded = new Vector();
  612. dirsIncluded = new Vector();
  613. dirsNotIncluded = new Vector();
  614. dirsExcluded = new Vector();
  615. if (isIncluded("")) {
  616. if (!isExcluded("")) {
  617. dirsIncluded.addElement("");
  618. } else {
  619. dirsExcluded.addElement("");
  620. }
  621. } else {
  622. dirsNotIncluded.addElement("");
  623. }
  624. scandir(basedir, "", true);
  625. }
  626. /**
  627. * Toplevel invocation for the scan.
  628. *
  629. * <p>Returns immediately if a slow scan has already been requested.
  630. */
  631. protected void slowScan() {
  632. if (haveSlowResults) {
  633. return;
  634. }
  635. String[] excl = new String[dirsExcluded.size()];
  636. dirsExcluded.copyInto(excl);
  637. String[] notIncl = new String[dirsNotIncluded.size()];
  638. dirsNotIncluded.copyInto(notIncl);
  639. for (int i=0; i<excl.length; i++) {
  640. if (!couldHoldIncluded(excl[i])) {
  641. scandir(new File(basedir, excl[i]),
  642. excl[i]+File.separator, false);
  643. }
  644. }
  645. for (int i=0; i<notIncl.length; i++) {
  646. if (!couldHoldIncluded(notIncl[i])) {
  647. scandir(new File(basedir, notIncl[i]),
  648. notIncl[i]+File.separator, false);
  649. }
  650. }
  651. haveSlowResults = true;
  652. }
  653. /**
  654. * Scans the passed dir for files and directories. Found files and
  655. * directories are placed in their respective collections, based on the
  656. * matching of includes and excludes. When a directory is found, it is
  657. * scanned recursively.
  658. *
  659. * @param dir the directory to scan
  660. * @param vpath the path relative to the basedir (needed to prevent
  661. * problems with an absolute path when using dir)
  662. *
  663. * @see #filesIncluded
  664. * @see #filesNotIncluded
  665. * @see #filesExcluded
  666. * @see #dirsIncluded
  667. * @see #dirsNotIncluded
  668. * @see #dirsExcluded
  669. */
  670. protected void scandir(File dir, String vpath, boolean fast) {
  671. String[] newfiles = dir.list();
  672. if (newfiles == null) {
  673. /*
  674. * two reasons are mentioned in the API docs for File.list
  675. * (1) dir is not a directory. This is impossible as
  676. * we wouldn't get here in this case.
  677. * (2) an IO error occurred (why doesn't it throw an exception
  678. * then???)
  679. */
  680. throw new BuildException("IO error scanning directory "
  681. + dir.getAbsolutePath());
  682. }
  683. for (int i = 0; i < newfiles.length; i++) {
  684. String name = vpath+newfiles[i];
  685. File file = new File(dir,newfiles[i]);
  686. if (file.isDirectory()) {
  687. if (isIncluded(name)) {
  688. if (!isExcluded(name)) {
  689. dirsIncluded.addElement(name);
  690. if (fast) {
  691. scandir(file, name+File.separator, fast);
  692. }
  693. } else {
  694. dirsExcluded.addElement(name);
  695. if (fast && couldHoldIncluded(name)) {
  696. scandir(file, name+File.separator, fast);
  697. }
  698. }
  699. } else {
  700. dirsNotIncluded.addElement(name);
  701. if (fast && couldHoldIncluded(name)) {
  702. scandir(file, name+File.separator, fast);
  703. }
  704. }
  705. if (!fast) {
  706. scandir(file, name+File.separator, fast);
  707. }
  708. } else if (file.isFile()) {
  709. if (isIncluded(name)) {
  710. if (!isExcluded(name)) {
  711. filesIncluded.addElement(name);
  712. } else {
  713. filesExcluded.addElement(name);
  714. }
  715. } else {
  716. filesNotIncluded.addElement(name);
  717. }
  718. }
  719. }
  720. }
  721. /**
  722. * Tests whether a name matches against at least one include pattern.
  723. *
  724. * @param name the name to match
  725. * @return <code>true</code> when the name matches against at least one
  726. * include pattern, <code>false</code> otherwise.
  727. */
  728. protected boolean isIncluded(String name) {
  729. for (int i = 0; i < includes.length; i++) {
  730. if (matchPath(includes[i],name)) {
  731. return true;
  732. }
  733. }
  734. return false;
  735. }
  736. /**
  737. * Tests whether a name matches the start of at least one include pattern.
  738. *
  739. * @param name the name to match
  740. * @return <code>true</code> when the name matches against at least one
  741. * include pattern, <code>false</code> otherwise.
  742. */
  743. protected boolean couldHoldIncluded(String name) {
  744. for (int i = 0; i < includes.length; i++) {
  745. if (matchPatternStart(includes[i],name)) {
  746. return true;
  747. }
  748. }
  749. return false;
  750. }
  751. /**
  752. * Tests whether a name matches against at least one exclude pattern.
  753. *
  754. * @param name the name to match
  755. * @return <code>true</code> when the name matches against at least one
  756. * exclude pattern, <code>false</code> otherwise.
  757. */
  758. protected boolean isExcluded(String name) {
  759. for (int i = 0; i < excludes.length; i++) {
  760. if (matchPath(excludes[i],name)) {
  761. return true;
  762. }
  763. }
  764. return false;
  765. }
  766. /**
  767. * Get the names of the files that matched at least one of the include
  768. * patterns, and matched none of the exclude patterns.
  769. * The names are relative to the basedir.
  770. *
  771. * @return the names of the files
  772. */
  773. public String[] getIncludedFiles() {
  774. int count = filesIncluded.size();
  775. String[] files = new String[count];
  776. for (int i = 0; i < count; i++) {
  777. files[i] = (String)filesIncluded.elementAt(i);
  778. }
  779. return files;
  780. }
  781. /**
  782. * Get the names of the files that matched at none of the include patterns.
  783. * The names are relative to the basedir.
  784. *
  785. * @return the names of the files
  786. */
  787. public String[] getNotIncludedFiles() {
  788. slowScan();
  789. int count = filesNotIncluded.size();
  790. String[] files = new String[count];
  791. for (int i = 0; i < count; i++) {
  792. files[i] = (String)filesNotIncluded.elementAt(i);
  793. }
  794. return files;
  795. }
  796. /**
  797. * Get the names of the files that matched at least one of the include
  798. * patterns, an matched also at least one of the exclude patterns.
  799. * The names are relative to the basedir.
  800. *
  801. * @return the names of the files
  802. */
  803. public String[] getExcludedFiles() {
  804. slowScan();
  805. int count = filesExcluded.size();
  806. String[] files = new String[count];
  807. for (int i = 0; i < count; i++) {
  808. files[i] = (String)filesExcluded.elementAt(i);
  809. }
  810. return files;
  811. }
  812. /**
  813. * Get the names of the directories that matched at least one of the include
  814. * patterns, an matched none of the exclude patterns.
  815. * The names are relative to the basedir.
  816. *
  817. * @return the names of the directories
  818. */
  819. public String[] getIncludedDirectories() {
  820. int count = dirsIncluded.size();
  821. String[] directories = new String[count];
  822. for (int i = 0; i < count; i++) {
  823. directories[i] = (String)dirsIncluded.elementAt(i);
  824. }
  825. return directories;
  826. }
  827. /**
  828. * Get the names of the directories that matched at none of the include
  829. * patterns.
  830. * The names are relative to the basedir.
  831. *
  832. * @return the names of the directories
  833. */
  834. public String[] getNotIncludedDirectories() {
  835. slowScan();
  836. int count = dirsNotIncluded.size();
  837. String[] directories = new String[count];
  838. for (int i = 0; i < count; i++) {
  839. directories[i] = (String)dirsNotIncluded.elementAt(i);
  840. }
  841. return directories;
  842. }
  843. /**
  844. * Get the names of the directories that matched at least one of the include
  845. * patterns, an matched also at least one of the exclude patterns.
  846. * The names are relative to the basedir.
  847. *
  848. * @return the names of the directories
  849. */
  850. public String[] getExcludedDirectories() {
  851. slowScan();
  852. int count = dirsExcluded.size();
  853. String[] directories = new String[count];
  854. for (int i = 0; i < count; i++) {
  855. directories[i] = (String)dirsExcluded.elementAt(i);
  856. }
  857. return directories;
  858. }
  859. /**
  860. * Adds the array with default exclusions to the current exclusions set.
  861. *
  862. */
  863. public void addDefaultExcludes() {
  864. int excludesLength = excludes == null ? 0 : excludes.length;
  865. String[] newExcludes;
  866. newExcludes = new String[excludesLength + DEFAULTEXCLUDES.length];
  867. if (excludesLength > 0) {
  868. System.arraycopy(excludes,0,newExcludes,0,excludesLength);
  869. }
  870. for (int i = 0; i < DEFAULTEXCLUDES.length; i++) {
  871. newExcludes[i+excludesLength] = DEFAULTEXCLUDES[i].replace('/',File.separatorChar).replace('\\',File.separatorChar);
  872. }
  873. excludes = newExcludes;
  874. }
  875. }