Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » jasper » compiler » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    * 
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    * 
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   package org.apache.jasper.compiler;
   18   
   19   import java.io.CharArrayWriter;
   20   import java.io.FileNotFoundException;
   21   import java.net.URL;
   22   import java.util.Iterator;
   23   import java.util.List;
   24   
   25   import javax.servlet.jsp.tagext.TagAttributeInfo;
   26   import javax.servlet.jsp.tagext.TagFileInfo;
   27   import javax.servlet.jsp.tagext.TagInfo;
   28   import javax.servlet.jsp.tagext.TagLibraryInfo;
   29   
   30   import org.apache.jasper.Constants;
   31   import org.apache.jasper.JasperException;
   32   import org.apache.jasper.JspCompilationContext;
   33   import org.xml.sax.Attributes;
   34   import org.xml.sax.helpers.AttributesImpl;
   35   
   36   /**
   37    * This class implements a parser for a JSP page (non-xml view). JSP page
   38    * grammar is included here for reference. The token '#' that appears in the
   39    * production indicates the current input token location in the production.
   40    * 
   41    * @author Kin-man Chung
   42    * @author Shawn Bayern
   43    * @author Mark Roth
   44    */
   45   
   46   class Parser implements TagConstants {
   47   
   48       private ParserController parserController;
   49   
   50       private JspCompilationContext ctxt;
   51   
   52       private JspReader reader;
   53   
   54       private String currentFile;
   55   
   56       private Mark start;
   57   
   58       private ErrorDispatcher err;
   59   
   60       private int scriptlessCount;
   61   
   62       private boolean isTagFile;
   63   
   64       private boolean directivesOnly;
   65   
   66       private URL jarFileUrl;
   67   
   68       private PageInfo pageInfo;
   69   
   70       // Virtual body content types, to make parsing a little easier.
   71       // These are not accessible from outside the parser.
   72       private static final String JAVAX_BODY_CONTENT_PARAM = "JAVAX_BODY_CONTENT_PARAM";
   73   
   74       private static final String JAVAX_BODY_CONTENT_PLUGIN = "JAVAX_BODY_CONTENT_PLUGIN";
   75   
   76       private static final String JAVAX_BODY_CONTENT_TEMPLATE_TEXT = "JAVAX_BODY_CONTENT_TEMPLATE_TEXT";
   77   
   78       /**
   79        * The constructor
   80        */
   81       private Parser(ParserController pc, JspReader reader, boolean isTagFile,
   82               boolean directivesOnly, URL jarFileUrl) {
   83           this.parserController = pc;
   84           this.ctxt = pc.getJspCompilationContext();
   85           this.pageInfo = pc.getCompiler().getPageInfo();
   86           this.err = pc.getCompiler().getErrorDispatcher();
   87           this.reader = reader;
   88           this.currentFile = reader.mark().getFile();
   89           this.scriptlessCount = 0;
   90           this.isTagFile = isTagFile;
   91           this.directivesOnly = directivesOnly;
   92           this.jarFileUrl = jarFileUrl;
   93           start = reader.mark();
   94       }
   95   
   96       /**
   97        * The main entry for Parser
   98        * 
   99        * @param pc
  100        *            The ParseController, use for getting other objects in compiler
  101        *            and for parsing included pages
  102        * @param reader
  103        *            To read the page
  104        * @param parent
  105        *            The parent node to this page, null for top level page
  106        * @return list of nodes representing the parsed page
  107        */
  108       public static Node.Nodes parse(ParserController pc, JspReader reader,
  109               Node parent, boolean isTagFile, boolean directivesOnly,
  110               URL jarFileUrl, String pageEnc, String jspConfigPageEnc,
  111               boolean isDefaultPageEncoding, boolean isBomPresent) throws JasperException {
  112   
  113           Parser parser = new Parser(pc, reader, isTagFile, directivesOnly,
  114                   jarFileUrl);
  115   
  116           Node.Root root = new Node.Root(reader.mark(), parent, false);
  117           root.setPageEncoding(pageEnc);
  118           root.setJspConfigPageEncoding(jspConfigPageEnc);
  119           root.setIsDefaultPageEncoding(isDefaultPageEncoding);
  120           root.setIsBomPresent(isBomPresent);
  121   
  122           if (directivesOnly) {
  123               parser.parseTagFileDirectives(root);
  124               return new Node.Nodes(root);
  125           }
  126   
  127           // For the Top level page, add inlcude-prelude and include-coda
  128           PageInfo pageInfo = pc.getCompiler().getPageInfo();
  129           if (parent == null) {
  130               parser.addInclude(root, pageInfo.getIncludePrelude());
  131           }
  132           while (reader.hasMoreInput()) {
  133               parser.parseElements(root);
  134           }
  135           if (parent == null) {
  136               parser.addInclude(root, pageInfo.getIncludeCoda());
  137           }
  138   
  139           Node.Nodes page = new Node.Nodes(root);
  140           return page;
  141       }
  142   
  143       /**
  144        * Attributes ::= (S Attribute)* S?
  145        */
  146       Attributes parseAttributes() throws JasperException {
  147           AttributesImpl attrs = new AttributesImpl();
  148   
  149           reader.skipSpaces();
  150           while (parseAttribute(attrs))
  151               reader.skipSpaces();
  152   
  153           return attrs;
  154       }
  155   
  156       /**
  157        * Parse Attributes for a reader, provided for external use
  158        */
  159       public static Attributes parseAttributes(ParserController pc,
  160               JspReader reader) throws JasperException {
  161           Parser tmpParser = new Parser(pc, reader, false, false, null);
  162           return tmpParser.parseAttributes();
  163       }
  164   
  165       /**
  166        * Attribute ::= Name S? Eq S? ( '"<%=' RTAttributeValueDouble | '"'
  167        * AttributeValueDouble | "'<%=" RTAttributeValueSingle | "'"
  168        * AttributeValueSingle } Note: JSP and XML spec does not allow while spaces
  169        * around Eq. It is added to be backward compatible with Tomcat, and with
  170        * other xml parsers.
  171        */
  172       private boolean parseAttribute(AttributesImpl attrs) throws JasperException {
  173   
  174           // Get the qualified name
  175           String qName = parseName();
  176           if (qName == null)
  177               return false;
  178   
  179           // Determine prefix and local name components
  180           String localName = qName;
  181           String uri = "";
  182           int index = qName.indexOf(':');
  183           if (index != -1) {
  184               String prefix = qName.substring(0, index);
  185               uri = pageInfo.getURI(prefix);
  186               if (uri == null) {
  187                   err.jspError(reader.mark(),
  188                           "jsp.error.attribute.invalidPrefix", prefix);
  189               }
  190               localName = qName.substring(index + 1);
  191           }
  192   
  193           reader.skipSpaces();
  194           if (!reader.matches("="))
  195               err.jspError(reader.mark(), "jsp.error.attribute.noequal");
  196   
  197           reader.skipSpaces();
  198           char quote = (char) reader.nextChar();
  199           if (quote != '\'' && quote != '"')
  200               err.jspError(reader.mark(), "jsp.error.attribute.noquote");
  201   
  202           String watchString = "";
  203           if (reader.matches("<%="))
  204               watchString = "%>";
  205           watchString = watchString + quote;
  206   
  207           String attrValue = parseAttributeValue(watchString);
  208           attrs.addAttribute(uri, localName, qName, "CDATA", attrValue);
  209           return true;
  210       }
  211   
  212       /**
  213        * Name ::= (Letter | '_' | ':') (Letter | Digit | '.' | '_' | '-' | ':')*
  214        */
  215       private String parseName() throws JasperException {
  216           char ch = (char) reader.peekChar();
  217           if (Character.isLetter(ch) || ch == '_' || ch == ':') {
  218               StringBuffer buf = new StringBuffer();
  219               buf.append(ch);
  220               reader.nextChar();
  221               ch = (char) reader.peekChar();
  222               while (Character.isLetter(ch) || Character.isDigit(ch) || ch == '.'
  223                       || ch == '_' || ch == '-' || ch == ':') {
  224                   buf.append(ch);
  225                   reader.nextChar();
  226                   ch = (char) reader.peekChar();
  227               }
  228               return buf.toString();
  229           }
  230           return null;
  231       }
  232   
  233       /**
  234        * AttributeValueDouble ::= (QuotedChar - '"')* ('"' | <TRANSLATION_ERROR>)
  235        * RTAttributeValueDouble ::= ((QuotedChar - '"')* - ((QuotedChar-'"')'%>"')
  236        * ('%>"' | TRANSLATION_ERROR)
  237        */
  238       private String parseAttributeValue(String watch) throws JasperException {
  239           Mark start = reader.mark();
  240           Mark stop = reader.skipUntilIgnoreEsc(watch);
  241           if (stop == null) {
  242               err.jspError(start, "jsp.error.attribute.unterminated", watch);
  243           }
  244   
  245           String ret = parseQuoted(reader.getText(start, stop));
  246           if (watch.length() == 1) // quote
  247               return ret;
  248   
  249           // putback delimiter '<%=' and '%>', since they are needed if the
  250           // attribute does not allow RTexpression.
  251           return "<%=" + ret + "%>";
  252       }
  253   
  254       /**
  255        * QuotedChar ::= '&apos;' | '&quot;' | '\\' | '\"' | "\'" | '\>' | '\$' |
  256        * Char
  257        */
  258       private String parseQuoted(String tx) {
  259           StringBuffer buf = new StringBuffer();
  260           int size = tx.length();
  261           int i = 0;
  262           while (i < size) {
  263               char ch = tx.charAt(i);
  264               if (ch == '&') {
  265                   if (i + 5 < size && tx.charAt(i + 1) == 'a'
  266                           && tx.charAt(i + 2) == 'p' && tx.charAt(i + 3) == 'o'
  267                           && tx.charAt(i + 4) == 's' && tx.charAt(i + 5) == ';') {
  268                       buf.append('\'');
  269                       i += 6;
  270                   } else if (i + 5 < size && tx.charAt(i + 1) == 'q'
  271                           && tx.charAt(i + 2) == 'u' && tx.charAt(i + 3) == 'o'
  272                           && tx.charAt(i + 4) == 't' && tx.charAt(i + 5) == ';') {
  273                       buf.append('"');
  274                       i += 6;
  275                   } else {
  276                       buf.append(ch);
  277                       ++i;
  278                   }
  279               } else if (ch == '\\' && i + 1 < size) {
  280                   ch = tx.charAt(i + 1);
  281                   if (ch == '\\' || ch == '\"' || ch == '\'' || ch == '>') {
  282                       buf.append(ch);
  283                       i += 2;
  284                   } else if (ch == '$') {
  285                       // Replace "\$" with some special char. XXX hack!
  286                       buf.append(Constants.ESC);
  287                       i += 2;
  288                   } else {
  289                       buf.append('\\');
  290                       ++i;
  291                   }
  292               } else {
  293                   buf.append(ch);
  294                   ++i;
  295               }
  296           }
  297           return buf.toString();
  298       }
  299   
  300       private String parseScriptText(String tx) {
  301           CharArrayWriter cw = new CharArrayWriter();
  302           int size = tx.length();
  303           int i = 0;
  304           while (i < size) {
  305               char ch = tx.charAt(i);
  306               if (i + 2 < size && ch == '%' && tx.charAt(i + 1) == '\\'
  307                       && tx.charAt(i + 2) == '>') {
  308                   cw.write('%');
  309                   cw.write('>');
  310                   i += 3;
  311               } else {
  312                   cw.write(ch);
  313                   ++i;
  314               }
  315           }
  316           cw.close();
  317           return cw.toString();
  318       }
  319   
  320       /*
  321        * Invokes parserController to parse the included page
  322        */
  323       private void processIncludeDirective(String file, Node parent)
  324               throws JasperException {
  325           if (file == null) {
  326               return;
  327           }
  328   
  329           try {
  330               parserController.parse(file, parent, jarFileUrl);
  331           } catch (FileNotFoundException ex) {
  332               err.jspError(start, "jsp.error.file.not.found", file);
  333           } catch (Exception ex) {
  334               err.jspError(start, ex.getMessage());
  335           }
  336       }
  337   
  338       /*
  339        * Parses a page directive with the following syntax: PageDirective ::= ( S
  340        * Attribute)*
  341        */
  342       private void parsePageDirective(Node parent) throws JasperException {
  343           Attributes attrs = parseAttributes();
  344           Node.PageDirective n = new Node.PageDirective(attrs, start, parent);
  345   
  346           /*
  347            * A page directive may contain multiple 'import' attributes, each of
  348            * which consists of a comma-separated list of package names. Store each
  349            * list with the node, where it is parsed.
  350            */
  351           for (int i = 0; i < attrs.getLength(); i++) {
  352               if ("import".equals(attrs.getQName(i))) {
  353                   n.addImport(attrs.getValue(i));
  354               }
  355           }
  356       }
  357   
  358       /*
  359        * Parses an include directive with the following syntax: IncludeDirective
  360        * ::= ( S Attribute)*
  361        */
  362       private void parseIncludeDirective(Node parent) throws JasperException {
  363           Attributes attrs = parseAttributes();
  364   
  365           // Included file expanded here
  366           Node includeNode = new Node.IncludeDirective(attrs, start, parent);
  367           processIncludeDirective(attrs.getValue("file"), includeNode);
  368       }
  369   
  370       /**
  371        * Add a list of files. This is used for implementing include-prelude and
  372        * include-coda of jsp-config element in web.xml
  373        */
  374       private void addInclude(Node parent, List files) throws JasperException {
  375           if (files != null) {
  376               Iterator iter = files.iterator();
  377               while (iter.hasNext()) {
  378                   String file = (String) iter.next();
  379                   AttributesImpl attrs = new AttributesImpl();
  380                   attrs.addAttribute("", "file", "file", "CDATA", file);
  381   
  382                   // Create a dummy Include directive node
  383                   Node includeNode = new Node.IncludeDirective(attrs, reader
  384                           .mark(), parent);
  385                   processIncludeDirective(file, includeNode);
  386               }
  387           }
  388       }
  389   
  390       /*
  391        * Parses a taglib directive with the following syntax: Directive ::= ( S
  392        * Attribute)*
  393        */
  394       private void parseTaglibDirective(Node parent) throws JasperException {
  395   
  396           Attributes attrs = parseAttributes();
  397           String uri = attrs.getValue("uri");
  398           String prefix = attrs.getValue("prefix");
  399           if (prefix != null) {
  400               Mark prevMark = pageInfo.getNonCustomTagPrefix(prefix);
  401               if (prevMark != null) {
  402                   err.jspError(reader.mark(), "jsp.error.prefix.use_before_dcl",
  403                           prefix, prevMark.getFile(), ""
  404                                   + prevMark.getLineNumber());
  405               }
  406               if (uri != null) {
  407                   String uriPrev = pageInfo.getURI(prefix);
  408                   if (uriPrev != null && !uriPrev.equals(uri)) {
  409                       err.jspError(reader.mark(), "jsp.error.prefix.refined",
  410                               prefix, uri, uriPrev);
  411                   }
  412                   if (pageInfo.getTaglib(uri) == null) {
  413                       TagLibraryInfoImpl impl = null;
  414                       if (ctxt.getOptions().isCaching()) {
  415                           impl = (TagLibraryInfoImpl) ctxt.getOptions()
  416                                   .getCache().get(uri);
  417                       }
  418                       if (impl == null) {
  419                           String[] location = ctxt.getTldLocation(uri);
  420                           impl = new TagLibraryInfoImpl(ctxt, parserController, pageInfo,
  421                                   prefix, uri, location, err);
  422                           if (ctxt.getOptions().isCaching()) {
  423                               ctxt.getOptions().getCache().put(uri, impl);
  424                           }
  425                       } else {
  426                           // Current compilation context needs location of cached
  427                           // tag files
  428                           for (TagFileInfo info : impl.getTagFiles()) {
  429                               ctxt.setTagFileJarUrl(info.getPath(),
  430                                       ctxt.getTagFileJarUrl());
  431                           }
  432                       }
  433                       pageInfo.addTaglib(uri, impl);
  434                   }
  435                   pageInfo.addPrefixMapping(prefix, uri);
  436               } else {
  437                   String tagdir = attrs.getValue("tagdir");
  438                   if (tagdir != null) {
  439                       String urnTagdir = URN_JSPTAGDIR + tagdir;
  440                       if (pageInfo.getTaglib(urnTagdir) == null) {
  441                           pageInfo.addTaglib(urnTagdir,
  442                                   new ImplicitTagLibraryInfo(ctxt,
  443                                           parserController, pageInfo, prefix, tagdir, err));
  444                       }
  445                       pageInfo.addPrefixMapping(prefix, urnTagdir);
  446                   }
  447               }
  448           }
  449   
  450           new Node.TaglibDirective(attrs, start, parent);
  451       }
  452   
  453       /*
  454        * Parses a directive with the following syntax: Directive ::= S? ( 'page'
  455        * PageDirective | 'include' IncludeDirective | 'taglib' TagLibDirective) S?
  456        * '%>'
  457        * 
  458        * TagDirective ::= S? ('tag' PageDirective | 'include' IncludeDirective |
  459        * 'taglib' TagLibDirective) | 'attribute AttributeDirective | 'variable
  460        * VariableDirective S? '%>'
  461        */
  462       private void parseDirective(Node parent) throws JasperException {
  463           reader.skipSpaces();
  464   
  465           String directive = null;
  466           if (reader.matches("page")) {
  467               directive = "&lt;%@ page";
  468               if (isTagFile) {
  469                   err.jspError(reader.mark(), "jsp.error.directive.istagfile",
  470                           directive);
  471               }
  472               parsePageDirective(parent);
  473           } else if (reader.matches("include")) {
  474               directive = "&lt;%@ include";
  475               parseIncludeDirective(parent);
  476           } else if (reader.matches("taglib")) {
  477               if (directivesOnly) {
  478                   // No need to get the tagLibInfo objects. This alos suppresses
  479                   // parsing of any tag files used in this tag file.
  480                   return;
  481               }
  482               directive = "&lt;%@ taglib";
  483               parseTaglibDirective(parent);
  484           } else if (reader.matches("tag")) {
  485               directive = "&lt;%@ tag";
  486               if (!isTagFile) {
  487                   err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
  488                           directive);
  489               }
  490               parseTagDirective(parent);
  491           } else if (reader.matches("attribute")) {
  492               directive = "&lt;%@ attribute";
  493               if (!isTagFile) {
  494                   err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
  495                           directive);
  496               }
  497               parseAttributeDirective(parent);
  498           } else if (reader.matches("variable")) {
  499               directive = "&lt;%@ variable";
  500               if (!isTagFile) {
  501                   err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
  502                           directive);
  503               }
  504               parseVariableDirective(parent);
  505           } else {
  506               err.jspError(reader.mark(), "jsp.error.invalid.directive");
  507           }
  508   
  509           reader.skipSpaces();
  510           if (!reader.matches("%>")) {
  511               err.jspError(start, "jsp.error.unterminated", directive);
  512           }
  513       }
  514   
  515       /*
  516        * Parses a directive with the following syntax:
  517        * 
  518        * XMLJSPDirectiveBody ::= S? ( ( 'page' PageDirectiveAttrList S? ( '/>' | (
  519        * '>' S? ETag ) ) | ( 'include' IncludeDirectiveAttrList S? ( '/>' | ( '>'
  520        * S? ETag ) ) | <TRANSLATION_ERROR>
  521        * 
  522        * XMLTagDefDirectiveBody ::= ( ( 'tag' TagDirectiveAttrList S? ( '/>' | (
  523        * '>' S? ETag ) ) | ( 'include' IncludeDirectiveAttrList S? ( '/>' | ( '>'
  524        * S? ETag ) ) | ( 'attribute' AttributeDirectiveAttrList S? ( '/>' | ( '>'
  525        * S? ETag ) ) | ( 'variable' VariableDirectiveAttrList S? ( '/>' | ( '>' S?
  526        * ETag ) ) ) | <TRANSLATION_ERROR>
  527        */
  528       private void parseXMLDirective(Node parent) throws JasperException {
  529           reader.skipSpaces();
  530   
  531           String eTag = null;
  532           if (reader.matches("page")) {
  533               eTag = "jsp:directive.page";
  534               if (isTagFile) {
  535                   err.jspError(reader.mark(), "jsp.error.directive.istagfile",
  536                           "&lt;" + eTag);
  537               }
  538               parsePageDirective(parent);
  539           } else if (reader.matches("include")) {
  540               eTag = "jsp:directive.include";
  541               parseIncludeDirective(parent);
  542           } else if (reader.matches("tag")) {
  543               eTag = "jsp:directive.tag";
  544               if (!isTagFile) {
  545                   err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
  546                           "&lt;" + eTag);
  547               }
  548               parseTagDirective(parent);
  549           } else if (reader.matches("attribute")) {
  550               eTag = "jsp:directive.attribute";
  551               if (!isTagFile) {
  552                   err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
  553                           "&lt;" + eTag);
  554               }
  555               parseAttributeDirective(parent);
  556           } else if (reader.matches("variable")) {
  557               eTag = "jsp:directive.variable";
  558               if (!isTagFile) {
  559                   err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
  560                           "&lt;" + eTag);
  561               }
  562               parseVariableDirective(parent);
  563           } else {
  564               err.jspError(reader.mark(), "jsp.error.invalid.directive");
  565           }
  566   
  567           reader.skipSpaces();
  568           if (reader.matches(">")) {
  569               reader.skipSpaces();
  570               if (!reader.matchesETag(eTag)) {
  571                   err.jspError(start, "jsp.error.unterminated", "&lt;" + eTag);
  572               }
  573           } else if (!reader.matches("/>")) {
  574               err.jspError(start, "jsp.error.unterminated", "&lt;" + eTag);
  575           }
  576       }
  577   
  578       /*
  579        * Parses a tag directive with the following syntax: PageDirective ::= ( S
  580        * Attribute)*
  581        */
  582       private void parseTagDirective(Node parent) throws JasperException {
  583           Attributes attrs = parseAttributes();
  584           Node.TagDirective n = new Node.TagDirective(attrs, start, parent);
  585   
  586           /*
  587            * A page directive may contain multiple 'import' attributes, each of
  588            * which consists of a comma-separated list of package names. Store each
  589            * list with the node, where it is parsed.
  590            */
  591           for (int i = 0; i < attrs.getLength(); i++) {
  592               if ("import".equals(attrs.getQName(i))) {
  593                   n.addImport(attrs.getValue(i));
  594               }
  595           }
  596       }
  597   
  598       /*
  599        * Parses a attribute directive with the following syntax:
  600        * AttributeDirective ::= ( S Attribute)*
  601        */
  602       private void parseAttributeDirective(Node parent) throws JasperException {
  603           Attributes attrs = parseAttributes();
  604           Node.AttributeDirective n = new Node.AttributeDirective(attrs, start,
  605                   parent);
  606       }
  607   
  608       /*
  609        * Parses a variable directive with the following syntax: PageDirective ::= (
  610        * S Attribute)*
  611        */
  612       private void parseVariableDirective(Node parent) throws JasperException {
  613           Attributes attrs = parseAttributes();
  614           Node.VariableDirective n = new Node.VariableDirective(attrs, start,
  615                   parent);
  616       }
  617   
  618       /*
  619        * JSPCommentBody ::= (Char* - (Char* '--%>')) '--%>'
  620        */
  621       private void parseComment(Node parent) throws JasperException {
  622           start = reader.mark();
  623           Mark stop = reader.skipUntil("--%>");
  624           if (stop == null) {
  625               err.jspError(start, "jsp.error.unterminated", "&lt;%--");
  626           }
  627   
  628           new Node.Comment(reader.getText(start, stop), start, parent);
  629       }
  630   
  631       /*
  632        * DeclarationBody ::= (Char* - (char* '%>')) '%>'
  633        */
  634       private void parseDeclaration(Node parent) throws JasperException {
  635           start = reader.mark();
  636           Mark stop = reader.skipUntil("%>");
  637           if (stop == null) {
  638               err.jspError(start, "jsp.error.unterminated", "&lt;%!");
  639           }
  640   
  641           new Node.Declaration(parseScriptText(reader.getText(start, stop)),
  642                   start, parent);
  643       }
  644   
  645       /*
  646        * XMLDeclarationBody ::= ( S? '/>' ) | ( S? '>' (Char* - (char* '<'))
  647        * CDSect?)* ETag | <TRANSLATION_ERROR> CDSect ::= CDStart CData CDEnd
  648        * CDStart ::= '<![CDATA[' CData ::= (Char* - (Char* ']]>' Char*)) CDEnd
  649        * ::= ']]>'
  650        */
  651       private void parseXMLDeclaration(Node parent) throws JasperException {
  652           reader.skipSpaces();
  653           if (!reader.matches("/>")) {
  654               if (!reader.matches(">")) {
  655                   err.jspError(start, "jsp.error.unterminated",
  656                           "&lt;jsp:declaration&gt;");
  657               }
  658               Mark stop;
  659               String text;
  660               while (true) {
  661                   start = reader.mark();
  662                   stop = reader.skipUntil("<");
  663                   if (stop == null) {
  664                       err.jspError(start, "jsp.error.unterminated",
  665                               "&lt;jsp:declaration&gt;");
  666                   }
  667                   text = parseScriptText(reader.getText(start, stop));
  668                   new Node.Declaration(text, start, parent);
  669                   if (reader.matches("![CDATA[")) {
  670                       start = reader.mark();
  671                       stop = reader.skipUntil("]]>");
  672                       if (stop == null) {
  673                           err.jspError(start, "jsp.error.unterminated", "CDATA");
  674                       }
  675                       text = parseScriptText(reader.getText(start, stop));
  676                       new Node.Declaration(text, start, parent);
  677                   } else {
  678                       break;
  679                   }
  680               }
  681   
  682               if (!reader.matchesETagWithoutLessThan("jsp:declaration")) {
  683                   err.jspError(start, "jsp.error.unterminated",
  684                           "&lt;jsp:declaration&gt;");
  685               }
  686           }
  687       }
  688   
  689       /*
  690        * ExpressionBody ::= (Char* - (char* '%>')) '%>'
  691        */
  692       private void parseExpression(Node parent) throws JasperException {
  693           start = reader.mark();
  694           Mark stop = reader.skipUntil("%>");
  695           if (stop == null) {
  696               err.jspError(start, "jsp.error.unterminated", "&lt;%=");
  697           }
  698   
  699           new Node.Expression(parseScriptText(reader.getText(start, stop)),
  700                   start, parent);
  701       }
  702   
  703       /*
  704        * XMLExpressionBody ::= ( S? '/>' ) | ( S? '>' (Char* - (char* '<'))
  705        * CDSect?)* ETag ) | <TRANSLATION_ERROR>
  706        */
  707       private void parseXMLExpression(Node parent) throws JasperException {
  708           reader.skipSpaces();
  709           if (!reader.matches("/>")) {
  710               if (!reader.matches(">")) {
  711                   err.jspError(start, "jsp.error.unterminated",
  712                           "&lt;jsp:expression&gt;");
  713               }
  714               Mark stop;
  715               String text;
  716               while (true) {
  717                   start = reader.mark();
  718                   stop = reader.skipUntil("<");
  719                   if (stop == null) {
  720                       err.jspError(start, "jsp.error.unterminated",
  721                               "&lt;jsp:expression&gt;");
  722                   }
  723                   text = parseScriptText(reader.getText(start, stop));
  724                   new Node.Expression(text, start, parent);
  725                   if (reader.matches("![CDATA[")) {
  726                       start = reader.mark();
  727                       stop = reader.skipUntil("]]>");
  728                       if (stop == null) {
  729                           err.jspError(start, "jsp.error.unterminated", "CDATA");
  730                       }
  731                       text = parseScriptText(reader.getText(start, stop));
  732                       new Node.Expression(text, start, parent);
  733                   } else {
  734                       break;
  735                   }
  736               }
  737               if (!reader.matchesETagWithoutLessThan("jsp:expression")) {
  738                   err.jspError(start, "jsp.error.unterminated",
  739                           "&lt;jsp:expression&gt;");
  740               }
  741           }
  742       }
  743   
  744       /*
  745        * ELExpressionBody (following "${" to first unquoted "}") // XXX add formal
  746        * production and confirm implementation against it, // once it's decided
  747        */
  748       private void parseELExpression(Node parent, char type) throws JasperException {
  749           start = reader.mark();
  750           Mark last = null;
  751           boolean singleQuoted = false, doubleQuoted = false;
  752           int currentChar;
  753           do {
  754               // XXX could move this logic to JspReader
  755               last = reader.mark(); // XXX somewhat wasteful
  756               currentChar = reader.nextChar();
  757               if (currentChar == '\\' && (singleQuoted || doubleQuoted)) {
  758                   // skip character following '\' within quotes
  759                   reader.nextChar();
  760                   currentChar = reader.nextChar();
  761               }
  762               if (currentChar == -1)
  763                   err.jspError(start, "jsp.error.unterminated", type + "{");
  764               if (currentChar == '"')
  765                   doubleQuoted = !doubleQuoted;
  766               if (currentChar == '\'')
  767                   singleQuoted = !singleQuoted;
  768           } while (currentChar != '}' || (singleQuoted || doubleQuoted));
  769   
  770           new Node.ELExpression(type, reader.getText(start, last), start, parent);
  771       }
  772   
  773       /*
  774        * ScriptletBody ::= (Char* - (char* '%>')) '%>'
  775        */
  776       private void parseScriptlet(Node parent) throws JasperException {
  777           start = reader.mark();
  778           Mark stop = reader.skipUntil("%>");
  779           if (stop == null) {
  780               err.jspError(start, "jsp.error.unterminated", "&lt;%");
  781           }
  782   
  783           new Node.Scriptlet(parseScriptText(reader.getText(start, stop)), start,
  784                   parent);
  785       }
  786   
  787       /*
  788        * XMLScriptletBody ::= ( S? '/>' ) | ( S? '>' (Char* - (char* '<'))
  789        * CDSect?)* ETag ) | <TRANSLATION_ERROR>
  790        */
  791       private void parseXMLScriptlet(Node parent) throws JasperException {
  792           reader.skipSpaces();
  793           if (!reader.matches("/>")) {
  794               if (!reader.matches(">")) {
  795                   err.jspError(start, "jsp.error.unterminated",
  796                           "&lt;jsp:scriptlet&gt;");
  797               }
  798               Mark stop;
  799               String text;
  800               while (true) {
  801                   start = reader.mark();
  802                   stop = reader.skipUntil("<");
  803                   if (stop == null) {
  804                       err.jspError(start, "jsp.error.unterminated",
  805                               "&lt;jsp:scriptlet&gt;");
  806                   }
  807                   text = parseScriptText(reader.getText(start, stop));
  808                   new Node.Scriptlet(text, start, parent);
  809                   if (reader.matches("![CDATA[")) {
  810                       start = reader.mark();
  811                       stop = reader.skipUntil("]]>");
  812                       if (stop == null) {
  813                           err.jspError(start, "jsp.error.unterminated", "CDATA");
  814                       }
  815                       text = parseScriptText(reader.getText(start, stop));
  816                       new Node.Scriptlet(text, start, parent);
  817                   } else {
  818                       break;
  819                   }
  820               }
  821   
  822               if (!reader.matchesETagWithoutLessThan("jsp:scriptlet")) {
  823                   err.jspError(start, "jsp.error.unterminated",
  824                           "&lt;jsp:scriptlet&gt;");
  825               }
  826           }
  827       }
  828   
  829       /**
  830        * Param ::= '<jsp:param' S Attributes S? EmptyBody S?
  831        */
  832       private void parseParam(Node parent) throws JasperException {
  833           if (!reader.matches("<jsp:param")) {
  834               err.jspError(reader.mark(), "jsp.error.paramexpected");
  835           }
  836           Attributes attrs = parseAttributes();
  837           reader.skipSpaces();
  838   
  839           Node paramActionNode = new Node.ParamAction(attrs, start, parent);
  840   
  841           parseEmptyBody(paramActionNode, "jsp:param");
  842   
  843           reader.skipSpaces();
  844       }
  845   
  846       /*
  847        * For Include: StdActionContent ::= Attributes ParamBody
  848        * 
  849        * ParamBody ::= EmptyBody | ( '>' S? ( '<jsp:attribute' NamedAttributes )? '<jsp:body'
  850        * (JspBodyParam | <TRANSLATION_ERROR> ) S? ETag ) | ( '>' S? Param* ETag )
  851        * 
  852        * EmptyBody ::= '/>' | ( '>' ETag ) | ( '>' S? '<jsp:attribute'
  853        * NamedAttributes ETag )
  854        * 
  855        * JspBodyParam ::= S? '>' Param* '</jsp:body>'
  856        */
  857       private void parseInclude(Node parent) throws JasperException {
  858           Attributes attrs = parseAttributes();
  859           reader.skipSpaces();
  860   
  861           Node includeNode = new Node.IncludeAction(attrs, start, parent);
  862   
  863           parseOptionalBody(includeNode, "jsp:include", JAVAX_BODY_CONTENT_PARAM);
  864       }
  865   
  866       /*
  867        * For Forward: StdActionContent ::= Attributes ParamBody
  868        */
  869       private void parseForward(Node parent) throws JasperException {
  870           Attributes attrs = parseAttributes();
  871           reader.skipSpaces();
  872   
  873           Node forwardNode = new Node.ForwardAction(attrs, start, parent);
  874   
  875           parseOptionalBody(forwardNode, "jsp:forward", JAVAX_BODY_CONTENT_PARAM);
  876       }
  877   
  878       private void parseInvoke(Node parent) throws JasperException {
  879           Attributes attrs = parseAttributes();
  880           reader.skipSpaces();
  881   
  882           Node invokeNode = new Node.InvokeAction(attrs, start, parent);
  883   
  884           parseEmptyBody(invokeNode, "jsp:invoke");
  885       }
  886   
  887       private void parseDoBody(Node parent) throws JasperException {
  888           Attributes attrs = parseAttributes();
  889           reader.skipSpaces();
  890   
  891           Node doBodyNode = new Node.DoBodyAction(attrs, start, parent);
  892   
  893           parseEmptyBody(doBodyNode, "jsp:doBody");
  894       }
  895   
  896       private void parseElement(Node parent) throws JasperException {
  897           Attributes attrs = parseAttributes();
  898           reader.skipSpaces();
  899   
  900           Node elementNode = new Node.JspElement(attrs, start, parent);
  901   
  902           parseOptionalBody(elementNode, "jsp:element", TagInfo.BODY_CONTENT_JSP);
  903       }
  904   
  905       /*
  906        * For GetProperty: StdActionContent ::= Attributes EmptyBody
  907        */
  908       private void parseGetProperty(Node parent) throws JasperException {
  909           Attributes attrs = parseAttributes();
  910           reader.skipSpaces();
  911   
  912           Node getPropertyNode = new Node.GetProperty(attrs, start, parent);
  913   
  914           parseOptionalBody(getPropertyNode, "jsp:getProperty",
  915                   TagInfo.BODY_CONTENT_EMPTY);
  916       }
  917   
  918       /*
  919        * For SetProperty: StdActionContent ::= Attributes EmptyBody
  920        */
  921       private void parseSetProperty(Node parent) throws JasperException {
  922           Attributes attrs = parseAttributes();
  923           reader.skipSpaces();
  924   
  925           Node setPropertyNode = new Node.SetProperty(attrs, start, parent);
  926   
  927           parseOptionalBody(setPropertyNode, "jsp:setProperty",
  928                   TagInfo.BODY_CONTENT_EMPTY);
  929       }
  930   
  931       /*
  932        * EmptyBody ::= '/>' | ( '>' ETag ) | ( '>' S? '<jsp:attribute'
  933        * NamedAttributes ETag )
  934        */
  935       private void parseEmptyBody(Node parent, String tag) throws JasperException {
  936           if (reader.matches("/>")) {
  937               // Done
  938           } else if (reader.matches(">")) {
  939               if (reader.matchesETag(tag)) {
  940                   // Done
  941               } else if (reader.matchesOptionalSpacesFollowedBy("<jsp:attribute")) {
  942                   // Parse the one or more named attribute nodes
  943                   parseNamedAttributes(parent);
  944                   if (!reader.matchesETag(tag)) {
  945                       // Body not allowed
  946                       err.jspError(reader.mark(),
  947                               "jsp.error.jspbody.emptybody.only", "&lt;" + tag);
  948                   }
  949               } else {
  950                   err.jspError(reader.mark(), "jsp.error.jspbody.emptybody.only",
  951                           "&lt;" + tag);
  952               }
  953           } else {
  954               err.jspError(reader.mark(), "jsp.error.unterminated", "&lt;" + tag);
  955           }
  956       }
  957   
  958       /*
  959        * For UseBean: StdActionContent ::= Attributes OptionalBody
  960        */
  961       private void parseUseBean(Node parent) throws JasperException {
  962           Attributes attrs = parseAttributes();
  963           reader.skipSpaces();
  964   
  965           Node useBeanNode = new Node.UseBean(attrs, start, parent);
  966   
  967           parseOptionalBody(useBeanNode, "jsp:useBean", TagInfo.BODY_CONTENT_JSP);
  968       }
  969   
  970       /*
  971        * Parses OptionalBody, but also reused to parse bodies for plugin and param
  972        * since the syntax is identical (the only thing that differs substantially
  973        * is how to process the body, and thus we accept the body type as a
  974        * parameter).
  975        * 
  976        * OptionalBody ::= EmptyBody | ActionBody
  977        * 
  978        * ScriptlessOptionalBody ::= EmptyBody | ScriptlessActionBody
  979        * 
  980        * TagDependentOptionalBody ::= EmptyBody | TagDependentActionBody
  981        * 
  982        * EmptyBody ::= '/>' | ( '>' ETag ) | ( '>' S? '<jsp:attribute'
  983        * NamedAttributes ETag )
  984        * 
  985        * ActionBody ::= JspAttributeAndBody | ( '>' Body ETag )
  986        * 
  987        * ScriptlessActionBody ::= JspAttributeAndBody | ( '>' ScriptlessBody ETag )
  988        * 
  989        * TagDependentActionBody ::= JspAttributeAndBody | ( '>' TagDependentBody
  990        * ETag )
  991        * 
  992        */
  993       private void parseOptionalBody(Node parent, String tag, String bodyType)
  994               throws JasperException {
  995           if (reader.matches("/>")) {
  996               // EmptyBody
  997               return;
  998           }
  999   
 1000           if (!reader.matches(">")) {
 1001               err.jspError(reader.mark(), "jsp.error.unterminated", "&lt;" + tag);
 1002           }
 1003   
 1004           if (reader.matchesETag(tag)) {
 1005               // EmptyBody
 1006               return;
 1007           }
 1008   
 1009           if (!parseJspAttributeAndBody(parent, tag, bodyType)) {
 1010               // Must be ( '>' # Body ETag )
 1011               parseBody(parent, tag, bodyType);
 1012           }
 1013       }
 1014   
 1015       /**
 1016        * Attempts to parse 'JspAttributeAndBody' production. Returns true if it
 1017        * matched, or false if not. Assumes EmptyBody is okay as well.
 1018        * 
 1019        * JspAttributeAndBody ::= ( '>' # S? ( '<jsp:attribute' NamedAttributes )? '<jsp:body' (
 1020        * JspBodyBody | <TRANSLATION_ERROR> ) S? ETag )
 1021        */
 1022       private boolean parseJspAttributeAndBody(Node parent, String tag,
 1023               String bodyType) throws JasperException {
 1024           boolean result = false;
 1025   
 1026           if (reader.matchesOptionalSpacesFollowedBy("<jsp:attribute")) {
 1027               // May be an EmptyBody, depending on whether
 1028               // There's a "<jsp:body" before the ETag
 1029   
 1030               // First, parse <jsp:attribute> elements:
 1031               parseNamedAttributes(parent);
 1032   
 1033               result = true;
 1034           }
 1035   
 1036           if (reader.matchesOptionalSpacesFollowedBy("<jsp:body")) {
 1037               // ActionBody
 1038               parseJspBody(parent, bodyType);
 1039               reader.skipSpaces();
 1040               if (!reader.matchesETag(tag)) {
 1041                   err.jspError(reader.mark(), "jsp.error.unterminated", "&lt;"
 1042                           + tag);
 1043               }
 1044   
 1045               result = true;
 1046           } else if (result && !reader.matchesETag(tag)) {
 1047               // If we have <jsp:attribute> but something other than
 1048               // <jsp:body> or the end tag, translation error.
 1049               err.jspError(reader.mark(), "jsp.error.jspbody.required", "&lt;"
 1050                       + tag);
 1051           }
 1052   
 1053           return result;
 1054       }
 1055   
 1056       /*
 1057        * Params ::= `>' S? ( ( `<jsp:body>' ( ( S? Param+ S? `</jsp:body>' ) |
 1058        * <TRANSLATION_ERROR> ) ) | Param+ ) '</jsp:params>'
 1059        */
 1060       private void parseJspParams(Node parent) throws JasperException {
 1061           Node jspParamsNode = new Node.ParamsAction(start, parent);
 1062           parseOptionalBody(jspParamsNode, "jsp:params", JAVAX_BODY_CONTENT_PARAM);
 1063       }
 1064   
 1065       /*
 1066        * Fallback ::= '/>' | ( `>' S? `<jsp:body>' ( ( S? ( Char* - ( Char* `</jsp:body>' ) ) `</jsp:body>'
 1067        * S? ) | <TRANSLATION_ERROR> ) `</jsp:fallback>' ) | ( '>' ( Char* - (
 1068        * Char* '</jsp:fallback>' ) ) '</jsp:fallback>' )
 1069        */
 1070       private void parseFallBack(Node parent) throws JasperException {
 1071           Node fallBackNode = new Node.FallBackAction(start, parent);
 1072           parseOptionalBody(fallBackNode, "jsp:fallback",
 1073                   JAVAX_BODY_CONTENT_TEMPLATE_TEXT);
 1074       }
 1075   
 1076       /*
 1077        * For Plugin: StdActionContent ::= Attributes PluginBody
 1078        * 
 1079        * PluginBody ::= EmptyBody | ( '>' S? ( '<jsp:attribute' NamedAttributes )? '<jsp:body' (
 1080        * JspBodyPluginTags | <TRANSLATION_ERROR> ) S? ETag ) | ( '>' S? PluginTags
 1081        * ETag )
 1082        * 
 1083        * EmptyBody ::= '/>' | ( '>' ETag ) | ( '>' S? '<jsp:attribute'
 1084        * NamedAttributes ETag )
 1085        * 
 1086        */
 1087       private void parsePlugin(Node parent) throws JasperException {
 1088           Attributes attrs = parseAttributes();
 1089           reader.skipSpaces();
 1090   
 1091           Node pluginNode = new Node.PlugIn(attrs, start, parent);
 1092   
 1093           parseOptionalBody(pluginNode, "jsp:plugin", JAVAX_BODY_CONTENT_PLUGIN);
 1094       }
 1095   
 1096       /*
 1097        * PluginTags ::= ( '<jsp:params' Params S? )? ( '<jsp:fallback' Fallback?
 1098        * S? )?
 1099        */
 1100       private void parsePluginTags(Node parent) throws JasperException {
 1101           reader.skipSpaces();
 1102   
 1103           if (reader.matches("<jsp:params")) {
 1104               parseJspParams(parent);
 1105               reader.skipSpaces();
 1106           }
 1107   
 1108           if (reader.matches("<jsp:fallback")) {
 1109               parseFallBack(parent);
 1110               reader.skipSpaces();
 1111           }
 1112       }
 1113   
 1114       /*
 1115        * StandardAction ::= 'include' StdActionContent | 'forward'
 1116        * StdActionContent | 'invoke' StdActionContent | 'doBody' StdActionContent |
 1117        * 'getProperty' StdActionContent | 'setProperty' StdActionContent |
 1118        * 'useBean' StdActionContent | 'plugin' StdActionContent | 'element'
 1119        * StdActionContent
 1120        */
 1121       private void parseStandardAction(Node parent) throws JasperException {
 1122           Mark start = reader.mark();
 1123   
 1124           if (reader.matches(INCLUDE_ACTION)) {
 1125               parseInclude(parent);
 1126           } else if (reader.matches(FORWARD_ACTION)) {
 1127               parseForward(parent);
 1128           } else if (reader.matches(INVOKE_ACTION)) {
 1129               if (!isTagFile) {
 1130                   err.jspError(reader.mark(), "jsp.error.action.isnottagfile",
 1131                           "&lt;jsp:invoke");
 1132               }
 1133               parseInvoke(parent);
 1134           } else if (reader.matches(DOBODY_ACTION)) {
 1135               if (!isTagFile) {
 1136                   err.jspError(reader.mark(), "jsp.error.action.isnottagfile",
 1137                           "&lt;jsp:doBody");
 1138               }
 1139               parseDoBody(parent);
 1140           } else if (reader.matches(GET_PROPERTY_ACTION)) {
 1141               parseGetProperty(parent);
 1142           } else if (reader.matches(SET_PROPERTY_ACTION)) {
 1143               parseSetProperty(parent);
 1144           } else if (reader.matches(USE_BEAN_ACTION)) {
 1145               parseUseBean(parent);
 1146           } else if (reader.matches(PLUGIN_ACTION)) {
 1147               parsePlugin(parent);
 1148           } else if (reader.matches(ELEMENT_ACTION)) {
 1149               parseElement(parent);
 1150           } else if (reader.matches(ATTRIBUTE_ACTION)) {
 1151               err.jspError(start, "jsp.error.namedAttribute.invalidUse");
 1152           } else if (reader.matches(BODY_ACTION)) {
 1153               err.jspError(start, "jsp.error.jspbody.invalidUse");
 1154           } else if (reader.matches(FALLBACK_ACTION)) {
 1155               err.jspError(start, "jsp.error.fallback.invalidUse");
 1156           } else if (reader.matches(PARAMS_ACTION)) {
 1157               err.jspError(start, "jsp.error.params.invalidUse");
 1158           } else if (reader.matches(PARAM_ACTION)) {
 1159               err.jspError(start, "jsp.error.param.invalidUse");
 1160           } else if (reader.matches(OUTPUT_ACTION)) {
 1161               err.jspError(start, "jsp.error.jspoutput.invalidUse");
 1162           } else {
 1163               err.jspError(start, "jsp.error.badStandardAction");
 1164           }
 1165       }
 1166   
 1167       /*
 1168        * # '<' CustomAction CustomActionBody
 1169        * 
 1170        * CustomAction ::= TagPrefix ':' CustomActionName
 1171        * 
 1172        * TagPrefix ::= Name
 1173        * 
 1174        * CustomActionName ::= Name
 1175        * 
 1176        * CustomActionBody ::= ( Attributes CustomActionEnd ) | <TRANSLATION_ERROR>
 1177        * 
 1178        * Attributes ::= ( S Attribute )* S?
 1179        * 
 1180        * CustomActionEnd ::= CustomActionTagDependent | CustomActionJSPContent |
 1181        * CustomActionScriptlessContent
 1182        * 
 1183        * CustomActionTagDependent ::= TagDependentOptionalBody
 1184        * 
 1185        * CustomActionJSPContent ::= OptionalBody
 1186        * 
 1187        * CustomActionScriptlessContent ::= ScriptlessOptionalBody
 1188        */
 1189       private boolean parseCustomTag(Node parent) throws JasperException {
 1190   
 1191           if (reader.peekChar() != '<') {
 1192               return false;
 1193           }
 1194   
 1195           // Parse 'CustomAction' production (tag prefix and custom action name)
 1196           reader.nextChar(); // skip '<'
 1197           String tagName = reader.parseToken(false);
 1198           int i = tagName.indexOf(':');
 1199           if (i == -1) {
 1200               reader.reset(start);
 1201               return false;
 1202           }
 1203   
 1204           String prefix = tagName.substring(0, i);
 1205           String shortTagName = tagName.substring(i + 1);
 1206   
 1207           // Check if this is a user-defined tag.
 1208           String uri = pageInfo.getURI(prefix);
 1209           if (uri == null) {
 1210               reader.reset(start);
 1211               // Remember the prefix for later error checking
 1212               pageInfo.putNonCustomTagPrefix(prefix, reader.mark());
 1213               return false;
 1214           }
 1215   
 1216           TagLibraryInfo tagLibInfo = pageInfo.getTaglib(uri);
 1217           TagInfo tagInfo = tagLibInfo.getTag(shortTagName);
 1218           TagFileInfo tagFileInfo = tagLibInfo.getTagFile(shortTagName);
 1219           if (tagInfo == null && tagFileInfo == null) {
 1220               err.jspError(start, "jsp.error.bad_tag", shortTagName, prefix);
 1221           }
 1222           Class tagHandlerClass = null;
 1223           if (tagInfo != null) {
 1224               // Must be a classic tag, load it here.
 1225               // tag files will be loaded later, in TagFileProcessor
 1226               String handlerClassName = tagInfo.getTagClassName();
 1227               try {
 1228                   tagHandlerClass = ctxt.getClassLoader().loadClass(
 1229                           handlerClassName);
 1230               } catch (Exception e) {
 1231                   err.jspError(start, "jsp.error.loadclass.taghandler",
 1232                           handlerClassName, tagName);
 1233               }
 1234           }
 1235   
 1236           // Parse 'CustomActionBody' production:
 1237           // At this point we are committed - if anything fails, we produce
 1238           // a translation error.
 1239   
 1240           // Parse 'Attributes' production:
 1241           Attributes attrs = parseAttributes();
 1242           reader.skipSpaces();
 1243   
 1244           // Parse 'CustomActionEnd' production:
 1245           if (reader.matches("/>")) {
 1246               if (tagInfo != null) {
 1247                   new Node.CustomTag(tagName, prefix, shortTagName, uri, attrs,
 1248                           start, parent, tagInfo, tagHandlerClass);
 1249               } else {
 1250                   new Node.CustomTag(tagName, prefix, shortTagName, uri, attrs,
 1251                           start, parent, tagFileInfo);
 1252               }
 1253               return true;
 1254           }
 1255   
 1256           // Now we parse one of 'CustomActionTagDependent',
 1257           // 'CustomActionJSPContent', or 'CustomActionScriptlessContent'.
 1258           // depending on body-content in TLD.
 1259   
 1260           // Looking for a body, it still can be empty; but if there is a
 1261           // a tag body, its syntax would be dependent on the type of
 1262           // body content declared in the TLD.
 1263           String bc;
 1264           if (tagInfo != null) {
 1265               bc = tagInfo.getBodyContent();
 1266           } else {
 1267               bc = tagFileInfo.getTagInfo().getBodyContent();
 1268           }
 1269   
 1270           Node tagNode = null;
 1271           if (tagInfo != null) {
 1272               tagNode = new Node.CustomTag(tagName, prefix, shortTagName, uri,
 1273                       attrs, start, parent, tagInfo, tagHandlerClass);
 1274           } else {
 1275               tagNode = new Node.CustomTag(tagName, prefix, shortTagName, uri,
 1276                       attrs, start, parent, tagFileInfo);
 1277           }
 1278   
 1279           parseOptionalBody(tagNode, tagName, bc);
 1280   
 1281           return true;
 1282       }
 1283   
 1284       /*
 1285        * Parse for a template text string until '<' or "${" or "#{" is encountered,
 1286        * recognizing escape sequences "\%", "\$", and "\#".
 1287        */
 1288       private void parseTemplateText(Node parent) throws JasperException {
 1289   
 1290           if (!reader.hasMoreInput())
 1291               return;
 1292   
 1293           CharArrayWriter ttext = new CharArrayWriter();
 1294           // Output the first character
 1295           int ch = reader.nextChar();
 1296           if (ch == '\\') {
 1297               reader.pushChar();
 1298           } else {
 1299               ttext.write(ch);
 1300           }
 1301   
 1302           while (reader.hasMoreInput()) {
 1303               ch = reader.nextChar();
 1304               if (ch == '<') {
 1305                   reader.pushChar();
 1306                   break;
 1307               } else if (ch == '$' || ch == '#') {
 1308                   if (!reader.hasMoreInput()) {
 1309                       ttext.write(ch);
 1310                       break;
 1311                   }
 1312                   if (reader.nextChar() == '{') {
 1313                       reader.pushChar();
 1314                       reader.pushChar();
 1315                       break;
 1316                   }
 1317                   ttext.write(ch);
 1318                   reader.pushChar();
 1319                   continue;
 1320               } else if (ch == '\\') {
 1321                   if (!reader.hasMoreInput()) {
 1322                       ttext.write('\\');
 1323                       break;
 1324                   }
 1325                   char next = (char) reader.peekChar();
 1326                   // Looking for \% or \$ or \#
 1327                   // TODO: only recognize \$ or \# if isELIgnored is false, but since
 1328                   // it can be set in a page directive, it cannot be determined
 1329                   // here. Argh! (which is the way it should be since we shouldn't
 1330                   // convolude multiple steps at once and create confusing parsers...)
 1331                   if (next == '%' || next == '$' || next == '#') {
 1332                       ch = reader.nextChar();
 1333                   }
 1334               }
 1335               ttext.write(ch);
 1336           }
 1337           new Node.TemplateText(ttext.toString(), start, parent);
 1338       }
 1339   
 1340       /*
 1341        * XMLTemplateText ::= ( S? '/>' ) | ( S? '>' ( ( Char* - ( Char* ( '<' |
 1342        * '${' ) ) ) ( '${' ELExpressionBody )? CDSect? )* ETag ) |
 1343        * <TRANSLATION_ERROR>
 1344        */
 1345       private void parseXMLTemplateText(Node parent) throws JasperException {
 1346           reader.skipSpaces();
 1347           if (!reader.matches("/>")) {
 1348               if (!reader.matches(">")) {
 1349                   err.jspError(start, "jsp.error.unterminated",
 1350                           "&lt;jsp:text&gt;");
 1351               }
 1352               CharArrayWriter ttext = new CharArrayWriter();
 1353               while (reader.hasMoreInput()) {
 1354                   int ch = reader.nextChar();
 1355                   if (ch == '<') {
 1356                       // Check for <![CDATA[
 1357                       if (!reader.matches("![CDATA[")) {
 1358                           break;
 1359                       }
 1360                       start = reader.mark();
 1361                       Mark stop = reader.skipUntil("]]>");
 1362                       if (stop == null) {
 1363                           err.jspError(start, "jsp.error.unterminated", "CDATA");
 1364                       }
 1365                       String text = reader.getText(start, stop);
 1366                       ttext.write(text, 0, text.length());
 1367                   } else if (ch == '\\') {
 1368                       if (!reader.hasMoreInput()) {
 1369                           ttext.write('\\');
 1370                           break;
 1371                       }
 1372                       ch = reader.nextChar();
 1373                       if (ch != '$' && ch != '#') {
 1374                           ttext.write('\\');
 1375                       }
 1376                       ttext.write(ch);
 1377                   } else if (ch == '$' || ch == '#') {
 1378                       if (!reader.hasMoreInput()) {
 1379                           ttext.write(ch);
 1380                           break;
 1381                       }
 1382                       if (reader.nextChar() != '{') {
 1383                           ttext.write(ch);
 1384                           reader.pushChar();
 1385                           continue;
 1386                       }
 1387                       // Create a template text node
 1388                       new Node.TemplateText(ttext.toString(), start, parent);
 1389   
 1390                       // Mark and parse the EL expression and create its node:
 1391                       start = reader.mark();
 1392                       parseELExpression(parent, (char) ch);
 1393   
 1394                       start = reader.mark();
 1395                       ttext = new CharArrayWriter();
 1396                   } else {
 1397                       ttext.write(ch);
 1398                   }
 1399               }
 1400   
 1401               new Node.TemplateText(ttext.toString(), start, parent);
 1402   
 1403               if (!reader.hasMoreInput()) {
 1404                   err.jspError(start, "jsp.error.unterminated",
 1405                           "&lt;jsp:text&gt;");
 1406               } else if (!reader.matchesETagWithoutLessThan("jsp:text")) {
 1407                   err.jspError(start, "jsp.error.jsptext.badcontent");
 1408               }
 1409           }
 1410       }
 1411   
 1412       /*
 1413        * AllBody ::= ( '<%--' JSPCommentBody ) | ( '<%@' DirectiveBody ) | ( '<jsp:directive.'
 1414        * XMLDirectiveBody ) | ( '<%!' DeclarationBody ) | ( '<jsp:declaration'
 1415        * XMLDeclarationBody ) | ( '<%=' ExpressionBody ) | ( '<jsp:expression'
 1416        * XMLExpressionBody ) | ( '${' ELExpressionBody ) | ( '<%' ScriptletBody ) | ( '<jsp:scriptlet'
 1417        * XMLScriptletBody ) | ( '<jsp:text' XMLTemplateText ) | ( '<jsp:'
 1418        * StandardAction ) | ( '<' CustomAction CustomActionBody ) | TemplateText
 1419        */
 1420       private void parseElements(Node parent) throws JasperException {
 1421           if (scriptlessCount > 0) {
 1422               // vc: ScriptlessBody
 1423               // We must follow the ScriptlessBody production if one of
 1424               // our parents is ScriptlessBody.
 1425               parseElementsScriptless(parent);
 1426               return;
 1427           }
 1428   
 1429           start = reader.mark();
 1430           if (reader.matches("<%--")) {
 1431               parseComment(parent);
 1432           } else if (reader.matches("<%@")) {
 1433               parseDirective(parent);
 1434           } else if (reader.matches("<jsp:directive.")) {
 1435               parseXMLDirective(parent);
 1436           } else if (reader.matches("<%!")) {
 1437               parseDeclaration(parent);
 1438           } else if (reader.matches("<jsp:declaration")) {
 1439               parseXMLDeclaration(parent);
 1440           } else if (reader.matches("<%=")) {
 1441               parseExpression(parent);
 1442           } else if (reader.matches("<jsp:expression")) {
 1443               parseXMLExpression(parent);
 1444           } else if (reader.matches("<%")) {
 1445               parseScriptlet(parent);
 1446           } else if (reader.matches("<jsp:scriptlet")) {
 1447               parseXMLScriptlet(parent);
 1448           } else if (reader.matches("<jsp:text")) {
 1449               parseXMLTemplateText(parent);
 1450           } else if (reader.matches("${")) {
 1451               parseELExpression(parent, '$');
 1452           } else if (reader.matches("#{")) {
 1453               parseELExpression(parent, '#');
 1454           } else if (reader.matches("<jsp:")) {
 1455               parseStandardAction(parent);
 1456           } else if (!parseCustomTag(parent)) {
 1457               checkUnbalancedEndTag();
 1458               parseTemplateText(parent);
 1459           }
 1460       }
 1461   
 1462       /*
 1463        * ScriptlessBody ::= ( '<%--' JSPCommentBody ) | ( '<%@' DirectiveBody ) | ( '<jsp:directive.'
 1464        * XMLDirectiveBody ) | ( '<%!' <TRANSLATION_ERROR> ) | ( '<jsp:declaration'
 1465        * <TRANSLATION_ERROR> ) | ( '<%=' <TRANSLATION_ERROR> ) | ( '<jsp:expression'
 1466        * <TRANSLATION_ERROR> ) | ( '<%' <TRANSLATION_ERROR> ) | ( '<jsp:scriptlet'
 1467        * <TRANSLATION_ERROR> ) | ( '<jsp:text' XMLTemplateText ) | ( '${'
 1468        * ELExpressionBody ) | ( '<jsp:' StandardAction ) | ( '<' CustomAction
 1469        * CustomActionBody ) | TemplateText
 1470        */
 1471       private void parseElementsScriptless(Node parent) throws JasperException {
 1472           // Keep track of how many scriptless nodes we've encountered
 1473           // so we know whether our child nodes are forced scriptless
 1474           scriptlessCount++;
 1475   
 1476           start = reader.mark();
 1477           if (reader.matches("<%--")) {
 1478               parseComment(parent);
 1479           } else if (reader.matches("<%@")) {
 1480               parseDirective(parent);
 1481           } else if (reader.matches("<jsp:directive.")) {
 1482               parseXMLDirective(parent);
 1483           } else if (reader.matches("<%!")) {
 1484               err.jspError(reader.mark(), "jsp.error.no.scriptlets");
 1485           } else if (reader.matches("<jsp:declaration")) {
 1486               err.jspError(reader.mark(), "jsp.error.no.scriptlets");
 1487           } else if (reader.matches("<%=")) {
 1488               err.jspError(reader.mark(), "jsp.error.no.scriptlets");
 1489           } else if (reader.matches("<jsp:expression")) {
 1490               err.jspError(reader.mark(), "jsp.error.no.scriptlets");
 1491           } else if (reader.matches("<%")) {
 1492               err.jspError(reader.mark(), "jsp.error.no.scriptlets");
 1493           } else if (reader.matches("<jsp:scriptlet")) {
 1494               err.jspError(reader.mark(), "jsp.error.no.scriptlets");
 1495           } else if (reader.matches("<jsp:text")) {
 1496               parseXMLTemplateText(parent);
 1497           } else if (reader.matches("${")) {
 1498               parseELExpression(parent, '$');
 1499           } else if (reader.matches("#{")) {
 1500               parseELExpression(parent, '#');
 1501           } else if (reader.matches("<jsp:")) {
 1502               parseStandardAction(parent);
 1503           } else if (!parseCustomTag(parent)) {
 1504               checkUnbalancedEndTag();
 1505               parseTemplateText(parent);
 1506           }
 1507   
 1508           scriptlessCount--;
 1509       }
 1510   
 1511       /*
 1512        * TemplateTextBody ::= ( '<%--' JSPCommentBody ) | ( '<%@' DirectiveBody ) | ( '<jsp:directive.'
 1513        * XMLDirectiveBody ) | ( '<%!' <TRANSLATION_ERROR> ) | ( '<jsp:declaration'
 1514        * <TRANSLATION_ERROR> ) | ( '<%=' <TRANSLATION_ERROR> ) | ( '<jsp:expression'
 1515        * <TRANSLATION_ERROR> ) | ( '<%' <TRANSLATION_ERROR> ) | ( '<jsp:scriptlet'
 1516        * <TRANSLATION_ERROR> ) | ( '<jsp:text' <TRANSLATION_ERROR> ) | ( '${'
 1517        * <TRANSLATION_ERROR> ) | ( '<jsp:' <TRANSLATION_ERROR> ) | TemplateText
 1518        */
 1519       private void parseElementsTemplateText(Node parent) throws JasperException {
 1520           start = reader.mark();
 1521           if (reader.matches("<%--")) {
 1522               parseComment(parent);
 1523           } else if (reader.matches("<%@")) {
 1524               parseDirective(parent);
 1525           } else if (reader.matches("<jsp:directive.")) {
 1526               parseXMLDirective(parent);
 1527           } else if (reader.matches("<%!")) {
 1528               err.jspError(reader.mark(), "jsp.error.not.in.template",
 1529                       "Declarations");
 1530           } else if (reader.matches("<jsp:declaration")) {
 1531               err.jspError(reader.mark(), "jsp.error.not.in.template",
 1532                       "Declarations");
 1533           } else if (reader.matches("<%=")) {
 1534               err.jspError(reader.mark(), "jsp.error.not.in.template",
 1535                       "Expressions");
 1536           } else if (reader.matches("<jsp:expression")) {
 1537               err.jspError(reader.mark(), "jsp.error.not.in.template",
 1538                       "Expressions");
 1539           } else if (reader.matches("<%")) {
 1540               err.jspError(reader.mark(), "jsp.error.not.in.template",
 1541                       "Scriptlets");
 1542           } else if (reader.matches("<jsp:scriptlet")) {
 1543               err.jspError(reader.mark(), "jsp.error.not.in.template",
 1544                       "Scriptlets");
 1545           } else if (reader.matches("<jsp:text")) {
 1546               err.jspError(reader.mark(), "jsp.error.not.in.template",
 1547                       "&lt;jsp:text");
 1548           } else if (reader.matches("${")) {
 1549               err.jspError(reader.mark(), "jsp.error.not.in.template",
 1550                       "Expression language");
 1551           } else if (reader.matches("#{")) {
 1552               err.jspError(reader.mark(), "jsp.error.not.in.template",
 1553                       "Expression language");
 1554           } else if (reader.matches("<jsp:")) {
 1555               err.jspError(reader.mark(), "jsp.error.not.in.template",
 1556                       "Standard actions");
 1557           } else if (parseCustomTag(parent)) {
 1558               err.jspError(reader.mark(), "jsp.error.not.in.template",
 1559                       "Custom actions");
 1560           } else {
 1561               checkUnbalancedEndTag();
 1562               parseTemplateText(parent);
 1563           }
 1564       }
 1565   
 1566       /*
 1567        * Flag as error if an unbalanced end tag appears by itself.
 1568        */
 1569       private void checkUnbalancedEndTag() throws JasperException {
 1570   
 1571           if (!reader.matches("</")) {
 1572               return;
 1573           }
 1574   
 1575           // Check for unbalanced standard actions
 1576           if (reader.matches("jsp:")) {
 1577               err.jspError(start, "jsp.error.unbalanced.endtag", "jsp:");
 1578           }
 1579   
 1580           // Check for unbalanced custom actions
 1581           String tagName = reader.parseToken(false);
 1582           int i = tagName.indexOf(':');
 1583           if (i == -1 || pageInfo.getURI(tagName.substring(0, i)) == null) {
 1584               reader.reset(start);
 1585               return;
 1586           }
 1587   
 1588           err.jspError(start, "jsp.error.unbalanced.endtag", tagName);
 1589       }
 1590   
 1591       /**
 1592        * TagDependentBody :=
 1593        */
 1594       private void parseTagDependentBody(Node parent, String tag)
 1595               throws JasperException {
 1596           Mark bodyStart = reader.mark();
 1597           Mark bodyEnd = reader.skipUntilETag(tag);
 1598           if (bodyEnd == null) {
 1599               err.jspError(start, "jsp.error.unterminated", "&lt;" + tag);
 1600           }
 1601           new Node.TemplateText(reader.getText(bodyStart, bodyEnd), bodyStart,
 1602                   parent);
 1603       }
 1604   
 1605       /*
 1606        * Parses jsp:body action.
 1607        */
 1608       private void parseJspBody(Node parent, String bodyType)
 1609               throws JasperException {
 1610           Mark start = reader.mark();
 1611           Node bodyNode = new Node.JspBody(start, parent);
 1612   
 1613           reader.skipSpaces();
 1614           if (!reader.matches("/>")) {
 1615               if (!reader.matches(">")) {
 1616                   err.jspError(start, "jsp.error.unterminated", "&lt;jsp:body");
 1617               }
 1618               parseBody(bodyNode, "jsp:body", bodyType);
 1619           }
 1620       }
 1621   
 1622       /*
 1623        * Parse the body as JSP content. @param tag The name of the tag whose end
 1624        * tag would terminate the body @param bodyType One of the TagInfo body
 1625        * types
 1626        */
 1627       private void parseBody(Node parent, String tag, String bodyType)
 1628               throws JasperException {
 1629           if (bodyType.equalsIgnoreCase(TagInfo.BODY_CONTENT_TAG_DEPENDENT)) {
 1630               parseTagDependentBody(parent, tag);
 1631           } else if (bodyType.equalsIgnoreCase(TagInfo.BODY_CONTENT_EMPTY)) {
 1632               if (!reader.matchesETag(tag)) {
 1633                   err.jspError(start, "jasper.error.emptybodycontent.nonempty",
 1634                           tag);
 1635               }
 1636           } else if (bodyType == JAVAX_BODY_CONTENT_PLUGIN) {
 1637               // (note the == since we won't recognize JAVAX_*
 1638               // from outside this module).
 1639               parsePluginTags(parent);
 1640               if (!reader.matchesETag(tag)) {
 1641                   err.jspError(reader.mark(), "jsp.error.unterminated", "&lt;"
 1642                           + tag);
 1643               }
 1644           } else if (bodyType.equalsIgnoreCase(TagInfo.BODY_CONTENT_JSP)
 1645                   || bodyType.equalsIgnoreCase(TagInfo.BODY_CONTENT_SCRIPTLESS)
 1646                   || (bodyType == JAVAX_BODY_CONTENT_PARAM)
 1647                   || (bodyType == JAVAX_BODY_CONTENT_TEMPLATE_TEXT)) {
 1648               while (reader.hasMoreInput()) {
 1649                   if (reader.matchesETag(tag)) {
 1650                       return;
 1651                   }
 1652   
 1653                   // Check for nested jsp:body or jsp:attribute
 1654                   if (tag.equals("jsp:body") || tag.equals("jsp:attribute")) {
 1655                       if (reader.matches("<jsp:attribute")) {
 1656                           err.jspError(reader.mark(),
 1657                                   "jsp.error.nested.jspattribute");
 1658                       } else if (reader.matches("<jsp:body")) {
 1659                           err.jspError(reader.mark(), "jsp.error.nested.jspbody");
 1660                       }
 1661                   }
 1662   
 1663                   if (bodyType.equalsIgnoreCase(TagInfo.BODY_CONTENT_JSP)) {
 1664                       parseElements(parent);
 1665                   } else if (bodyType
 1666                           .equalsIgnoreCase(TagInfo.BODY_CONTENT_SCRIPTLESS)) {
 1667                       parseElementsScriptless(parent);
 1668                   } else if (bodyType == JAVAX_BODY_CONTENT_PARAM) {
 1669                       // (note the == since we won't recognize JAVAX_*
 1670                       // from outside this module).
 1671                       reader.skipSpaces();
 1672                       parseParam(parent);
 1673                   } else if (bodyType == JAVAX_BODY_CONTENT_TEMPLATE_TEXT) {
 1674                       parseElementsTemplateText(parent);
 1675                   }
 1676               }
 1677               err.jspError(start, "jsp.error.unterminated", "&lt;" + tag);
 1678           } else {
 1679               err.jspError(start, "jasper.error.bad.bodycontent.type");
 1680           }
 1681       }
 1682   
 1683       /*
 1684        * Parses named attributes.
 1685        */
 1686       private void parseNamedAttributes(Node parent) throws JasperException {
 1687           do {
 1688               Mark start = reader.mark();
 1689               Attributes attrs = parseAttributes();
 1690               Node.NamedAttribute namedAttributeNode = new Node.NamedAttribute(
 1691                       attrs, start, parent);
 1692   
 1693               reader.skipSpaces();
 1694               if (!reader.matches("/>")) {
 1695                   if (!reader.matches(">")) {
 1696                       err.jspError(start, "jsp.error.unterminated",
 1697                               "&lt;jsp:attribute");
 1698                   }
 1699                   if (namedAttributeNode.isTrim()) {
 1700                       reader.skipSpaces();
 1701                   }
 1702                   parseBody(namedAttributeNode, "jsp:attribute",
 1703                           getAttributeBodyType(parent, attrs.getValue("name")));
 1704                   if (namedAttributeNode.isTrim()) {
 1705                       Node.Nodes subElems = namedAttributeNode.getBody();
 1706                       if (subElems != null) {
 1707                           Node lastNode = subElems.getNode(subElems.size() - 1);
 1708                           if (lastNode instanceof Node.TemplateText) {
 1709                               ((Node.TemplateText) lastNode).rtrim();
 1710                           }
 1711                       }
 1712                   }
 1713               }
 1714               reader.skipSpaces();
 1715           } while (reader.matches("<jsp:attribute"));
 1716       }
 1717   
 1718       /**
 1719        * Determine the body type of <jsp:attribute> from the enclosing node
 1720        */
 1721       private String getAttributeBodyType(Node n, String name) {
 1722   
 1723           if (n instanceof Node.CustomTag) {
 1724               TagInfo tagInfo = ((Node.CustomTag) n).getTagInfo();
 1725               TagAttributeInfo[] tldAttrs = tagInfo.getAttributes();
 1726               for (int i = 0; i < tldAttrs.length; i++) {
 1727                   if (name.equals(tldAttrs[i].getName())) {
 1728                       if (tldAttrs[i].isFragment()) {
 1729                           return TagInfo.BODY_CONTENT_SCRIPTLESS;
 1730                       }
 1731                       if (tldAttrs[i].canBeRequestTime()) {
 1732                           return TagInfo.BODY_CONTENT_JSP;
 1733                       }
 1734                   }
 1735               }
 1736               if (tagInfo.hasDynamicAttributes()) {
 1737                   return TagInfo.BODY_CONTENT_JSP;
 1738               }
 1739           } else if (n instanceof Node.IncludeAction) {
 1740               if ("page".equals(name)) {
 1741                   return TagInfo.BODY_CONTENT_JSP;
 1742               }
 1743           } else if (n instanceof Node.ForwardAction) {
 1744               if ("page".equals(name)) {
 1745                   return TagInfo.BODY_CONTENT_JSP;
 1746               }
 1747           } else if (n instanceof Node.SetProperty) {
 1748               if ("value".equals(name)) {
 1749                   return TagInfo.BODY_CONTENT_JSP;
 1750               }
 1751           } else if (n instanceof Node.UseBean) {
 1752               if ("beanName".equals(name)) {
 1753                   return TagInfo.BODY_CONTENT_JSP;
 1754               }
 1755           } else if (n instanceof Node.PlugIn) {
 1756               if ("width".equals(name) || "height".equals(name)) {
 1757                   return TagInfo.BODY_CONTENT_JSP;
 1758               }
 1759           } else if (n instanceof Node.ParamAction) {
 1760               if ("value".equals(name)) {
 1761                   return TagInfo.BODY_CONTENT_JSP;
 1762               }
 1763           } else if (n instanceof Node.JspElement) {
 1764               return TagInfo.BODY_CONTENT_JSP;
 1765           }
 1766   
 1767           return JAVAX_BODY_CONTENT_TEMPLATE_TEXT;
 1768       }
 1769   
 1770       private void parseTagFileDirectives(Node parent) throws JasperException {
 1771           reader.setSingleFile(true);
 1772           reader.skipUntil("<");
 1773           while (reader.hasMoreInput()) {
 1774               start = reader.mark();
 1775               if (reader.matches("%--")) {
 1776                   parseComment(parent);
 1777               } else if (reader.matches("%@")) {
 1778                   parseDirective(parent);
 1779               } else if (reader.matches("jsp:directive.")) {
 1780                   parseXMLDirective(parent);
 1781               }
 1782               reader.skipUntil("<");
 1783           }
 1784       }
 1785   }

Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » jasper » compiler » [javadoc | source]