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.ArrayList;
057: import java.util.Collection;
058: import java.util.Collections;
059: import java.util.List;
060:
061: import freemarker.ext.beans.BeansWrapper;
062:
063: /**
064: * <p>A convenient implementation of a list. This
065: * object implements {@link TemplateSequenceModel}, using an underlying
066: * <tt>java.util.List</tt> implementation.</p>
067: *
068: * <p>A <tt>SimpleSequence</tt> can act as a cache for a
069: * <tt>TemplateCollectionModel</tt>, e.g. one that gets data from a
070: * database. When passed a <tt>TemplateCollectionModel</tt> as an
071: * argument to its constructor, the <tt>SimpleSequence</tt> immediately
072: * copies all the elements and discards the <tt>TemplateCollectionModel</tt>.</p>
073: *
074: * <p>This class is thread-safe if you don't call the <tt>add</tt> method after you
075: * have made the object available for multiple threads.
076: *
077: * <p><b>Note:</b><br />
078: * As of 2.0, this class is unsynchronized by default.
079: * To obtain a synchronized wrapper, call the {@link #synchronizedWrapper} method.</p>
080: *
081: * @version $Id: SimpleSequence.java,v 1.53 2005/06/21 18:17:54 ddekany Exp $
082: * @see SimpleHash
083: * @see SimpleScalar
084: */
085: public class SimpleSequence extends WrappingTemplateModel implements
086: TemplateSequenceModel, Serializable {
087:
088: /**
089: * @serial The <tt>List</tt> that this <tt>SimpleSequence</tt> wraps.
090: */
091: protected final List list;
092: private List unwrappedList;
093:
094: /**
095: * Constructs an empty simple sequence that will use the the default object
096: * wrapper set in
097: * {@link WrappingTemplateModel#setDefaultObjectWrapper(ObjectWrapper)}.
098: */
099: public SimpleSequence() {
100: this ((ObjectWrapper) null);
101: }
102:
103: /**
104: * Constructs an empty simple sequence with preallocated capacity and using
105: * the default object wrapper set in
106: * {@link WrappingTemplateModel#setDefaultObjectWrapper(ObjectWrapper)}.
107: */
108: public SimpleSequence(int capacity) {
109: list = new ArrayList(capacity);
110: }
111:
112: /**
113: * Constructs a simple sequence that will contain the elements
114: * from the specified {@link Collection} and will use the the default
115: * object wrapper set in
116: * {@link WrappingTemplateModel#setDefaultObjectWrapper(ObjectWrapper)}.
117: * @param collection the collection containing initial values. Note that a
118: * copy of the collection is made for internal use.
119: */
120: public SimpleSequence(Collection collection) {
121: this (collection, null);
122: }
123:
124: /**
125: * Constructs a simple sequence from the passed collection model using the
126: * default object wrapper set in
127: * {@link WrappingTemplateModel#setDefaultObjectWrapper(ObjectWrapper)}.
128: */
129: public SimpleSequence(TemplateCollectionModel tcm)
130: throws TemplateModelException {
131: ArrayList alist = new ArrayList();
132: for (TemplateModelIterator it = tcm.iterator(); it.hasNext();) {
133: alist.add(it.next());
134: }
135: alist.trimToSize();
136: list = alist;
137: }
138:
139: /**
140: * Constructs an empty simple sequence using the specified object wrapper.
141: * @param wrapper The object wrapper to use to wrap objects into
142: * {@link TemplateModel} instances. If null, the default wrapper set in
143: * {@link WrappingTemplateModel#setDefaultObjectWrapper(ObjectWrapper)} is
144: * used.
145: */
146: public SimpleSequence(ObjectWrapper wrapper) {
147: super (wrapper);
148: list = new ArrayList();
149: }
150:
151: /**
152: * Constructs a simple sequence that will contain the elements
153: * from the specified {@link Collection} and will use the specified object
154: * wrapper.
155: * @param collection the collection containing initial values. Note that a
156: * copy of the collection is made for internal use.
157: * @param wrapper The object wrapper to use to wrap objects into
158: * {@link TemplateModel} instances. If null, the default wrapper set in
159: * {@link WrappingTemplateModel#setDefaultObjectWrapper(ObjectWrapper)} is
160: * used.
161: */
162: public SimpleSequence(Collection collection, ObjectWrapper wrapper) {
163: super (wrapper);
164: list = new ArrayList(collection);
165: }
166:
167: /**
168: * Adds an arbitrary object to the end of this <tt>SimpleSequence</tt>.
169: * If the object itself does not implement the {@link TemplateModel}
170: * interface, it will be wrapped into an appropriate adapter on the first
171: * call to {@link #get(int)}.
172: *
173: * @param obj the boolean to be added.
174: */
175: public void add(Object obj) {
176: list.add(obj);
177: unwrappedList = null;
178: }
179:
180: /**
181: * Adds a boolean to the end of this <tt>SimpleSequence</tt>, by
182: * coercing the boolean into {@link TemplateBooleanModel#TRUE} or
183: * {@link TemplateBooleanModel#FALSE}.
184: *
185: * @param b the boolean to be added.
186: */
187: public void add(boolean b) {
188: if (b) {
189: add(TemplateBooleanModel.TRUE);
190: } else {
191: add(TemplateBooleanModel.FALSE);
192: }
193: }
194:
195: /**
196: * Note that this method creates and returns a deep-copy of the underlying list used
197: * internally. This could be a gotcha for some people
198: * at some point who want to alter something in the data model,
199: * but we should maintain our immutability semantics (at least using default SimpleXXX wrappers)
200: * for the data model. It will recursively unwrap the stuff in the underlying container.
201: */
202: public List toList() throws TemplateModelException {
203: if (unwrappedList == null) {
204: Class listClass = list.getClass();
205: List result = null;
206: try {
207: result = (List) listClass.newInstance();
208: } catch (Exception e) {
209: throw new TemplateModelException(
210: "Error instantiating an object of type "
211: + listClass.getName() + "\n"
212: + e.getMessage());
213: }
214: BeansWrapper bw = BeansWrapper.getDefaultInstance();
215: for (int i = 0; i < list.size(); i++) {
216: Object elem = list.get(i);
217: if (elem instanceof TemplateModel) {
218: elem = bw.unwrap((TemplateModel) elem);
219: }
220: result.add(elem);
221: }
222: unwrappedList = result;
223: }
224: return unwrappedList;
225: }
226:
227: /**
228: * @return the specified index in the list
229: */
230: public TemplateModel get(int i) throws TemplateModelException {
231: try {
232: Object value = list.get(i);
233: if (value instanceof TemplateModel) {
234: return (TemplateModel) value;
235: }
236: TemplateModel tm = wrap(value);
237: list.set(i, tm);
238: return tm;
239: } catch (IndexOutOfBoundsException e) {
240: return null;
241: // throw new TemplateModelException(i + " out of bounds [0, " + list.size() + ")");
242: }
243: }
244:
245: public int size() {
246: return list.size();
247: }
248:
249: /**
250: * @return a synchronized wrapper for list.
251: */
252: public SimpleSequence synchronizedWrapper() {
253: return new SynchronizedSequence();
254: }
255:
256: public String toString() {
257: return list.toString();
258: }
259:
260: private class SynchronizedSequence extends SimpleSequence {
261:
262: public void add(Object obj) {
263: synchronized (SimpleSequence.this ) {
264: SimpleSequence.this .add(obj);
265: }
266: }
267:
268: public TemplateModel get(int i) throws TemplateModelException {
269: synchronized (SimpleSequence.this ) {
270: return SimpleSequence.this .get(i);
271: }
272: }
273:
274: public int size() {
275: synchronized (SimpleSequence.this ) {
276: return SimpleSequence.this .size();
277: }
278: }
279:
280: public List toList() throws TemplateModelException {
281: synchronized (SimpleSequence.this) {
282: return SimpleSequence.this.toList();
283: }
284: }
285: }
286: }
|