/*
* FreeMarker: a tool that allows Java programs to generate HTML
* output using templates.
* Copyright (C) 1998-2004 Benjamin Geer
* Email: beroul@users.sourceforge.net
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
package freemarker.template.expression;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.ObjectStreamField;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import freemarker.template.FastList;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateWriteableHashModel;
/**
* Represents a literal List model in a FM-Classic template. The list model is
* not evaluated until run time, since the model may contain variables or other
* more complex expressions that can't be determined at compile time.
*
* @version $Id: ListLiteral.java 1149 2005-10-09 07:41:19Z run2000 $
*/
public final class ListLiteral implements Expression, Serializable {
private List values;
/** Serialization UUID for this class. */
private static final long serialVersionUID = -470184301148036096L;
/**
* Serialized form is an array of zero or more Expression objects. This is
* primarily for type correctness and avoids having to serialize a List
* object.
*
* @serialField
* values Expression[] an array of Expression objects that
* will be evaluated as template models at run time
*/
private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("values", Expression[].class) };
/**
* Constructor that takes a list of {@link Expression} elements to be
* evaluated as a list model at run time.
*
* @param values
* the values to be added to the <code>ListLiteral</code>
* @throws NullPointerException
* the value list is null
*/
public ListLiteral(List values) {
if (values == null) {
throw new NullPointerException("List literal cannot be null");
}
this.values = values;
}
/**
* The {@link freemarker.template.TemplateModel} value of this
* <code>Expression</code>.
*
* @param modelRoot
* the template model that will be evaluated by the expression
* @return a <code>FastList</code> containing the values in the list model
* @throws TemplateException
* the expression could not be evaluated for some reason
*/
public TemplateModel getAsTemplateModel(TemplateWriteableHashModel modelRoot) throws TemplateException {
return new FastList(getModelList(modelRoot));
}
/**
* For the benefit of method calls, return the list of arguments as a list
* of <code>TemplateModel</code> values.
*
* @param modelRoot
* the template model that will be evaluated by the expression
* @return a <code>List</code> of <code>TemplateModel</code>s contained in
* the <code>ListLiteral</code>
* @throws TemplateException
* the literal list could not be returned
*/
public List getModelList(TemplateWriteableHashModel modelRoot) throws TemplateException {
List list = new ArrayList(values.size());
Iterator iterator = values.iterator();
while (iterator.hasNext()) {
Expression cItem = (Expression) iterator.next();
list.add(cItem.getAsTemplateModel(modelRoot));
}
return list;
}
/**
* For the benefit of method calls, return the list of arguments as a list
* of <code>String</code> values.
*
* @param modelRoot
* the template model that will be evaluated by the expression
* @return a <code>List</code> of <code>TemplateModel</code>s contained in
* the <code>ListLiteral</code>
* @throws TemplateException
* the literal list could not be returned
*/
public List getValueList(TemplateWriteableHashModel modelRoot) throws TemplateException {
List list = new ArrayList(values.size());
Iterator iterator = values.iterator();
while (iterator.hasNext()) {
Expression cItem = (Expression) iterator.next();
list.add(ExpressionUtils.getAsStringOrEmpty(cItem.getAsTemplateModel(modelRoot)));
}
return list;
}
/**
* Has the <code>ListLiteral</code> been populated?
*
* @return <code>true</code> if the <code>ListLiteral</code> is populated,
* otherwise <code>false</code>
*/
public boolean isComplete() {
return true;
}
/**
* Determine the type of result that can be calculated by this expression.
* This is in the form of an integer constant ored together from values in
* the {@link ExpressionUtils} class.
*/
public int getType() {
return ExpressionUtils.EXPRESSION_TYPE_LIST;
}
/**
* Determine whether result calculated by this expression is a constant
* value.
*/
public boolean isConstant() {
return false;
}
/**
* For serialization, write this object as an array of Expressions.
*
* @param stream
* the output stream to write this object to
*/
private void writeObject(ObjectOutputStream stream) throws IOException {
ObjectOutputStream.PutField fields = stream.putFields();
Expression[] expressionArray = new Expression[values.size()];
values.toArray(expressionArray);
fields.put("values", expressionArray);
stream.writeFields();
}
/**
* For serialization purposes, resolve a deserialized instance to an
* instance in the expression cache.
*/
private Object readResolve() throws ObjectStreamException {
return ExpressionCache.cacheExpression(this);
}
/**
* For serialization, read this object as an array of Expressions, then
* check whether the list has been deserialized to null.
*
* @param stream
* the input stream to read serialized objects from
*/
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
ObjectInputStream.GetField fields = stream.readFields();
Expression[] expressionArray = (Expression[]) fields.get("values", null);
if (expressionArray == null) {
throw new InvalidObjectException("Cannot create a ListLiteral with a null value list");
}
values = new ArrayList(Arrays.asList(expressionArray));
}
/**
* Returns a string representation of the object.
*
* @return a <code>String</code> representation of this expression
*/
public String toString() {
return values.toString();
}
/**
* Determines whether this object is equal to the given object.
*
* @param o
* the object to be compared with
* @return <code>true</code> if the objects are equal, otherwise
* <code>false</code>
*/
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof ListLiteral)) {
return false;
}
final ListLiteral listLiteral = (ListLiteral) o;
return values.equals(listLiteral.values);
}
/**
* Returns the hash code for this operator.
*
* @return the hash code of this object
*/
public int hashCode() {
return values.hashCode() + 13;
}
/**
* Resolves the current expression, possibly into a different expression
* object. This is loosely equivalent to the serialization protocol's
* <code>readResolve</code> method. Situations where this may be used are:
* <ul>
* <li>Caching frequently-used expression objects</li>
* <li>Evaluating constant expressions, and returning a constant reference</li>
* </ul>
*/
public Expression resolveExpression() throws TemplateException {
// See if we can intelligently determine whether all the elements
// in this list are constant
List list = new ArrayList(values.size());
Iterator iterator = values.iterator();
while (iterator.hasNext()) {
Expression cItem = (Expression) iterator.next();
if (cItem.isConstant()) {
list.add(cItem.getAsTemplateModel(ExpressionBuilder.emptyModel));
} else {
list = null;
break;
}
}
Expression expr = this;
if (list != null) {
expr = new Constant(new FastList(list));
}
return ExpressionCache.cacheExpression(expr);
}
}