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.FileNotFoundException;
058: import java.io.CharArrayWriter;
059: import java.util.Hashtable;
060: import javax.servlet.jsp.tagext.TagLibraryInfo;
061: import javax.servlet.jsp.tagext.TagInfo;
062: import org.xml.sax.Attributes;
063: import org.xml.sax.helpers.AttributesImpl;
064: import com.rimfaxe.webserver.compiler.JspToJavaException;
065:
066: /**
067: * This class implements a parser for a JSP page (non-xml view).
068: * JSP page grammar is included here for reference. The token '#'
069: * that appears in the production indicates the current input token
070: * location in the production.
071: *
072: * @author Kin-man Chung
073: * @author Lars Andersen
074: */
075:
076: public class Parser {
077:
078: private ParserController parserController;
079: private JspReader reader;
080: private String currentFile;
081: private Mark start;
082: private Hashtable taglibs = new Hashtable();
083:
084: /**
085: * The constructor
086: */
087: private Parser(ParserController pc, JspReader reader) {
088: this .parserController = pc;
089:
090: this .taglibs = pc.getCompiler().getPageInfo().getTagLibraries();
091:
092: this .reader = reader;
093: this .currentFile = reader.mark().getFile();
094: start = reader.mark();
095: }
096:
097: /**
098: * The main entry for Parser
099: *
100: * @param pc The ParseController, use for getting other objects in compiler
101: * and for parsing included pages
102: * @param reader To read the page
103: * @param parent The parent node to this page, null for top level page
104: * @return list of nodes representing the parsed page
105: */
106: public static Node.Nodes parse(ParserController pc,
107: JspReader reader, Node parent) throws JasperException,
108: JspToJavaException {
109:
110: Parser parser = new Parser(pc, reader);
111:
112: Node.Root root = new Node.Root(null, reader.mark(), parent);
113:
114: while (reader.hasMoreInput()) {
115: parser.parseElements(root);
116: }
117:
118: Node.Nodes page = new Node.Nodes(root);
119: return page;
120: }
121:
122: /**
123: * Attributes ::= (S Attribute)* S?
124: */
125: Attributes parseAttributes() throws JasperException,
126: JspToJavaException {
127: //System.out.println("Parse Attributes");
128: AttributesImpl attrs = new AttributesImpl();
129:
130: reader.skipSpaces();
131: while (parseAttribute(attrs))
132: reader.skipSpaces();
133:
134: return attrs;
135: }
136:
137: /**
138: * Parse Attributes for a reader, provided for external use
139: */
140: public static Attributes parseAttributes(ParserController pc,
141: JspReader reader) throws JasperException,
142: JspToJavaException {
143: Parser tmpParser = new Parser(pc, reader);
144: return tmpParser.parseAttributes();
145: }
146:
147: /**
148: * Attribute ::= Name S? Eq S?
149: * ( '"<%= RTAttributeValueDouble
150: * | '"' AttributeValueDouble
151: * | "'<%= RTAttributeValueSingle
152: * | "'" AttributeValueSingle
153: * }
154: * Note: JSP and XML spec does not allow while spaces around Eq. It is
155: * added to be backward compatible with Tomcat, and with other xml parsers.
156: */
157: private boolean parseAttribute(AttributesImpl attrs)
158: throws JasperException, JspToJavaException {
159: //System.out.println("Parse Attribute 1");
160: String name = parseName();
161: if (name == null)
162: return false;
163:
164: reader.skipSpaces();
165:
166: if (!reader.matches("="))
167: System.out.println("jsp.error.attribute.noequal");
168:
169: reader.skipSpaces();
170: char quote = (char) reader.nextChar();
171:
172: if (quote != '\'' && quote != '"')
173: System.out.println("jsp.error.attribute.noquote");
174:
175: String watchString = "";
176: if (reader.matches("<%="))
177: watchString = "%>";
178: watchString = watchString + quote;
179:
180: String attr = parseAttributeValue(watchString);
181: attrs.addAttribute("", name, name, "CDATA", attr);
182: return true;
183: }
184:
185: /**
186: * Name ::= (Letter | '_' | ':') (Letter | Digit | '.' | '_' | '-' | ':')*
187: */
188: private String parseName() throws JasperException,
189: JspToJavaException {
190: //System.out.println("Parse Name");
191: char ch = (char) reader.peekChar();
192: if (Character.isLetter(ch) || ch == '_' || ch == ':') {
193: StringBuffer buf = new StringBuffer();
194: buf.append(ch);
195: reader.nextChar();
196: ch = (char) reader.peekChar();
197: while (Character.isLetter(ch) || Character.isDigit(ch)
198: || ch == '.' || ch == '_' || ch == '-' || ch == ':') {
199: buf.append(ch);
200: reader.nextChar();
201: ch = (char) reader.peekChar();
202: }
203: return buf.toString();
204: }
205: return null;
206: }
207:
208: /**
209: * AttributeValueDouble ::= (QuotedChar - '"')*
210: * ('"' | <TRANSLATION_ERROR>)
211: * RTAttributeValueDouble ::= ((QuotedChar - '"')* - ((QuotedChar-'"')'%>"')
212: * ('%>"' | TRANSLATION_ERROR)
213: */
214: private String parseAttributeValue(String watch)
215: throws JasperException, JspToJavaException {
216: //System.out.println("Parse AttributeValue");
217: Mark start = reader.mark();
218: Mark stop = reader.skipUntilIgnoreEsc(watch);
219:
220: if (stop == null) {
221: throw new JspToJavaException(start,
222: "Unterminated attribute", watch);
223:
224: }
225:
226: String ret = parseQuoted(reader.getText(start, stop));
227: if (watch.length() == 1) // quote
228: return ret;
229:
230: // putback delimiter '<%=' and '%>', since they are needed if the
231: // attribute does not allow RTexpression.
232: return "<%=" + ret + "%>";
233: }
234:
235: /**
236: * QuotedChar ::= '''
237: * | '"'
238: * | '\\'
239: * | '\"'
240: * | "\'"
241: * | '\>'
242: * | Char
243: */
244: private String parseQuoted(char[] tx) {
245: //System.out.println("Parse Quoted");
246: StringBuffer buf = new StringBuffer();
247: int size = tx.length;
248: int i = 0;
249: while (i < size) {
250: char ch = tx[i];
251: if (ch == '&') {
252: if (i + 5 < size && tx[i + 1] == 'a'
253: && tx[i + 2] == 'p' && tx[i + 3] == 'o'
254: && tx[i + 4] == 's' && tx[i + 5] == ';') {
255: buf.append('\'');
256: i += 6;
257: } else if (i + 5 < size && tx[i + 1] == 'q'
258: && tx[i + 2] == 'u' && tx[i + 3] == 'o'
259: && tx[i + 4] == 't' && tx[i + 5] == ';') {
260: buf.append('"');
261: i += 6;
262: } else {
263: buf.append(ch);
264: ++i;
265: }
266: } else if (ch == '\\' && i + 1 < size) {
267: ch = tx[i + 1];
268: if (ch == '\\' || ch == '\"' || ch == '\'' || ch == '>') {
269: buf.append(ch);
270: i += 2;
271: } else {
272: buf.append('\\');
273: ++i;
274: }
275: } else {
276: buf.append(ch);
277: ++i;
278: }
279: }
280: return buf.toString();
281: }
282:
283: private char[] parseScriptText(char[] tx) {
284: //System.out.println("Parse ScriptText");
285: CharArrayWriter cw = new CharArrayWriter();
286: int size = tx.length;
287: int i = 0;
288: while (i < size) {
289: char ch = tx[i];
290: if (i + 2 < size && ch == '%' && tx[i + 1] == '\\'
291: && tx[i + 2] == '>') {
292: cw.write('%');
293: cw.write('>');
294: i += 3;
295: } else {
296: cw.write(ch);
297: ++i;
298: }
299: }
300: cw.close();
301: return cw.toCharArray();
302: }
303:
304: /*
305: * Invokes parserController to parse the included page
306: */
307: private void processIncludeDirective(String file, Node parent)
308: throws JasperException, JspToJavaException {
309: //System.out.println("Include Directive");
310: if (file == null) {
311: return;
312: }
313:
314: try {
315: parserController.parse(file, parent);
316: } catch (FileNotFoundException ex) {
317: throw new JspToJavaException(start, "File not found ["
318: + file + "]");
319:
320: } catch (Exception ex) {
321: throw new JspToJavaException(start, ex.getMessage());
322:
323: }
324: }
325:
326: /*
327: * Parses a page directive with the following syntax:
328: * PageDirective ::= ( S Attribute)*
329: */
330: private void parsePageDirective(Node parent)
331: throws JasperException, JspToJavaException {
332: Attributes attrs = parseAttributes();
333: Node.PageDirective n = new Node.PageDirective(attrs, start,
334: parent);
335:
336: /*
337: * A page directive may contain multiple 'import' attributes, each of
338: * which consists of a comma-separated list of package names.
339: * Store each list with the node, where it is parsed.
340: */
341: for (int i = 0; i < attrs.getLength(); i++) {
342: if ("import".equals(attrs.getQName(i))) {
343: //System.out.println("Add Import value -> [["+attrs.getValue(i)+"]]");
344: n.addImport(attrs.getValue(i));
345: }
346: }
347: }
348:
349: /*
350: * Parses an include directive with the following syntax:
351: * IncludeDirective ::= ( S Attribute)*
352: */
353: private void parseIncludeDirective(Node parent)
354: throws JasperException, JspToJavaException {
355: Attributes attrs = parseAttributes();
356:
357: // Included file expanded here
358: Node includeNode = new Node.IncludeDirective(attrs, start,
359: parent);
360: processIncludeDirective(attrs.getValue("file"), includeNode);
361: }
362:
363: /*
364: * Parses a taglib directive with the following syntax:
365: * Directive ::= ( S Attribute)*
366: */
367: private void parseTaglibDirective(Node parent)
368: throws JasperException, JspToJavaException {
369: //System.out.println("Parser - parseTaglibDirective");
370: Attributes attrs = parseAttributes();
371: String uri = attrs.getValue("uri");
372: String prefix = attrs.getValue("prefix");
373:
374: if (uri != null && prefix != null) {
375: // Errors to be checked in Validator
376: //String[] location = ctxt.getTldLocation(uri);
377: TagLibraryInfo tl = this .parserController.getWebContext()
378: .getTagLibrary(uri, prefix);
379: //
380: taglibs.put(prefix, tl);
381: }
382:
383: new Node.TaglibDirective(attrs, start, parent);
384: }
385:
386: /*
387: * Parses a directive with the following syntax:
388: * Directive ::= S? ( 'page' PageDirective
389: * | 'include' IncludeDirective
390: * | 'taglib' TagLibDirective)
391: * S? '%>'
392: */
393: private void parseDirective(Node parent) throws JspToJavaException,
394: JasperException {
395: //System.out.println("Parse Directive");
396: reader.skipSpaces();
397:
398: String directive = null;
399: if (reader.matches("page")) {
400: directive = "<%@ page";
401: //System.out.println("Parser, parsePageDirective <%@ page ...");
402: parsePageDirective(parent);
403: } else if (reader.matches("include")) {
404: directive = "<%@ include";
405: parseIncludeDirective(parent);
406: } else if (reader.matches("taglib")) {
407: directive = "<%@ taglib";
408: parseTaglibDirective(parent);
409: } else {
410: throw new JspToJavaException(start, "Invalid directive");
411:
412: }
413:
414: reader.skipSpaces();
415: if (!reader.matches("%>")) {
416: throw new JspToJavaException(start,
417: "Unterminated directive : " + directive);
418:
419: }
420: }
421:
422: /*
423: * JSPCommentBody ::= (Char* - (Char* '--%>')) '--%>'
424: */
425: private void parseComment(Node parent) throws JspToJavaException,
426: JasperException {
427: //System.out.println("Parse Comment");
428: start = reader.mark();
429: Mark stop = reader.skipUntil("--%>");
430:
431: if (stop == null) {
432: throw new JspToJavaException(start, "Unterminated comment",
433: "<%--");
434:
435: }
436:
437: new Node.Comment(reader.getText(start, stop), start, parent);
438: }
439:
440: /*
441: * DeclarationBody ::= (Char* - (char* '%>')) '%>'
442: */
443: private void parseDeclaration(Node parent)
444: throws JspToJavaException, JasperException {
445: //System.out.println("Parse Declaration");
446: start = reader.mark();
447: Mark stop = reader.skipUntil("%>");
448: if (stop == null) {
449: throw new JspToJavaException(start,
450: "Unterminated declaration", "<%!");
451:
452: }
453:
454: new Node.Declaration(parseScriptText(reader
455: .getText(start, stop)), start, parent);
456: }
457:
458: /*
459: * ExpressionBody ::= (Char* - (char* '%>')) '%>'
460: */
461: private void parseExpression(Node parent)
462: throws JspToJavaException, JasperException {
463: //System.out.println("Parse Expression");
464: start = reader.mark();
465: Mark stop = reader.skipUntil("%>");
466: if (stop == null) {
467: throw new JspToJavaException(start,
468: "Unterminated expression", "<%=");
469: }
470:
471: new Node.Expression(
472: parseScriptText(reader.getText(start, stop)), start,
473: parent);
474: }
475:
476: /*
477: * Scriptlet ::= (Char* - (char* '%>')) '%>'
478: */
479: private void parseScriptlet(Node parent) throws JspToJavaException,
480: JasperException {
481: //System.out.println("Parse Scriptlet");
482: start = reader.mark();
483: Mark stop = reader.skipUntil("%>");
484: if (stop == null) {
485: throw new JspToJavaException(start,
486: "Unterminated scriptlet", "<%");
487:
488: }
489:
490: new Node.Scriptlet(
491: parseScriptText(reader.getText(start, stop)), start,
492: parent);
493: }
494:
495: /**
496: * Param ::= '<jsp:param' Attributes* '/>'
497: */
498: private void parseParam(Node parent) throws JspToJavaException,
499: JasperException {
500: //System.out.println("Parse Param");
501: if (!reader.matches("<jsp:param")) {
502: //err.jspError(reader.mark(), "jsp.error.paramexpectedonly");
503: }
504: Attributes attrs = parseAttributes();
505: reader.skipSpaces();
506:
507: if (!reader.matches("/>")) {
508: throw new JspToJavaException(start,
509: "Unterminated parameter", "<jsp:param");
510:
511: }
512:
513: new Node.ParamAction(attrs, start, parent);
514: }
515:
516: /**
517: * Params ::= (Param S?)*
518: */
519: private void parseParams(Node parent, String tag)
520: throws JasperException, JspToJavaException {
521:
522: Mark start = reader.mark();
523:
524: while (reader.hasMoreInput()) {
525: if (reader.matchesETag(tag)) {
526: break;
527: }
528:
529: parseParam(parent);
530: reader.skipSpaces();
531: }
532: }
533:
534: /*
535: * IncludeAction ::= Attributes
536: * ( '/>'
537: * | '>' S? Params '</jsp:include' S? '>'
538: * )
539: */
540: private void parseInclude(Node parent) throws JasperException,
541: JspToJavaException {
542: Attributes attrs = parseAttributes();
543: reader.skipSpaces();
544:
545: if (reader.matches("/>")) {
546: // No body
547: new Node.IncludeAction(attrs, start, parent);
548: return;
549: }
550:
551: if (!reader.matches(">")) {
552: throw new JspToJavaException(reader.mark(),
553: "Unterminated include", "<jsp:include");
554:
555: }
556:
557: reader.skipSpaces();
558: Node includeNode = new Node.IncludeAction(attrs, start, parent);
559: parseParams(includeNode, "jsp:include");
560: }
561:
562: /*
563: * ForwardAction ::= Attributes
564: * ( '/>'
565: * | '>' S? Params '<jsp:forward' S? '>'
566: * )
567: */
568: private void parseForward(Node parent) throws JasperException,
569: JspToJavaException {
570:
571: Attributes attrs = parseAttributes();
572: reader.skipSpaces();
573:
574: if (reader.matches("/>")) {
575: // No body
576: new Node.ForwardAction(attrs, start, parent);
577: return;
578: }
579:
580: if (!reader.matches(">")) {
581: throw new JspToJavaException(reader.mark(),
582: "Unterminated forward", "<jsp:forward");
583:
584: }
585:
586: reader.skipSpaces();
587: Node forwardNode = new Node.ForwardAction(attrs, start, parent);
588: parseParams(forwardNode, "jsp:forward");
589: }
590:
591: /*
592: * GetProperty ::= (S? Attribute)* S? '/>/
593: */
594: private void parseGetProperty(Node parent) throws JasperException,
595: JspToJavaException {
596: Attributes attrs = parseAttributes();
597: reader.skipSpaces();
598:
599: if (!reader.matches("/>")) {
600:
601: throw new JspToJavaException(reader.mark(),
602: "Unterminated getProperty", "<jsp:getProperty");
603: }
604:
605: new Node.GetProperty(attrs, start, parent);
606: }
607:
608: /*
609: * SetProperty ::= (S Attribute)* S? '/>'
610: */
611: private void parseSetProperty(Node parent) throws JasperException,
612: JspToJavaException {
613: Attributes attrs = parseAttributes();
614: reader.skipSpaces();
615:
616: if (!reader.matches("/>")) {
617: throw new JspToJavaException(reader.mark(),
618: "Unterminated setProperty", "<jsp:setProperty");
619:
620: }
621:
622: new Node.SetProperty(attrs, start, parent);
623: }
624:
625: /*
626: * UseBean ::= (S Attribute)* S?
627: * ('/>' | ( '>' Body '</jsp:useBean' S? '>' ))
628: */
629: private void parseUseBean(Node parent) throws JasperException,
630: JspToJavaException {
631: Attributes attrs = parseAttributes();
632: reader.skipSpaces();
633:
634: if (reader.matches("/>")) {
635: new Node.UseBean(attrs, start, parent);
636: return;
637: }
638:
639: if (!reader.matches(">")) {
640: throw new JspToJavaException(reader.mark(),
641: "Unterminated useBean", "<jsp:useBean");
642:
643: }
644:
645: Node beanNode = new Node.UseBean(attrs, start, parent);
646: parseBody(beanNode, "jsp:useBean");
647: }
648:
649: /*
650: * JspParams ::= S? '>' S? Params+ </jsp:params' S? '>'
651: */
652: private void parseJspParams(Node parent) throws JasperException,
653: JspToJavaException {
654:
655: reader.skipSpaces();
656: if (!reader.matches(">")) {
657: throw new JspToJavaException(reader.mark(),
658: "Params not closed");
659:
660: }
661:
662: reader.skipSpaces();
663: Node jspParamsNode = new Node.ParamsAction(start, parent);
664: parseParams(jspParamsNode, "jsp:params");
665: }
666:
667: /*
668: * FallBack ::= S? '>' Char* '</jsp:fallback' S? '>'
669: */
670: private void parseFallBack(Node parent) throws JasperException,
671: JspToJavaException {
672:
673: reader.skipSpaces();
674: if (!reader.matches(">")) {
675: throw new JspToJavaException(reader.mark(),
676: "Fallback not closed");
677:
678: }
679:
680: Mark bodyStart = reader.mark();
681: Mark bodyEnd = reader.skipUntilETag("jsp:fallback");
682: if (bodyEnd == null) {
683: throw new JspToJavaException(reader.mark(),
684: "Unterminated fallback", "<jsp:fallback");
685:
686: }
687: char[] text = reader.getText(bodyStart, bodyEnd);
688: new Node.FallBackAction(start, text, parent);
689: }
690:
691: /*
692: * PlugIn ::= Attributes '>' PlugInBody '</jsp:plugin' S? '>'
693: * PlugBody ::= S? ('<jsp:params' JspParams S?)?
694: * ('<jsp:fallback' JspFallack S?)?
695: */
696: private void parsePlugin(Node parent) throws JasperException,
697: JspToJavaException {
698: Attributes attrs = parseAttributes();
699: reader.skipSpaces();
700:
701: if (!reader.matches(">")) {
702: throw new JspToJavaException(reader.mark(),
703: "Plugin not closed");
704:
705: }
706:
707: reader.skipSpaces();
708: Node pluginNode = new Node.PlugIn(attrs, start, parent);
709: if (reader.matches("<jsp:params")) {
710: parseJspParams(pluginNode);
711: reader.skipSpaces();
712: }
713:
714: if (reader.matches("<jsp:fallback")) {
715: parseFallBack(pluginNode);
716: reader.skipSpaces();
717: }
718:
719: if (!reader.matchesETag("jsp:plugin")) {
720: throw new JspToJavaException(reader.mark(),
721: "Plugin not closed");
722:
723: }
724: }
725:
726: /*
727: * StandardAction ::= 'include' IncludeAction
728: * | 'forward' ForwardAction
729: * | 'getProperty' GetPropertyAction
730: * | 'setProperty' SetPropertyAction
731: * | 'useBean' UseBeanAction
732: * | 'plugin' PlugInAction
733: */
734: private void parseAction(Node parent) throws JasperException,
735: JspToJavaException {
736: Mark start = reader.mark();
737:
738: if (reader.matches("include")) {
739: parseInclude(parent);
740: } else if (reader.matches("forward")) {
741: parseForward(parent);
742: } else if (reader.matches("getProperty")) {
743: parseGetProperty(parent);
744: } else if (reader.matches("setProperty")) {
745: parseSetProperty(parent);
746: } else if (reader.matches("useBean")) {
747: parseUseBean(parent);
748: } else if (reader.matches("plugin")) {
749: parsePlugin(parent);
750: } else {
751: throw new JspToJavaException(start, "Bad action");
752:
753: }
754: }
755:
756: /*
757: * ActionElement ::= EmptyElemTag | Stag Body Etag
758: * EmptyElemTag ::= '<' Name ( S Attribute )* S? '/>'
759: * Stag ::= '<' Name ( S Attribute)* S? '>'
760: * Etag ::= '</' Name S? '>'
761: */
762: private boolean parseCustomTag(Node parent) throws JasperException,
763: JspToJavaException {
764: //System.out.println("Parse Custom tag");
765: if (reader.peekChar() != '<') {
766: return false;
767: }
768:
769: reader.nextChar(); // skip '<'
770: String tagName = reader.parseToken(false);
771: int i = tagName.indexOf(':');
772: if (i == -1) {
773: reader.reset(start);
774: return false;
775: }
776:
777: String prefix = tagName.substring(0, i);
778: String shortTagName = tagName.substring(i + 1);
779:
780: // Check if this is a user-defined tag.
781: TagLibraryInfo tagLibInfo = (TagLibraryInfo) taglibs
782: .get(prefix);
783: if (tagLibInfo == null) {
784: reader.reset(start);
785: return false;
786: }
787: TagInfo tagInfo = tagLibInfo.getTag(shortTagName);
788: if (tagInfo == null) {
789: throw new JspToJavaException(start, "Bad tag : "
790: + shortTagName, prefix);
791:
792: }
793:
794: // EmptyElemTag ::= '<' Name ( #S Attribute )* S? '/>'
795: // or Stag ::= '<' Name ( #S Attribute)* S? '>'
796: Attributes attrs = parseAttributes();
797: reader.skipSpaces();
798:
799: if (reader.matches("/>")) {
800: // EmptyElemTag ::= '<' Name ( S Attribute )* S? '/>'#
801: new Node.CustomTag(attrs, start, tagName, prefix,
802: shortTagName, tagInfo, parent);
803: return true;
804: }
805:
806: if (!reader.matches(">")) {
807: throw new JspToJavaException(start, "Unterminated tag");
808:
809: }
810:
811: // ActionElement ::= Stag #Body Etag
812:
813: // Looking for a body, it still can be empty; but if there is a
814: // a tag body, its syntax would be dependent on the type of
815: // body content declared in TLD.
816: String bc = tagInfo.getBodyContent();
817:
818: Node tagNode = new Node.CustomTag(attrs, start, tagName,
819: prefix, shortTagName, tagInfo, parent);
820: // There are 3 body content types: empty, jsp, or tag-dependent.
821: if (bc.equalsIgnoreCase(TagInfo.BODY_CONTENT_EMPTY)) {
822: if (!reader.matchesETag(tagName)) {
823: throw new JspToJavaException(start,
824: "empty body content is nonempty");
825:
826: }
827: } else if (bc
828: .equalsIgnoreCase(TagInfo.BODY_CONTENT_TAG_DEPENDENT)) {
829: // parse the body as text
830: parseBodyText(tagNode, tagName);
831: } else if (bc.equalsIgnoreCase(TagInfo.BODY_CONTENT_JSP)) {
832: // parse body as JSP page
833: parseBody(tagNode, tagName);
834: } else {
835: throw new JspToJavaException(start, "Bad content type");
836:
837: }
838:
839: return true;
840: }
841:
842: /*
843: *
844: */
845: private void parseTemplateText(Node parent) throws JasperException,
846: JspToJavaException {
847: // Note except for the beginning of a page, the current char is '<'.
848: // Quoting in template text is handled here.
849: // JSP2.6 "A literal <% is quoted by <\%"
850: if (reader.matches("<\\%")) {
851: char[] content = reader.nextContent();
852: char[] text = new char[content.length + 2];
853: text[0] = '<';
854: text[1] = '%';
855: System.arraycopy(content, 0, text, 2, content.length);
856: new Node.TemplateText(text, start, parent);
857: } else {
858: new Node.TemplateText(reader.nextContent(), start, parent);
859: }
860: }
861:
862: /*
863: * BodyElement ::= '<%--' JSPCommentBody
864: * | '<%@' DirectiveBody
865: * | '<%!' DeclarationBody
866: * | '<%=' ExpressionBody
867: * | '<%' ScriptletBody
868: * | '<jsp:' StandardAction
869: * | '<' CustomAction
870: * | TemplateText
871: */
872: private void parseElements(Node parent) throws JasperException,
873: JspToJavaException {
874: start = reader.mark();
875: if (reader.matches("<%--")) {
876: parseComment(parent);
877: } else if (reader.matches("<%@")) {
878: parseDirective(parent);
879: } else if (reader.matches("<%!")) {
880: parseDeclaration(parent);
881: } else if (reader.matches("<%=")) {
882: parseExpression(parent);
883: } else if (reader.matches("<%")) {
884: parseScriptlet(parent);
885: } else if (reader.matches("<jsp:")) {
886: parseAction(parent);
887: } else if (!parseCustomTag(parent)) {
888: parseTemplateText(parent);
889: }
890: }
891:
892: /*
893: *
894: */
895: private void parseBodyText(Node parent, String tag)
896: throws JasperException, JspToJavaException {
897: Mark bodyStart = reader.mark();
898: Mark bodyEnd = reader.skipUntilETag(tag);
899:
900: if (bodyEnd == null) {
901: throw new JspToJavaException(start, "Unterminated tag", "<"
902: + tag + ">");
903:
904: }
905: new Node.TemplateText(reader.getText(bodyStart, bodyEnd),
906: bodyStart, parent);
907: }
908:
909: /*
910: * Parse the body as JSP content.
911: * @param tag The name of the tag whose end tag would terminate the body
912: */
913: private void parseBody(Node parent, String tag)
914: throws JasperException, JspToJavaException {
915:
916: while (reader.hasMoreInput()) {
917: if (reader.matchesETag(tag)) {
918: return;
919: }
920: parseElements(parent);
921: }
922: throw new JspToJavaException(start, "Unterminated tag", "<"
923: + tag + ">");
924:
925: }
926:
927: }
|