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.List;
022: import java.util.Stack;
023: import java.util.Iterator;
024: import java.util.ArrayList;
025: import java.util.Collection;
026: import java.util.Collections;
027:
028: import org.apache.tools.ant.Project;
029: import org.apache.tools.ant.BuildException;
030: import org.apache.tools.ant.types.DataType;
031: import org.apache.tools.ant.types.ResourceCollection;
032:
033: /**
034: * Base class for ResourceCollections that nest multiple ResourceCollections.
035: * @since Ant 1.7
036: */
037: public abstract class BaseResourceCollectionContainer extends DataType
038: implements ResourceCollection, Cloneable {
039: private List rc = new ArrayList();
040: private Collection coll = null;
041: private boolean cache = true;
042:
043: /**
044: * Set whether to cache collections.
045: * @param b boolean cache flag.
046: */
047: public synchronized void setCache(boolean b) {
048: cache = b;
049: }
050:
051: /**
052: * Learn whether to cache collections. Default is <code>true</code>.
053: * @return boolean cache flag.
054: */
055: public synchronized boolean isCache() {
056: return cache;
057: }
058:
059: /**
060: * Clear the container.
061: * @throws BuildException on error.
062: */
063: public synchronized void clear() throws BuildException {
064: if (isReference()) {
065: throw noChildrenAllowed();
066: }
067: rc.clear();
068: FailFast.invalidate(this );
069: coll = null;
070: setChecked(false);
071: }
072:
073: /**
074: * Add a ResourceCollection to the container.
075: * @param c the ResourceCollection to add.
076: * @throws BuildException on error.
077: */
078: public synchronized void add(ResourceCollection c)
079: throws BuildException {
080: if (isReference()) {
081: throw noChildrenAllowed();
082: }
083: if (c == null) {
084: return;
085: }
086: rc.add(c);
087: FailFast.invalidate(this );
088: coll = null;
089: setChecked(false);
090: }
091:
092: /**
093: * Add the Collection of ResourceCollections to the container.
094: * @param c the Collection whose elements to add.
095: * @throws BuildException on error.
096: */
097: public synchronized void addAll(Collection c) throws BuildException {
098: if (isReference()) {
099: throw noChildrenAllowed();
100: }
101: try {
102: for (Iterator i = c.iterator(); i.hasNext();) {
103: add((ResourceCollection) i.next());
104: }
105: } catch (ClassCastException e) {
106: throw new BuildException(e);
107: }
108: }
109:
110: /**
111: * Fulfill the ResourceCollection contract. The Iterator returned
112: * will throw ConcurrentModificationExceptions if ResourceCollections
113: * are added to this container while the Iterator is in use.
114: * @return a "fail-fast" Iterator.
115: */
116: public final synchronized Iterator iterator() {
117: if (isReference()) {
118: return ((BaseResourceCollectionContainer) getCheckedRef())
119: .iterator();
120: }
121: dieOnCircularReference();
122: return new FailFast(this , cacheCollection().iterator());
123: }
124:
125: /**
126: * Fulfill the ResourceCollection contract.
127: * @return number of elements as int.
128: */
129: public synchronized int size() {
130: if (isReference()) {
131: return ((BaseResourceCollectionContainer) getCheckedRef())
132: .size();
133: }
134: dieOnCircularReference();
135: return cacheCollection().size();
136: }
137:
138: /**
139: * Fulfill the ResourceCollection contract.
140: * @return whether this is a filesystem-only resource collection.
141: */
142: public synchronized boolean isFilesystemOnly() {
143: if (isReference()) {
144: return ((BaseResourceCollectionContainer) getCheckedRef())
145: .isFilesystemOnly();
146: }
147: dieOnCircularReference();
148: //first the easy way, if all children are filesystem-only, return true:
149: boolean goEarly = true;
150: for (Iterator i = rc.iterator(); goEarly && i.hasNext();) {
151: goEarly &= ((ResourceCollection) i.next())
152: .isFilesystemOnly();
153: }
154: if (goEarly) {
155: return true;
156: }
157: /* now check each Resource in case the child only
158: lets through files from any children IT may have: */
159: for (Iterator i = cacheCollection().iterator(); i.hasNext();) {
160: if (!(i.next() instanceof FileResource)) {
161: return false;
162: }
163: }
164: return true;
165: }
166:
167: /**
168: * Overrides the version of DataType to recurse on all DataType
169: * child elements that may have been added.
170: * @param stk the stack of data types to use (recursively).
171: * @param p the project to use to dereference the references.
172: * @throws BuildException on error.
173: */
174: protected synchronized void dieOnCircularReference(Stack stk,
175: Project p) throws BuildException {
176: if (isChecked()) {
177: return;
178: }
179: if (isReference()) {
180: super .dieOnCircularReference(stk, p);
181: } else {
182: for (Iterator i = rc.iterator(); i.hasNext();) {
183: Object o = i.next();
184: if (o instanceof DataType) {
185: stk.push(o);
186: invokeCircularReferenceCheck((DataType) o, stk, p);
187: stk.pop();
188: }
189: }
190: setChecked(true);
191: }
192: }
193:
194: /**
195: * Get the nested ResourceCollections.
196: * @return List.
197: */
198: protected final synchronized List getResourceCollections() {
199: dieOnCircularReference();
200: return Collections.unmodifiableList(rc);
201: }
202:
203: /**
204: * Template method for subclasses to return a Collection object of Resources.
205: * @return Collection.
206: */
207: protected abstract Collection getCollection();
208:
209: /**
210: * Implement clone. The set of nested resource
211: * collections is shallowly cloned.
212: * @return a cloned instance.
213: */
214: public Object clone() {
215: try {
216: BaseResourceCollectionContainer c = (BaseResourceCollectionContainer) super
217: .clone();
218: c.rc = new ArrayList(rc);
219: c.coll = null;
220: return c;
221: } catch (CloneNotSupportedException e) {
222: throw new BuildException(e);
223: }
224: }
225:
226: /**
227: * Format this BaseResourceCollectionContainer as a String.
228: * @return a descriptive <code>String</code>.
229: */
230: public synchronized String toString() {
231: if (isReference()) {
232: return getCheckedRef().toString();
233: }
234: if (cacheCollection().size() == 0) {
235: return "";
236: }
237: StringBuffer sb = new StringBuffer();
238: for (Iterator i = coll.iterator(); i.hasNext();) {
239: if (sb.length() > 0) {
240: sb.append(File.pathSeparatorChar);
241: }
242: sb.append(i.next());
243: }
244: return sb.toString();
245: }
246:
247: private synchronized Collection cacheCollection() {
248: if (coll == null || !isCache()) {
249: coll = getCollection();
250: }
251: return coll;
252: }
253:
254: }
|