/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package freemarker.template;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import freemarker.ext.beans.BeansWrapper;

A simple implementation of the TemplateSequenceModel interface, using its own underlying List for storing the list items. If you are wrapping an already existing List or array, you should certainly use DefaultMapAdapter or DefaultArrayAdapter (see comparison below).

This class is thread-safe if you don't call modifying methods (like add(Object)) after you have made the object available for multiple threads (assuming you have published it safely to the other threads; see JSR-133 Java Memory Model). These methods aren't called by FreeMarker, so it's usually not a concern.

SimpleSequence VS DefaultListAdapter/DefaultArrayAdapter - Which to use when?

For a List or array that exists regardless of FreeMarker, only you need to access it from templates, DefaultMapAdapter should be the default choice, as it can be unwrapped to the originally wrapped object (important when passing it to Java methods from the template). It also has more predictable performance (no spikes).

For a sequence that's made specifically to be used from templates, creating an empty SimpleSequence then filling it with add(Object) is usually the way to go, as the resulting sequence is significantly faster to read from templates than a DefaultListAdapter (though it's somewhat slower to read from a plain Java method to which it had to be passed adapted to a List).

It also matters if for how many times will the same List entry be read from the template(s) later, on average. If, on average, you read each entry for more than 4 times, SimpleSequence will be most certainly faster, but if for 2 times or less (and especially if not at all) then DefaultMapAdapter will be faster. Before choosing based on performance though, pay attention to the behavioral differences; SimpleSequence will shallow-copy the original List at construction time, so it won't reflect List content changes after the SimpleSequence construction, also SimpleSequence can't be unwrapped to the original wrapped instance.

