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.Stack;
022: import java.util.Iterator;
023: import java.util.Collection;
024:
025: import org.apache.tools.ant.Project;
026: import org.apache.tools.ant.BuildException;
027: import org.apache.tools.ant.types.DataType;
028: import org.apache.tools.ant.types.ResourceCollection;
029:
030: /**
031: * Base class for a ResourceCollection that wraps a single nested
032: * ResourceCollection.
033: * @since Ant 1.7
034: */
035: public abstract class BaseResourceCollectionWrapper extends DataType
036: implements ResourceCollection, Cloneable {
037: private static final String ONE_NESTED_MESSAGE = " expects exactly one nested resource collection.";
038:
039: private ResourceCollection rc;
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: * Add a ResourceCollection to the container.
061: * @param c the ResourceCollection to add.
062: * @throws BuildException on error.
063: */
064: public synchronized void add(ResourceCollection c)
065: throws BuildException {
066: if (isReference()) {
067: throw noChildrenAllowed();
068: }
069: if (c == null) {
070: return;
071: }
072: if (rc != null) {
073: throw oneNested();
074: }
075: rc = c;
076: setChecked(false);
077: }
078:
079: /**
080: * Fulfill the ResourceCollection contract.
081: * @return an Iterator of Resources.
082: */
083: public final synchronized Iterator iterator() {
084: if (isReference()) {
085: return ((BaseResourceCollectionWrapper) getCheckedRef())
086: .iterator();
087: }
088: dieOnCircularReference();
089: return new FailFast(this , cacheCollection().iterator());
090: }
091:
092: /**
093: * Fulfill the ResourceCollection contract.
094: * @return number of elements as int.
095: */
096: public synchronized int size() {
097: if (isReference()) {
098: return ((BaseResourceCollectionWrapper) getCheckedRef())
099: .size();
100: }
101: dieOnCircularReference();
102: return cacheCollection().size();
103: }
104:
105: /**
106: * Fulfill the ResourceCollection contract.
107: * @return whether this is a filesystem-only resource collection.
108: */
109: public synchronized boolean isFilesystemOnly() {
110: if (isReference()) {
111: return ((BaseResourceCollectionContainer) getCheckedRef())
112: .isFilesystemOnly();
113: }
114: dieOnCircularReference();
115:
116: if (rc == null || rc.isFilesystemOnly()) {
117: return true;
118: }
119: /* now check each Resource in case the child only
120: lets through files from any children IT may have: */
121: for (Iterator i = cacheCollection().iterator(); i.hasNext();) {
122: if (!(i.next() instanceof FileResource)) {
123: return false;
124: }
125: }
126: return true;
127: }
128:
129: /**
130: * Overrides the version of DataType to recurse on all DataType
131: * child elements that may have been added.
132: * @param stk the stack of data types to use (recursively).
133: * @param p the project to use to dereference the references.
134: * @throws BuildException on error.
135: */
136: protected synchronized void dieOnCircularReference(Stack stk,
137: Project p) throws BuildException {
138: if (isChecked()) {
139: return;
140: }
141: if (isReference()) {
142: super .dieOnCircularReference(stk, p);
143: } else {
144: if (rc instanceof DataType) {
145: stk.push(rc);
146: invokeCircularReferenceCheck((DataType) rc, stk, p);
147: stk.pop();
148: }
149: setChecked(true);
150: }
151: }
152:
153: /**
154: * Get the nested ResourceCollection.
155: * @return a ResourceCollection.
156: * @throws BuildException if no nested ResourceCollection has been provided.
157: */
158: protected final synchronized ResourceCollection getResourceCollection() {
159: dieOnCircularReference();
160: if (rc == null) {
161: throw oneNested();
162: }
163: return rc;
164: }
165:
166: /**
167: * Template method for subclasses to return a Collection of Resources.
168: * @return Collection.
169: */
170: protected abstract Collection getCollection();
171:
172: /**
173: * Format this BaseResourceCollectionWrapper as a String.
174: * @return a descriptive <code>String</code>.
175: */
176: public synchronized String toString() {
177: if (isReference()) {
178: return getCheckedRef().toString();
179: }
180: if (cacheCollection().size() == 0) {
181: return "";
182: }
183: StringBuffer sb = new StringBuffer();
184: for (Iterator i = coll.iterator(); i.hasNext();) {
185: if (sb.length() > 0) {
186: sb.append(File.pathSeparatorChar);
187: }
188: sb.append(i.next());
189: }
190: return sb.toString();
191: }
192:
193: private synchronized Collection cacheCollection() {
194: if (coll == null || !isCache()) {
195: coll = getCollection();
196: }
197: return coll;
198: }
199:
200: private BuildException oneNested() {
201: return new BuildException(super.toString() + ONE_NESTED_MESSAGE);
202: }
203:
204: }
|