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:
056: package com.rimfaxe.webserver.compiler.jsp;
057:
058: import java.io.*;
059: import java.util.*;
060:
061: //import org.apache.jasper.logging.*;
062: import org.xml.sax.Attributes;
063: import org.xml.sax.helpers.AttributesImpl;
064:
065: /**
066: * JspReader is an input buffer for the JSP parser. It should allow
067: * unlimited lookahead and pushback. It also has a bunch of parsing
068: * utility methods for understanding htmlesque thingies.
069: *
070: * @author Anil K. Vijendran
071: * @author Anselm Baird-Smith
072: * @author Harish Prabandham
073: * @author Rajiv Mordani
074: * @author Mandar Raje
075: * @author Danno Ferrin
076: * @author Lars Andersen
077: */
078:
079: public class JspReader {
080:
081: protected Mark current = null;
082: String master = null;
083:
084: Vector sourceFiles = new Vector();
085: int currFileId = 0;
086: int size = 0;
087:
088: //private ErrorDispatcher err;
089:
090: /*
091: * Set to true when using the JspReader on a single file where we read up
092: * to the end and reset to the beginning many times.
093: * (as in ParserCtl.figureOutJspDocument().
094: */
095: boolean singleFile = false;
096:
097: //Logger.Helper loghelper = new Logger.Helper("JASPER_LOG", "JspReader");
098:
099: public String getFile(int fileid) {
100: return (String) sourceFiles.elementAt(fileid);
101: }
102:
103: /**
104: * Register a new source file.
105: * This method is used to implement file inclusion. Each included file
106: * gets a uniq identifier (which is the index in the array of source files).
107: * @return The index of the now registered file.
108: */
109: protected int registerSourceFile(String file) {
110: if (sourceFiles.contains(file))
111: return -1;
112: sourceFiles.addElement(file);
113: this .size++;
114: return sourceFiles.size() - 1;
115: }
116:
117: /**
118: * Unregister the source file.
119: * This method is used to implement file inclusion. Each included file
120: * gets a uniq identifier (which is the index in the array of source
121: * files).
122: * @return The index of the now registered file.
123: */
124: protected int unregisterSourceFile(String file) {
125: if (!sourceFiles.contains(file))
126: return -1;
127: sourceFiles.removeElement(file);
128: this .size--;
129: return sourceFiles.size() - 1;
130: }
131:
132: private void pushFile2(String file, String encoding,
133: InputStreamReader reader) throws JasperException,
134: FileNotFoundException {
135:
136: // Register the file
137: String longName = file;
138:
139: int fileid = registerSourceFile(longName);
140:
141: if (fileid == -1) {
142: //OBS
143: //err.jspError("jsp.error.file.already.registered", file);
144: }
145:
146: currFileId = fileid;
147:
148: try {
149: CharArrayWriter caw = new CharArrayWriter();
150: char buf[] = new char[1024];
151: for (int i = 0; (i = reader.read(buf)) != -1;)
152: caw.write(buf, 0, i);
153: caw.close();
154: if (current == null) {
155: current = new Mark(this , caw.toCharArray(), fileid,
156: getFile(fileid), master, encoding);
157: } else {
158: current.pushStream(caw.toCharArray(), fileid,
159: getFile(fileid), longName, encoding);
160: }
161: } catch (Throwable ex) {
162:
163: //OBS
164: //loghelper.log("Exception parsing file ", ex);
165: // Pop state being constructed:
166: popFile();
167: //err.jspError("jsp.error.file.cannot.read", "ze file");
168: } finally {
169: if (reader != null) {
170: try {
171: reader.close();
172: } catch (Exception any) {
173: }
174: }
175: }
176: }
177:
178: public boolean popFile() throws JasperException {
179: // Is stack created ? (will happen if the Jsp file we'r looking at is
180: // missing.
181: if (current == null)
182: return false;
183:
184: // Restore parser state:
185: //size--;
186: if (currFileId < 0) {
187: //OBS
188: //err.jspError("jsp.error.no.more.content");
189: }
190:
191: String fName = getFile(currFileId);
192: currFileId = unregisterSourceFile(fName);
193: if (currFileId < -1) {
194: //OBS
195: //err.jspError("jsp.error.file.not.registered", fName);
196: }
197:
198: boolean result = current.popStream();
199: if (result)
200: master = current.baseDir;
201: return (result);
202: }
203:
204: protected JspReader(String file, String encoding,
205: InputStreamReader reader) throws JasperException,
206: FileNotFoundException {
207: pushFile2(file, encoding, reader);
208: }
209:
210: public boolean hasMoreInput() throws JasperException {
211: if (current.cursor >= current.stream.length) {
212: if (singleFile)
213: return false;
214: while (popFile()) {
215: if (current.cursor < current.stream.length)
216: return true;
217: }
218: return false;
219: }
220: return true;
221: }
222:
223: public int nextChar() throws JasperException {
224: if (!hasMoreInput())
225: return -1;
226:
227: int ch = current.stream[current.cursor];
228:
229: current.cursor++;
230:
231: if (ch == '\n') {
232: current.line++;
233: current.col = 0;
234: } else {
235: current.col++;
236: }
237: return ch;
238: }
239:
240: /**
241: * Gets Content until the next potential JSP element. Because all elements
242: * begin with a '<' we can just move until we see the next one.
243: */
244: char[] nextContent() {
245: int cur_cursor = current.cursor;
246: int len = current.stream.length;
247: char ch;
248:
249: if (peekChar() == '\n') {
250: current.line++;
251: current.col = 0;
252: } else
253: current.col++;
254:
255: // pure obsfuscated genius!
256: while ((++current.cursor < len)
257: && ((ch = current.stream[current.cursor]) != '<')) {
258:
259: if (ch == '\n') {
260: current.line++;
261: current.col = 0;
262: } else {
263: current.col++;
264: }
265: }
266:
267: len = current.cursor - cur_cursor;
268: char[] content = new char[len];
269: System.arraycopy(current.stream, cur_cursor, content, 0, len);
270:
271: return content;
272: }
273:
274: char[] getText(Mark start, Mark stop) throws JasperException {
275: Mark oldstart = mark();
276: reset(start);
277: CharArrayWriter caw = new CharArrayWriter();
278: while (!stop.equals(mark()))
279: caw.write(nextChar());
280: caw.close();
281: reset(oldstart);
282: return caw.toCharArray();
283: }
284:
285: public int peekChar() {
286: return current.stream[current.cursor];
287: }
288:
289: public Mark mark() {
290: return new Mark(current);
291: }
292:
293: public void reset(Mark mark) {
294: current = new Mark(mark);
295: }
296:
297: public boolean matchesIgnoreCase(String string)
298: throws JasperException {
299: Mark mark = mark();
300: int ch = 0;
301: int i = 0;
302: do {
303: ch = nextChar();
304: if (Character.toLowerCase((char) ch) != string.charAt(i++)) {
305: reset(mark);
306: return false;
307: }
308: } while (i < string.length());
309: reset(mark);
310: return true;
311: }
312:
313: /**
314: * search the stream for a match to a string
315: * @param string The string to match
316: * @return <stront>true</strong> is one is found, the current position
317: * in stream is positioned after the search string, <strong>
318: * false</strong> otherwise, position in stream unchanged.
319: */
320: public boolean matches(String string) throws JasperException {
321: Mark mark = mark();
322: int ch = 0;
323: int i = 0;
324: do {
325: ch = nextChar();
326: if (((char) ch) != string.charAt(i++)) {
327: reset(mark);
328: return false;
329: }
330: } while (i < string.length());
331: return true;
332: }
333:
334: public boolean matchesETag(String tagName) throws JasperException {
335: Mark mark = mark();
336:
337: if (!matches("</" + tagName))
338: return false;
339: skipSpaces();
340: if (nextChar() == '>')
341: return true;
342:
343: reset(mark);
344: return false;
345: }
346:
347: public void advance(int n) throws JasperException {
348: while (--n >= 0)
349: nextChar();
350: }
351:
352: public int skipSpaces() throws JasperException {
353: int i = 0;
354: while (isSpace()) {
355: i++;
356: nextChar();
357: }
358: return i;
359: }
360:
361: /**
362: * Skip until the given string is matched in the stream.
363: * When returned, the context is positioned past the end of the match.
364: * @param s The String to match.
365: * @return A non-null <code>Mark</code> instance (positioned immediately
366: * before the search string) if found, <strong>null</strong>
367: * otherwise.
368: */
369: public Mark skipUntil(String limit) throws JasperException {
370: Mark ret = null;
371: int limlen = limit.length();
372: int ch;
373:
374: skip: for (ret = mark(), ch = nextChar(); ch != -1; ret = mark(), ch = nextChar()) {
375: if (ch == limit.charAt(0)) {
376: for (int i = 1; i < limlen; i++) {
377: if (peekChar() == limit.charAt(i))
378: nextChar();
379: else
380: continue skip;
381: }
382: return ret;
383: }
384: }
385: return null;
386: }
387:
388: /**
389: * Skip until the given string is matched in the stream, but ignoring
390: * chars initially escaped by a '\'.
391: * When returned, the context is positioned past the end of the match.
392: * @param s The String to match.
393: * @return A non-null <code>Mark</code> instance (positioned immediately
394: * before the search string) if found, <strong>null</strong>
395: * otherwise.
396: */
397: public Mark skipUntilIgnoreEsc(String limit) throws JasperException {
398: Mark ret = null;
399: int limlen = limit.length();
400: int ch;
401: int prev = 'x'; // Doesn't matter
402:
403: skip: for (ret = mark(), ch = nextChar(); ch != -1; ret = mark(), prev = ch, ch = nextChar()) {
404: if (ch == limit.charAt(0) && prev != '\\') {
405: for (int i = 1; i < limlen; i++) {
406: if (peekChar() == limit.charAt(i))
407: nextChar();
408: else
409: continue skip;
410: }
411: return ret;
412: }
413: }
414: return null;
415: }
416:
417: /**
418: * Skip until the given end tag is matched in the stream.
419: * When returned, the context is positioned past the end of the tag.
420: * @param tag The name of the tag whose ETag (</tag>) to match.
421: * @return A non-null <code>Mark</code> instance (positioned immediately
422: * before the ETag) if found, <strong>null</strong> otherwise.
423: */
424: public Mark skipUntilETag(String tag) throws JasperException {
425: Mark ret = skipUntil("</" + tag);
426: if (ret != null) {
427: skipSpaces();
428: if (nextChar() != '>')
429: ret = null;
430: }
431: return ret;
432: }
433:
434: final boolean isSpace() {
435: return peekChar() <= ' ';
436: }
437:
438: /**
439: * Parse a space delimited token.
440: * If quoted the token will consume all characters up to a matching quote,
441: * otherwise, it consumes up to the first delimiter character.
442: * @param quoted If <strong>true</strong> accept quoted strings.
443: */
444: public String parseToken(boolean quoted) throws JasperException {
445: StringBuffer stringBuffer = new StringBuffer();
446: skipSpaces();
447: stringBuffer.setLength(0);
448:
449: int ch = peekChar();
450:
451: if (quoted) {
452: if (ch == '"' || ch == '\'') {
453:
454: char endQuote = ch == '"' ? '"' : '\'';
455: // Consume the open quote:
456: ch = nextChar();
457: for (ch = nextChar(); ch != -1 && ch != endQuote; ch = nextChar()) {
458: if (ch == '\\')
459: ch = nextChar();
460: stringBuffer.append((char) ch);
461: }
462: // Check end of quote, skip closing quote:
463: if (ch == -1) {
464: //OBS
465: //err.jspError(mark(), "jsp.error.quotes.unterminated");
466: }
467: } else {
468: //OBS
469: //err.jspError(mark(), "jsp.error.attr.quoted");
470: }
471: } else {
472: if (!isDelimiter()) {
473: // Read value until delimiter is found:
474: do {
475: ch = nextChar();
476: // Take care of the quoting here.
477: if (ch == '\\') {
478: if (peekChar() == '"' || peekChar() == '\''
479: || peekChar() == '>'
480: || peekChar() == '%')
481: ch = nextChar();
482: }
483: stringBuffer.append((char) ch);
484: } while (!isDelimiter());
485: }
486: }
487:
488: return stringBuffer.toString();
489: }
490:
491: /**
492: * Parse utils - Is current character a token delimiter ?
493: * Delimiters are currently defined to be =, >, <, ", and ' or any
494: * any space character as defined by <code>isSpace</code>.
495: * @return A boolean.
496: */
497: private boolean isDelimiter() throws JasperException {
498: if (!isSpace()) {
499: int ch = peekChar();
500: // Look for a single-char work delimiter:
501: if (ch == '=' || ch == '>' || ch == '"' || ch == '\''
502: || ch == '/') {
503: return true;
504: }
505: // Look for an end-of-comment or end-of-tag:
506: if (ch == '-') {
507: Mark mark = mark();
508: if (((ch = nextChar()) == '>')
509: || ((ch == '-') && (nextChar() == '>'))) {
510: reset(mark);
511: return true;
512: } else {
513: reset(mark);
514: return false;
515: }
516: }
517: return false;
518: } else {
519: return true;
520: }
521: }
522:
523: public void setSingleFile(boolean val) {
524: singleFile = val;
525: }
526: }
|