001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018: package org.apache.tools.ant.types.resources;
019:
020: import java.io.File;
021: import java.util.Vector;
022: import java.util.Iterator;
023: import java.util.Collections;
024:
025: import org.apache.tools.ant.Project;
026: import org.apache.tools.ant.BuildException;
027: import org.apache.tools.ant.DirectoryScanner;
028: import org.apache.tools.ant.types.Reference;
029: import org.apache.tools.ant.types.PatternSet;
030: import org.apache.tools.ant.types.ResourceCollection;
031: import org.apache.tools.ant.types.selectors.FileSelector;
032: import org.apache.tools.ant.types.selectors.AbstractSelectorContainer;
033:
034: /**
035: * ResourceCollection implementation; like AbstractFileSet with absolute paths.
036: * @since Ant 1.7
037: */
038: public class Files extends AbstractSelectorContainer implements
039: Cloneable, ResourceCollection {
040:
041: private static final Iterator EMPTY_ITERATOR = Collections.EMPTY_SET
042: .iterator();
043:
044: private PatternSet defaultPatterns = new PatternSet();
045: private Vector additionalPatterns = new Vector();
046: private Vector selectors = new Vector();
047:
048: private boolean useDefaultExcludes = true;
049: private boolean caseSensitive = true;
050: private boolean followSymlinks = true;
051:
052: /* cached DirectoryScanner instance */
053: private DirectoryScanner ds = null;
054:
055: /**
056: * Construct a new <code>Files</code> collection.
057: */
058: public Files() {
059: super ();
060: }
061:
062: /**
063: * Construct a new <code>Files</code> collection, shallowly cloned
064: * from the specified <code>Files</code>.
065: * @param f the <code>Files</code> to use as a template.
066: */
067: protected Files(Files f) {
068: this .defaultPatterns = f.defaultPatterns;
069: this .additionalPatterns = f.additionalPatterns;
070: this .selectors = f.selectors;
071: this .useDefaultExcludes = f.useDefaultExcludes;
072: this .caseSensitive = f.caseSensitive;
073: this .followSymlinks = f.followSymlinks;
074: this .ds = f.ds;
075: setProject(f.getProject());
076: }
077:
078: /**
079: * Make this instance in effect a reference to another instance.
080: *
081: * <p>You must not set another attribute or nest elements inside
082: * this element if you make it a reference.</p>
083: * @param r the <code>Reference</code> to use.
084: * @throws BuildException if there is a problem.
085: */
086: public void setRefid(Reference r) throws BuildException {
087: if (hasPatterns(defaultPatterns)) {
088: throw tooManyAttributes();
089: }
090: if (!additionalPatterns.isEmpty()) {
091: throw noChildrenAllowed();
092: }
093: if (!selectors.isEmpty()) {
094: throw noChildrenAllowed();
095: }
096: super .setRefid(r);
097: }
098:
099: /**
100: * Create a nested patternset.
101: * @return <code>PatternSet</code>.
102: */
103: public synchronized PatternSet createPatternSet() {
104: if (isReference()) {
105: throw noChildrenAllowed();
106: }
107: PatternSet patterns = new PatternSet();
108: additionalPatterns.addElement(patterns);
109: ds = null;
110: return patterns;
111: }
112:
113: /**
114: * Add a name entry to the include list.
115: * @return <code>PatternSet.NameEntry</code>.
116: */
117: public synchronized PatternSet.NameEntry createInclude() {
118: if (isReference()) {
119: throw noChildrenAllowed();
120: }
121: ds = null;
122: return defaultPatterns.createInclude();
123: }
124:
125: /**
126: * Add a name entry to the include files list.
127: * @return <code>PatternSet.NameEntry</code>.
128: */
129: public synchronized PatternSet.NameEntry createIncludesFile() {
130: if (isReference()) {
131: throw noChildrenAllowed();
132: }
133: ds = null;
134: return defaultPatterns.createIncludesFile();
135: }
136:
137: /**
138: * Add a name entry to the exclude list.
139: * @return <code>PatternSet.NameEntry</code>.
140: */
141: public synchronized PatternSet.NameEntry createExclude() {
142: if (isReference()) {
143: throw noChildrenAllowed();
144: }
145: ds = null;
146: return defaultPatterns.createExclude();
147: }
148:
149: /**
150: * Add a name entry to the excludes files list.
151: * @return <code>PatternSet.NameEntry</code>.
152: */
153: public synchronized PatternSet.NameEntry createExcludesFile() {
154: if (isReference()) {
155: throw noChildrenAllowed();
156: }
157: ds = null;
158: return defaultPatterns.createExcludesFile();
159: }
160:
161: /**
162: * Append <code>includes</code> to the current list of include
163: * patterns.
164: *
165: * <p>Patterns may be separated by a comma or a space.</p>
166: *
167: * @param includes the <code>String</code> containing the include patterns.
168: */
169: public synchronized void setIncludes(String includes) {
170: checkAttributesAllowed();
171: defaultPatterns.setIncludes(includes);
172: ds = null;
173: }
174:
175: /**
176: * Append <code>includes</code> to the current list of include
177: * patterns.
178: *
179: * @param includes array containing the include patterns.
180: */
181: public synchronized void appendIncludes(String[] includes) {
182: checkAttributesAllowed();
183: if (includes != null) {
184: for (int i = 0; i < includes.length; i++) {
185: defaultPatterns.createInclude().setName(includes[i]);
186: }
187: ds = null;
188: }
189: }
190:
191: /**
192: * Append <code>excludes</code> to the current list of exclude
193: * patterns.
194: *
195: * <p>Patterns may be separated by a comma or a space.</p>
196: *
197: * @param excludes the <code>String</code> containing the exclude patterns.
198: */
199: public synchronized void setExcludes(String excludes) {
200: checkAttributesAllowed();
201: defaultPatterns.setExcludes(excludes);
202: ds = null;
203: }
204:
205: /**
206: * Append <code>excludes</code> to the current list of include
207: * patterns.
208: *
209: * @param excludes array containing the exclude patterns.
210: */
211: public synchronized void appendExcludes(String[] excludes) {
212: checkAttributesAllowed();
213: if (excludes != null) {
214: for (int i = 0; i < excludes.length; i++) {
215: defaultPatterns.createExclude().setName(excludes[i]);
216: }
217: ds = null;
218: }
219: }
220:
221: /**
222: * Set the <code>File</code> containing the includes patterns.
223: *
224: * @param incl <code>File</code> instance.
225: * @throws BuildException if there is a problem.
226: */
227: public synchronized void setIncludesfile(File incl)
228: throws BuildException {
229: checkAttributesAllowed();
230: defaultPatterns.setIncludesfile(incl);
231: ds = null;
232: }
233:
234: /**
235: * Set the <code>File</code> containing the excludes patterns.
236: *
237: * @param excl <code>File</code> instance.
238: * @throws BuildException if there is a problem.
239: */
240: public synchronized void setExcludesfile(File excl)
241: throws BuildException {
242: checkAttributesAllowed();
243: defaultPatterns.setExcludesfile(excl);
244: ds = null;
245: }
246:
247: /**
248: * Set whether default exclusions should be used or not.
249: *
250: * @param useDefaultExcludes <code>boolean</code>.
251: */
252: public synchronized void setDefaultexcludes(
253: boolean useDefaultExcludes) {
254: checkAttributesAllowed();
255: this .useDefaultExcludes = useDefaultExcludes;
256: ds = null;
257: }
258:
259: /**
260: * Get whether default exclusions should be used or not.
261: * @return the defaultexclusions value.
262: */
263: public synchronized boolean getDefaultexcludes() {
264: return (isReference()) ? getRef().getDefaultexcludes()
265: : useDefaultExcludes;
266: }
267:
268: /**
269: * Set case-sensitivity of the Files collection.
270: *
271: * @param caseSensitive <code>boolean</code>.
272: */
273: public synchronized void setCaseSensitive(boolean caseSensitive) {
274: checkAttributesAllowed();
275: this .caseSensitive = caseSensitive;
276: ds = null;
277: }
278:
279: /**
280: * Find out if this Files collection is case-sensitive.
281: *
282: * @return <code>boolean</code> indicating whether the Files
283: * collection is case-sensitive.
284: */
285: public synchronized boolean isCaseSensitive() {
286: return (isReference()) ? getRef().isCaseSensitive()
287: : caseSensitive;
288: }
289:
290: /**
291: * Set whether or not symbolic links should be followed.
292: *
293: * @param followSymlinks whether or not symbolic links should be followed.
294: */
295: public synchronized void setFollowSymlinks(boolean followSymlinks) {
296: checkAttributesAllowed();
297: this .followSymlinks = followSymlinks;
298: ds = null;
299: }
300:
301: /**
302: * Find out whether symbolic links should be followed.
303: *
304: * @return <code>boolean</code> indicating whether symbolic links
305: * should be followed.
306: */
307: public synchronized boolean isFollowSymlinks() {
308: return (isReference()) ? getRef().isFollowSymlinks()
309: : followSymlinks;
310: }
311:
312: /**
313: * Fulfill the ResourceCollection contract.
314: * @return an Iterator of Resources.
315: */
316: public synchronized Iterator iterator() {
317: if (isReference()) {
318: return getRef().iterator();
319: }
320: ensureDirectoryScannerSetup();
321: ds.scan();
322: int fct = ds.getIncludedFilesCount();
323: int dct = ds.getIncludedDirsCount();
324: if (fct + dct == 0) {
325: return EMPTY_ITERATOR;
326: }
327: FileResourceIterator result = new FileResourceIterator();
328: if (fct > 0) {
329: result.addFiles(ds.getIncludedFiles());
330: }
331: if (dct > 0) {
332: result.addFiles(ds.getIncludedDirectories());
333: }
334: return result;
335: }
336:
337: /**
338: * Fulfill the ResourceCollection contract.
339: * @return number of elements as int.
340: */
341: public synchronized int size() {
342: if (isReference()) {
343: return getRef().size();
344: }
345: ensureDirectoryScannerSetup();
346: ds.scan();
347: return ds.getIncludedFilesCount() + ds.getIncludedDirsCount();
348: }
349:
350: /**
351: * Find out whether this Files collection has patterns.
352: *
353: * @return whether any patterns are in this container.
354: */
355: public synchronized boolean hasPatterns() {
356: if (isReference()) {
357: return getRef().hasPatterns();
358: }
359: if (hasPatterns(defaultPatterns)) {
360: return true;
361: }
362: for (Iterator i = additionalPatterns.iterator(); i.hasNext();) {
363: if (hasPatterns((PatternSet) i.next())) {
364: return true;
365: }
366: }
367: return false;
368: }
369:
370: /**
371: * Add a new selector into this container.
372: *
373: * @param selector the new <code>FileSelector</code> to add.
374: */
375: public synchronized void appendSelector(FileSelector selector) {
376: if (isReference()) {
377: throw noChildrenAllowed();
378: }
379: super .appendSelector(selector);
380: ds = null;
381: }
382:
383: /**
384: * Format this Files collection as a String.
385: * @return a descriptive <code>String</code>.
386: */
387: public String toString() {
388: if (isReference()) {
389: return getRef().toString();
390: }
391: Iterator i = iterator();
392: if (!i.hasNext()) {
393: return "";
394: }
395: StringBuffer sb = new StringBuffer();
396: while (i.hasNext()) {
397: if (sb.length() > 0) {
398: sb.append(File.pathSeparatorChar);
399: }
400: sb.append(i.next());
401: }
402: return sb.toString();
403: }
404:
405: /**
406: * Create a deep clone of this instance, except for the nested selectors
407: * (the list of selectors is a shallow clone of this instance's list).
408: * @return a cloned Object.
409: */
410: public synchronized Object clone() {
411: if (isReference()) {
412: return getRef().clone();
413: }
414: try {
415: Files f = (Files) super .clone();
416: f.defaultPatterns = (PatternSet) defaultPatterns.clone();
417: f.additionalPatterns = new Vector(additionalPatterns.size());
418: for (Iterator iter = additionalPatterns.iterator(); iter
419: .hasNext();) {
420: PatternSet ps = (PatternSet) iter.next();
421: f.additionalPatterns.add(ps.clone());
422: }
423: f.selectors = new Vector(selectors);
424: return f;
425: } catch (CloneNotSupportedException e) {
426: throw new BuildException(e);
427: }
428: }
429:
430: /**
431: * Get the merged include patterns for this Files collection.
432: * @param p Project instance.
433: * @return the include patterns of the default pattern set and all
434: * nested patternsets.
435: */
436: public String[] mergeIncludes(Project p) {
437: return mergePatterns(p).getIncludePatterns(p);
438: }
439:
440: /**
441: * Get the merged exclude patterns for this Files collection.
442: * @param p Project instance.
443: * @return the exclude patterns of the default pattern set and all
444: * nested patternsets.
445: */
446: public String[] mergeExcludes(Project p) {
447: return mergePatterns(p).getExcludePatterns(p);
448: }
449:
450: /**
451: * Get the merged patterns for this Files collection.
452: * @param p Project instance.
453: * @return the default patternset merged with the additional sets
454: * in a new PatternSet instance.
455: */
456: public synchronized PatternSet mergePatterns(Project p) {
457: if (isReference()) {
458: return getRef().mergePatterns(p);
459: }
460: PatternSet ps = new PatternSet();
461: ps.append(defaultPatterns, p);
462: final int count = additionalPatterns.size();
463: for (int i = 0; i < count; i++) {
464: Object o = additionalPatterns.elementAt(i);
465: ps.append((PatternSet) o, p);
466: }
467: return ps;
468: }
469:
470: /**
471: * Always returns true.
472: * @return true indicating that all elements of a Files collection
473: * will be FileResources.
474: */
475: public boolean isFilesystemOnly() {
476: return true;
477: }
478:
479: /**
480: * Perform the check for circular references and return the
481: * referenced Files collection.
482: * @return <code>FileCollection</code>.
483: */
484: protected Files getRef() {
485: return (Files) getCheckedRef();
486: }
487:
488: private synchronized void ensureDirectoryScannerSetup() {
489: if (ds == null) {
490: ds = new DirectoryScanner();
491: PatternSet ps = mergePatterns(getProject());
492: ds.setIncludes(ps.getIncludePatterns(getProject()));
493: ds.setExcludes(ps.getExcludePatterns(getProject()));
494: ds.setSelectors(getSelectors(getProject()));
495: if (useDefaultExcludes) {
496: ds.addDefaultExcludes();
497: }
498: ds.setCaseSensitive(caseSensitive);
499: ds.setFollowSymlinks(followSymlinks);
500: }
501: }
502:
503: private boolean hasPatterns(PatternSet ps) {
504: return ps.getIncludePatterns(getProject()).length > 0
505: || ps.getExcludePatterns(getProject()).length > 0;
506: }
507:
508: }
|