KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > wings > template > parser > PageParser


1 /*
2  * $Id: PageParser.java,v 1.5 2005/01/28 14:47:51 blueshift Exp $
3  * Copyright 2000,2005 wingS development team.
4  *
5  * This file is part of wingS (http://www.j-wings.org).
6  *
7  * wingS is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU Lesser General Public License
9  * as published by the Free Software Foundation; either version 2.1
10  * of the License, or (at your option) any later version.
11  *
12  * Please see COPYING for the complete licence.
13  */

14 package org.wings.template.parser;
15
16 import org.wings.template.FileTemplateSource;
17 import org.wings.template.LabelTagHandler;
18 import org.wings.template.TemplateSource;
19 import org.apache.commons.logging.Log;
20 import org.apache.commons.logging.LogFactory;
21
22 import java.io.*;
23 import java.util.*;
24
25 /**
26  * <CODE>PageParser</CODE>
27  * parses SGML markup'd pages and executes
28  * <em>active</em> Tag. Returns its output
29  * through a HttpServletResponse (given in a ParseContext).
30  * Active Tags are handled with SpecialTagHandlers which
31  * can be registered for a specific Tag.
32  * <p/>
33  * <p><h4>Error handling:</h4>
34  * To simplify error detection and correction,
35  * exceptions thrown by the <CODE>executeTag()</CODE>-methods of the
36  * pluggable handlers (e.g. called servlets) are printed,
37  * enclosed in comments ("&lt;!-- ... --&gt;"), in the HTML output.
38  *
39  * @author <A HREF="mailto:zeller@think.de">Henner Zeller</A>
40  * @version $Revision: 1.5 $ $Date: 2005/01/28 14:47:51 $
41  * @see javax.servlet.http.HttpServlet
42  */

