001: /*
002: * Copyright (c) 2003 The Visigoth Software Society. All rights
003: * reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions
007: * are met:
008: *
009: * 1. Redistributions of source code must retain the above copyright
010: * notice, this list of conditions and the following disclaimer.
011: *
012: * 2. Redistributions in binary form must reproduce the above copyright
013: * notice, this list of conditions and the following disclaimer in
014: * the documentation and/or other materials provided with the
015: * distribution.
016: *
017: * 3. The end-user documentation included with the redistribution, if
018: * any, must include the following acknowledgement:
019: * "This product includes software developed by the
020: * Visigoth Software Society (http://www.visigoths.org/)."
021: * Alternately, this acknowledgement may appear in the software itself,
022: * if and wherever such third-party acknowledgements normally appear.
023: *
024: * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
025: * project contributors may be used to endorse or promote products derived
026: * from this software without prior written permission. For written
027: * permission, please contact visigoths@visigoths.org.
028: *
029: * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
030: * nor may "FreeMarker" or "Visigoth" appear in their names
031: * without prior written permission of the Visigoth Software Society.
032: *
033: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
034: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
035: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
036: * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
037: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
038: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
039: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
040: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
041: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
042: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
043: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
044: * SUCH DAMAGE.
045: * ====================================================================
046: *
047: * This software consists of voluntary contributions made by many
048: * individuals on behalf of the Visigoth Software Society. For more
049: * information on the Visigoth Software Society, please see
050: * http://www.visigoths.org/
051: */
052:
053: package freemarker.template;
054:
055: import java.io.Serializable;
056: import java.util.*;
057:
058: /**
059: * A simple implementation of {@link TemplateCollectionModel}.
060: * It's able to wrap <tt>java.util.Iterator</tt>-s and <tt>java.util.Collection</tt>-s.
061: * If you wrap an <tt>Iterator</tt>, the variable can be <list>-ed (<forach>-ed) only once!
062: *
063: * <p>Consider using {@link SimpleSequence} instead of this class if you want to wrap <tt>Iterator</tt>s.
064: * <tt>SimpleSequence</tt> will read all elements of the <tt>Iterator</tt>, and store them in a <tt>List</tt>
065: * (this may cause too high resource consumption in some applications), so you can list the variable
066: * for unlimited times. Also, if you want to wrap <tt>Collection</tt>s, and then list the resulting
067: * variable for many times, <tt>SimpleSequence</tt> may gives better performance, as the
068: * wrapping of non-<tt>TemplateModel</tt> objects happens only once.
069: *
070: * <p>This class is thread-safe. The returned <tt>TemplateModelIterator</tt>-s
071: * are <em>not</em> thread-safe.
072: *
073: * @version $Id: SimpleCollection.java,v 1.13 2004/11/27 14:49:57 ddekany Exp $
074: */
075: public class SimpleCollection extends WrappingTemplateModel implements
076: TemplateCollectionModel, Serializable {
077:
078: private boolean iteratorDirty;
079: private Iterator iterator;
080: private Collection collection;
081:
082: public SimpleCollection(Iterator iterator) {
083: this .iterator = iterator;
084: }
085:
086: public SimpleCollection(Collection collection) {
087: this .collection = collection;
088: }
089:
090: public SimpleCollection(Iterator iterator, ObjectWrapper wrapper) {
091: super (wrapper);
092: this .iterator = iterator;
093: }
094:
095: public SimpleCollection(Collection collection, ObjectWrapper wrapper) {
096: super (wrapper);
097: this .collection = collection;
098: }
099:
100: /**
101: * Retrieves a template model iterator that is used to iterate over the elements in this collection.
102: *
103: * <p>When you wrap an <tt>Iterator</tt> and you get <tt>TemplateModelIterator</tt> for multiple times,
104: * only on of the returned <tt>TemplateModelIterator</tt> instances can be really used. When you have called a
105: * method of a <tt>TemplateModelIterator</tt> instance, all other instance will throw a
106: * <tt>TemplateModelException</tt> when you try to call their methods, since the wrapped <tt>Iterator</tt>
107: * can't return the first element.
108: */
109: public TemplateModelIterator iterator() {
110: if (iterator != null) {
111: return new SimpleTemplateModelIterator(iterator, true);
112: } else {
113: synchronized (collection) {
114: return new SimpleTemplateModelIterator(collection
115: .iterator(), false);
116: }
117: }
118: }
119:
120: /*
121: * An instance of this class must be accessed only from a single thread.
122: * The encapsulated Iterator may accessible from multiple threads (as multiple
123: * SimpleTemplateModelIterator instance can wrap the same Iterator instance),
124: * but the first thread which uses the shared Iterator will monopolize that.
125: */
126: private class SimpleTemplateModelIterator implements
127: TemplateModelIterator {
128:
129: private Iterator iterator;
130: private boolean iteratorShared;
131:
132: SimpleTemplateModelIterator(Iterator iterator,
133: boolean iteratorShared) {
134: this .iterator = iterator;
135: this .iteratorShared = iteratorShared;
136: }
137:
138: public TemplateModel next() throws TemplateModelException {
139: if (iteratorShared)
140: makeIteratorDirty();
141:
142: if (!iterator.hasNext()) {
143: throw new TemplateModelException(
144: "The collection has no more elements.");
145: }
146:
147: Object value = iterator.next();
148: if (value instanceof TemplateModel) {
149: return (TemplateModel) value;
150: } else {
151: return wrap(value);
152: }
153: }
154:
155: public boolean hasNext() throws TemplateModelException {
156: /*
157: * Theorically this should not make the iterator dirty,
158: * but I met sync. problems if I don't do it here. :(
159: */
160: if (iteratorShared)
161: makeIteratorDirty();
162: return iterator.hasNext();
163: }
164:
165: private void makeIteratorDirty() throws TemplateModelException {
166: synchronized (SimpleCollection.this ) {
167: if (iteratorDirty) {
168: throw new TemplateModelException(
169: "This collection variable wraps a java.util.Iterator, "
170: + "thus it can be <list>-ed or <foreach>-ed only once");
171: } else {
172: iteratorDirty = true;
173: iteratorShared = false;
174: }
175: }
176: }
177: }
178: }
|