001: /*
002: * The Apache Software License, Version 1.1
003: *
004: * Copyright (c) 1999 The Apache Software Foundation. All rights
005: * reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The end-user documentation included with the redistribution, if
020: * any, must include the following acknowlegement:
021: * "This product includes software developed by the
022: * Apache Software Foundation (http://www.apache.org/)."
023: * Alternately, this acknowlegement may appear in the software itself,
024: * if and wherever such third-party acknowlegements normally appear.
025: *
026: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
027: * Foundation" must not be used to endorse or promote products derived
028: * from this software without prior written permission. For written
029: * permission, please contact apache@apache.org.
030: *
031: * 5. Products derived from this software may not be called "Apache"
032: * nor may "Apache" appear in their names without prior written
033: * permission of the Apache Group.
034: *
035: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: *
049: * This software consists of voluntary contributions made by many
050: * individuals on behalf of the Apache Software Foundation. For more
051: * information on the Apache Software Foundation, please see
052: * <http://www.apache.org/>.
053: *
054: */
055: package com.rimfaxe.webserver.compiler.jsp;
056:
057: import java.io.*;
058: import java.util.*;
059: import javax.servlet.jsp.tagext.*;
060: import org.xml.sax.InputSource;
061: import org.xml.sax.Attributes;
062: import com.rimfaxe.webserver.compiler.JspToJavaException;
063:
064: import com.rimfaxe.webserver.WebContext;
065:
066: /**
067: * Controller for the parsing of a JSP page.
068: * <p>
069: * A translation unit (JSP source file and any files included via the
070: * include directive) may involve the processing of JSP pages
071: * written with different syntaxes (currently the original JSP syntax,
072: * as well as the XML syntax (as of JSP 1.2)). This class encapsulates
073: * the behavior related to the selection and invocation of
074: * the proper parser.
075: *
076: * @author Pierre Delisle
077: * @author Lars Andersen
078: */
079: public class ParserController {
080:
081: /*
082: * A stack to keep track of the 'current base directory'
083: * for include directives that refer to relative paths.
084: */
085: private Stack baseDirStack = new Stack();
086:
087: /*
088: * Document information which tells us what
089: * kind of document we are dealing with.
090: */
091: private boolean isXml;
092:
093: /*
094: * Static information used in the process of figuring out
095: * the kind of document we're dealing with.
096: */
097: private static final String JSP_ROOT_TAG = "<jsp:root";
098:
099: /*
100: * Tells if the file being processed is the "top" file
101: * in the translation unit.
102: */
103: private boolean isTopFile = true;
104:
105: /*
106: * The encoding of the "top" file. This encoding is used
107: * for included files by default.
108: * Defaults to "ISO-8859-1" per JSP spec.
109: */
110: private String topFileEncoding = "ISO-8859-1";
111:
112: /*
113: * The 'new' encoding required to read a page.
114: */
115: private String newEncoding;
116:
117: private WebContext ctxt;
118:
119: private String jspuri;
120:
121: private JspC compiler;
122:
123: //*********************************************************************
124: // Constructor
125:
126: public ParserController(JspC compiler, WebContext ctxt,
127: String jspuri) {
128: this .compiler = compiler;
129: this .jspuri = jspuri;
130: this .ctxt = ctxt;
131: }
132:
133: public JspC getCompiler() {
134: return compiler;
135: }
136:
137: public WebContext getWebContext() {
138: return ctxt;
139: }
140:
141: //*********************************************************************
142: // Parse
143:
144: /**
145: * Parse the jsp page provided as an argument.
146: * This is only invoked by the compiler.
147: *
148: * @param inFileName The name of the JSP file to be parsed.
149: */
150: public Node.Nodes parse(String inFileName)
151: throws FileNotFoundException, JasperException,
152: JspToJavaException {
153: return parse(inFileName, null);
154: }
155:
156: /**
157: * Parse the JSP page provided as an argument.
158: * This is invoked recursively to handle 'include' directives.
159: *
160: * @param inFileName The name of the jsp file to be parsed.
161: * @param parent The node for the 'include' directive.
162: */
163: public Node.Nodes parse(String inFileName, Node parent)
164: throws FileNotFoundException, JasperException,
165: JspToJavaException {
166:
167: Node.Nodes parsedPage = null;
168: String absFileName = resolveFileName(inFileName);
169:
170: //System.out.println("Filename "+inFileName+" resolved to "+absFileName);
171:
172: String encoding = topFileEncoding;
173: InputStreamReader reader = null;
174: try {
175: // Figure out what type of JSP document we are dealing with
176: reader = getReader(absFileName, encoding);
177: figureOutJspDocument(absFileName, encoding, reader);
178: if (newEncoding != null)
179: encoding = newEncoding;
180: if (isTopFile) {
181: // Set the "top level" file encoding that will be used
182: // for all included files where encoding is not defined.
183: topFileEncoding = encoding;
184: isTopFile = false;
185: } else {
186: // OBS
187: compiler.getPageInfo().addInclude(absFileName);
188: }
189: try {
190: reader.close();
191: } catch (IOException ex) {
192: }
193:
194: // dispatch to the proper parser
195:
196: reader = getReader(absFileName, encoding);
197:
198: if (isXml) {
199:
200: //System.out.println("Parse XML");
201: parsedPage = JspDocumentParser.parse(this , absFileName,
202: reader, parent);
203: } else {
204: //System.out.println("Parse JSP");
205: JspReader r = new JspReader(absFileName, encoding,
206: reader);
207: parsedPage = Parser.parse(this , r, parent);
208: }
209: baseDirStack.pop();
210: } finally {
211: if (reader != null) {
212: try {
213: reader.close();
214: } catch (Exception any) {
215: }
216: }
217: }
218:
219: return parsedPage;
220: }
221:
222: //*********************************************************************
223: // Figure out input Document
224:
225: private void figureOutJspDocument(String file, String encoding,
226: InputStreamReader reader) throws JasperException,
227: JspToJavaException {
228: JspReader jspReader;
229: try {
230: jspReader = new JspReader(file, encoding, reader);
231: } catch (FileNotFoundException ex) {
232: throw new JasperException(ex);
233: }
234: jspReader.setSingleFile(true);
235: Mark startMark = jspReader.mark();
236:
237: // Check for the jsp:root tag
238: // No check for xml prolog, since nothing prevents a page
239: // to output XML and still use JSP syntax.
240: jspReader.reset(startMark);
241: Mark mark = jspReader.skipUntil(JSP_ROOT_TAG);
242: if (mark != null) {
243: isXml = true;
244: } else {
245: isXml = false;
246: }
247:
248: newEncoding = null;
249:
250: // Figure out the encoding of the page
251: // FIXME: We assume xml parser will take care of
252: // encoding for page in XML syntax. Correct?
253: if (!isXml) {
254: jspReader.reset(startMark);
255: while (jspReader.skipUntil("<%@") != null) {
256: jspReader.skipSpaces();
257: if (jspReader.matches("page")) {
258: jspReader.skipSpaces();
259: Attributes attrs = Parser.parseAttributes(this ,
260: jspReader);
261: String attribute = "pageEncoding";
262: newEncoding = attrs.getValue("pageEncoding");
263: if (newEncoding == null) {
264: String contentType = attrs
265: .getValue("contentType");
266: if (contentType != null) {
267: int loc = contentType.indexOf("charset=");
268: if (loc != -1) {
269: newEncoding = contentType
270: .substring(loc + 8);
271: return;
272: }
273: }
274: if (newEncoding == null)
275: newEncoding = "ISO-8859-1";
276: } else {
277: return;
278: }
279: }
280: }
281: }
282: }
283:
284: //*********************************************************************
285: // Utility methods
286:
287: /*
288: * Resolve the name of the file and update
289: * baseDirStack() to keep track ot the current
290: * base directory for each included file.
291: * The 'root' file is always an 'absolute' path,
292: * so no need to put an initial value in the
293: * baseDirStack.
294: */
295: private String resolveFileName(String inFileName) {
296: String fileName = inFileName.replace('\\', '/');
297: boolean isAbsolute = fileName.startsWith("/");
298: fileName = isAbsolute ? fileName : (String) baseDirStack.peek()
299: + fileName;
300: String baseDir = fileName.substring(0, fileName
301: .lastIndexOf("/") + 1);
302: baseDirStack.push(baseDir);
303: return fileName;
304: }
305:
306: private InputStreamReader getReader(String file, String encoding)
307: throws FileNotFoundException, JasperException {
308: InputStream in;
309: InputStreamReader reader;
310:
311: try {
312: in = ctxt.getResourceAsStream(file);
313: if (in == null) {
314: throw new FileNotFoundException(file);
315: }
316: return new InputStreamReader(in, encoding);
317: } catch (UnsupportedEncodingException ex) {
318: throw new JasperException(Constants.getString(
319: "jsp.error.unsupported.encoding",
320: new Object[] { encoding }));
321: }
322: }
323:
324: private void p(String s) {
325: System.out.println("[ParserController] " + s);
326: }
327:
328: private void p(String s, Throwable ex) {
329: p(s);
330: p(ex.getMessage());
331:
332: }
333: }
|