/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package com.rimfaxe.webserver.compiler.jsp;
import java.io.FileNotFoundException;
import java.io.CharArrayWriter;
import java.util.Hashtable;
import javax.servlet.jsp.tagext.TagLibraryInfo;
import javax.servlet.jsp.tagext.TagInfo;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.AttributesImpl;
import com.rimfaxe.webserver.compiler.JspToJavaException;
/**
* This class implements a parser for a JSP page (non-xml view).
* JSP page grammar is included here for reference. The token '#'
* that appears in the production indicates the current input token
* location in the production.
*
* @author Kin-man Chung
* @author Lars Andersen
*/
public class Parser
{
private ParserController parserController;
private JspReader reader;
private String currentFile;
private Mark start;
private Hashtable taglibs = new Hashtable();
/**
* The constructor
*/
private Parser(ParserController pc, JspReader reader)
{
this.parserController = pc;
this.taglibs = pc.getCompiler().getPageInfo().getTagLibraries();
this.reader = reader;
this.currentFile = reader.mark().getFile();
start = reader.mark();
}
/**
* The main entry for Parser
*
* @param pc The ParseController, use for getting other objects in compiler
* and for parsing included pages
* @param reader To read the page
* @param parent The parent node to this page, null for top level page
* @return list of nodes representing the parsed page
*/
public static Node.Nodes parse(ParserController pc, JspReader reader, Node parent)
throws JasperException, JspToJavaException
{
Parser parser = new Parser(pc, reader);
Node.Root root = new Node.Root(null, reader.mark(), parent);
while (reader.hasMoreInput())
{
parser.parseElements(root);
}
Node.Nodes page = new Node.Nodes(root);
return page;
}
/**
* Attributes ::= (S Attribute)* S?
*/
Attributes parseAttributes()
throws JasperException, JspToJavaException
{
//System.out.println("Parse Attributes");
AttributesImpl attrs = new AttributesImpl();
reader.skipSpaces();
while (parseAttribute(attrs))
reader.skipSpaces();
return attrs;
}
/**
* Parse Attributes for a reader, provided for external use
*/
public static Attributes parseAttributes(ParserController pc,JspReader reader)
throws JasperException, JspToJavaException
{
Parser tmpParser = new Parser(pc, reader);
return tmpParser.parseAttributes();
}
/**
* Attribute ::= Name S? Eq S?
* ( '"<%= RTAttributeValueDouble
* | '"' AttributeValueDouble
* | "'<%= RTAttributeValueSingle
* | "'" AttributeValueSingle
* }
* Note: JSP and XML spec does not allow while spaces around Eq. It is
* added to be backward compatible with Tomcat, and with other xml parsers.
*/
private boolean parseAttribute(AttributesImpl attrs)
throws JasperException, JspToJavaException
{
//System.out.println("Parse Attribute 1");
String name = parseName();
if (name == null)
return false;
reader.skipSpaces();
if (!reader.matches("="))
System.out.println("jsp.error.attribute.noequal");
reader.skipSpaces();
char quote = (char) reader.nextChar();
if (quote != '\'' && quote != '"')
System.out.println("jsp.error.attribute.noquote");
String watchString = "";
if (reader.matches("<%="))
watchString = "%>";
watchString = watchString + quote;
String attr = parseAttributeValue(watchString);
attrs.addAttribute("", name, name, "CDATA", attr);
return true;
}
/**
* Name ::= (Letter | '_' | ':') (Letter | Digit | '.' | '_' | '-' | ':')*
*/
private String parseName()
throws JasperException, JspToJavaException
{
//System.out.println("Parse Name");
char ch = (char)reader.peekChar();
if (Character.isLetter(ch) || ch == '_' || ch == ':') {
StringBuffer buf = new StringBuffer();
buf.append(ch);
reader.nextChar();
ch = (char)reader.peekChar();
while (Character.isLetter(ch) || Character.isDigit(ch) ||
ch == '.' || ch == '_' || ch == '-' || ch == ':') {
buf.append(ch);
reader.nextChar();
ch = (char) reader.peekChar();
}
return buf.toString();
}
return null;
}
/**
* AttributeValueDouble ::= (QuotedChar - '"')*
* ('"' | <TRANSLATION_ERROR>)
* RTAttributeValueDouble ::= ((QuotedChar - '"')* - ((QuotedChar-'"')'%>"')
* ('%>"' | TRANSLATION_ERROR)
*/
private String parseAttributeValue(String watch)
throws JasperException, JspToJavaException
{
//System.out.println("Parse AttributeValue");
Mark start = reader.mark();
Mark stop = reader.skipUntilIgnoreEsc(watch);
if (stop == null)
{
throw new JspToJavaException(start,"Unterminated attribute",watch);
}
String ret = parseQuoted(reader.getText(start, stop));
if (watch.length() == 1) // quote
return ret;
// putback delimiter '<%=' and '%>', since they are needed if the
// attribute does not allow RTexpression.
return "<%=" + ret + "%>";
}
/**
* QuotedChar ::= '''
* | '"'
* | '\\'
* | '\"'
* | "\'"
* | '\>'
* | Char
*/
private String parseQuoted(char[] tx)
{
//System.out.println("Parse Quoted");
StringBuffer buf = new StringBuffer();
int size = tx.length;
int i = 0;
while (i < size) {
char ch = tx[i];
if (ch == '&') {
if (i+5 < size && tx[i+1] == 'a' && tx[i+2] == 'p' &&
tx[i+3] == 'o' && tx[i+4] == 's' && tx[i+5] == ';') {
buf.append('\'');
i += 6;
} else if (i+5 < size && tx[i+1] == 'q' && tx[i+2] == 'u' &&
tx[i+3] == 'o' && tx[i+4] == 't' && tx[i+5] == ';') {
buf.append('"');
i += 6;
} else {
buf.append(ch);
++i;
}
} else if (ch == '\\' && i+1 < size) {
ch = tx[i+1];
if (ch == '\\' || ch == '\"' || ch == '\'' || ch == '>') {
buf.append(ch);
i += 2;
} else {
buf.append('\\');
++i;
}
} else {
buf.append(ch);
++i;
}
}
return buf.toString();
}
private char[] parseScriptText(char[] tx)
{
//System.out.println("Parse ScriptText");
CharArrayWriter cw = new CharArrayWriter();
int size = tx.length;
int i = 0;
while (i < size) {
char ch = tx[i];
if (i+2 < size && ch == '%' && tx[i+1] == '\\' && tx[i+2] == '>') {
cw.write('%');
cw.write('>');
i += 3;
} else {
cw.write(ch);
++i;
}
}
cw.close();
return cw.toCharArray();
}
/*
* Invokes parserController to parse the included page
*/
private void processIncludeDirective(String file, Node parent)
throws JasperException, JspToJavaException
{
//System.out.println("Include Directive");
if (file == null)
{
return;
}
try
{
parserController.parse(file, parent);
}
catch (FileNotFoundException ex)
{
throw new JspToJavaException(start, "File not found ["+ file+"]");
}
catch (Exception ex)
{
throw new JspToJavaException(start, ex.getMessage());
}
}
/*
* Parses a page directive with the following syntax:
* PageDirective ::= ( S Attribute)*
*/
private void parsePageDirective(Node parent)
throws JasperException, JspToJavaException
{
Attributes attrs = parseAttributes();
Node.PageDirective n = new Node.PageDirective(attrs, start, parent);
/*
* A page directive may contain multiple 'import' attributes, each of
* which consists of a comma-separated list of package names.
* Store each list with the node, where it is parsed.
*/
for (int i = 0; i < attrs.getLength(); i++)
{
if ("import".equals(attrs.getQName(i)))
{
//System.out.println("Add Import value -> [["+attrs.getValue(i)+"]]");
n.addImport(attrs.getValue(i));
}
}
}
/*
* Parses an include directive with the following syntax:
* IncludeDirective ::= ( S Attribute)*
*/
private void parseIncludeDirective(Node parent)
throws JasperException, JspToJavaException
{
Attributes attrs = parseAttributes();
// Included file expanded here
Node includeNode = new Node.IncludeDirective(attrs, start, parent);
processIncludeDirective(attrs.getValue("file"), includeNode);
}
/*
* Parses a taglib directive with the following syntax:
* Directive ::= ( S Attribute)*
*/
private void parseTaglibDirective(Node parent)
throws JasperException, JspToJavaException
{
//System.out.println("Parser - parseTaglibDirective");
Attributes attrs = parseAttributes();
String uri = attrs.getValue("uri");
String prefix = attrs.getValue("prefix");
if (uri != null && prefix != null)
{
// Errors to be checked in Validator
//String[] location = ctxt.getTldLocation(uri);
TagLibraryInfo tl = this.parserController.getWebContext().getTagLibrary(uri,prefix);
//
taglibs.put(prefix, tl);
}
new Node.TaglibDirective(attrs, start, parent);
}
/*
* Parses a directive with the following syntax:
* Directive ::= S? ( 'page' PageDirective
* | 'include' IncludeDirective
* | 'taglib' TagLibDirective)
* S? '%>'
*/
private void parseDirective(Node parent)
throws JspToJavaException, JasperException
{
//System.out.println("Parse Directive");
reader.skipSpaces();
String directive = null;
if (reader.matches("page"))
{
directive = "<%@ page";
//System.out.println("Parser, parsePageDirective <%@ page ...");
parsePageDirective(parent);
}
else if (reader.matches("include"))
{
directive = "<%@ include";
parseIncludeDirective(parent);
}
else if (reader.matches("taglib"))
{
directive = "<%@ taglib";
parseTaglibDirective(parent);
}
else
{
throw new JspToJavaException(start,"Invalid directive");
}
reader.skipSpaces();
if (!reader.matches("%>"))
{
throw new JspToJavaException(start,"Unterminated directive : " + directive);
}
}
/*
* JSPCommentBody ::= (Char* - (Char* '--%>')) '--%>'
*/
private void parseComment(Node parent)
throws JspToJavaException, JasperException
{
//System.out.println("Parse Comment");
start = reader.mark();
Mark stop = reader.skipUntil("--%>");
if (stop == null)
{
throw new JspToJavaException(start,"Unterminated comment","<%--");
}
new Node.Comment(reader.getText(start, stop), start, parent);
}
/*
* DeclarationBody ::= (Char* - (char* '%>')) '%>'
*/
private void parseDeclaration(Node parent)
throws JspToJavaException, JasperException
{
//System.out.println("Parse Declaration");
start = reader.mark();
Mark stop = reader.skipUntil("%>");
if (stop == null)
{
throw new JspToJavaException(start,"Unterminated declaration","<%!");
}
new Node.Declaration(parseScriptText(reader.getText(start, stop)),start, parent);
}
/*
* ExpressionBody ::= (Char* - (char* '%>')) '%>'
*/
private void parseExpression(Node parent)
throws JspToJavaException, JasperException
{
//System.out.println("Parse Expression");
start = reader.mark();
Mark stop = reader.skipUntil("%>");
if (stop == null)
{
throw new JspToJavaException(start,"Unterminated expression","<%=");
}
new Node.Expression(parseScriptText(reader.getText(start, stop)),start, parent);
}
/*
* Scriptlet ::= (Char* - (char* '%>')) '%>'
*/
private void parseScriptlet(Node parent)
throws JspToJavaException, JasperException
{
//System.out.println("Parse Scriptlet");
start = reader.mark();
Mark stop = reader.skipUntil("%>");
if (stop == null)
{
throw new JspToJavaException(start,"Unterminated scriptlet","<%");
}
new Node.Scriptlet(parseScriptText(reader.getText(start, stop)), start, parent);
}
/**
* Param ::= '<jsp:param' Attributes* '/>'
*/
private void parseParam(Node parent)
throws JspToJavaException, JasperException
{
//System.out.println("Parse Param");
if (!reader.matches("<jsp:param"))
{
//err.jspError(reader.mark(), "jsp.error.paramexpectedonly");
}
Attributes attrs = parseAttributes();
reader.skipSpaces();
if (!reader.matches("/>"))
{
throw new JspToJavaException(start,"Unterminated parameter","<jsp:param");
}
new Node.ParamAction(attrs, start, parent);
}
/**
* Params ::= (Param S?)*
*/
private void parseParams(Node parent, String tag)
throws JasperException, JspToJavaException
{
Mark start = reader.mark();
while (reader.hasMoreInput())
{
if (reader.matchesETag(tag))
{
break;
}
parseParam(parent);
reader.skipSpaces();
}
}
/*
* IncludeAction ::= Attributes
* ( '/>'
* | '>' S? Params '</jsp:include' S? '>'
* )
*/
private void parseInclude(Node parent)
throws JasperException, JspToJavaException
{
Attributes attrs = parseAttributes();
reader.skipSpaces();
if (reader.matches("/>")) {
// No body
new Node.IncludeAction(attrs, start, parent);
return;
}
if (!reader.matches(">"))
{
throw new JspToJavaException(reader.mark(), "Unterminated include","<jsp:include");
}
reader.skipSpaces();
Node includeNode = new Node.IncludeAction(attrs, start, parent);
parseParams(includeNode, "jsp:include");
}
/*
* ForwardAction ::= Attributes
* ( '/>'
* | '>' S? Params '<jsp:forward' S? '>'
* )
*/
private void parseForward(Node parent)
throws JasperException, JspToJavaException
{
Attributes attrs = parseAttributes();
reader.skipSpaces();
if (reader.matches("/>")) {
// No body
new Node.ForwardAction(attrs, start, parent);
return;
}
if (!reader.matches(">"))
{
throw new JspToJavaException(reader.mark(), "Unterminated forward","<jsp:forward");
}
reader.skipSpaces();
Node forwardNode = new Node.ForwardAction(attrs, start, parent);
parseParams(forwardNode, "jsp:forward");
}
/*
* GetProperty ::= (S? Attribute)* S? '/>/
*/
private void parseGetProperty(Node parent)
throws JasperException, JspToJavaException
{
Attributes attrs = parseAttributes();
reader.skipSpaces();
if (!reader.matches("/>"))
{
throw new JspToJavaException(reader.mark(), "Unterminated getProperty","<jsp:getProperty");
}
new Node.GetProperty(attrs, start, parent);
}
/*
* SetProperty ::= (S Attribute)* S? '/>'
*/
private void parseSetProperty(Node parent)
throws JasperException, JspToJavaException
{
Attributes attrs = parseAttributes();
reader.skipSpaces();
if (!reader.matches("/>"))
{
throw new JspToJavaException(reader.mark(), "Unterminated setProperty","<jsp:setProperty");
}
new Node.SetProperty(attrs, start, parent);
}
/*
* UseBean ::= (S Attribute)* S?
* ('/>' | ( '>' Body '</jsp:useBean' S? '>' ))
*/
private void parseUseBean(Node parent)
throws JasperException, JspToJavaException
{
Attributes attrs = parseAttributes();
reader.skipSpaces();
if (reader.matches("/>")) {
new Node.UseBean(attrs, start, parent);
return;
}
if (!reader.matches(">"))
{
throw new JspToJavaException(reader.mark(), "Unterminated useBean","<jsp:useBean");
}
Node beanNode = new Node.UseBean(attrs, start, parent);
parseBody(beanNode, "jsp:useBean");
}
/*
* JspParams ::= S? '>' S? Params+ </jsp:params' S? '>'
*/
private void parseJspParams(Node parent)
throws JasperException, JspToJavaException
{
reader.skipSpaces();
if (!reader.matches(">"))
{
throw new JspToJavaException(reader.mark(), "Params not closed");
}
reader.skipSpaces();
Node jspParamsNode = new Node.ParamsAction(start, parent);
parseParams(jspParamsNode, "jsp:params");
}
/*
* FallBack ::= S? '>' Char* '</jsp:fallback' S? '>'
*/
private void parseFallBack(Node parent)
throws JasperException, JspToJavaException
{
reader.skipSpaces();
if (!reader.matches(">"))
{
throw new JspToJavaException(reader.mark(), "Fallback not closed");
}
Mark bodyStart = reader.mark();
Mark bodyEnd = reader.skipUntilETag("jsp:fallback");
if (bodyEnd == null)
{
throw new JspToJavaException(reader.mark(), "Unterminated fallback","<jsp:fallback");
}
char[] text = reader.getText(bodyStart, bodyEnd);
new Node.FallBackAction(start, text, parent);
}
/*
* PlugIn ::= Attributes '>' PlugInBody '</jsp:plugin' S? '>'
* PlugBody ::= S? ('<jsp:params' JspParams S?)?
* ('<jsp:fallback' JspFallack S?)?
*/
private void parsePlugin(Node parent)
throws JasperException, JspToJavaException
{
Attributes attrs = parseAttributes();
reader.skipSpaces();
if (!reader.matches(">"))
{
throw new JspToJavaException(reader.mark(), "Plugin not closed");
}
reader.skipSpaces();
Node pluginNode = new Node.PlugIn(attrs, start, parent);
if (reader.matches("<jsp:params"))
{
parseJspParams(pluginNode);
reader.skipSpaces();
}
if (reader.matches("<jsp:fallback")) {
parseFallBack(pluginNode);
reader.skipSpaces();
}
if (!reader.matchesETag("jsp:plugin"))
{
throw new JspToJavaException(reader.mark(), "Plugin not closed");
}
}
/*
* StandardAction ::= 'include' IncludeAction
* | 'forward' ForwardAction
* | 'getProperty' GetPropertyAction
* | 'setProperty' SetPropertyAction
* | 'useBean' UseBeanAction
* | 'plugin' PlugInAction
*/
private void parseAction(Node parent)
throws JasperException, JspToJavaException
{
Mark start = reader.mark();
if (reader.matches("include"))
{
parseInclude(parent);
}
else if (reader.matches("forward"))
{
parseForward(parent);
}
else if (reader.matches("getProperty"))
{
parseGetProperty(parent);
}
else if (reader.matches("setProperty"))
{
parseSetProperty(parent);
}
else if (reader.matches("useBean"))
{
parseUseBean(parent);
}
else if (reader.matches("plugin"))
{
parsePlugin(parent);
}
else
{
throw new JspToJavaException(start, "Bad action");
}
}
/*
* ActionElement ::= EmptyElemTag | Stag Body Etag
* EmptyElemTag ::= '<' Name ( S Attribute )* S? '/>'
* Stag ::= '<' Name ( S Attribute)* S? '>'
* Etag ::= '</' Name S? '>'
*/
private boolean parseCustomTag(Node parent)
throws JasperException, JspToJavaException
{
//System.out.println("Parse Custom tag");
if (reader.peekChar() != '<')
{
return false;
}
reader.nextChar(); // skip '<'
String tagName = reader.parseToken(false);
int i = tagName.indexOf(':');
if (i == -1) {
reader.reset(start);
return false;
}
String prefix = tagName.substring(0, i);
String shortTagName = tagName.substring(i+1);
// Check if this is a user-defined tag.
TagLibraryInfo tagLibInfo = (TagLibraryInfo) taglibs.get(prefix);
if (tagLibInfo == null)
{
reader.reset(start);
return false;
}
TagInfo tagInfo = tagLibInfo.getTag(shortTagName);
if (tagInfo == null)
{
throw new JspToJavaException(start, "Bad tag : "+shortTagName, prefix);
}
// EmptyElemTag ::= '<' Name ( #S Attribute )* S? '/>'
// or Stag ::= '<' Name ( #S Attribute)* S? '>'
Attributes attrs = parseAttributes();
reader.skipSpaces();
if (reader.matches("/>")) {
// EmptyElemTag ::= '<' Name ( S Attribute )* S? '/>'#
new Node.CustomTag(attrs, start, tagName, prefix, shortTagName,
tagInfo, parent);
return true;
}
if (!reader.matches(">"))
{
throw new JspToJavaException(start, "Unterminated tag");
}
// ActionElement ::= Stag #Body Etag
// Looking for a body, it still can be empty; but if there is a
// a tag body, its syntax would be dependent on the type of
// body content declared in TLD.
String bc = tagInfo.getBodyContent();
Node tagNode = new Node.CustomTag(attrs, start, tagName, prefix,
shortTagName, tagInfo, parent);
// There are 3 body content types: empty, jsp, or tag-dependent.
if (bc.equalsIgnoreCase(TagInfo.BODY_CONTENT_EMPTY))
{
if (!reader.matchesETag(tagName))
{
throw new JspToJavaException(start, "empty body content is nonempty");
}
}
else if (bc.equalsIgnoreCase(TagInfo.BODY_CONTENT_TAG_DEPENDENT))
{
// parse the body as text
parseBodyText(tagNode, tagName);
}
else if (bc.equalsIgnoreCase(TagInfo.BODY_CONTENT_JSP))
{
// parse body as JSP page
parseBody(tagNode, tagName);
}
else
{
throw new JspToJavaException(start, "Bad content type");
}
return true;
}
/*
*
*/
private void parseTemplateText(Node parent)
throws JasperException, JspToJavaException
{
// Note except for the beginning of a page, the current char is '<'.
// Quoting in template text is handled here.
// JSP2.6 "A literal <% is quoted by <\%"
if (reader.matches("<\\%")) {
char[] content = reader.nextContent();
char[] text = new char[content.length + 2];
text[0] = '<';
text[1] = '%';
System.arraycopy(content, 0, text, 2, content.length);
new Node.TemplateText(text, start, parent);
} else {
new Node.TemplateText(reader.nextContent(), start, parent);
}
}
/*
* BodyElement ::= '<%--' JSPCommentBody
* | '<%@' DirectiveBody
* | '<%!' DeclarationBody
* | '<%=' ExpressionBody
* | '<%' ScriptletBody
* | '<jsp:' StandardAction
* | '<' CustomAction
* | TemplateText
*/
private void parseElements(Node parent)
throws JasperException, JspToJavaException
{
start = reader.mark();
if (reader.matches("<%--")) {
parseComment(parent);
} else if (reader.matches("<%@")) {
parseDirective(parent);
} else if (reader.matches("<%!")) {
parseDeclaration(parent);
} else if (reader.matches("<%=")) {
parseExpression(parent);
} else if (reader.matches("<%")) {
parseScriptlet(parent);
} else if (reader.matches("<jsp:")) {
parseAction(parent);
} else if (!parseCustomTag(parent)) {
parseTemplateText(parent);
}
}
/*
*
*/
private void parseBodyText(Node parent, String tag)
throws JasperException, JspToJavaException
{
Mark bodyStart = reader.mark();
Mark bodyEnd = reader.skipUntilETag(tag);
if (bodyEnd == null)
{
throw new JspToJavaException(start, "Unterminated tag", "<"+tag+">");
}
new Node.TemplateText(reader.getText(bodyStart, bodyEnd), bodyStart,parent);
}
/*
* Parse the body as JSP content.
* @param tag The name of the tag whose end tag would terminate the body
*/
private void parseBody(Node parent, String tag)
throws JasperException, JspToJavaException
{
while (reader.hasMoreInput())
{
if (reader.matchesETag(tag))
{
return;
}
parseElements(parent);
}
throw new JspToJavaException(start, "Unterminated tag", "<"+tag+">");
}
}