Source code: nextapp/echoservlet/Template.java
1 /*
2 * This file is part of the Echo Web Application Framework (hereinafter "Echo").
3 * Copyright (C) 2002-2004 NextApp, Inc.
4 *
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * Alternatively, the contents of this file may be used under the terms of
18 * either the GNU General Public License Version 2 or later (the "GPL"), or
19 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
20 * in which case the provisions of the GPL or the LGPL are applicable instead
21 * of those above. If you wish to allow use of your version of this file only
22 * under the terms of either the GPL or the LGPL, and not to allow others to
23 * use your version of this file under the terms of the MPL, indicate your
24 * decision by deleting the provisions above and replace them with the notice
25 * and other provisions required by the GPL or the LGPL. If you do not delete
26 * the provisions above, a recipient may use your version of this file under
27 * the terms of any one of the MPL, the GPL or the LGPL.
28 */
29
30 package nextapp.echoservlet;
31
32 import java.io.IOException;
33 import java.io.PrintWriter;
34 import java.io.Serializable;
35 import java.util.ArrayList;
36 import java.util.List;
37
38 import nextapp.echoservlet.util.ContentType;
39 import nextapp.echoservlet.util.Extensions;
40 import nextapp.echoservlet.util.JavaScriptCompressor;
41 import nextapp.echoservlet.util.Resource;
42
43 /**
44 * A service which produces templated text output. Templates are used in
45 * cases where the server must render a mostly static document that contains
46 * text that may vary. This service is similar to <code>StaticText</code>,
47 * but offers the ability to have dynamically inserted text at specified
48 * points. A dynamic area is specified by adding a "variable name"
49 * to the template. Variables names are enclosed in dollar signs.<br>
50 * <br>
51 * The following example document shows how a Template can be used:<br>
52 * <br>
53 * <code>In the year $Year$, annual sales were $$$AnnualSales$.</code><br>
54 * <br>
55 * This sample has two variables, "Year" and "Annual
56 * Sales". The double dollar sign will be rendered as a single dollar
57 * sign. The variable data will be extracted from the renderer's
58 * <code>VariableData</code> object.
59 */
60 public class Template
61 implements Service, Serializable {
62
63 private static final String MARKERSTRING = "$";
64
65 /**
66 * Creates a <code>Template</code> object by way of a resource name.
67 * The content type will be determined from the resource's extension.
68 * JavaScript resources will be compressed.
69 *
70 * @param resourceName The name of the resource from which to create the
71 * <code>StaticText</code> object.
72 * @return A <code>Template</code> object containing the content of the
73 * specified resource.
74 * @deprecated The createFromResource(String, String) method
75 * should be used instead. Use of this version may result
76 * in errors if applications are migrated between VMs,
77 * due to the Service having a different identifier on each VM.
78 */
79 public static Template createFromResource(String resourceName) {
80 return createFromResource(null, resourceName);
81 }
82
83 /**
84 * Creates a <code>Template</code> object by way of a resource name.
85 * The content type will be determined from the resource's extension.
86 * JavaScript resources will be compressed.
87 *
88 * @param identifier A unique identifier for this service.
89 * @param resourceName The name of the resource from which to create the
90 * <code>StaticText</code> object.
91 * @return A <code>Template</code> object containing the content of the
92 * specified resource.
93 */
94 public static Template createFromResource(String identifier, String resourceName) {
95 ContentType contentType = Extensions.getFileNameContentType(resourceName);
96 String resource = Resource.getResourceAsString(resourceName);
97 if (contentType.equals(ContentType.TEXT_JAVASCRIPT)) {
98 resource = JavaScriptCompressor.compress(resource);
99 }
100 Template template = new Template(identifier, contentType, resource);
101 return template;
102 }
103
104 private String[] staticSections = null;
105 private String[] fieldNames = null;
106
107 private ContentType contentType = null;
108 private Id id = null;
109
110 /**
111 * Creates a content template from the given String.
112 *
113 * @param contentType The content type of the output.
114 * @param templateData A String containing the template data.
115 * @deprecated The Template(String, ContentType, String) constructor
116 * should be used instead. Use of this version may result
117 * in errors if applications are migrated between VMs,
118 * due to the Service having a different identifier on each VM.
119 */
120 public Template(ContentType contentType, String templateData) {
121 this(null, contentType, templateData);
122 }
123
124 /**
125 * Creates a content template from the given String.
126 *
127 * @param contentType The content type of the output.
128 * @param templateData A String containing the template data.
129 * @param identifier A unique identifier for this service.
130 */
131 public Template(String identifier, ContentType contentType, String templateData) {
132 super();
133
134 if (identifier == null) {
135 id = new Id();
136 } else {
137 id = new Id(identifier);
138 }
139 this.contentType = contentType;
140
141 List fieldNameList = new ArrayList();
142 List staticSectionList = new ArrayList();
143
144 int startPosition = 0;
145 int endPosition = 0;
146 do {
147 // Search for next field name
148 endPosition = templateData.indexOf(MARKERSTRING, startPosition);
149
150 // Add static text up to variable to list of static content.
151 if (endPosition == -1) {
152 staticSectionList.add(templateData.substring(startPosition));
153 } else {
154 staticSectionList.add(templateData.substring(startPosition, endPosition));
155 }
156
157 if (endPosition != -1) {
158 // determine variable name.
159 startPosition = endPosition + 1;
160 endPosition = templateData.indexOf(MARKERSTRING, startPosition);
161
162 if (endPosition == startPosition) {
163 // MARKERSTRING detected twice, therefore MARKERSTRING is added to last static
164 // content section.
165 staticSectionList.set(staticSectionList.size() - 1,
166 staticSectionList.get(staticSectionList.size() - 1) + MARKERSTRING);
167 } else if (endPosition == -1) {
168 // MARKERSTRING was not found again, meaning document is not valid.
169 throw new IllegalArgumentException("Data contains unterminated variable string.");
170 } else {
171 // Add field name to list
172 fieldNameList.add(templateData.substring(startPosition, endPosition));
173 }
174
175 startPosition = endPosition + 1;
176 }
177 } while (endPosition != -1);
178
179 staticSections = (String[]) staticSectionList.toArray(new String[staticSectionList.size()]);
180 fieldNames = (String[]) fieldNameList.toArray(new String[fieldNameList.size()]);
181 }
182
183 /**
184 * Returns the content type.
185 *
186 * @return The content type.
187 */
188 public ContentType getContentType() {
189 return contentType;
190 }
191
192 /**
193 * @see nextapp.echoservlet.Service#getId()
194 */
195 public Id getId() {
196 return id;
197 }
198
199 /**
200 * @see nextapp.echoservlet.Service#service(Connection)
201 */
202 public void service(Connection conn)
203 throws IOException {
204 conn.setContentType(contentType);
205 PrintWriter pw = conn.getWriter();
206
207 VariableData variableData = conn.getVariableData();
208 String customField = null;
209 for (int index = 0; index < staticSections.length; ++index) {
210 if (staticSections[index] != null) {
211 pw.write(staticSections[index]);
212 }
213 if (index < fieldNames.length && fieldNames[index] != null) {
214 customField = variableData.get(fieldNames[index]);
215 if (customField != null) {
216 pw.write(customField);
217 }
218 }
219 }
220
221 pw.close();
222 }
223
224 /**
225 * Renders the content template as a String. Should be used only for
226 * debugging purposes.
227 *
228 * @return A String representation of the template.
229 */
230 public String toString() {
231 StringBuffer sb = new StringBuffer();
232
233 for (int index = 0; index < staticSections.length; ++index) {
234 if (staticSections[index] != null) {
235 sb.append("STATIC: " + staticSections[index]);
236 }
237 sb.append("\n");
238 if (fieldNames.length - 1 >= index && fieldNames[index] != null) {
239 sb.append("DYNAMIC: \"" + fieldNames[index] + "\"");
240 }
241 sb.append("\n");
242 }
243
244 return sb.toString();
245 }
246 }