001: package com.quadcap.http.servlets.jsp;
002:
003: /* Copyright 1999 - 2003 Quadcap Software. All rights reserved.
004: *
005: * This software is distributed under the Quadcap Free Software License.
006: * This software may be used or modified for any purpose, personal or
007: * commercial. Open Source redistributions are permitted. Commercial
008: * redistribution of larger works derived from, or works which bundle
009: * this software requires a "Commercial Redistribution License"; see
010: * http://www.quadcap.com/purchase.
011: *
012: * Redistributions qualify as "Open Source" under one of the following terms:
013: *
014: * Redistributions are made at no charge beyond the reasonable cost of
015: * materials and delivery.
016: *
017: * Redistributions are accompanied by a copy of the Source Code or by an
018: * irrevocable offer to provide a copy of the Source Code for up to three
019: * years at the cost of materials and delivery. Such redistributions
020: * must allow further use, modification, and redistribution of the Source
021: * Code under substantially the same terms as this license.
022: *
023: * Redistributions of source code must retain the copyright notices as they
024: * appear in each source code file, these license terms, and the
025: * disclaimer/limitation of liability set forth as paragraph 6 below.
026: *
027: * Redistributions in binary form must reproduce this Copyright Notice,
028: * these license terms, and the disclaimer/limitation of liability set
029: * forth as paragraph 6 below, in the documentation and/or other materials
030: * provided with the distribution.
031: *
032: * The Software is provided on an "AS IS" basis. No warranty is
033: * provided that the Software is free of defects, or fit for a
034: * particular purpose.
035: *
036: * Limitation of Liability. Quadcap Software shall not be liable
037: * for any damages suffered by the Licensee or any third party resulting
038: * from use of the Software.
039: */
040:
041: import java.io.CharArrayWriter;
042: import java.io.IOException;
043: import java.io.Reader;
044:
045: import org.xml.sax.AttributeList;
046: import org.xml.sax.DocumentHandler;
047: import org.xml.sax.DTDHandler;
048: import org.xml.sax.EntityResolver;
049: import org.xml.sax.ErrorHandler;
050: import org.xml.sax.InputSource;
051: import org.xml.sax.Parser;
052: import org.xml.sax.SAXException;
053:
054: import org.xml.sax.helpers.AttributeListImpl;
055:
056: import com.quadcap.util.collections.ArrayQueue;
057:
058: /**
059: * JSP Parser class that fires SAX events.
060: *
061: * @author Stan Bailes
062: */
063: public class JspParser implements Parser {
064: InputSource in;
065: Reader r;
066: DocumentHandler docHandler = null;
067: DTDHandler dtdHandler = null;
068: EntityResolver entityResolver = null;
069: CharArrayWriter tag = new CharArrayWriter();
070: CharArrayWriter data = new CharArrayWriter();
071: AttributeListImpl attributes = new AttributeListImpl();
072: int tagState = 0;
073: String tagName = null;
074: JspHandler jspHandler = null;
075: String nonJspTag = null;
076: ArrayQueue inStack = null;
077:
078: final static int TAG = 1;
079: final static int DIRECTIVE = 2;
080: final static int DECLARATION = 3;
081: final static int SCRIPTLET = 4;
082: final static int EXPRESSION = 5;
083:
084: public JspParser() {
085: }
086:
087: public void parse(InputSource in) throws SAXException, IOException {
088: this .in = in;
089: this .r = in.getCharacterStream();
090: tag.reset();
091: data.reset();
092: parse();
093: }
094:
095: public void pushInputSource(InputSource in2) {
096: if (inStack == null)
097: inStack = new ArrayQueue();
098: inStack.push(in);
099: in = in2;
100: r = in.getCharacterStream();
101: }
102:
103: boolean popInputSource() {
104: if (inStack == null || inStack.size() == 0)
105: return false;
106: in = (InputSource) inStack.pop();
107: r = in.getCharacterStream();
108: return true;
109: }
110:
111: public void setJspHandler(JspHandler jspHandler) {
112: this .jspHandler = jspHandler;
113: }
114:
115: public void parse(String s) {
116: }
117:
118: public void setDocumentHandler(DocumentHandler dh) {
119: this .docHandler = dh;
120: }
121:
122: public void setDTDHandler(DTDHandler dh) {
123: this .dtdHandler = dh;
124: }
125:
126: public void setEntityResolver(EntityResolver er) {
127: this .entityResolver = er;
128: }
129:
130: public EntityResolver getEntityResolver() {
131: return entityResolver;
132: }
133:
134: public void setErrorHandler(ErrorHandler er) {
135: }
136:
137: public void setLocale(java.util.Locale locale) {
138: }
139:
140: public void parse() throws SAXException, IOException {
141: int state = 0;
142: int commentState = 0;
143: String attrName = null;
144: docHandler.startDocument();
145: docHandler.startElement("jsp:root", attributes);
146: while (state >= 0) {
147: int c = r.read();
148: //System.out.println("[" + ((char)c) + "] [" + state + "] <" + tag.toString() + ">");
149: if (c < 0) {
150: if (popInputSource())
151: continue;
152: state = -1;
153: break;
154: }
155: switch (commentState) {
156: case 0:
157: break;
158: case 1:
159: if (c == '-')
160: commentState = 2;
161: break;
162: case 2:
163: if (c == '-')
164: commentState = 3;
165: else
166: commentState = 1;
167: break;
168: case 3:
169: if (c == '>')
170: commentState = 0;
171: else if (c != '-')
172: commentState = 1;
173: }
174:
175: switch (state) {
176: case 0:
177: if (c == '<') {
178: if (data.size() > 0) {
179: docHandler.characters(data.toCharArray(), 0,
180: data.size());
181: data.reset();
182: }
183: state = 1;
184: } else {
185: data.write(c);
186: }
187: break;
188: case 1: // seen '<'
189: switch (c) {
190: case '%':
191: state = 2;
192: break;
193: case '!':
194: data.write('<');
195: data.write('!');
196: commentState = 1;
197: state = 0;
198: break;
199: case '\\':
200: state = 4;
201: break;
202: case '/':
203: state = 8;
204: break;
205: default:
206: tag.write(c);
207: tagState = TAG;
208: state = 5;
209: break;
210: }
211: break;
212: case 2: // seen '<%'
213: tag.reset();
214: state = 3;
215: switch (c) {
216: case '@':
217: state = 51;
218: tagState = DIRECTIVE;
219: break;
220: case '!':
221: tagState = DECLARATION;
222: break;
223: case '=':
224: tagState = EXPRESSION;
225: break;
226: case '-':
227: state = 15;
228: break;
229: default:
230: tag.write(c);
231: tagState = SCRIPTLET;
232: break;
233: }
234: break;
235: case 3: // in <% %>
236: if (c == '%')
237: state = 7;
238: else
239: tag.write(c);
240: break;
241: case 4: // seen <\
242: data.write('<');
243: data.write(c);
244: state = 0;
245: break;
246: case 51: // collect directive name
247: if (c != ' ') {
248: tag.write(c);
249: state = 52;
250: }
251: break;
252: case 52:
253: if (c == ' ') {
254: tagName = tag.toString();
255: tag.reset();
256: state = 6;
257: } else {
258: tag.write(c);
259: }
260: break;
261: case 5: // collect tag name
262: switch (c) {
263: case ' ':
264: tagName = tag.toString();
265: tag.reset();
266: state = checkTag(6);
267: break;
268: case '/':
269: tagName = tag.toString();
270: tag.reset();
271: state = 9;
272: break;
273: case '>':
274: tagName = tag.toString();
275: tag.reset();
276: docHandler.startElement(tagName, attributes);
277: attributes.clear();
278: state = 0;
279: break;
280: case '<':
281: tagName = tag.toString();
282: tag.reset();
283: if (checkTag(1) == 0) {
284: if (data.size() > 0) {
285: docHandler.characters(data.toCharArray(),
286: 0, data.size());
287: data.reset();
288: }
289: state = 1;
290: }
291: break;
292: default:
293: tag.write(c);
294: }
295: break;
296: case 6: // collect attributes
297: switch (c) {
298: case ' ':
299: case '\n':
300: case '\r':
301: case '\t':
302: break;
303: case '/':
304: if (tagState == TAG) {
305: state = 9;
306: } else {
307: tag.write(c);
308: }
309: break;
310: case '%':
311: if (tagState != TAG) {
312: state = 11;
313: } else {
314: tag.write(c);
315: }
316: break;
317: case '>':
318: docHandler.startElement(tagName, attributes);
319: attributes.clear();
320: state = 0;
321: break;
322: case '=':
323: attrName = tag.toString();
324: tag.reset();
325: state = 10;
326: break;
327: default:
328: tag.write(c);
329: }
330: break;
331: case 7: // in <%, seen %
332: if (c == '>') {
333: doTag();
334: state = 0;
335: } else {
336: tag.write('%');
337: if (c != '%') {
338: tag.write(c);
339: state = 3;
340: }
341: }
342: break;
343: case 8: // seen </
344: if (c == '>') {
345: tagName = tag.toString();
346: tag.reset();
347: if (nonJspTag != null && tagName.equals(nonJspTag)) {
348: nonJspTag = null;
349: data.write("</");
350: data.write(tagName);
351: data.write('>');
352: } else {
353: docHandler.endElement(tagName);
354: }
355: state = 0;
356: } else {
357: tag.write(c);
358: }
359: break;
360: case 9: // in <tag, seen /
361: if (c == '>') {
362: docHandler.startElement(tagName, attributes);
363: attributes.clear();
364: docHandler.endElement(tagName);
365: state = 0;
366: } else {
367: tag.write('/');
368: tag.write(c);
369: state = 6;
370: }
371: break;
372: case 10: // in attriblist, seen name=
373: if (c == '"') {
374: state = 12;
375: } else if (c == '\'') {
376: state = 121;
377: } else {
378: tag.write(c);
379: state = 13;
380: }
381: break;
382: case 11: // in <%@, seen %
383: if (c == '>') {
384: doDirective();
385: state = 0;
386: } else {
387: tag.write('%');
388: tag.write(c);
389: state = 6;
390: }
391: break;
392: case 12: // in attriblist, seen name="
393: if (c == '"') {
394: attributes.addAttribute(attrName, "string", tag
395: .toString());
396: tag.reset();
397: state = 6;
398: } else {
399: tag.write(c);
400: }
401: break;
402: case 121: // in attriblist, seen name='
403: if (c == '\'') {
404: attributes.addAttribute(attrName, "string", tag
405: .toString());
406: tag.reset();
407: state = 6;
408: } else {
409: tag.write(c);
410: }
411: break;
412: case 13: // in attriblist, seen name=c
413: switch (c) {
414: case ' ':
415: attributes.addAttribute(attrName, "string", tag
416: .toString());
417: tag.reset();
418: state = 6;
419: break;
420: case '/':
421: state = 14;
422: break;
423: case '>':
424: attributes.addAttribute(attrName, "string", tag
425: .toString());
426: tag.reset();
427: docHandler.startElement(tagName, attributes);
428: attributes.clear();
429: state = 0;
430: break;
431: default:
432: tag.write(c);
433: }
434: break;
435: case 14: // in attriblist, seen name=dfdf/
436: if (c == '>') {
437: attributes.addAttribute(attrName, "string", tag
438: .toString());
439: tag.reset();
440: docHandler.startElement(tagName, attributes);
441: attributes.clear();
442: state = 0;
443: } else {
444: tag.write('/');
445: if (c != '/') {
446: tag.write(c);
447: state = 13;
448: }
449: }
450: break;
451: case 15:
452: if (c == '-')
453: state = 16;
454: break;
455: case 16:
456: if (c == '-')
457: state = 17;
458: else
459: state = 15;
460: break;
461: case 17:
462: if (c == '>')
463: state = 0;
464: else if (c != '-')
465: state = 15;
466: break;
467: }
468: }
469: docHandler.endElement("jsp:root");
470: }
471:
472: public int checkTag(int validState) throws IOException {
473: if (jspHandler.isValidTag(tagName)) {
474: return validState;
475: } else {
476: nonJspTag = tagName;
477: data.write('<');
478: data.write(tagName);
479: data.write(' ');
480: return 0;
481: }
482: }
483:
484: public void doTag() throws SAXException {
485: switch (tagState) {
486: case TAG:
487: break;
488: case EXPRESSION:
489: docHandler.startElement("jsp:expression", attributes);
490: docHandler.characters(tag.toCharArray(), 0, tag.size());
491: docHandler.endElement("jsp:expression");
492: tag.reset();
493: attributes.clear();
494: break;
495: case DECLARATION:
496: docHandler.startElement("jsp:decl", attributes);
497: docHandler.characters(tag.toCharArray(), 0, tag.size());
498: docHandler.endElement("jsp:decl");
499: tag.reset();
500: attributes.clear();
501: break;
502: case SCRIPTLET:
503: docHandler.startElement("jsp:scriptlet", attributes);
504: docHandler.characters(tag.toCharArray(), 0, tag.size());
505: docHandler.endElement("jsp:scriptlet");
506: tag.reset();
507: attributes.clear();
508: break;
509: }
510: }
511:
512: public void doDirective() throws SAXException {
513: docHandler.startElement("jsp:directive." + tagName, attributes);
514: docHandler.endElement("jsp:directive." + tagName);
515: attributes.clear();
516: }
517: }
|