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 69 kB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. package org.apache.tools.ant;
  19. import java.io.File;
  20. import java.io.IOException;
  21. import java.util.ArrayList;
  22. import java.util.Arrays;
  23. import java.util.HashMap;
  24. import java.util.HashSet;
  25. import java.util.Iterator;
  26. import java.util.LinkedList;
  27. import java.util.Map;
  28. import java.util.Set;
  29. import java.util.Vector;
  30. import org.apache.tools.ant.taskdefs.condition.Os;
  31. import org.apache.tools.ant.types.Resource;
  32. import org.apache.tools.ant.types.ResourceFactory;
  33. import org.apache.tools.ant.types.resources.FileResource;
  34. import org.apache.tools.ant.types.selectors.FileSelector;
  35. import org.apache.tools.ant.types.selectors.SelectorScanner;
  36. import org.apache.tools.ant.types.selectors.SelectorUtils;
  37. import org.apache.tools.ant.types.selectors.TokenizedPath;
  38. import org.apache.tools.ant.types.selectors.TokenizedPattern;
  39. import org.apache.tools.ant.util.CollectionUtils;
  40. import org.apache.tools.ant.util.FileUtils;
  41. import org.apache.tools.ant.util.SymbolicLinkUtils;
  42. import org.apache.tools.ant.util.VectorSet;
  43. /**
  44. * Class for scanning a directory for files/directories which match certain
  45. * criteria.
  46. * <p>
  47. * These criteria consist of selectors and patterns which have been specified.
  48. * With the selectors you can select which files you want to have included.
  49. * Files which are not selected are excluded. With patterns you can include
  50. * or exclude files based on their filename.
  51. * <p>
  52. * The idea is simple. A given directory is recursively scanned for all files
  53. * and directories. Each file/directory is matched against a set of selectors,
  54. * including special support for matching against filenames with include and
  55. * and exclude patterns. Only files/directories which match at least one
  56. * pattern of the include pattern list or other file selector, and don't match
  57. * any pattern of the exclude pattern list or fail to match against a required
  58. * selector will be placed in the list of files/directories found.
  59. * <p>
  60. * When no list of include patterns is supplied, "**" will be used, which
  61. * means that everything will be matched. When no list of exclude patterns is
  62. * supplied, an empty list is used, such that nothing will be excluded. When
  63. * no selectors are supplied, none are applied.
  64. * <p>
  65. * The filename pattern matching is done as follows:
  66. * The name to be matched is split up in path segments. A path segment is the
  67. * name of a directory or file, which is bounded by
  68. * <code>File.separator</code> ('/' under UNIX, '\' under Windows).
  69. * For example, "abc/def/ghi/xyz.java" is split up in the segments "abc",
  70. * "def","ghi" and "xyz.java".
  71. * The same is done for the pattern against which should be matched.
  72. * <p>
  73. * The segments of the name and the pattern are then matched against each
  74. * other. When '**' is used for a path segment in the pattern, it matches
  75. * zero or more path segments of the name.
  76. * <p>
  77. * There is a special case regarding the use of <code>File.separator</code>s
  78. * at the beginning of the pattern and the string to match:<br>
  79. * When a pattern starts with a <code>File.separator</code>, the string
  80. * to match must also start with a <code>File.separator</code>.
  81. * When a pattern does not start with a <code>File.separator</code>, the
  82. * string to match may not start with a <code>File.separator</code>.
  83. * When one of these rules is not obeyed, the string will not
  84. * match.
  85. * <p>
  86. * When a name path segment is matched against a pattern path segment, the
  87. * following special characters can be used:<br>
  88. * '*' matches zero or more characters<br>
  89. * '?' matches one character.
  90. * <p>
  91. * Examples:
  92. * <p>
  93. * "**\*.class" matches all .class files/dirs in a directory tree.
  94. * <p>
  95. * "test\a??.java" matches all files/dirs which start with an 'a', then two
  96. * more characters and then ".java", in a directory called test.
  97. * <p>
  98. * "**" matches everything in a directory tree.
  99. * <p>
  100. * "**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where
  101. * there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123").
  102. * <p>
  103. * Case sensitivity may be turned off if necessary. By default, it is
  104. * turned on.
  105. * <p>
  106. * Example of usage:
  107. * <pre>
  108. * String[] includes = {"**\\*.class"};
  109. * String[] excludes = {"modules\\*\\**"};
  110. * ds.setIncludes(includes);
  111. * ds.setExcludes(excludes);
  112. * ds.setBasedir(new File("test"));
  113. * ds.setCaseSensitive(true);
  114. * ds.scan();
  115. *
  116. * System.out.println("FILES:");
  117. * String[] files = ds.getIncludedFiles();
  118. * for (int i = 0; i < files.length; i++) {
  119. * System.out.println(files[i]);
  120. * }
  121. * </pre>
  122. * This will scan a directory called test for .class files, but excludes all
  123. * files in all proper subdirectories of a directory called "modules"
  124. *
  125. */
  126. public class DirectoryScanner
  127. implements FileScanner, SelectorScanner, ResourceFactory {
  128. /** Is OpenVMS the operating system we're running on? */
  129. private static final boolean ON_VMS = Os.isFamily("openvms");
  130. /**
  131. * Patterns which should be excluded by default.
  132. *
  133. * <p>Note that you can now add patterns to the list of default
  134. * excludes. Added patterns will not become part of this array
  135. * that has only been kept around for backwards compatibility
  136. * reasons.</p>
  137. *
  138. * @deprecated since 1.6.x.
  139. * Use the {@link #getDefaultExcludes getDefaultExcludes}
  140. * method instead.
  141. */
  142. protected static final String[] DEFAULTEXCLUDES = {
  143. // Miscellaneous typical temporary files
  144. SelectorUtils.DEEP_TREE_MATCH + "/*~",
  145. SelectorUtils.DEEP_TREE_MATCH + "/#*#",
  146. SelectorUtils.DEEP_TREE_MATCH + "/.#*",
  147. SelectorUtils.DEEP_TREE_MATCH + "/%*%",
  148. SelectorUtils.DEEP_TREE_MATCH + "/._*",
  149. // CVS
  150. SelectorUtils.DEEP_TREE_MATCH + "/CVS",
  151. SelectorUtils.DEEP_TREE_MATCH + "/CVS/" + SelectorUtils.DEEP_TREE_MATCH,
  152. SelectorUtils.DEEP_TREE_MATCH + "/.cvsignore",
  153. // SCCS
  154. SelectorUtils.DEEP_TREE_MATCH + "/SCCS",
  155. SelectorUtils.DEEP_TREE_MATCH + "/SCCS/" + SelectorUtils.DEEP_TREE_MATCH,
  156. // Visual SourceSafe
  157. SelectorUtils.DEEP_TREE_MATCH + "/vssver.scc",
  158. // Subversion
  159. SelectorUtils.DEEP_TREE_MATCH + "/.svn",
  160. SelectorUtils.DEEP_TREE_MATCH + "/.svn/" + SelectorUtils.DEEP_TREE_MATCH,
  161. // Git
  162. SelectorUtils.DEEP_TREE_MATCH + "/.git",
  163. SelectorUtils.DEEP_TREE_MATCH + "/.git/" + SelectorUtils.DEEP_TREE_MATCH,
  164. SelectorUtils.DEEP_TREE_MATCH + "/.gitattributes",
  165. SelectorUtils.DEEP_TREE_MATCH + "/.gitignore",
  166. SelectorUtils.DEEP_TREE_MATCH + "/.gitmodules",
  167. // Mercurial
  168. SelectorUtils.DEEP_TREE_MATCH + "/.hg",
  169. SelectorUtils.DEEP_TREE_MATCH + "/.hg/" + SelectorUtils.DEEP_TREE_MATCH,
  170. SelectorUtils.DEEP_TREE_MATCH + "/.hgignore",
  171. SelectorUtils.DEEP_TREE_MATCH + "/.hgsub",
  172. SelectorUtils.DEEP_TREE_MATCH + "/.hgsubstate",
  173. SelectorUtils.DEEP_TREE_MATCH + "/.hgtags",
  174. // Bazaar
  175. SelectorUtils.DEEP_TREE_MATCH + "/.bzr",
  176. SelectorUtils.DEEP_TREE_MATCH + "/.bzr/" + SelectorUtils.DEEP_TREE_MATCH,
  177. SelectorUtils.DEEP_TREE_MATCH + "/.bzrignore",
  178. // Mac
  179. SelectorUtils.DEEP_TREE_MATCH + "/.DS_Store"
  180. };
  181. /**
  182. * default value for {@link #maxLevelsOfSymlinks maxLevelsOfSymlinks}
  183. * @since Ant 1.8.0
  184. */
  185. public static final int MAX_LEVELS_OF_SYMLINKS = 5;
  186. /**
  187. * The end of the exception message if something that should be
  188. * there doesn't exist.
  189. */
  190. public static final String DOES_NOT_EXIST_POSTFIX = " does not exist.";
  191. /** Helper. */
  192. private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
  193. /** Helper. */
  194. private static final SymbolicLinkUtils SYMLINK_UTILS =
  195. SymbolicLinkUtils.getSymbolicLinkUtils();
  196. /**
  197. * Patterns which should be excluded by default.
  198. *
  199. * @see #addDefaultExcludes()
  200. */
  201. private static final Set defaultExcludes = new HashSet();
  202. static {
  203. resetDefaultExcludes();
  204. }
  205. // CheckStyle:VisibilityModifier OFF - bc
  206. /** The base directory to be scanned. */
  207. protected File basedir;
  208. /** The patterns for the files to be included. */
  209. protected String[] includes;
  210. /** The patterns for the files to be excluded. */
  211. protected String[] excludes;
  212. /** Selectors that will filter which files are in our candidate list. */
  213. protected FileSelector[] selectors = null;
  214. /**
  215. * The files which matched at least one include and no excludes
  216. * and were selected.
  217. */
  218. protected Vector filesIncluded;
  219. /** The files which did not match any includes or selectors. */
  220. protected Vector filesNotIncluded;
  221. /**
  222. * The files which matched at least one include and at least
  223. * one exclude.
  224. */
  225. protected Vector filesExcluded;
  226. /**
  227. * The directories which matched at least one include and no excludes
  228. * and were selected.
  229. */
  230. protected Vector dirsIncluded;
  231. /** The directories which were found and did not match any includes. */
  232. protected Vector dirsNotIncluded;
  233. /**
  234. * The directories which matched at least one include and at least one
  235. * exclude.
  236. */
  237. protected Vector dirsExcluded;
  238. /**
  239. * The files which matched at least one include and no excludes and
  240. * which a selector discarded.
  241. */
  242. protected Vector filesDeselected;
  243. /**
  244. * The directories which matched at least one include and no excludes
  245. * but which a selector discarded.
  246. */
  247. protected Vector dirsDeselected;
  248. /** Whether or not our results were built by a slow scan. */
  249. protected boolean haveSlowResults = false;
  250. /**
  251. * Whether or not the file system should be treated as a case sensitive
  252. * one.
  253. */
  254. protected boolean isCaseSensitive = true;
  255. /**
  256. * Whether a missing base directory is an error.
  257. * @since Ant 1.7.1
  258. */
  259. protected boolean errorOnMissingDir = true;
  260. /**
  261. * Whether or not symbolic links should be followed.
  262. *
  263. * @since Ant 1.5
  264. */
  265. private boolean followSymlinks = true;
  266. /** Whether or not everything tested so far has been included. */
  267. protected boolean everythingIncluded = true;
  268. // CheckStyle:VisibilityModifier ON
  269. /**
  270. * List of all scanned directories.
  271. *
  272. * @since Ant 1.6
  273. */
  274. private Set scannedDirs = new HashSet();
  275. /**
  276. * Map of all include patterns that are full file names and don't
  277. * contain any wildcards.
  278. *
  279. * <p>Maps pattern string to TokenizedPath.</p>
  280. *
  281. * <p>If this instance is not case sensitive, the file names get
  282. * turned to upper case.</p>
  283. *
  284. * <p>Gets lazily initialized on the first invocation of
  285. * isIncluded or isExcluded and cleared at the end of the scan
  286. * method (cleared in clearCaches, actually).</p>
  287. *
  288. * @since Ant 1.8.0
  289. */
  290. private Map includeNonPatterns = new HashMap();
  291. /**
  292. * Map of all exclude patterns that are full file names and don't
  293. * contain any wildcards.
  294. *
  295. * <p>Maps pattern string to TokenizedPath.</p>
  296. *
  297. * <p>If this instance is not case sensitive, the file names get
  298. * turned to upper case.</p>
  299. *
  300. * <p>Gets lazily initialized on the first invocation of
  301. * isIncluded or isExcluded and cleared at the end of the scan
  302. * method (cleared in clearCaches, actually).</p>
  303. *
  304. * @since Ant 1.8.0
  305. */
  306. private Map excludeNonPatterns = new HashMap();
  307. /**
  308. * Array of all include patterns that contain wildcards.
  309. *
  310. * <p>Gets lazily initialized on the first invocation of
  311. * isIncluded or isExcluded and cleared at the end of the scan
  312. * method (cleared in clearCaches, actually).</p>
  313. */
  314. private TokenizedPattern[] includePatterns;
  315. /**
  316. * Array of all exclude patterns that contain wildcards.
  317. *
  318. * <p>Gets lazily initialized on the first invocation of
  319. * isIncluded or isExcluded and cleared at the end of the scan
  320. * method (cleared in clearCaches, actually).</p>
  321. */
  322. private TokenizedPattern[] excludePatterns;
  323. /**
  324. * Have the non-pattern sets and pattern arrays for in- and
  325. * excludes been initialized?
  326. *
  327. * @since Ant 1.6.3
  328. */
  329. private boolean areNonPatternSetsReady = false;
  330. /**
  331. * Scanning flag.
  332. *
  333. * @since Ant 1.6.3
  334. */
  335. private boolean scanning = false;
  336. /**
  337. * Scanning lock.
  338. *
  339. * @since Ant 1.6.3
  340. */
  341. private Object scanLock = new Object();
  342. /**
  343. * Slow scanning flag.
  344. *
  345. * @since Ant 1.6.3
  346. */
  347. private boolean slowScanning = false;
  348. /**
  349. * Slow scanning lock.
  350. *
  351. * @since Ant 1.6.3
  352. */
  353. private Object slowScanLock = new Object();
  354. /**
  355. * Exception thrown during scan.
  356. *
  357. * @since Ant 1.6.3
  358. */
  359. private IllegalStateException illegal = null;
  360. /**
  361. * The maximum number of times a symbolic link may be followed
  362. * during a scan.
  363. *
  364. * @since Ant 1.8.0
  365. */
  366. private int maxLevelsOfSymlinks = MAX_LEVELS_OF_SYMLINKS;
  367. /**
  368. * Absolute paths of all symlinks that haven't been followed but
  369. * would have been if followsymlinks had been true or
  370. * maxLevelsOfSymlinks had been higher.
  371. *
  372. * @since Ant 1.8.0
  373. */
  374. private Set/*<String>*/ notFollowedSymlinks = new HashSet();
  375. /**
  376. * Sole constructor.
  377. */
  378. public DirectoryScanner() {
  379. }
  380. /**
  381. * Test whether or not a given path matches the start of a given
  382. * pattern up to the first "**".
  383. * <p>
  384. * This is not a general purpose test and should only be used if you
  385. * can live with false positives. For example, <code>pattern=**\a</code>
  386. * and <code>str=b</code> will yield <code>true</code>.
  387. *
  388. * @param pattern The pattern to match against. Must not be
  389. * <code>null</code>.
  390. * @param str The path to match, as a String. Must not be
  391. * <code>null</code>.
  392. *
  393. * @return whether or not a given path matches the start of a given
  394. * pattern up to the first "**".
  395. */
  396. protected static boolean matchPatternStart(String pattern, String str) {
  397. return SelectorUtils.matchPatternStart(pattern, str);
  398. }
  399. /**
  400. * Test whether or not a given path matches the start of a given
  401. * pattern up to the first "**".
  402. * <p>
  403. * This is not a general purpose test and should only be used if you
  404. * can live with false positives. For example, <code>pattern=**\a</code>
  405. * and <code>str=b</code> will yield <code>true</code>.
  406. *
  407. * @param pattern The pattern to match against. Must not be
  408. * <code>null</code>.
  409. * @param str The path to match, as a String. Must not be
  410. * <code>null</code>.
  411. * @param isCaseSensitive Whether or not matching should be performed
  412. * case sensitively.
  413. *
  414. * @return whether or not a given path matches the start of a given
  415. * pattern up to the first "**".
  416. */
  417. protected static boolean matchPatternStart(String pattern, String str,
  418. boolean isCaseSensitive) {
  419. return SelectorUtils.matchPatternStart(pattern, str, isCaseSensitive);
  420. }
  421. /**
  422. * Test whether or not a given path matches a given pattern.
  423. *
  424. * @param pattern The pattern to match against. Must not be
  425. * <code>null</code>.
  426. * @param str The path to match, as a String. Must not be
  427. * <code>null</code>.
  428. *
  429. * @return <code>true</code> if the pattern matches against the string,
  430. * or <code>false</code> otherwise.
  431. */
  432. protected static boolean matchPath(String pattern, String str) {
  433. return SelectorUtils.matchPath(pattern, str);
  434. }
  435. /**
  436. * Test whether or not a given path matches a given pattern.
  437. *
  438. * @param pattern The pattern to match against. Must not be
  439. * <code>null</code>.
  440. * @param str The path to match, as a String. Must not be
  441. * <code>null</code>.
  442. * @param isCaseSensitive Whether or not matching should be performed
  443. * case sensitively.
  444. *
  445. * @return <code>true</code> if the pattern matches against the string,
  446. * or <code>false</code> otherwise.
  447. */
  448. protected static boolean matchPath(String pattern, String str,
  449. boolean isCaseSensitive) {
  450. return SelectorUtils.matchPath(pattern, str, isCaseSensitive);
  451. }
  452. /**
  453. * Test whether or not a string matches against a pattern.
  454. * The pattern may contain two special characters:<br>
  455. * '*' means zero or more characters<br>
  456. * '?' means one and only one character
  457. *
  458. * @param pattern The pattern to match against.
  459. * Must not be <code>null</code>.
  460. * @param str The string which must be matched against the pattern.
  461. * Must not be <code>null</code>.
  462. *
  463. * @return <code>true</code> if the string matches against the pattern,
  464. * or <code>false</code> otherwise.
  465. */
  466. public static boolean match(String pattern, String str) {
  467. return SelectorUtils.match(pattern, str);
  468. }
  469. /**
  470. * Test whether or not a string matches against a pattern.
  471. * The pattern may contain two special characters:<br>
  472. * '*' means zero or more characters<br>
  473. * '?' means one and only one character
  474. *
  475. * @param pattern The pattern to match against.
  476. * Must not be <code>null</code>.
  477. * @param str The string which must be matched against the pattern.
  478. * Must not be <code>null</code>.
  479. * @param isCaseSensitive Whether or not matching should be performed
  480. * case sensitively.
  481. *
  482. *
  483. * @return <code>true</code> if the string matches against the pattern,
  484. * or <code>false</code> otherwise.
  485. */
  486. protected static boolean match(String pattern, String str,
  487. boolean isCaseSensitive) {
  488. return SelectorUtils.match(pattern, str, isCaseSensitive);
  489. }
  490. /**
  491. * Get the list of patterns that should be excluded by default.
  492. *
  493. * @return An array of <code>String</code> based on the current
  494. * contents of the <code>defaultExcludes</code>
  495. * <code>Set</code>.
  496. *
  497. * @since Ant 1.6
  498. */
  499. public static String[] getDefaultExcludes() {
  500. synchronized (defaultExcludes) {
  501. return (String[]) defaultExcludes.toArray(new String[defaultExcludes
  502. .size()]);
  503. }
  504. }
  505. /**
  506. * Add a pattern to the default excludes unless it is already a
  507. * default exclude.
  508. *
  509. * @param s A string to add as an exclude pattern.
  510. * @return <code>true</code> if the string was added;
  511. * <code>false</code> if it already existed.
  512. *
  513. * @since Ant 1.6
  514. */
  515. public static boolean addDefaultExclude(String s) {
  516. synchronized (defaultExcludes) {
  517. return defaultExcludes.add(s);
  518. }
  519. }
  520. /**
  521. * Remove a string if it is a default exclude.
  522. *
  523. * @param s The string to attempt to remove.
  524. * @return <code>true</code> if <code>s</code> was a default
  525. * exclude (and thus was removed);
  526. * <code>false</code> if <code>s</code> was not
  527. * in the default excludes list to begin with.
  528. *
  529. * @since Ant 1.6
  530. */
  531. public static boolean removeDefaultExclude(String s) {
  532. synchronized (defaultExcludes) {
  533. return defaultExcludes.remove(s);
  534. }
  535. }
  536. /**
  537. * Go back to the hardwired default exclude patterns.
  538. *
  539. * @since Ant 1.6
  540. */
  541. public static void resetDefaultExcludes() {
  542. synchronized (defaultExcludes) {
  543. defaultExcludes.clear();
  544. for (int i = 0; i < DEFAULTEXCLUDES.length; i++) {
  545. defaultExcludes.add(DEFAULTEXCLUDES[i]);
  546. }
  547. }
  548. }
  549. /**
  550. * Set the base directory to be scanned. This is the directory which is
  551. * scanned recursively. All '/' and '\' characters are replaced by
  552. * <code>File.separatorChar</code>, so the separator used need not match
  553. * <code>File.separatorChar</code>.
  554. *
  555. * @param basedir The base directory to scan.
  556. */
  557. public void setBasedir(String basedir) {
  558. setBasedir(basedir == null ? (File) null
  559. : new File(basedir.replace('/', File.separatorChar).replace(
  560. '\\', File.separatorChar)));
  561. }
  562. /**
  563. * Set the base directory to be scanned. This is the directory which is
  564. * scanned recursively.
  565. *
  566. * @param basedir The base directory for scanning.
  567. */
  568. public synchronized void setBasedir(File basedir) {
  569. this.basedir = basedir;
  570. }
  571. /**
  572. * Return the base directory to be scanned.
  573. * This is the directory which is scanned recursively.
  574. *
  575. * @return the base directory to be scanned.
  576. */
  577. public synchronized File getBasedir() {
  578. return basedir;
  579. }
  580. /**
  581. * Find out whether include exclude patterns are matched in a
  582. * case sensitive way.
  583. * @return whether or not the scanning is case sensitive.
  584. * @since Ant 1.6
  585. */
  586. public synchronized boolean isCaseSensitive() {
  587. return isCaseSensitive;
  588. }
  589. /**
  590. * Set whether or not include and exclude patterns are matched
  591. * in a case sensitive way.
  592. *
  593. * @param isCaseSensitive whether or not the file system should be
  594. * regarded as a case sensitive one.
  595. */
  596. public synchronized void setCaseSensitive(boolean isCaseSensitive) {
  597. this.isCaseSensitive = isCaseSensitive;
  598. }
  599. /**
  600. * Sets whether or not a missing base directory is an error
  601. *
  602. * @param errorOnMissingDir whether or not a missing base directory
  603. * is an error
  604. * @since Ant 1.7.1
  605. */
  606. public void setErrorOnMissingDir(boolean errorOnMissingDir) {
  607. this.errorOnMissingDir = errorOnMissingDir;
  608. }
  609. /**
  610. * Get whether or not a DirectoryScanner follows symbolic links.
  611. *
  612. * @return flag indicating whether symbolic links should be followed.
  613. *
  614. * @since Ant 1.6
  615. */
  616. public synchronized boolean isFollowSymlinks() {
  617. return followSymlinks;
  618. }
  619. /**
  620. * Set whether or not symbolic links should be followed.
  621. *
  622. * @param followSymlinks whether or not symbolic links should be followed.
  623. */
  624. public synchronized void setFollowSymlinks(boolean followSymlinks) {
  625. this.followSymlinks = followSymlinks;
  626. }
  627. /**
  628. * The maximum number of times a symbolic link may be followed
  629. * during a scan.
  630. *
  631. * @since Ant 1.8.0
  632. */
  633. public void setMaxLevelsOfSymlinks(int max) {
  634. maxLevelsOfSymlinks = max;
  635. }
  636. /**
  637. * Set the list of include patterns to use. All '/' and '\' characters
  638. * are replaced by <code>File.separatorChar</code>, so the separator used
  639. * need not match <code>File.separatorChar</code>.
  640. * <p>
  641. * When a pattern ends with a '/' or '\', "**" is appended.
  642. *
  643. * @param includes A list of include patterns.
  644. * May be <code>null</code>, indicating that all files
  645. * should be included. If a non-<code>null</code>
  646. * list is given, all elements must be
  647. * non-<code>null</code>.
  648. */
  649. public synchronized void setIncludes(String[] includes) {
  650. if (includes == null) {
  651. this.includes = null;
  652. } else {
  653. this.includes = new String[includes.length];
  654. for (int i = 0; i < includes.length; i++) {
  655. this.includes[i] = normalizePattern(includes[i]);
  656. }
  657. }
  658. }
  659. /**
  660. * Set the list of exclude patterns to use. All '/' and '\' characters
  661. * are replaced by <code>File.separatorChar</code>, so the separator used
  662. * need not match <code>File.separatorChar</code>.
  663. * <p>
  664. * When a pattern ends with a '/' or '\', "**" is appended.
  665. *
  666. * @param excludes A list of exclude patterns.
  667. * May be <code>null</code>, indicating that no files
  668. * should be excluded. If a non-<code>null</code> list is
  669. * given, all elements must be non-<code>null</code>.
  670. */
  671. public synchronized void setExcludes(String[] excludes) {
  672. if (excludes == null) {
  673. this.excludes = null;
  674. } else {
  675. this.excludes = new String[excludes.length];
  676. for (int i = 0; i < excludes.length; i++) {
  677. this.excludes[i] = normalizePattern(excludes[i]);
  678. }
  679. }
  680. }
  681. /**
  682. * Add to the list of exclude patterns to use. All '/' and '\'
  683. * characters are replaced by <code>File.separatorChar</code>, so
  684. * the separator used need not match <code>File.separatorChar</code>.
  685. * <p>
  686. * When a pattern ends with a '/' or '\', "**" is appended.
  687. *
  688. * @param excludes A list of exclude patterns.
  689. * May be <code>null</code>, in which case the
  690. * exclude patterns don't get changed at all.
  691. *
  692. * @since Ant 1.6.3
  693. */
  694. public synchronized void addExcludes(String[] excludes) {
  695. if (excludes != null && excludes.length > 0) {
  696. if (this.excludes != null && this.excludes.length > 0) {
  697. String[] tmp = new String[excludes.length
  698. + this.excludes.length];
  699. System.arraycopy(this.excludes, 0, tmp, 0,
  700. this.excludes.length);
  701. for (int i = 0; i < excludes.length; i++) {
  702. tmp[this.excludes.length + i] =
  703. normalizePattern(excludes[i]);
  704. }
  705. this.excludes = tmp;
  706. } else {
  707. setExcludes(excludes);
  708. }
  709. }
  710. }
  711. /**
  712. * All '/' and '\' characters are replaced by
  713. * <code>File.separatorChar</code>, so the separator used need not
  714. * match <code>File.separatorChar</code>.
  715. *
  716. * <p> When a pattern ends with a '/' or '\', "**" is appended.
  717. *
  718. * @since Ant 1.6.3
  719. */
  720. private static String normalizePattern(String p) {
  721. String pattern = p.replace('/', File.separatorChar)
  722. .replace('\\', File.separatorChar);
  723. if (pattern.endsWith(File.separator)) {
  724. pattern += SelectorUtils.DEEP_TREE_MATCH;
  725. }
  726. return pattern;
  727. }
  728. /**
  729. * Set the selectors that will select the filelist.
  730. *
  731. * @param selectors specifies the selectors to be invoked on a scan.
  732. */
  733. public synchronized void setSelectors(FileSelector[] selectors) {
  734. this.selectors = selectors;
  735. }
  736. /**
  737. * Return whether or not the scanner has included all the files or
  738. * directories it has come across so far.
  739. *
  740. * @return <code>true</code> if all files and directories which have
  741. * been found so far have been included.
  742. */
  743. public synchronized boolean isEverythingIncluded() {
  744. return everythingIncluded;
  745. }
  746. /**
  747. * Scan for files which match at least one include pattern and don't match
  748. * any exclude patterns. If there are selectors then the files must pass
  749. * muster there, as well. Scans under basedir, if set; otherwise the
  750. * include patterns without leading wildcards specify the absolute paths of
  751. * the files that may be included.
  752. *
  753. * @exception IllegalStateException if the base directory was set
  754. * incorrectly (i.e. if it doesn't exist or isn't a directory).
  755. */
  756. public void scan() throws IllegalStateException {
  757. synchronized (scanLock) {
  758. if (scanning) {
  759. while (scanning) {
  760. try {
  761. scanLock.wait();
  762. } catch (InterruptedException e) {
  763. continue;
  764. }
  765. }
  766. if (illegal != null) {
  767. throw illegal;
  768. }
  769. return;
  770. }
  771. scanning = true;
  772. }
  773. File savedBase = basedir;
  774. try {
  775. synchronized (this) {
  776. illegal = null;
  777. clearResults();
  778. // set in/excludes to reasonable defaults if needed:
  779. boolean nullIncludes = (includes == null);
  780. includes = nullIncludes
  781. ? new String[] {SelectorUtils.DEEP_TREE_MATCH} : includes;
  782. boolean nullExcludes = (excludes == null);
  783. excludes = nullExcludes ? new String[0] : excludes;
  784. if (basedir != null && !followSymlinks
  785. && SYMLINK_UTILS.isSymbolicLink(basedir)) {
  786. notFollowedSymlinks.add(basedir.getAbsolutePath());
  787. basedir = null;
  788. }
  789. if (basedir == null) {
  790. // if no basedir and no includes, nothing to do:
  791. if (nullIncludes) {
  792. return;
  793. }
  794. } else {
  795. if (!basedir.exists()) {
  796. if (errorOnMissingDir) {
  797. illegal = new IllegalStateException("basedir "
  798. + basedir
  799. + DOES_NOT_EXIST_POSTFIX);
  800. } else {
  801. // Nothing to do - basedir does not exist
  802. return;
  803. }
  804. } else if (!basedir.isDirectory()) {
  805. illegal = new IllegalStateException("basedir "
  806. + basedir
  807. + " is not a"
  808. + " directory.");
  809. }
  810. if (illegal != null) {
  811. throw illegal;
  812. }
  813. }
  814. if (isIncluded(TokenizedPath.EMPTY_PATH)) {
  815. if (!isExcluded(TokenizedPath.EMPTY_PATH)) {
  816. if (isSelected("", basedir)) {
  817. dirsIncluded.addElement("");
  818. } else {
  819. dirsDeselected.addElement("");
  820. }
  821. } else {
  822. dirsExcluded.addElement("");
  823. }
  824. } else {
  825. dirsNotIncluded.addElement("");
  826. }
  827. checkIncludePatterns();
  828. clearCaches();
  829. includes = nullIncludes ? null : includes;
  830. excludes = nullExcludes ? null : excludes;
  831. }
  832. } catch (IOException ex) {
  833. throw new BuildException(ex);
  834. } finally {
  835. basedir = savedBase;
  836. synchronized (scanLock) {
  837. scanning = false;
  838. scanLock.notifyAll();
  839. }
  840. }
  841. }
  842. /**
  843. * This routine is actually checking all the include patterns in
  844. * order to avoid scanning everything under base dir.
  845. * @since Ant 1.6
  846. */
  847. private void checkIncludePatterns() {
  848. ensureNonPatternSetsReady();
  849. Map newroots = new HashMap();
  850. // put in the newroots map the include patterns without
  851. // wildcard tokens
  852. for (int i = 0; i < includePatterns.length; i++) {
  853. String pattern = includePatterns[i].toString();
  854. if (!shouldSkipPattern(pattern)) {
  855. newroots.put(includePatterns[i].rtrimWildcardTokens(),
  856. pattern);
  857. }
  858. }
  859. for (Iterator iter = includeNonPatterns.entrySet().iterator();
  860. iter.hasNext(); ) {
  861. Map.Entry entry = (Map.Entry) iter.next();
  862. String pattern = (String) entry.getKey();
  863. if (!shouldSkipPattern(pattern)) {
  864. newroots.put((TokenizedPath) entry.getValue(), pattern);
  865. }
  866. }
  867. if (newroots.containsKey(TokenizedPath.EMPTY_PATH)
  868. && basedir != null) {
  869. // we are going to scan everything anyway
  870. scandir(basedir, "", true);
  871. } else {
  872. // only scan directories that can include matched files or
  873. // directories
  874. Iterator it = newroots.entrySet().iterator();
  875. File canonBase = null;
  876. if (basedir != null) {
  877. try {
  878. canonBase = basedir.getCanonicalFile();
  879. } catch (IOException ex) {
  880. throw new BuildException(ex);
  881. }
  882. }
  883. while (it.hasNext()) {
  884. Map.Entry entry = (Map.Entry) it.next();
  885. TokenizedPath currentPath = (TokenizedPath) entry.getKey();
  886. String currentelement = currentPath.toString();
  887. if (basedir == null
  888. && !FileUtils.isAbsolutePath(currentelement)) {
  889. continue;
  890. }
  891. File myfile = new File(basedir, currentelement);
  892. if (myfile.exists()) {
  893. // may be on a case insensitive file system. We want
  894. // the results to show what's really on the disk, so
  895. // we need to double check.
  896. try {
  897. String path = (basedir == null)
  898. ? myfile.getCanonicalPath()
  899. : FILE_UTILS.removeLeadingPath(canonBase,
  900. myfile.getCanonicalFile());
  901. if (!path.equals(currentelement) || ON_VMS) {
  902. myfile = currentPath.findFile(basedir, true);
  903. if (myfile != null && basedir != null) {
  904. currentelement = FILE_UTILS.removeLeadingPath(
  905. basedir, myfile);
  906. if (!currentPath.toString()
  907. .equals(currentelement)) {
  908. currentPath =
  909. new TokenizedPath(currentelement);
  910. }
  911. }
  912. }
  913. } catch (IOException ex) {
  914. throw new BuildException(ex);
  915. }
  916. }
  917. if ((myfile == null || !myfile.exists()) && !isCaseSensitive()) {
  918. File f = currentPath.findFile(basedir, false);
  919. if (f != null && f.exists()) {
  920. // adapt currentelement to the case we've
  921. // actually found
  922. currentelement = (basedir == null)
  923. ? f.getAbsolutePath()
  924. : FILE_UTILS.removeLeadingPath(basedir, f);
  925. myfile = f;
  926. currentPath = new TokenizedPath(currentelement);
  927. }
  928. }
  929. if (myfile != null && myfile.exists()) {
  930. if (!followSymlinks && currentPath.isSymlink(basedir)) {
  931. if (!isExcluded(currentPath)) {
  932. notFollowedSymlinks.add(myfile.getAbsolutePath());
  933. }
  934. continue;
  935. }
  936. if (myfile.isDirectory()) {
  937. if (isIncluded(currentPath)
  938. && currentelement.length() > 0) {
  939. accountForIncludedDir(currentPath, myfile, true);
  940. } else {
  941. scandir(myfile, currentPath, true);
  942. }
  943. } else {
  944. String originalpattern = (String) entry.getValue();
  945. boolean included = isCaseSensitive()
  946. ? originalpattern.equals(currentelement)
  947. : originalpattern.equalsIgnoreCase(currentelement);
  948. if (included) {
  949. accountForIncludedFile(currentPath, myfile);
  950. }
  951. }
  952. }
  953. }
  954. }
  955. }
  956. /**
  957. * true if the pattern specifies a relative path without basedir
  958. * or an absolute path not inside basedir.
  959. *
  960. * @since Ant 1.8.0
  961. */
  962. private boolean shouldSkipPattern(String pattern) {
  963. if (FileUtils.isAbsolutePath(pattern)) {
  964. //skip abs. paths not under basedir, if set:
  965. if (basedir != null
  966. && !SelectorUtils.matchPatternStart(pattern,
  967. basedir.getAbsolutePath(),
  968. isCaseSensitive())) {
  969. return true;
  970. }
  971. } else if (basedir == null) {
  972. //skip non-abs. paths if basedir == null:
  973. return true;
  974. }
  975. return false;
  976. }
  977. /**
  978. * Clear the result caches for a scan.
  979. */
  980. protected synchronized void clearResults() {
  981. filesIncluded = new VectorSet();
  982. filesNotIncluded = new VectorSet();
  983. filesExcluded = new VectorSet();
  984. filesDeselected = new VectorSet();
  985. dirsIncluded = new VectorSet();
  986. dirsNotIncluded = new VectorSet();
  987. dirsExcluded = new VectorSet();
  988. dirsDeselected = new VectorSet();
  989. everythingIncluded = (basedir != null);
  990. scannedDirs.clear();
  991. notFollowedSymlinks.clear();
  992. }
  993. /**
  994. * Top level invocation for a slow scan. A slow scan builds up a full
  995. * list of excluded/included files/directories, whereas a fast scan
  996. * will only have full results for included files, as it ignores
  997. * directories which can't possibly hold any included files/directories.
  998. * <p>
  999. * Returns immediately if a slow scan has already been completed.
  1000. */
  1001. protected void slowScan() {
  1002. synchronized (slowScanLock) {
  1003. if (haveSlowResults) {
  1004. return;
  1005. }
  1006. if (slowScanning) {
  1007. while (slowScanning) {
  1008. try {
  1009. slowScanLock.wait();
  1010. } catch (InterruptedException e) {
  1011. // Empty
  1012. }
  1013. }
  1014. return;
  1015. }
  1016. slowScanning = true;
  1017. }
  1018. try {
  1019. synchronized (this) {
  1020. // set in/excludes to reasonable defaults if needed:
  1021. boolean nullIncludes = (includes == null);
  1022. includes = nullIncludes
  1023. ? new String[] {SelectorUtils.DEEP_TREE_MATCH} : includes;
  1024. boolean nullExcludes = (excludes == null);
  1025. excludes = nullExcludes ? new String[0] : excludes;
  1026. String[] excl = new String[dirsExcluded.size()];
  1027. dirsExcluded.copyInto(excl);
  1028. String[] notIncl = new String[dirsNotIncluded.size()];
  1029. dirsNotIncluded.copyInto(notIncl);
  1030. ensureNonPatternSetsReady();
  1031. processSlowScan(excl);
  1032. processSlowScan(notIncl);
  1033. clearCaches();
  1034. includes = nullIncludes ? null : includes;
  1035. excludes = nullExcludes ? null : excludes;
  1036. }
  1037. } finally {
  1038. synchronized (slowScanLock) {
  1039. haveSlowResults = true;
  1040. slowScanning = false;
  1041. slowScanLock.notifyAll();
  1042. }
  1043. }
  1044. }
  1045. private void processSlowScan(String[] arr) {
  1046. for (int i = 0; i < arr.length; i++) {
  1047. TokenizedPath path = new TokenizedPath(arr[i]);
  1048. if (!couldHoldIncluded(path) || contentsExcluded(path)) {
  1049. scandir(new File(basedir, arr[i]), path, false);
  1050. }
  1051. }
  1052. }
  1053. /**
  1054. * Scan the given directory for files and directories. Found files and
  1055. * directories are placed in their respective collections, based on the
  1056. * matching of includes, excludes, and the selectors. When a directory
  1057. * is found, it is scanned recursively.
  1058. *
  1059. * @param dir The directory to scan. Must not be <code>null</code>.
  1060. * @param vpath The path relative to the base directory (needed to
  1061. * prevent problems with an absolute path when using
  1062. * dir). Must not be <code>null</code>.
  1063. * @param fast Whether or not this call is part of a fast scan.
  1064. *
  1065. * @see #filesIncluded
  1066. * @see #filesNotIncluded
  1067. * @see #filesExcluded
  1068. * @see #dirsIncluded
  1069. * @see #dirsNotIncluded
  1070. * @see #dirsExcluded
  1071. * @see #slowScan
  1072. */
  1073. protected void scandir(File dir, String vpath, boolean fast) {
  1074. scandir(dir, new TokenizedPath(vpath), fast);
  1075. }
  1076. /**
  1077. * Scan the given directory for files and directories. Found files and
  1078. * directories are placed in their respective collections, based on the
  1079. * matching of includes, excludes, and the selectors. When a directory
  1080. * is found, it is scanned recursively.
  1081. *
  1082. * @param dir The directory to scan. Must not be <code>null</code>.
  1083. * @param path The path relative to the base directory (needed to
  1084. * prevent problems with an absolute path when using
  1085. * dir). Must not be <code>null</code>.
  1086. * @param fast Whether or not this call is part of a fast scan.
  1087. *
  1088. * @see #filesIncluded
  1089. * @see #filesNotIncluded
  1090. * @see #filesExcluded
  1091. * @see #dirsIncluded
  1092. * @see #dirsNotIncluded
  1093. * @see #dirsExcluded
  1094. * @see #slowScan
  1095. */
  1096. private void scandir(File dir, TokenizedPath path, boolean fast) {
  1097. if (dir == null) {
  1098. throw new BuildException("dir must not be null.");
  1099. }
  1100. String[] newfiles = dir.list();
  1101. if (newfiles == null) {
  1102. if (!dir.exists()) {
  1103. throw new BuildException(dir + DOES_NOT_EXIST_POSTFIX);
  1104. } else if (!dir.isDirectory()) {
  1105. throw new BuildException(dir + " is not a directory.");
  1106. } else {
  1107. throw new BuildException("IO error scanning directory '"
  1108. + dir.getAbsolutePath() + "'");
  1109. }
  1110. }
  1111. scandir(dir, path, fast, newfiles, new LinkedList());
  1112. }
  1113. private void scandir(File dir, TokenizedPath path, boolean fast,
  1114. String[] newfiles, LinkedList directoryNamesFollowed) {
  1115. String vpath = path.toString();
  1116. if (vpath.length() > 0 && !vpath.endsWith(File.separator)) {
  1117. vpath += File.separator;
  1118. }
  1119. // avoid double scanning of directories, can only happen in fast mode
  1120. if (fast && hasBeenScanned(vpath)) {
  1121. return;
  1122. }
  1123. if (!followSymlinks) {
  1124. ArrayList noLinks = new ArrayList();
  1125. for (int i = 0; i < newfiles.length; i++) {
  1126. try {
  1127. if (SYMLINK_UTILS.isSymbolicLink(dir, newfiles[i])) {
  1128. String name = vpath + newfiles[i];
  1129. File file = new File(dir, newfiles[i]);
  1130. (file.isDirectory()
  1131. ? dirsExcluded : filesExcluded).addElement(name);
  1132. if (!isExcluded(name)) {
  1133. notFollowedSymlinks.add(file.getAbsolutePath());
  1134. }
  1135. } else {
  1136. noLinks.add(newfiles[i]);
  1137. }
  1138. } catch (IOException ioe) {
  1139. String msg = "IOException caught while checking "
  1140. + "for links, couldn't get canonical path!";
  1141. // will be caught and redirected to Ant's logging system
  1142. System.err.println(msg);
  1143. noLinks.add(newfiles[i]);
  1144. }
  1145. }
  1146. newfiles = (String[]) (noLinks.toArray(new String[noLinks.size()]));
  1147. } else {
  1148. directoryNamesFollowed.addFirst(dir.getName());
  1149. }
  1150. for (int i = 0; i < newfiles.length; i++) {
  1151. String name = vpath + newfiles[i];
  1152. TokenizedPath newPath = new TokenizedPath(path, newfiles[i]);
  1153. File file = new File(dir, newfiles[i]);
  1154. String[] children = file.list();
  1155. if (children == null || (children.length == 0 && file.isFile())) {
  1156. if (isIncluded(newPath)) {
  1157. accountForIncludedFile(newPath, file);
  1158. } else {
  1159. everythingIncluded = false;
  1160. filesNotIncluded.addElement(name);
  1161. }
  1162. } else { // dir
  1163. if (followSymlinks
  1164. && causesIllegalSymlinkLoop(newfiles[i], dir,
  1165. directoryNamesFollowed)) {
  1166. // will be caught and redirected to Ant's logging system
  1167. System.err.println("skipping symbolic link "
  1168. + file.getAbsolutePath()
  1169. + " -- too many levels of symbolic"
  1170. + " links.");
  1171. notFollowedSymlinks.add(file.getAbsolutePath());
  1172. continue;
  1173. }
  1174. if (isIncluded(newPath)) {
  1175. accountForIncludedDir(newPath, file, fast, children,
  1176. directoryNamesFollowed);
  1177. } else {
  1178. everythingIncluded = false;
  1179. dirsNotIncluded.addElement(name);
  1180. if (fast && couldHoldIncluded(newPath)
  1181. && !contentsExcluded(newPath)) {
  1182. scandir(file, newPath, fast, children,
  1183. directoryNamesFollowed);
  1184. }
  1185. }
  1186. if (!fast) {
  1187. scandir(file, newPath, fast, children, directoryNamesFollowed);
  1188. }
  1189. }
  1190. }
  1191. if (followSymlinks) {
  1192. directoryNamesFollowed.removeFirst();
  1193. }
  1194. }
  1195. /**
  1196. * Process included file.
  1197. * @param name path of the file relative to the directory of the FileSet.
  1198. * @param file included File.
  1199. */
  1200. private void accountForIncludedFile(TokenizedPath name, File file) {
  1201. processIncluded(name, file, filesIncluded, filesExcluded,
  1202. filesDeselected);
  1203. }
  1204. /**
  1205. * Process included directory.
  1206. * @param name path of the directory relative to the directory of
  1207. * the FileSet.
  1208. * @param file directory as File.
  1209. * @param fast whether to perform fast scans.
  1210. */
  1211. private void accountForIncludedDir(TokenizedPath name, File file,
  1212. boolean fast) {
  1213. processIncluded(name, file, dirsIncluded, dirsExcluded, dirsDeselected);
  1214. if (fast && couldHoldIncluded(name) && !contentsExcluded(name)) {
  1215. scandir(file, name, fast);
  1216. }
  1217. }
  1218. private void accountForIncludedDir(TokenizedPath name,
  1219. File file, boolean fast,
  1220. String[] children,
  1221. LinkedList directoryNamesFollowed) {
  1222. processIncluded(name, file, dirsIncluded, dirsExcluded, dirsDeselected);
  1223. if (fast && couldHoldIncluded(name) && !contentsExcluded(name)) {
  1224. scandir(file, name, fast, children, directoryNamesFollowed);
  1225. }
  1226. }
  1227. private void processIncluded(TokenizedPath path,
  1228. File file, Vector inc, Vector exc,
  1229. Vector des) {
  1230. String name = path.toString();
  1231. if (inc.contains(name) || exc.contains(name) || des.contains(name)) {
  1232. return;
  1233. }
  1234. boolean included = false;
  1235. if (isExcluded(path)) {
  1236. exc.add(name);
  1237. } else if (isSelected(name, file)) {
  1238. included = true;
  1239. inc.add(name);
  1240. } else {
  1241. des.add(name);
  1242. }
  1243. everythingIncluded &= included;
  1244. }
  1245. /**
  1246. * Test whether or not a name matches against at least one include
  1247. * pattern.
  1248. *
  1249. * @param name The name to match. Must not be <code>null</code>.
  1250. * @return <code>true</code> when the name matches against at least one
  1251. * include pattern, or <code>false</code> otherwise.
  1252. */
  1253. protected boolean isIncluded(String name) {
  1254. return isIncluded(new TokenizedPath(name));
  1255. }
  1256. /**
  1257. * Test whether or not a name matches against at least one include
  1258. * pattern.
  1259. *
  1260. * @param name The name to match. Must not be <code>null</code>.
  1261. * @return <code>true</code> when the name matches against at least one
  1262. * include pattern, or <code>false</code> otherwise.
  1263. */
  1264. private boolean isIncluded(TokenizedPath path) {
  1265. ensureNonPatternSetsReady();
  1266. if (isCaseSensitive()
  1267. ? includeNonPatterns.containsKey(path.toString())
  1268. : includeNonPatterns.containsKey(path.toString().toUpperCase())) {
  1269. return true;
  1270. }
  1271. for (int i = 0; i < includePatterns.length; i++) {
  1272. if (includePatterns[i].matchPath(path, isCaseSensitive())) {
  1273. return true;
  1274. }
  1275. }
  1276. return false;
  1277. }
  1278. /**
  1279. * Test whether or not a name matches the start of at least one include
  1280. * pattern.
  1281. *
  1282. * @param name The name to match. Must not be <code>null</code>.
  1283. * @return <code>true</code> when the name matches against the start of at
  1284. * least one include pattern, or <code>false</code> otherwise.
  1285. */
  1286. protected boolean couldHoldIncluded(String name) {
  1287. return couldHoldIncluded(new TokenizedPath(name));
  1288. }
  1289. /**
  1290. * Test whether or not a name matches the start of at least one include
  1291. * pattern.
  1292. *
  1293. * @param tokenizedName The name to match. Must not be <code>null</code>.
  1294. * @return <code>true</code> when the name matches against the start of at
  1295. * least one include pattern, or <code>false</code> otherwise.
  1296. */
  1297. private boolean couldHoldIncluded(TokenizedPath tokenizedName) {
  1298. for (int i = 0; i < includePatterns.length; i++) {
  1299. if (couldHoldIncluded(tokenizedName, includePatterns[i])) {
  1300. return true;
  1301. }
  1302. }
  1303. for (Iterator iter = includeNonPatterns.values().iterator();
  1304. iter.hasNext(); ) {
  1305. if (couldHoldIncluded(tokenizedName,
  1306. ((TokenizedPath) iter.next()).toPattern())) {
  1307. return true;
  1308. }
  1309. }
  1310. return false;
  1311. }
  1312. /**
  1313. * Test whether or not a name matches the start of the given
  1314. * include pattern.
  1315. *
  1316. * @param tokenizedName The name to match. Must not be <code>null</code>.
  1317. * @return <code>true</code> when the name matches against the start of the
  1318. * include pattern, or <code>false</code> otherwise.
  1319. */
  1320. private boolean couldHoldIncluded(TokenizedPath tokenizedName,
  1321. TokenizedPattern tokenizedInclude) {
  1322. return tokenizedInclude.matchStartOf(tokenizedName, isCaseSensitive())
  1323. && isMorePowerfulThanExcludes(tokenizedName.toString())
  1324. && isDeeper(tokenizedInclude, tokenizedName);
  1325. }
  1326. /**
  1327. * Verify that a pattern specifies files deeper
  1328. * than the level of the specified file.
  1329. * @param pattern the pattern to check.
  1330. * @param name the name to check.
  1331. * @return whether the pattern is deeper than the name.
  1332. * @since Ant 1.6.3
  1333. */
  1334. private boolean isDeeper(TokenizedPattern pattern, TokenizedPath name) {
  1335. return pattern.containsPattern(SelectorUtils.DEEP_TREE_MATCH)
  1336. || pattern.depth() > name.depth();
  1337. }
  1338. /**
  1339. * Find out whether one particular include pattern is more powerful
  1340. * than all the excludes.
  1341. * Note: the power comparison is based on the length of the include pattern
  1342. * and of the exclude patterns without the wildcards.
  1343. * Ideally the comparison should be done based on the depth
  1344. * of the match; that is to say how many file separators have been matched
  1345. * before the first ** or the end of the pattern.
  1346. *
  1347. * IMPORTANT : this function should return false "with care".
  1348. *
  1349. * @param name the relative path to test.
  1350. * @return true if there is no exclude pattern more powerful than
  1351. * this include pattern.
  1352. * @since Ant 1.6
  1353. */
  1354. private boolean isMorePowerfulThanExcludes(String name) {
  1355. final String soughtexclude =
  1356. name + File.separatorChar + SelectorUtils.DEEP_TREE_MATCH;
  1357. for (int counter = 0; counter < excludePatterns.length; counter++) {
  1358. if (excludePatterns[counter].toString().equals(soughtexclude)) {
  1359. return false;
  1360. }
  1361. }
  1362. return true;
  1363. }
  1364. /**
  1365. * Test whether all contents of the specified directory must be excluded.
  1366. * @param path the path to check.
  1367. * @return whether all the specified directory's contents are excluded.
  1368. */
  1369. /* package */ boolean contentsExcluded(TokenizedPath path) {
  1370. for (int i = 0; i < excludePatterns.length; i++) {
  1371. if (excludePatterns[i].endsWith(SelectorUtils.DEEP_TREE_MATCH)
  1372. && excludePatterns[i].withoutLastToken()
  1373. .matchPath(path, isCaseSensitive())) {
  1374. return true;
  1375. }
  1376. }
  1377. return false;
  1378. }
  1379. /**
  1380. * Test whether or not a name matches against at least one exclude
  1381. * pattern.
  1382. *
  1383. * @param name The name to match. Must not be <code>null</code>.
  1384. * @return <code>true</code> when the name matches against at least one
  1385. * exclude pattern, or <code>false</code> otherwise.
  1386. */
  1387. protected boolean isExcluded(String name) {
  1388. return isExcluded(new TokenizedPath(name));
  1389. }
  1390. /**
  1391. * Test whether or not a name matches against at least one exclude
  1392. * pattern.
  1393. *
  1394. * @param name The name to match. Must not be <code>null</code>.
  1395. * @return <code>true</code> when the name matches against at least one
  1396. * exclude pattern, or <code>false</code> otherwise.
  1397. */
  1398. private boolean isExcluded(TokenizedPath name) {
  1399. ensureNonPatternSetsReady();
  1400. if (isCaseSensitive()
  1401. ? excludeNonPatterns.containsKey(name.toString())
  1402. : excludeNonPatterns.containsKey(name.toString().toUpperCase())) {
  1403. return true;
  1404. }
  1405. for (int i = 0; i < excludePatterns.length; i++) {
  1406. if (excludePatterns[i].matchPath(name, isCaseSensitive())) {
  1407. return true;
  1408. }
  1409. }
  1410. return false;
  1411. }
  1412. /**
  1413. * Test whether a file should be selected.
  1414. *
  1415. * @param name the filename to check for selecting.
  1416. * @param file the java.io.File object for this filename.
  1417. * @return <code>false</code> when the selectors says that the file
  1418. * should not be selected, <code>true</code> otherwise.
  1419. */
  1420. protected boolean isSelected(String name, File file) {
  1421. if (selectors != null) {
  1422. for (int i = 0; i < selectors.length; i++) {
  1423. if (!selectors[i].isSelected(basedir, name, file)) {
  1424. return false;
  1425. }
  1426. }
  1427. }
  1428. return true;
  1429. }
  1430. /**
  1431. * Return the names of the files which matched at least one of the
  1432. * include patterns and none of the exclude patterns.
  1433. * The names are relative to the base directory.
  1434. *
  1435. * @return the names of the files which matched at least one of the
  1436. * include patterns and none of the exclude patterns.
  1437. */
  1438. public String[] getIncludedFiles() {
  1439. String[] files;
  1440. synchronized (this) {
  1441. if (filesIncluded == null) {
  1442. throw new IllegalStateException("Must call scan() first");
  1443. }
  1444. files = new String[filesIncluded.size()];
  1445. filesIncluded.copyInto(files);
  1446. }
  1447. Arrays.sort(files);
  1448. return files;
  1449. }
  1450. /**
  1451. * Return the count of included files.
  1452. * @return <code>int</code>.
  1453. * @since Ant 1.6.3
  1454. */
  1455. public synchronized int getIncludedFilesCount() {
  1456. if (filesIncluded == null) {
  1457. throw new IllegalStateException("Must call scan() first");
  1458. }
  1459. return filesIncluded.size();
  1460. }
  1461. /**
  1462. * Return the names of the files which matched none of the include
  1463. * patterns. The names are relative to the base directory. This involves
  1464. * performing a slow scan if one has not already been completed.
  1465. *
  1466. * @return the names of the files which matched none of the include
  1467. * patterns.
  1468. *
  1469. * @see #slowScan
  1470. */
  1471. public synchronized String[] getNotIncludedFiles() {
  1472. slowScan();
  1473. String[] files = new String[filesNotIncluded.size()];
  1474. filesNotIncluded.copyInto(files);
  1475. return files;
  1476. }
  1477. /**
  1478. * Return the names of the files which matched at least one of the
  1479. * include patterns and at least one of the exclude patterns.
  1480. * The names are relative to the base directory. This involves
  1481. * performing a slow scan if one has not already been completed.
  1482. *
  1483. * @return the names of the files which matched at least one of the
  1484. * include patterns and at least one of the exclude patterns.
  1485. *
  1486. * @see #slowScan
  1487. */
  1488. public synchronized String[] getExcludedFiles() {
  1489. slowScan();
  1490. String[] files = new String[filesExcluded.size()];
  1491. filesExcluded.copyInto(files);
  1492. return files;
  1493. }
  1494. /**
  1495. * <p>Return the names of the files which were selected out and
  1496. * therefore not ultimately included.</p>
  1497. *
  1498. * <p>The names are relative to the base directory. This involves
  1499. * performing a slow scan if one has not already been completed.</p>
  1500. *
  1501. * @return the names of the files which were deselected.
  1502. *
  1503. * @see #slowScan
  1504. */
  1505. public synchronized String[] getDeselectedFiles() {
  1506. slowScan();
  1507. String[] files = new String[filesDeselected.size()];
  1508. filesDeselected.copyInto(files);
  1509. return files;
  1510. }
  1511. /**
  1512. * Return the names of the directories which matched at least one of the
  1513. * include patterns and none of the exclude patterns.
  1514. * The names are relative to the base directory.
  1515. *
  1516. * @return the names of the directories which matched at least one of the
  1517. * include patterns and none of the exclude patterns.
  1518. */
  1519. public String[] getIncludedDirectories() {
  1520. String[] directories;
  1521. synchronized (this) {
  1522. if (dirsIncluded == null) {
  1523. throw new IllegalStateException("Must call scan() first");
  1524. }
  1525. directories = new String[dirsIncluded.size()];
  1526. dirsIncluded.copyInto(directories);
  1527. }
  1528. Arrays.sort(directories);
  1529. return directories;
  1530. }
  1531. /**
  1532. * Return the count of included directories.
  1533. * @return <code>int</code>.
  1534. * @since Ant 1.6.3
  1535. */
  1536. public synchronized int getIncludedDirsCount() {
  1537. if (dirsIncluded == null) {
  1538. throw new IllegalStateException("Must call scan() first");
  1539. }
  1540. return dirsIncluded.size();
  1541. }
  1542. /**
  1543. * Return the names of the directories which matched none of the include
  1544. * patterns. The names are relative to the base directory. This involves
  1545. * performing a slow scan if one has not already been completed.
  1546. *
  1547. * @return the names of the directories which matched none of the include
  1548. * patterns.
  1549. *
  1550. * @see #slowScan
  1551. */
  1552. public synchronized String[] getNotIncludedDirectories() {
  1553. slowScan();
  1554. String[] directories = new String[dirsNotIncluded.size()];
  1555. dirsNotIncluded.copyInto(directories);
  1556. return directories;
  1557. }
  1558. /**
  1559. * Return the names of the directories which matched at least one of the
  1560. * include patterns and at least one of the exclude patterns.
  1561. * The names are relative to the base directory. This involves
  1562. * performing a slow scan if one has not already been completed.
  1563. *
  1564. * @return the names of the directories which matched at least one of the
  1565. * include patterns and at least one of the exclude patterns.
  1566. *
  1567. * @see #slowScan
  1568. */
  1569. public synchronized String[] getExcludedDirectories() {
  1570. slowScan();
  1571. String[] directories = new String[dirsExcluded.size()];
  1572. dirsExcluded.copyInto(directories);
  1573. return directories;
  1574. }
  1575. /**
  1576. * <p>Return the names of the directories which were selected out and
  1577. * therefore not ultimately included.</p>
  1578. *
  1579. * <p>The names are relative to the base directory. This involves
  1580. * performing a slow scan if one has not already been completed.</p>
  1581. *
  1582. * @return the names of the directories which were deselected.
  1583. *
  1584. * @see #slowScan
  1585. */
  1586. public synchronized String[] getDeselectedDirectories() {
  1587. slowScan();
  1588. String[] directories = new String[dirsDeselected.size()];
  1589. dirsDeselected.copyInto(directories);
  1590. return directories;
  1591. }
  1592. /**
  1593. * Absolute paths of all symbolic links that haven't been followed
  1594. * but would have been followed had followsymlinks been true or
  1595. * maxLevelsOfSymlinks been bigger.
  1596. *
  1597. * @since Ant 1.8.0
  1598. */
  1599. public synchronized String[] getNotFollowedSymlinks() {
  1600. String[] links;
  1601. synchronized (this) {
  1602. links = (String[]) notFollowedSymlinks
  1603. .toArray(new String[notFollowedSymlinks.size()]);
  1604. }
  1605. Arrays.sort(links);
  1606. return links;
  1607. }
  1608. /**
  1609. * Add default exclusions to the current exclusions set.
  1610. */
  1611. public synchronized void addDefaultExcludes() {
  1612. int excludesLength = excludes == null ? 0 : excludes.length;
  1613. String[] newExcludes;
  1614. String[] defaultExcludesTemp = getDefaultExcludes();
  1615. newExcludes = new String[excludesLength + defaultExcludesTemp.length];
  1616. if (excludesLength > 0) {
  1617. System.arraycopy(excludes, 0, newExcludes, 0, excludesLength);
  1618. }
  1619. for (int i = 0; i < defaultExcludesTemp.length; i++) {
  1620. newExcludes[i + excludesLength] =
  1621. defaultExcludesTemp[i].replace('/', File.separatorChar)
  1622. .replace('\\', File.separatorChar);
  1623. }
  1624. excludes = newExcludes;
  1625. }
  1626. /**
  1627. * Get the named resource.
  1628. * @param name path name of the file relative to the dir attribute.
  1629. *
  1630. * @return the resource with the given name.
  1631. * @since Ant 1.5.2
  1632. */
  1633. public synchronized Resource getResource(String name) {
  1634. return new FileResource(basedir, name);
  1635. }
  1636. /**
  1637. * Has the directory with the given path relative to the base
  1638. * directory already been scanned?
  1639. *
  1640. * <p>Registers the given directory as scanned as a side effect.</p>
  1641. *
  1642. * @since Ant 1.6
  1643. */
  1644. private boolean hasBeenScanned(String vpath) {
  1645. return !scannedDirs.add(vpath);
  1646. }
  1647. /**
  1648. * This method is of interest for testing purposes. The returned
  1649. * Set is live and should not be modified.
  1650. * @return the Set of relative directory names that have been scanned.
  1651. */
  1652. /* package-private */ Set getScannedDirs() {
  1653. return scannedDirs;
  1654. }
  1655. /**
  1656. * Clear internal caches.
  1657. *
  1658. * @since Ant 1.6
  1659. */
  1660. private synchronized void clearCaches() {
  1661. includeNonPatterns.clear();
  1662. excludeNonPatterns.clear();
  1663. includePatterns = null;
  1664. excludePatterns = null;
  1665. areNonPatternSetsReady = false;
  1666. }
  1667. /**
  1668. * Ensure that the in|exclude &quot;patterns&quot;
  1669. * have been properly divided up.
  1670. *
  1671. * @since Ant 1.6.3
  1672. */
  1673. /* package */ synchronized void ensureNonPatternSetsReady() {
  1674. if (!areNonPatternSetsReady) {
  1675. includePatterns = fillNonPatternSet(includeNonPatterns, includes);
  1676. excludePatterns = fillNonPatternSet(excludeNonPatterns, excludes);
  1677. areNonPatternSetsReady = true;
  1678. }
  1679. }
  1680. /**
  1681. * Add all patterns that are not real patterns (do not contain
  1682. * wildcards) to the set and returns the real patterns.
  1683. *
  1684. * @param map Map to populate.
  1685. * @param patterns String[] of patterns.
  1686. * @since Ant 1.8.0
  1687. */
  1688. private TokenizedPattern[] fillNonPatternSet(Map map, String[] patterns) {
  1689. ArrayList al = new ArrayList(patterns.length);
  1690. for (int i = 0; i < patterns.length; i++) {
  1691. if (!SelectorUtils.hasWildcards(patterns[i])) {
  1692. String s = isCaseSensitive()
  1693. ? patterns[i] : patterns[i].toUpperCase();
  1694. map.put(s, new TokenizedPath(s));
  1695. } else {
  1696. al.add(new TokenizedPattern(patterns[i]));
  1697. }
  1698. }
  1699. return (TokenizedPattern[]) al.toArray(new TokenizedPattern[al.size()]);
  1700. }
  1701. /**
  1702. * Would following the given directory cause a loop of symbolic
  1703. * links deeper than allowed?
  1704. *
  1705. * <p>Can only happen if the given directory has been seen at
  1706. * least more often than allowed during the current scan and it is
  1707. * a symbolic link and enough other occurences of the same name
  1708. * higher up are symbolic links that point to the same place.</p>
  1709. *
  1710. * @since Ant 1.8.0
  1711. */
  1712. private boolean causesIllegalSymlinkLoop(String dirName, File parent,
  1713. LinkedList directoryNamesFollowed) {
  1714. try {
  1715. if (directoryNamesFollowed.size() >= maxLevelsOfSymlinks
  1716. && CollectionUtils.frequency(directoryNamesFollowed, dirName)
  1717. >= maxLevelsOfSymlinks
  1718. && SYMLINK_UTILS.isSymbolicLink(parent, dirName)) {
  1719. ArrayList files = new ArrayList();
  1720. File f = FILE_UTILS.resolveFile(parent, dirName);
  1721. String target = f.getCanonicalPath();
  1722. files.add(target);
  1723. String relPath = "";
  1724. for (Iterator i = directoryNamesFollowed.iterator();
  1725. i.hasNext(); ) {
  1726. relPath += "../";
  1727. String dir = (String) i.next();
  1728. if (dirName.equals(dir)) {
  1729. f = FILE_UTILS.resolveFile(parent, relPath + dir);
  1730. files.add(f.getCanonicalPath());
  1731. if (files.size() > maxLevelsOfSymlinks
  1732. && CollectionUtils.frequency(files, target)
  1733. > maxLevelsOfSymlinks) {
  1734. return true;
  1735. }
  1736. }
  1737. }
  1738. }
  1739. return false;
  1740. } catch (IOException ex) {
  1741. throw new BuildException("Caught error while checking for"
  1742. + " symbolic links", ex);
  1743. }
  1744. }
  1745. }