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

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