See Also:
/** * A simple implementation of the {@link TemplateSequenceModel} interface, using its own underlying {@link List} for * storing the list items. If you are wrapping an already existing {@link List} or {@code array}, you should certainly * use {@link DefaultMapAdapter} or {@link DefaultArrayAdapter} (see comparison below). * * <p> * This class is thread-safe if you don't call modifying methods (like {@link #add(Object)}) after you have made the * object available for multiple threads (assuming you have published it safely to the other threads; see JSR-133 Java * Memory Model). These methods aren't called by FreeMarker, so it's usually not a concern. * * <p> * <b>{@link SimpleSequence} VS {@link DefaultListAdapter}/{@link DefaultArrayAdapter} - Which to use when?</b> * </p> * * <p> * For a {@link List} or {@code array} that exists regardless of FreeMarker, only you need to access it from templates, * {@link DefaultMapAdapter} should be the default choice, as it can be unwrapped to the originally wrapped object * (important when passing it to Java methods from the template). It also has more predictable performance (no spikes). * * <p> * For a sequence that's made specifically to be used from templates, creating an empty {@link SimpleSequence} then * filling it with {@link SimpleSequence#add(Object)} is usually the way to go, as the resulting sequence is * significantly faster to read from templates than a {@link DefaultListAdapter} (though it's somewhat slower to read * from a plain Java method to which it had to be passed adapted to a {@link List}). * * <p> * It also matters if for how many times will the <em>same</em> {@link List} entry be read from the template(s) later, * on average. If, on average, you read each entry for more than 4 times, {@link SimpleSequence} will be most * certainly faster, but if for 2 times or less (and especially if not at all) then {@link DefaultMapAdapter} will * be faster. Before choosing based on performance though, pay attention to the behavioral differences; * {@link SimpleSequence} will shallow-copy the original {@link List} at construction time, so it won't reflect * {@link List} content changes after the {@link SimpleSequence} construction, also {@link SimpleSequence} can't be * unwrapped to the original wrapped instance. * * @see DefaultListAdapter * @see DefaultArrayAdapter * @see TemplateSequenceModel */
public class SimpleSequence extends WrappingTemplateModel implements TemplateSequenceModel, Serializable {
The List that stored the elements of this sequence. It migth contains both TemplateModel elements and non-TemplateModel elements.
/** * The {@link List} that stored the elements of this sequence. It migth contains both {@link TemplateModel} elements * and non-{@link TemplateModel} elements. */
protected final List list; private List unwrappedList;
Constructs an empty simple sequence that will use the the default object wrapper set in WrappingTemplateModel.setDefaultObjectWrapper(ObjectWrapper).
Deprecated:Use SimpleSequence(ObjectWrapper) instead.
/** * Constructs an empty simple sequence that will use the the default object * wrapper set in * {@link WrappingTemplateModel#setDefaultObjectWrapper(ObjectWrapper)}. * * @deprecated Use {@link #SimpleSequence(ObjectWrapper)} instead. */
@Deprecated public SimpleSequence() { this((ObjectWrapper) null); }
Constructs an empty simple sequence with preallocated capacity and using the default object wrapper set in WrappingTemplateModel.setDefaultObjectWrapper(ObjectWrapper).
Deprecated:Use SimpleSequence(Collection, ObjectWrapper).
/** * Constructs an empty simple sequence with preallocated capacity and using * the default object wrapper set in * {@link WrappingTemplateModel#setDefaultObjectWrapper(ObjectWrapper)}. * * @deprecated Use {@link #SimpleSequence(Collection, ObjectWrapper)}. */
@Deprecated public SimpleSequence(int capacity) { list = new ArrayList(capacity); }
Constructs a simple sequence that will contain the elements from the specified Collection and will use the the default object wrapper set in WrappingTemplateModel.setDefaultObjectWrapper(ObjectWrapper).
Params:
  • collection – the collection containing initial values. Note that a copy of the collection is made for internal use.
Deprecated:Use SimpleSequence(Collection, ObjectWrapper).
/** * Constructs a simple sequence that will contain the elements * from the specified {@link Collection} and will use the the default * object wrapper set in * {@link WrappingTemplateModel#setDefaultObjectWrapper(ObjectWrapper)}. * @param collection the collection containing initial values. Note that a * copy of the collection is made for internal use. * * @deprecated Use {@link #SimpleSequence(Collection, ObjectWrapper)}. */
@Deprecated public SimpleSequence(Collection collection) { this(collection, null); }
Constructs a simple sequence from the passed collection model, which shouldn't be added to later. The internal list will be build immediately (not lazily). The resulting sequence shouldn't be extended with add(Object), because the appropriate ObjectWrapper won't be available; use SimpleSequence(Collection, ObjectWrapper) instead, if you need that.
/** * Constructs a simple sequence from the passed collection model, which shouldn't be added to later. The internal * list will be build immediately (not lazily). The resulting sequence shouldn't be extended with * {@link #add(Object)}, because the appropriate {@link ObjectWrapper} won't be available; use * {@link #SimpleSequence(Collection, ObjectWrapper)} instead, if you need that. */
public SimpleSequence(TemplateCollectionModel tcm) throws TemplateModelException { ArrayList alist = new ArrayList(); for (TemplateModelIterator it = tcm.iterator(); it.hasNext(); ) { alist.add(it.next()); } alist.trimToSize(); list = alist; }
Constructs an empty sequence using the specified object wrapper.
Params:
/** * Constructs an empty sequence using the specified object wrapper. * * @param wrapper * The object wrapper to use to wrap the list items into {@link TemplateModel} instances. {@code null} is * allowed, but deprecated, and will cause the deprecated default object wrapper (set in * {@link WrappingTemplateModel#setDefaultObjectWrapper(ObjectWrapper)}) to be used. */
public SimpleSequence(ObjectWrapper wrapper) { super(wrapper); list = new ArrayList(); }
Constructs an empty simple sequence with preallocated capacity.
Params:
Since:2.3.21
/** * Constructs an empty simple sequence with preallocated capacity. * * @param wrapper * See the similar parameter of {@link SimpleSequence#SimpleSequence(ObjectWrapper)}. * * @since 2.3.21 */
public SimpleSequence(int capacity, ObjectWrapper wrapper) { super(wrapper); list = new ArrayList(capacity); }
Constructs a simple sequence that will contain the elements from the specified Collection; consider using DefaultListAdapter instead.
Params:
  • collection – The collection containing the initial items of the sequence. A shallow copy of this collection is made immediately for internal use (thus, later modification on the parameter collection won't be visible in the resulting sequence). The items however, will be only wrapped with the ObjectWrapper lazily, when first needed.
  • wrapper – See the similar parameter of SimpleSequence(ObjectWrapper).
/** * Constructs a simple sequence that will contain the elements from the specified {@link Collection}; consider * using {@link DefaultListAdapter} instead. * * @param collection * The collection containing the initial items of the sequence. A shallow copy of this collection is made * immediately for internal use (thus, later modification on the parameter collection won't be visible in * the resulting sequence). The items however, will be only wrapped with the {@link ObjectWrapper} * lazily, when first needed. * @param wrapper * See the similar parameter of {@link SimpleSequence#SimpleSequence(ObjectWrapper)}. */
public SimpleSequence(Collection collection, ObjectWrapper wrapper) { super(wrapper); list = new ArrayList(collection); }
Adds an arbitrary object to the end of this sequence. If the newly added object does not implement the TemplateModel interface, it will be wrapped into the appropriate TemplateModel interface when it's first read (lazily).
Params:
  • obj – The object to be added.
/** * Adds an arbitrary object to the end of this sequence. If the newly added object does not implement the * {@link TemplateModel} interface, it will be wrapped into the appropriate {@link TemplateModel} interface when * it's first read (lazily). * * @param obj * The object to be added. */
public void add(Object obj) { list.add(obj); unwrappedList = null; }
Adds a boolean value to the end of this sequence. The newly added boolean will be immediately converted into TemplateBooleanModel.TRUE or TemplateBooleanModel.FALSE, without using the ObjectWrapper.
Params:
  • b – The boolean value to be added.
Deprecated:Use add(Object) instead, as this bypasses the ObjectWrapper.
/** * Adds a boolean value to the end of this sequence. The newly added boolean will be immediately converted into * {@link TemplateBooleanModel#TRUE} or {@link TemplateBooleanModel#FALSE}, without using the {@link ObjectWrapper}. * * @param b * The boolean value to be added. * * @deprecated Use {@link #add(Object)} instead, as this bypasses the {@link ObjectWrapper}. */
@Deprecated public void add(boolean b) { add(b ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE); }
Builds a deep-copy of the underlying list, unwrapping any values that were already converted to TemplateModel-s. When called for the second time (or later), it just reuses the first result, unless the sequence was modified since then.
Deprecated:No replacement exists; not a reliable way of getting back the original list elemnts.
/** * Builds a deep-copy of the underlying list, unwrapping any values that were already converted to * {@link TemplateModel}-s. When called for the second time (or later), it just reuses the first result, unless the * sequence was modified since then. * * @deprecated No replacement exists; not a reliable way of getting back the original list elemnts. */
@Deprecated public List toList() throws TemplateModelException { if (unwrappedList == null) { Class listClass = list.getClass(); List result = null; try { result = (List) listClass.newInstance(); } catch (Exception e) { throw new TemplateModelException("Error instantiating an object of type " + listClass.getName(), e); } BeansWrapper bw = BeansWrapper.getDefaultInstance(); for (int i = 0; i < list.size(); i++) { Object elem = list.get(i); if (elem instanceof TemplateModel) { elem = bw.unwrap((TemplateModel) elem); } result.add(elem); } unwrappedList = result; } return unwrappedList; }
Returns the item at the specified index of the list. If the item isn't yet an TemplateModel, it will wrap it to one now, and writes it back into the backing list.
/** * Returns the item at the specified index of the list. If the item isn't yet an {@link TemplateModel}, it will wrap * it to one now, and writes it back into the backing list. */
public TemplateModel get(int index) throws TemplateModelException { try { Object value = list.get(index); if (value instanceof TemplateModel) { return (TemplateModel) value; } TemplateModel tm = wrap(value); list.set(index, tm); return tm; } catch (IndexOutOfBoundsException e) { return null; } } public int size() { return list.size(); }
Returns:a synchronized wrapper for list.
/** * @return a synchronized wrapper for list. */
public SimpleSequence synchronizedWrapper() { return new SynchronizedSequence(); } @Override public String toString() { return list.toString(); } private class SynchronizedSequence extends SimpleSequence { @Override public void add(Object obj) { synchronized (SimpleSequence.this) { SimpleSequence.this.add(obj); } } @Override public TemplateModel get(int i) throws TemplateModelException { synchronized (SimpleSequence.this) { return SimpleSequence.this.get(i); } } @Override public int size() { synchronized (SimpleSequence.this) { return SimpleSequence.this.size(); } } @Override public List toList() throws TemplateModelException { synchronized (SimpleSequence.this) { return SimpleSequence.this.toList(); } } } }