2009/05/20 - Apache Shale has been retired.
For more information, please explore the Attic.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.shale.clay.config;
22
23 import java.io.BufferedReader;
24 import java.io.IOException;
25 import java.io.InputStreamReader;
26 import java.io.StringWriter;
27 import java.net.URL;
28 import java.nio.charset.Charset;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Map;
32
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import org.apache.shale.clay.config.beans.ComponentBean;
36 import org.apache.shale.clay.config.beans.ComponentConfigBean;
37 import org.apache.shale.clay.config.beans.ConfigBean;
38 import org.apache.shale.clay.config.beans.ElementBean;
39 import org.apache.shale.clay.config.beans.TemplateConfigBean;
40 import org.apache.shale.clay.parser.AttributeTokenizer;
41 import org.apache.shale.clay.parser.Node;
42 import org.apache.shale.clay.parser.Parser;
43 import org.apache.shale.clay.parser.Token;
44 import org.apache.shale.clay.parser.builder.Builder;
45 import org.apache.shale.clay.parser.builder.BuilderFactory;
46 import org.apache.shale.util.Messages;
47 import org.xml.sax.SAXException;
48
49 /***
50 * <p>
51 * This class is responsible for loading an HTML template into a graph of
52 * {@link ComponentBean}'s that represents a JSF component tree. It is used by
53 * {@link org.apache.shale.clay.config.beans.TemplateConfigBean}, a subclass of
54 * {@link org.apache.shale.clay.config.beans.ComponentConfigBean}.
55 * </p>
56 */
57 public class ClayTemplateParser implements ClayConfigParser {
58
59 /***
60 * <p>
61 * Commons logging utility object static instance.
62 * </p>
63 */
64 private static Log log;
65 static {
66 log = LogFactory
67 .getLog(org.apache.shale.clay.config.ClayXmlParser.class);
68 }
69
70 /***
71 * <p>
72 * Message resources for this class.
73 * </p>
74 */
75 private static Messages messages = new Messages(
76 "org.apache.shale.clay.Bundle", ClayConfigureListener.class
77 .getClassLoader());
78
79 /***
80 * <p>
81 * Object pool for HTML template configuration files.
82 * </p>
83 */
84 private ConfigBean config = null;
85
86 /***
87 * <p>
88 * Sets an object pool for HTML template configuration files.
89 * </p>
90 *
91 * @param config
92 * handler
93 */
94 public void setConfig(ConfigBean config) {
95 this.config = config;
96 }
97
98 /***
99 * <p>
100 * Returns an object pool for HTML template configuration files.
101 * </p>
102 *
103 * @return config handler
104 */
105 public ConfigBean getConfig() {
106 return config;
107 }
108
109 /***
110 * <p>
111 * Loads the <code>templateURL</code> identified by the
112 * <code>templateName</code> into a graph of {@link ComponentBean}'s.
113 * </p>
114 *
115 * @param templateURL
116 * template file
117 * @param templateName
118 * jsfid
119 * @exception SAXException
120 * XML parse error
121 * @exception IOException
122 * XML parse error
123 */
124 public void loadConfigFile(URL templateURL, String templateName)
125 throws IOException, SAXException {
126
127 ((ComponentConfigBean) config).addChild(generateElement(templateURL,
128 templateName));
129 }
130
131 /***
132 * <p>
133 * Loads the template file and parses it into a composition of metadata
134 * that's used by the {@link org.apache.shale.clay.component.Clay}
135 * component. This metadata is used to construct a JSF subtree within target
136 * view.
137 * </p>
138 *
139 * @param templateURL
140 * template file
141 * @param templateName
142 * jsfid
143 * @return config bean graph holding the template file
144 * @exception IOException
145 * loading template file
146 */
147 protected ComponentBean generateElement(URL templateURL, String templateName)
148 throws IOException {
149
150 if (log.isInfoEnabled()) {
151 log.info(messages.getMessage("loading.template",
152 new Object[] { templateName }));
153 }
154
155 ComponentBean root = new ComponentBean();
156 root.setJsfid(templateName);
157 root.setComponentType("javax.faces.HtmlOutputText");
158
159
160
161 StringBuffer buffer = loadTemplate(templateURL);
162
163 List roots = new Parser().parse(buffer);
164 Iterator ri = roots.iterator();
165 while (ri.hasNext()) {
166 Node node = (Node) ri.next();
167 Builder renderer = getBuilder(node);
168 ElementBean child = renderer.createElement(node);
169
170 root.addChild(child);
171 if (renderer.isChildrenAllowed()) {
172 renderer.encode(node, child, child);
173 } else {
174 renderer.encode(node, child, root);
175 }
176 }
177
178 roots.clear();
179 roots = null;
180 buffer.setLength(0);
181 buffer = null;
182 ri = null;
183
184
185
186 config.checkTree(root);
187
188
189 if (config instanceof TemplateConfigBean) {
190 ((TemplateConfigBean) config).optimizeTree(root);
191 }
192
193 return root;
194 }
195
196 /***
197 * <p>Loads the template file respecting the encoding type.
198 * The file encoding type is determined by calling
199 * the <code>getCharacterEncoding()</code> method.
200 * </p>
201 *
202 * @param templateURL target template to load
203 * @return content of the template
204 * @throws IOException error loading the template
205 */
206 public StringBuffer loadTemplate(URL templateURL) throws IOException {
207
208 StringBuffer buff = new StringBuffer();
209 BufferedReader in = null;
210 String enc = getCharacterEncoding(templateURL);
211
212 if (log.isDebugEnabled()) {
213 log.debug(messages.getMessage("template.encoding",
214 new Object[] { enc, templateURL.getFile() }));
215 }
216
217 try {
218
219 in = new BufferedReader(new InputStreamReader(templateURL.openStream(), enc));
220 while (in.ready()) {
221 buff.append(in.readLine()).append("\n");
222 }
223
224 } catch (IOException e) {
225 log.error(messages.getMessage("loading.template.exception",
226 new Object[] { templateURL.getFile() }), e);
227 throw e;
228 } finally {
229 if (in != null) {
230 in.close();
231 }
232 }
233
234 return buff;
235
236 }
237
238
239 /***
240 * <p>Returns the encoding type used to open the <code>templateURL</code>.
241 * The template encoding type is resolved using three overrides. The first
242 * step is to look in the target template for a comment token that defines
243 * the charset. The first 512 chars of the <code>templateURL</code> are read
244 * and scanned for a special comment token.<br/></br/>
245 *
246 * For example: <-- ### clay:page charset="UTF-8" /### --><br/><br/>
247 *
248 * If the Clay page directive is not found, the next override is an
249 * initialization parameter in the web.xml. The value of this parameter
250 * is a global override for all templates.<br/><br/>
251 *
252 * For example:<br/>
253 * <context-param><br/>
254 * <param-name>org.apache.shale.clay.HTML_TEMPLATE_CHARSET
255 * </param-name><br/>
256 * <param-value>UTF-8</param-value><br/>
257 * </context-param><br/><br/>
258 *
259 * Otherwise, the defaut is the VM's "<code>file.encoding</code>"
260 * system parameter.
261 *
262 * @param templateURL template URL
263 * @return charset encoding used to read the template
264 * @throws IOException unable to read the template document
265 */
266 public String getCharacterEncoding(URL templateURL) throws IOException {
267
268 InputStreamReader in = null;
269 StringWriter snippet = new StringWriter();
270 char[] chars = new char[512];
271 String enc = null;
272
273
274 try {
275
276 in = new InputStreamReader(templateURL.openStream());
277 int n = in.read(chars);
278 snippet.write(chars, 0, n);
279
280
281 int s = snippet.getBuffer().indexOf(Parser.START_CHARSET_TOKEN);
282 if (s > -1) {
283 int e = snippet.getBuffer().indexOf(Parser.END_CHARSET_TOKEN, s);
284 AttributeTokenizer tokenizer = new AttributeTokenizer(snippet.getBuffer(), s, e, 1, 0);
285 Iterator ti = tokenizer.iterator();
286 while (ti.hasNext()) {
287 Map.Entry attribute = (Map.Entry) ti.next();
288 Token key = (Token) attribute.getKey();
289
290
291 if (key != null && key.getRawText() != null
292 && key.getRawText().equalsIgnoreCase("charset")) {
293 Token value = (Token) attribute.getValue();
294
295
296 if (value != null && value.getRawText() != null) {
297
298 if (Charset.isSupported(value.getRawText())) {
299 enc = value.getRawText();
300 } else {
301 log.error(messages.getMessage("template.encoding.notsupported",
302 new Object[] { value.getRawText() }));
303 }
304 }
305 }
306 }
307 }
308
309
310 } finally {
311 if (in != null) {
312 in.close();
313 }
314 if (snippet != null) {
315 snippet.close();
316 }
317 }
318
319 if (enc == null) {
320 enc = getConfig().getServletContext().getInitParameter(Globals.CLAY_HTML_CHARSET);
321 if (enc != null) {
322 if (!Charset.isSupported(enc)) {
323 log.error(messages.getMessage("template.encoding.notsupported",
324 new Object[] { enc }));
325 enc = System.getProperty("file.encoding");
326 }
327 } else {
328 enc = System.getProperty("file.encoding");
329 }
330 }
331
332 return enc;
333 }
334
335 /***
336 * <p>Returns the {@link org.apache.shale.clay.parser.builder.Builder} that
337 * is assigned the task of converting the html node to a corresponding component
338 * metadata used to construct a JSF resource.</p>
339 *
340 * @param node markup node
341 * @return builder that maps markup to config beans
342 */
343 public Builder getBuilder(Node node) {
344 return BuilderFactory.getRenderer(node);
345 }
346
347 }