/* * FreeMarker: a tool that allows Java programs to generate HTML * output using templates. * Copyright (C) 1998-2005 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; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; import java.io.Reader; import java.io.Serializable; import java.io.Writer; import java.util.Arrays; /** * <p> * An <code>UnparsedTemplate</code> consists only of text. No logic beyond * simple outputting is performed. * * <p> * You can pass the filename of the template to the constructor, in which case * it is read immediately. Once read, the unparsed template is stored in an an * character array for later use. * * <p> * To process the unparsed template, call the {@link #process} method, which * takes an optional tree of {@link TemplateModel} objects as its data model. * The root node of the tree must be a {@link TemplateWriteableHashModel}. * * <p> * Any error messages will be included as HTML comments in the output. * * <p> * To facilitate multithreading, <code>UnparsedTemplate</code> objects are * immutable; if you need to recompile a template, you must make a new * <code>UnparsedTemplate</code> object. In most cases, it will be sufficient to * let a {@link TemplateCache} do this for you. * * @see TemplateCache * @version $Id: UnparsedTemplate.java 1177 2005-10-10 13:24:39Z run2000 $ */ public class UnparsedTemplate extends AbstractTemplate implements Serializable { /** * The unparsed template text. * * @serial The unparsed template text */ protected char[] templateText; private transient int hashCode; /** Serialization UUID for this class. */ private static final long serialVersionUID = 6100824836126362261L; /** * Serialized unparsed template as a String object. This means that for the * 1.2 series of JVMs, the serialized unparsed template cannot be larger * than 64kB. * * @serialField * textValue String the <code>String</code> value of this * template */ private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("textValue", String.class) }; /** * Constructs an empty unparsed template. */ public UnparsedTemplate() { } /** * Constructs an unparsed template by compiling it from an InputSource. * Calls <code>compile()</code>. * * @param source * the source of the template file to be compiled. */ public UnparsedTemplate(InputSource source) throws IOException { super(source); } /** * Constructs an unparsed template by compiling it from a file. Calls * <code>compile()</code>. * * @param filePath * the absolute path of the template file to be compiled. * @deprecated use the {@link InputSource} contructor to supply source * streams to the template compiler */ public UnparsedTemplate(String filePath) throws IOException { super(filePath); } /** * Constructs an unparsed template by compiling it from a file. Calls * <code>compile()</code>. * * @param file * a <code>File</code> representing the template file to be * compiled. * @deprecated use the {@link InputSource} contructor to supply source * streams to the template compiler */ public UnparsedTemplate(File file) throws IOException { super(file); } /** * Constructs an unparsed template by compiling it from an * <code>InputStream</code>. Calls <code>compileFromStream()</code>. * * @param stream * an <code>InputStream</code> from which the template can be * read. * @deprecated use the {@link InputSource} contructor to supply source * streams to the template compiler */ public UnparsedTemplate(InputStream stream) throws IOException { super(stream); } /** * Constructs an unparsed template by compiling it from a * <code>Reader</code>. Calls <code>compileFromStream()</code>. * * @param stream * a <code>Reader</code> from which the template can be read. * @deprecated use the {@link InputSource} contructor to supply source * streams to the template compiler */ public UnparsedTemplate(Reader stream) throws IOException { super(stream); } /** * Compiles the template from an <code>InputSource</code>. If the template * has already been compiled, this method does nothing. Calls * {@link #compileFromStream(java.io.Reader)} to perform parsing. * * @param source * an <code>InputSource</code> from which the template can be * read. */ public void compile(InputSource source) throws IOException, IllegalArgumentException { synchronized (this) { if (templateText != null) { return; } } Reader reader = source.getReader(); if (reader == null) { InputStream stream = source.getInputStream(); if (stream == null) { throw new IllegalArgumentException("InputSource contains neither character nor byte stream"); } String encoding = source.getEncoding(); if (encoding == null) { reader = new InputStreamReader(stream); } else { reader = new InputStreamReader(stream, encoding); } } StringBuffer textBuf = new StringBuffer(); BufferedReader br = new BufferedReader(reader); char cbuf[] = new char[1024]; int nSize; try { nSize = br.read(cbuf); while (nSize > 0) { textBuf.append(cbuf, 0, nSize); nSize = br.read(cbuf); } } finally { try { br.close(); } catch (IOException e1) { } } templateText = textBuf.toString().toCharArray(); hashCode = 0; } /** * Processes the contents of this <code>UnparsedTemplate</code> and outputs * the resulting text to a <code>Writer</code>. * * @param modelRoot * the root node of the data model. * @param out * a <code>Writer</code> to send the output to. * @param eventHandler * a <code>TemplateEventAdapter</code> for handling any events * that occur during processing. * @throws IOException * an IO error occurred with the <code>Writer</code> during * processing */ public short process(TemplateWriteableHashModel modelRoot, Writer out, TemplateRuntimeHandler eventHandler) throws IOException { if (templateText != null) { out.write(templateText); } return TemplateProcessor.OK; } /** * Processes the template, using data from a template model, and outputs the * resulting text to a <code>Writer</code>. * * @param modelRoot * the root node of the data model. * @param out * a <code>Writer</code> to output the text to. */ public void process(TemplateWriteableHashModel modelRoot, Writer out) throws IOException { if (templateText != null) { out.write(templateText); } } /** * Processes the template, using an empty data model, and outputs the * resulting text to a <code>Writer</code>. * * @param out * a <code>Writer</code> to output the text to. */ public void process(Writer out) throws IOException { if (templateText != null) { out.write(templateText); } } /** * Return the String value of this object * * @return the template text, if any, of this object */ public String toString() { if (templateText == null) { return "null"; } return new String(templateText); } /** * Tests this object for equality with the given object. * * @param o * the object to be compared against * @return <code>true</code> if the two objects are equal, otherwise * <code>false</code> */ public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof UnparsedTemplate)) { return false; } final UnparsedTemplate unparsedTemplate = (UnparsedTemplate) o; return Arrays.equals(templateText, unparsedTemplate.templateText); } /** * Retrieve the hash code for this object * * @return a hash value corresponding to the value of this object */ public int hashCode() { if (hashCode == 0) { if (templateText == null) { hashCode = 3; } hashCode = new String(templateText).hashCode() + 3; } return hashCode; } /** * For serialization, write this object as a String. * * @param stream * the output stream to write this object to */ private void writeObject(ObjectOutputStream stream) throws IOException { ObjectOutputStream.PutField fields = stream.putFields(); if (templateText == null) { fields.put("textValue", null); } else { fields.put("textValue", new String(templateText)); } stream.writeFields(); } /** * For serialization, read this object as a String. * * @param stream * the input stream to read serialized objects from */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { ObjectInputStream.GetField fields = stream.readFields(); String value = (String) fields.get("textValue", null); // Recreate the original value if (value != null) { templateText = value.toCharArray(); } } }