43
44 public class PageParser {
45     private final static Log log = LogFactory.getLog(PageParser.class);
46
47     private static PageParser sharedInstance = null;
48
49     /**
50      * returns a singleton instance of this PageParser.
51      * You usually want to use the singleton instance to make
52      * use of a central cache.
53      */

54     public static PageParser getInstance() {
55         if (sharedInstance == null) {
56             synchronized (PageParser.class) {
57                 if (sharedInstance == null)
58                     sharedInstance = new PageParser();
59             }
60         }
61
62         return sharedInstance;
63     }
64
65     /**
66      * This Map contains the cached parsed
67      * pages, saved in TemplateSourceInfo-Objects.
68      * The key is the canonical name of the Data
69      * Source.
70      */

71     private final Map/*<String, TemplateSourceInfo>*/ pages = new HashMap();
72
73     /**
74      * a Hashtable with key/value=tagname/handlerClass
75      */

76     private final Map/*<String,Class>*/ handlerClasses = new HashMap();
77
78     /**
79      * Constructs a new PageParser.
80      */

81     public PageParser() {
82     }
83
84     /**
85      * Process a general DataStore representing a Template
86      *
87      * @param source The template TemplateSource
88      * @param context The context used while parsing; contains
89      * at least the HttpServletRequest and
90      * HttpServletResponse.
91      * @see ParseContext
92      * @see TemplateSource
93      */

94     public void process(TemplateSource source,
95                         ParseContext context)
96             throws IOException {
97         interpretPage(source, getPageParts(source, context), context);
98     }
99
100     /**
101      * Processes a file.
102      *
103      * @param file The file containing SGML markup
104      * @param context The context used while parsing; contains
105      * at least the HttpServletRequest and HttpServletResponse.
106      * @see ParseContext
107      */

108     public void process(File file, ParseContext context)
109             throws IOException {
110         process(new FileTemplateSource(file), context);
111     }
112
113     public Map getLabels(TemplateSource source) {
114         String JavaDoc cName = source.getCanonicalName();
115         if (cName == null)
116             return null;
117
118         TemplateSourceInfo sourceInfo = (TemplateSourceInfo) pages.get(cName);
119         if (sourceInfo == null)
120             return null;
121
122         return sourceInfo.labels;
123     }
124
125     /**
126      * register a handler for a specific tag (Class name).
127      * Tags are case-insensitive.
128      *
129      * @param tagname the name of the tag like 'MYSPECIALTAG' or 'SERVLET'
130      * @param handlerClassName the <em>name of class</em> implementing the
131      * action for this tag. This class must
132      * implement the SpecialTagHandler
133      * interface.
134      * @throws ClassNotFoundException if the class with the specified
135      * name is not found.
136      */

137     public void addTagHandler(String JavaDoc tagname, String JavaDoc handlerClassName)
138             throws ClassNotFoundException JavaDoc {
139         handlerClasses.put(tagname.toUpperCase(), Class.forName(handlerClassName));
140     }
141
142     /**
143      * register a handler for a specific tag (Class).
144      * Tags are case-insensitive.
145      *
146      * @param tagname the name of the tag like 'MYSPECIALTAG' or 'SERVLET'
147      * @param handlerClass the <em>class</em> implementing the
148      * action for this tag. This class must
149      * implement the SpecialTagHandler
150      * interface.
151      */

152     public void addTagHandler(String JavaDoc tagname, Class JavaDoc handlerClass) {
153         handlerClasses.put(tagname.toUpperCase(), handlerClass);
154     }
155
156     /**
157      * @return Itearator of all Tags which are special to
158      * this PageParser
159      */

160     public Iterator getRegisteredTags() {
161         return handlerClasses.keySet().iterator();
162     }
163
164     /**
165      * If TemplateSource has changed or has not yet been loaded, load
166      * it and chop into sections, storing result for future use.
167      * Otherwise, return stored preprocessed page.
168      *
169      * @param source TemplateSource for which we want page section list
170      * @return list of page sections, as described in parsePage().
171      * @see #parsePage
172      */

173     private List getPageParts(TemplateSource source, ParseContext context)
174             throws IOException {
175         // first, check to see if we have cached version
176
String JavaDoc cName = source.getCanonicalName();
177         TemplateSourceInfo sourceInfo = null;
178         if (cName != null)
179             sourceInfo = (TemplateSourceInfo) pages.get(cName);
180
181         /*
182          * parse the page if it has changed or no cached
183          * information is available.
184          */

185         if (sourceInfo == null ||
186                 sourceInfo.lastModified != source.lastModified()) {
187             // if no cached version, or modified, load
188
sourceInfo = parsePage(source, context);
189             if (cName != null)
190                 pages.put(cName, sourceInfo);
191         }
192         return sourceInfo.parts;
193     }
194
195     /**
196      * Scan through vector of page sections and build
197      * output.
198      * Read the static areas of the TemplateSource and copy them to the
199      * output until the beginning of the next special tag. Invokes
200      * the <CODE>executeTag()</CODE> Method for the tagand goes on with copying.
201      * or invoking the servlets to which they refer.
202      *
203      * @param parts page sections, as provide by parsePage()
204      * @see #parsePage
205      */

206     private void interpretPage(TemplateSource source,
207                                List parts, ParseContext context)
208             throws IOException {
209
210         OutputStream out = context.getOutputStream();
211         InputStream inStream = null;
212         byte buf[] = null;
213
214         try {
215             // input
216
inStream = source.getInputStream();
217             long inPos = 0;
218
219             /*
220              * Get Copy Buffer.
221              * If we allocate it here once and pass it to the
222              * copy()-function we don't have to create and garbage collect
223              * a buffer each time we call copy().
224              *
225              * REVISE: this should use a buffer Manager:
226              * a queue which stores buffers. This
227              * way the JVM doesn't have to garbage collect the buffers
228              * created here, so we may use larger Buffers
229              * here.
230              */

231             buf = new byte[4096]; // Get buffer from Buffer Manager
232

233             for (int i = 0; i < parts.size(); i++) {
234                 /** <critical-path> **/
235                 SpecialTagHandler part = (SpecialTagHandler) parts.get(i);
236                 // copy TemplateSource content till the beginning of the Tag:
237
copy(inStream, out, part.getTagStart() - inPos, buf);
238
239                 context.startTag(i);
240                 try {
241                     part.executeTag(context, inStream);
242                 }
243                         /*
244                          * Display any Exceptions or Errors as
245                          * comment in the page
246                          */
catch (Throwable JavaDoc e) {
247                     out.flush();
248                     PrintWriter pout = new PrintWriter(out);
249                     pout.println("<!-- ERROR: ------------");
250                     e.printStackTrace(pout);
251                     pout.println("-->");
252                     pout.flush();
253                 }
254                 context.doneTag(i);
255
256                 inPos = part.getTagStart() + part.getTagLength();
257                 /** </critical-path> **/
258             }
259             // copy rest until end of TemplateSource
260
copy(inStream, out, -1, buf);
261         } finally {
262             // clean up resouce: opened input stream
263
if (inStream != null)
264                 inStream.close();
265             buf = null; // return buffer to Buffer Manager
266
}
267         out.flush();
268     }
269
270
271     /**
272      * copies an InputStream to an OutputStream. copies max. length
273      * bytes.
274      *
275      * @param in The source stream
276      * @param out The destination stream
277      * @param length number of bytes to copy; -1 for unlimited
278      * @param buf Buffer used as temporary space to copy
279      * block-wise.
280      */

281     private static void copy(InputStream in, OutputStream out, long length,
282                              byte buf[])
283             throws IOException {
284         int len;
285         boolean limited = (length >= 0);
286         int rest = limited ? (int) length : buf.length;
287         while (rest > 0 &&
288                 (len = in.read(buf, 0,
289                         (rest > buf.length) ? buf.length : rest)) > 0) {
290             out.write(buf, 0, len);
291             if (limited) rest -= len;
292         }
293     }
294
295     /**
296      * Open and read source, returning list of contents.
297      * The returned vector will contain a list of
298      * <CODE>SpecialTagHandler</CODE>s, containing the
299      * position/length within the input source they are
300      * responsible for.
301      * This Vector is used within <CODE>interpretPage()</CODE>
302      * to create the output.
303      *
304      * @param source source to open and process
305      * @param context The context used while parsing; contains
306      * at least the HttpServletRequest and HttpServletResponse
307      * @return TemplateSourceInfo containing page elements.
308      * <!-- see private <a HREF="#interpretPage">interpretPage()</a> -->
309      */

310     private TemplateSourceInfo parsePage(TemplateSource source, ParseContext context)
311             throws IOException {
312         /*
313          * read source contents. The SGMLTag requires
314          * to read from a Reader which supports the
315          * mark() operation so we need a BufferedReader
316          * here.
317          *
318          * The PositionReader may be asked at which Position
319          * it currently is (much like the java.io.LineNumberReader); this
320          * is used to determine the exact position of the Tags in the
321          * page to be able to loop through the fast copy/execute/copy
322          * sequence in interpretPage().
323          *
324          * Since interpreting is operating on an InputStream which
325          * copies and skip()s bytes, any source position count done here
326          * assumes that sizeof(char) == sizeof(byte).
327          * So we force the InputStreamReader to interpret the Stream's content
328          * as ISO8859_1, because the localized default behaviour may
329          * differ (e.g. UTF8 for which sizeof(char) != sizeof (byte)
330          */

331         PositionReader fin = null;
332         // from JDK 1.1.6, the name of the encoding is ISO8859_1, but the old
333
// value is still accepted.
334
fin = new PositionReader(new BufferedReader(new InputStreamReader(source.getInputStream(), "8859_1")));
335         TemplateSourceInfo sourceInfo = new TemplateSourceInfo();
336
337         try {
338             // scan through page parsing SpecialTag statements
339
sourceInfo.lastModified = source.lastModified();
340             sourceInfo.parts = new ArrayList();
341             sourceInfo.labels = new HashMap();
342             long startPos;
343             SGMLTag tag, endTag;
344             long startTime = System.currentTimeMillis();
345             do {
346                 endTag = null;
347                 startPos = fin.getPosition();
348                 tag = new SGMLTag(fin, false);
349                 if (tag.getName() != null) {
350                     String JavaDoc upName = tag.getName().toUpperCase();
351                     if (handlerClasses.containsKey(upName)) {
352                         SpecialTagHandler handler = null;
353                         try {
354                             Class JavaDoc handlerClass = (Class JavaDoc) handlerClasses.get(upName);
355                             handler = (SpecialTagHandler) handlerClass.newInstance();
356
357                             endTag = handler.parseTag(context, fin, startPos, tag);
358                         } catch (Exception JavaDoc e) {
359                             log.warn("Exception",e);
360                         }
361                         if (endTag != null) {
362                             if ("LABEL".equals(upName)) {
363                                 LabelTagHandler labelHandler = (LabelTagHandler) handler;
364                                 sourceInfo.labels.put(labelHandler.getFor(), labelHandler.getContent());
365                             }
366                             sourceInfo.parts.add(handler);
367                         }
368                     }
369                 }
370             } while (!tag.finished());
371             /***
372              sourceInfo.parseTime = System.currentTimeMillis() - startTime;
373              System.err.println ("PageParser: parsing '" +
374              source.getCanonicalName() + "' took " +
375              sourceInfo.parseTime + "ms for " +
376              sourceInfo.parts.size() + " handlers");
377              ***/

378         } finally {
379             if (fin != null) fin.close();
380         }
381         return sourceInfo;
382     }
383
384     /**
385      * Source info holds the parse information for
386      * a TemplateSource .. and some statistical stuff which
387      * may be interesting for administrative
388      * frontends
389      */

390     private static final class TemplateSourceInfo {
391         ArrayList parts;
392         Map labels;
393         long lastModified;
394 // long parseTime;
395

396         public TemplateSourceInfo() {}
397     }
398 }
399
400 /*
401  * Local variables:
402  * c-basic-offset: 4
403  * compile-command: "ant -emacs -find build.xml"
404  * End:
405  */

406
407
Popular Tags