All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.caucho.jsp.JspParser Maven / Gradle / Ivy

There is a newer version: 4.0.66
Show newest version
/*
 * Copyright (c) 1998-2018 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.jsp;

import com.caucho.java.LineMap;
import com.caucho.jsp.java.JspNode;
import com.caucho.util.CharBuffer;
import com.caucho.util.L10N;
import com.caucho.util.LineCompileException;
import com.caucho.vfs.Path;
import com.caucho.vfs.ReadStream;
import com.caucho.xml.QName;
import com.caucho.xml.XmlChar;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Parses the JSP page.  Both the XML and JSP tags are understood.  However,
 * escaping is always done using JSP rules.
 */
public class JspParser {
  static L10N L = new L10N(JspParser.class);
  private static final Logger log
    = Logger.getLogger(JspParser.class.getName());

  public static final String JSP_NS = "http://java.sun.com/JSP/Page";
  public static final String JSTL_CORE_URI = "http://java.sun.com/jsp/jstl/core";
  public static final String JSTL_FMT_URI = "http://java.sun.com/jsp/jstl/fmt";

  public static final QName PREFIX = new QName("prefix");
  public static final QName TAGLIB = new QName("taglib");
  public static final QName TAGDIR = new QName("tagdir");
  public static final QName URI = new QName("uri");

  public static final QName JSP_DECLARATION =
    new QName("jsp", "declaration", JSP_NS);

  public static final QName JSP_SCRIPTLET =
    new QName("jsp", "scriptlet", JSP_NS);

  public static final QName JSP_EXPRESSION =
    new QName("jsp", "expression", JSP_NS);

  public static final QName JSP_DIRECTIVE_PAGE =
    new QName("jsp", "directive.page", JSP_NS);

  public static final QName JSP_DIRECTIVE_INCLUDE =
    new QName("jsp", "directive.include", JSP_NS);

  public static final QName JSP_DIRECTIVE_CACHE =
    new QName("jsp", "directive.cache", JSP_NS);

  public static final QName JSP_DIRECTIVE_TAGLIB =
    new QName("jsp", "directive.taglib", JSP_NS);

  public static final QName JSP_DIRECTIVE_ATTRIBUTE =
    new QName("jsp", "directive.attribute", JSP_NS);

  public static final QName JSP_DIRECTIVE_VARIABLE =
    new QName("jsp", "directive.variable", JSP_NS);

  public static final QName JSP_DIRECTIVE_TAG =
    new QName("jsp", "directive.tag", JSP_NS);

  public static final QName JSTL_CORE_OUT =
    new QName("resin-c", "out", "urn:jsptld:" + JSTL_CORE_URI);

  public static final QName JSTL_CORE_CHOOSE =
    new QName("resin-c", "choose", "urn:jsptld:" + JSTL_CORE_URI);

  public static final QName JSTL_CORE_WHEN =
    new QName("resin-c", "when", "urn:jsptld:" + JSTL_CORE_URI);

  public static final QName JSTL_CORE_OTHERWISE =
    new QName("resin-c", "otherwise", "urn:jsptld:" + JSTL_CORE_URI);

  public static final QName JSTL_CORE_FOREACH =
    new QName("resin-c", "forEach", "urn:jsptld:" + JSTL_CORE_URI);

  private static final int TAG_UNKNOWN = 0;
  private static final int TAG_JSP = 1;
  private static final int TAG_RAW = 2;

  private ParseState _parseState;
  private JspBuilder _jspBuilder;
  private ParseTagManager _tagManager;

  private LineMap _lineMap;

  private ArrayList _preludeList = new ArrayList();
  private ArrayList _codaList = new ArrayList();

  private ArrayList _includes = new ArrayList();

  private Set _prefixes = new HashSet();
  // jsp/18cy, jsp/18cz
  private Set _localPrefixes = new HashSet();

  private Path _jspPath;
  private ReadStream _stream;
  private String _uriPwd;

  private String _contextPath = "";
  private String _filename = "";
  private int _line;
  private int _lineStart;

  private int _charCount;
  private int _startText;

  private int _peek = -1;
  private boolean _seenCr = false;

  private Namespace _namespaces
    = new Namespace(null, "jsp", JSP_NS);

  private boolean _isXml;
  private boolean _isTop = true;

  private CharBuffer _tag = new CharBuffer();
  private CharBuffer _value = new CharBuffer();

  private CharBuffer _text = new CharBuffer();

  /**
   * Sets the JSP builder, which receives the SAX-like events from
   * JSP parser.
   */
  void setJspBuilder(JspBuilder builder)
  {
    _jspBuilder = builder;
  }

  /**
   * Sets the context path for error messages.
   */
  void setContextPath(String contextPath)
  {
    _contextPath = contextPath;
  }

  /**
   * Sets the parse state, which stores state information for the parsing.
   */
  void setParseState(ParseState parseState)
  {
    _parseState = parseState;
  }

  /**
   * Sets the parse state, which stores state information for the parsing.
   */
  ParseState getParseState()
  {
    return _parseState;
  }

  /**
   * Sets the tag manager
   */
  void setTagManager(ParseTagManager manager)
  {
    _tagManager = manager;
  }

  /**
   * Returns true if JSP EL expressions are enabled.
   */
  private boolean isELIgnored()
  {
    return _parseState.isELIgnored();
  }

  /**
   * Returns true if Velocity-style statements are enabled.
   */
  private boolean isVelocity()
  {
    return _parseState.isVelocityEnabled();
  }

  /**
   * Returns true if JSP EL expressions are enabled.
   */
  private boolean isDeferredSyntaxAllowedAsLiteral()
  {
    return _parseState.isDeferredSyntaxAllowedAsLiteral();
  }

  /**
   * Adds a prelude.
   */
  public void addPrelude(String prelude)
  {
    _preludeList.add(prelude);
  }

  /**
   * Adds a coda.
   */
  public void addCoda(String coda)
  {
    _codaList.add(coda);
  }

  /**
   * Starts parsing the JSP page.
   *
   * @param path the JSP source file
   * @param uri the URI for the JSP source file.
   */
  void parse(Path path, String uri)
    throws Exception
  {
    _parseState.pushNamespace("jsp", JSP_NS);

    _isXml = _parseState.isXml();

    _filename = _contextPath + uri;

    if (uri != null) {
      int p = uri.lastIndexOf('/');
      _uriPwd = p <= 0 ? "/" : uri.substring(0, p + 1);
    }
    else {
      _uriPwd = "/";
    }
    _parseState.setUriPwd(_uriPwd);

    ReadStream is = path.openRead();
    path.setUserPath(uri);

    try {
      parseJsp(is);
    } finally {
      is.close();
      for (int i = 0; i < _includes.size(); i++) {
        Include inc = _includes.get(i);
        inc._stream.close();
      }
    }
  }

  /**
   * Starts parsing the JSP page as a tag.
   *
   * @param path the JSP source file
   * @param uri the URI for the JSP source file.
   */
  void parseTag(Path path, String uri)
    throws Exception
  {
    _parseState.setTag(true);

    parse(path, uri);
  }

  /**
   * Top-level JSP parser.
   *
   * @param stream the read stream containing the JSP file
   *
   * @return an XML DOM containing the JSP.
   */
  private void parseJsp(ReadStream stream)
    throws Exception
  {
    _text.clear();
    _includes.clear();

    String uriPwd = _uriPwd;

    for (int i = _codaList.size() - 1; i >= 0; i--)
      pushInclude(_codaList.get(i), true);

    addInclude(stream, uriPwd);

    for (int i = _preludeList.size() - 1; i >= 0; i--)
      pushInclude(_preludeList.get(i), true);

    setLocation();
    _jspBuilder.startDocument();

    String pageEncoding = _parseState.getPageEncoding();

    int ch;

    if (pageEncoding != null) {
      _parseState.setPageEncoding(pageEncoding);

      stream.setEncoding(pageEncoding);
    }

    switch ((ch = stream.read())) {
    case 0xfe:
      if ((ch = stream.read()) != 0xff) {
        throw error(L.l("Expected 0xff in UTF-16 header.  UTF-16 pages with the initial byte 0xfe expect 0xff immediately following.  The 0xfe 0xff sequence is used by some application to suggest UTF-16 encoding without a directive."));
      }
      else {
        //_parseState.setContentType("text/html; charset=UTF-16BE");
        log.finer(L.l("JSP '{0}': setting page encoding using BOM 'fe ff' -> 'UTF-16BE'", _jspPath.toString()));
        _parseState.setBom(0xfeff);
        _parseState.setPageEncoding("UTF-16BE");
        stream.setEncoding("UTF-16BE");
      }
      break;

    case 0xff:
      if ((ch = stream.read()) != 0xfe) {
        throw error(L.l("Expected 0xfe in UTF-16 header.  UTF-16 pages with the initial byte 0xff expect 0xfe immediately following.  The 0xff 0xfe sequence is used by some application to suggest UTF-16 encoding without a directive."));
      }
      else {
        //_parseState.setContentType("text/html; charset=UTF-16LE");
        log.finer(L.l("JSP '{0}': setting page encoding using BOM 'ff fe' -> 'UTF-16LE'", _jspPath.toString()));
        _parseState.setBom(0xfffe);
        _parseState.setPageEncoding("UTF-16LE");
        stream.setEncoding("UTF-16LE");
      }
      break;

    case 0xef:
      if ((ch = stream.read()) != 0xbb) {
        stream.unread();
        stream.unread();
      }
      else if ((ch = stream.read()) != 0xbf) {
        throw error(L.l("Expected 0xbf in UTF-8 header.  UTF-8 pages with the initial byte 0xbb expect 0xbf immediately following.  The 0xbb 0xbf sequence is used by some application to suggest UTF-8 encoding without a directive."));
      }
      else {
        // jsp/002a, #3062
        // _parseState.setContentType("text/html; charset=UTF-8");
        log.finer(L.l("JSP '{0}': setting page encoding using BOM 'ef bb bf' -> 'UTF-8'", _jspPath.toString()));
        _parseState.setBom(0xefbbbf);
        _parseState.setPageEncoding("UTF-8");
        stream.setEncoding("UTF-8");
      }
      break;

    case -1:
      break;

    default:
      stream.unread();
      break;
    }

    ch = read();

    ch = parseXmlDeclaration(ch);

    try {
      parseNode(ch);
    } finally {
      for (int i = 0; i < _includes.size(); i++) {
        Include inc = _includes.get(i);
        inc._stream.close();
      }
    }

    setLocation();
    _jspBuilder.endDocument();
  }

  private int parseXmlDeclaration(int ch)
    throws IOException, JspParseException
  {
    if (ch != '<')
      return ch;
    else if ((ch = read()) != '?') {
      unread(ch);
      return '<';
    }
    else if ((ch = read()) != 'x') {
      addText("') {
      addText('?');
      return ch;
    }
    else {
      addText("?>");

      if (encoding != null) {
        _stream.setEncoding(encoding);
        _parseState.setPageEncoding(encoding);
      }

      return read();
    }
  }

  private void parseNode(int ch)
    throws IOException, JspParseException
  {
    while (ch != -1) {
      switch (ch) {
      case '<':
        {
          switch ((ch = read())) {
          case '%':
            if (_isXml)
              throw error(L.l("'<%' syntax is not allowed in JSP/XML syntax."));

            parseScriptlet();
            _startText = _charCount;

            // escape '\\' after scriptlet at end of line
            if ((ch = read()) == '\\') {
              if ((ch = read()) == '\n') {
                ch = read();
              }
              else if (ch == '\r') {
                if ((ch = read()) == '\n')
                  ch = read();
              }
              else
                addText('\\');
            }
            break;

          case '/':
            ch = parseCloseTag();
            break;

          case '\\':
            if ((ch = read()) == '%') {
              addText("<%");
              ch = read();
            }
            else
              addText("<\\");
            break;

          case '!':
            if (! _isXml)
              addText(" and  are legal.  You can use '&!' to escape '' expected at end of file.  For XML, the top-level tag must have a matching closing tag.",
                      activeNode.getNodeName()));
    */
  }

  /**
   * JSTL-style expressions.  Currently understood:
   *
   * 
   * ${a * b} - any arbitrary expression
   * 
*/ private int parseJspExpression() throws IOException, JspParseException { addText(); Path jspPath = _jspPath; String filename = _filename; int line = _line; CharBuffer cb = CharBuffer.allocate(); int ch; cb.append("${"); for (ch = read(); ch >= 0 && ch != '}'; ch = read()) cb.append((char) ch); cb.append("}"); ch = read(); String prefix = _parseState.findPrefix(JSTL_CORE_URI); if (prefix == null) { prefix = "resin-c"; /* _jspBuilder.startElement(JSP_DIRECTIVE_TAGLIB); _jspBuilder.attribute(new QName("prefix"), prefix); _jspBuilder.attribute(new QName("uri"), JSTL_CORE_URI); _jspBuilder.endAttributes(); _jspBuilder.endElement(JSP_DIRECTIVE_TAGLIB.getName()); */ _jspBuilder.addNamespace(prefix, JSTL_CORE_URI); processTaglib(prefix, JSTL_CORE_URI); } setLocation(jspPath, filename, line); _jspBuilder.startElement(JSTL_CORE_OUT); _jspBuilder.attribute(new QName("value"), cb.close()); _jspBuilder.attribute(new QName("escapeXml"), "false"); _jspBuilder.endAttributes(); _jspBuilder.endElement(JSTL_CORE_OUT.getName()); return ch; } private int parseVelocity(int ch) throws IOException, JspParseException { if (ch == '{') { return parseVelocityScriptlet(); } else if ('a' <= ch && ch <= 'z') { ch = readName(ch); String name = _tag.toString(); if (name.equals("if")) { ch = parseVelocityIf("if"); } else if (name.equals("elseif")) { addText(); setLocation(); JspNode node = _jspBuilder.getCurrentNode(); if (! "resin-c:when".equals(node.getTagName())) throw error(L.l("#elseif is missing a corresponding #if. Velocity-style #if syntax needs matching #if ... #elseif ... #else ... #end. The #if statements must also nest properly with any tags.")); _jspBuilder.endElement("resin-c:when"); ch = parseVelocityIf("elseif"); } else if (name.equals("else")) { addText(); setLocation(); _jspBuilder.endElement("resin-c:when"); setLocation(_jspPath, _filename, _lineStart); _lineStart = _line; _jspBuilder.startElement(JSTL_CORE_OTHERWISE); _jspBuilder.endAttributes(); ch = skipWhitespaceToEndOfLine(ch); } else if (name.equals("foreach")) { ch = parseVelocityForeach("resin-c:forEach"); } else if (name.equals("end")) { addText(); JspNode node = _jspBuilder.getCurrentNode(); String nodeName = null; if (node != null) nodeName = node.getTagName(); if (nodeName.equals("resin-c:when") || nodeName.equals("resin-c:otherwise")) { _jspBuilder.endElement(nodeName); _jspBuilder.endElement(JSTL_CORE_CHOOSE.getName()); } else if (nodeName.equals("resin-c:forEach")) _jspBuilder.endElement(nodeName); else { throw error(L.l("#end is missing a corresponding #if or #foreach. Velocity-style #if syntax needs matching #if ... #elseif ... #else ... #end. The #if statements must also nest properly with any tags.")); } ch = skipWhitespaceToEndOfLine(ch); } else { addText('#'); addText(name); } } else addText('#'); return ch; } /** * This syntax isn't part of velocity. * *
   * #{ int foo = 3; }#
   * 
*/ private int parseVelocityScriptlet() throws IOException, JspParseException { addText(); setLocation(_jspPath, _filename, _line); _lineStart = _line; _jspBuilder.startElement(JSP_SCRIPTLET); _jspBuilder.endAttributes(); int ch = read(); while (ch >= 0) { if (ch == '}') { ch = read(); if (ch == '#') break; else addText('}'); } else { addText((char) ch); ch = read(); } } createText(); _jspBuilder.endElement(JSP_SCRIPTLET.getName()); ch = read(); if (ch == '\r') { ch = read(); if (ch == '\n') return read(); else return ch; } else if (ch == '\n') return read(); else return ch; } /** * parses a #foreach statement * *
   * #foreach ([Type] var in expr)
   * ...
   * #end
   * 
* *
   * #foreach ([Type] var in [min .. max])
   * ...
   * #end
   * 
*/ private int parseVelocityForeach(String eltName) throws IOException, JspParseException { int ch; for (ch = read(); XmlChar.isWhitespace(ch); ch = read()) { } if (ch != '(') throw error(L.l("Expected `(' after #foreach at `{0}'. The velocity-style #foreach syntax needs parentheses: #foreach ($a in expr)", badChar(ch))); addText(); processTaglib("resin-c", JSTL_CORE_URI); setLocation(_jspPath, _filename, _lineStart); _lineStart = _line; _jspBuilder.startElement(JSTL_CORE_FOREACH); CharBuffer cb = CharBuffer.allocate(); parseVelocityName(cb); if (cb.length() == 0) { throw error(L.l("Expected iteration variable for #foreach at `{0}'. The velocity-style #foreach syntax is: #foreach ($a in expr)", badChar(ch))); } String name = cb.toString(); cb.clear(); parseVelocityName(cb); if (cb.length() == 0) { throw error(L.l("Expected 'in' for #foreach at `{0}'. The velocity-style #foreach syntax is: #foreach ($a in expr)", badChar(ch))); } String value = cb.toString(); if (! value.equals("in")) { throw error(L.l("Expected 'in' for #foreach at `{0}'. The velocity-style #foreach syntax is: #foreach ($a in expr)", badChar(ch))); } if (name.startsWith("$")) name = name.substring(1); _jspBuilder.attribute(new QName("var"), name); cb.clear(); parseVelocityExpr(cb, ')'); String expr = cb.close(); if (expr.indexOf("..") > 0) { int h = 0; for (; Character.isWhitespace(expr.charAt(h)); h++) { } if (expr.charAt(h) != '[') throw error(L.l("Expected '[' for #foreach `{0}'. The velocity-style #foreach syntax is: #foreach ([Type] $a in [min .. max])", badChar(expr.charAt(h)))); int t = expr.length() - 1; for (; Character.isWhitespace(expr.charAt(t)); t--) { } if (expr.charAt(t) != ']') throw error(L.l("Expected ']' for #foreach `{0}'. The velocity-style #foreach syntax is: #foreach ($a in [min .. max])", badChar(expr.charAt(t)))); int p = expr.indexOf(".."); String min = expr.substring(h + 1, p); String max = expr.substring(p + 2, t); _jspBuilder.attribute(new QName("begin"), "${" + min + "}"); _jspBuilder.attribute(new QName("end"), "${" + max + "}"); } else { _jspBuilder.attribute(new QName("items"), "${" + expr + "}"); } _jspBuilder.endAttributes(); return skipWhitespaceToEndOfLine(read()); } /** * parses an #if statement */ private int parseVelocityIf(String eltName) throws IOException, JspParseException { int ch; for (ch = read(); XmlChar.isWhitespace(ch); ch = read()) { } if (ch != '(') throw error(L.l("Expected `(' after #if at `{0}'. The velocity-style #if syntax needs parentheses: #if (...)", badChar(ch))); addText(); processTaglib("resin-c", JSTL_CORE_URI); setLocation(_jspPath, _filename, _line); if (eltName.equals("if")) { _jspBuilder.startElement(JSTL_CORE_CHOOSE); _jspBuilder.endAttributes(); } _jspBuilder.startElement(JSTL_CORE_WHEN); _lineStart = _line; CharBuffer cb = CharBuffer.allocate(); parseVelocityExpr(cb, ')'); _jspBuilder.attribute(new QName("test"), "${" + cb.close() + "}"); _jspBuilder.endAttributes(); return skipWhitespaceToEndOfLine(read()); } private int parseVelocityName(CharBuffer cb) throws IOException, JspParseException { int ch; for (ch = read(); XmlChar.isWhitespace(ch); ch = read()) { } for (; Character.isJavaIdentifierPart((char) ch); ch = read()) cb.append((char) ch); return ch; } private int parseVelocityMin(CharBuffer cb) throws IOException, JspParseException { int ch; for (ch = read(); ch >= 0; ch = read()) { if (ch != '$') cb.append((char) ch); if (ch == '(') { ch = parseVelocityExpr(cb, ')'); cb.append((char) ch); } else if (ch == '[') { ch = parseVelocityExpr(cb, ']'); cb.append((char) ch); } else if (ch == '.') { ch = read(); if (ch == '.') return ch; else { cb.append('.'); _peek = ch; } } } return ch; } private int parseVelocityExpr(CharBuffer cb, int end) throws IOException, JspParseException { int ch; for (ch = read(); ch >= 0 && ch != end; ch = read()) { if (ch != '$') cb.append((char) ch); if (ch == '(') { ch = parseVelocityExpr(cb, ')'); cb.append((char) ch); } else if (ch == '[') { ch = parseVelocityExpr(cb, ']'); cb.append((char) ch); } } return ch; } /** * Parses a <![CDATA[ block. All text in the CDATA is uninterpreted. */ private void parseCdata() throws IOException, JspParseException { int ch; ch = readName(read()); String name = _tag.toString(); if (! name.equals("CDATA")) throw error(L.l("Expected .", String.valueOf(ch))); String filename = _filename; int line = _line; while ((ch = read()) >= 0) { while (ch == ']') { if ((ch = read()) != ']') addText(']'); else if ((ch = read()) != '>') addText("]]"); else return; } addText((char) ch); } throw error(L.l("Expected closing ]]> at end of file to match = 0) { switch (ch) { case '\\': addText('\\'); ch = read(); if (ch >= 0) addText((char) ch); ch = read(); break; case '%': ch = read(); if (ch == '>') { createText(); setLocation(); _jspBuilder.endElement(eltName.getName()); return; } else if (ch == '\\') { ch = read(); if (ch == '>') { addText("%"); } else addText("%\\"); } else addText('%'); break; default: addText((char) ch); ch = read(); break; } } createText(); setLocation(); _jspBuilder.endElement(eltName.getName()); } /** * Parses the JSP directive syntax. */ private void parseDirective() throws IOException, JspParseException { String language = null; int ch = skipWhitespace(read()); String directive = ""; if (XmlChar.isNameStart(ch)) { ch = readName(ch); directive = _tag.toString(); } else throw error(L.l("Expected jsp directive name at '{0}'. JSP directive syntax is <%@ name attr1='value1' ... %>", badChar(ch))); QName qname; if (directive.equals("page")) qname = JSP_DIRECTIVE_PAGE; else if (directive.equals("include")) qname = JSP_DIRECTIVE_INCLUDE; else if (directive.equals("taglib")) qname = JSP_DIRECTIVE_TAGLIB; else if (directive.equals("cache")) qname = JSP_DIRECTIVE_CACHE; else if (directive.equals("attribute")) qname = JSP_DIRECTIVE_ATTRIBUTE; else if (directive.equals("variable")) qname = JSP_DIRECTIVE_VARIABLE; else if (directive.equals("tag")) qname = JSP_DIRECTIVE_TAG; else throw error(L.l("'{0}' is an unknown jsp directive. Only <%@ page ... %>, <%@ include ... %>, <%@ taglib ... %>, and <%@ cache ... %> are known.", directive)); unread(ch); ArrayList keys = new ArrayList(); ArrayList values = new ArrayList(); ArrayList prefixes = new ArrayList(); ArrayList uris = new ArrayList(); parseAttributes(keys, values, prefixes, uris); ch = skipWhitespace(read()); if (ch != '%' || (ch = read()) != '>') { throw error(L.l("expected '%>' at {0}. JSP directive syntax is '<%@ name attr1='value1' ... %>'. (Started at line {1})", badChar(ch), _lineStart)); } setLocation(_jspPath, _filename, _lineStart); _lineStart = _line; _jspBuilder.startElement(qname); for (int i = 0; i < keys.size(); i++) { _jspBuilder.attribute(keys.get(i), values.get(i)); } _jspBuilder.endAttributes(); if (qname.equals(JSP_DIRECTIVE_TAGLIB)) processTaglibDirective(keys, values); setLocation(); _jspBuilder.endElement(qname.getName()); if (qname.equals(JSP_DIRECTIVE_PAGE) || qname.equals(JSP_DIRECTIVE_TAG)) { String contentEncoding = _parseState.getPageEncoding(); if (contentEncoding == null) contentEncoding = _parseState.getCharEncoding(); if (contentEncoding != null) { try { _stream.setEncoding(contentEncoding); } catch (Exception e) { log.log(Level.FINER, e.toString(), e); throw error(L.l("unknown content encoding '{0}'", contentEncoding), e); } } } /* if (directive.equals("include")) parseIncludeDirective(elt); else if (directive.equals("taglib")) parseTaglibDirective(elt); */ } /** * Parses an XML comment. */ private void parseComment() throws IOException, JspParseException { int ch = read(); while (ch >= 0) { if (ch == '-') { ch = read(); while (ch == '-') { if ((ch = read()) == '-') continue; else if (ch == '%' && (ch = read()) == '>') return; else if (ch == '-') ch = read(); } } else ch = read(); } } private void parseXmlComment() throws IOException, JspParseException { int ch; while ((ch = read()) >= 0) { while (ch == '-') { if ((ch = read()) == '-' && (ch = read()) == '>') return; } } } /** * Parses the open tag. */ private void parseOpenTag(String name, int ch, boolean isXml) throws IOException, JspParseException { addText(); ch = skipWhitespace(ch); ArrayList keys = new ArrayList(); ArrayList values = new ArrayList(); ArrayList prefixes = new ArrayList(); ArrayList uris = new ArrayList(); unread(ch); parseAttributes(keys, values, prefixes, uris); QName qname = getElementQName(name); setLocation(_jspPath, _filename, _lineStart); _lineStart = _line; _jspBuilder.startElement(qname); for (int i = 0; i < keys.size(); i++) { QName key = keys.get(i); String value = values.get(i); _jspBuilder.attribute(key, value); } _jspBuilder.endAttributes(); for (int i = 0; i < prefixes.size(); i++) { String prefix = prefixes.get(i); String uri = uris.get(i); _jspBuilder.addNamespace(prefix, uri); } if (qname.equals(JSP_DIRECTIVE_TAGLIB)) processTaglibDirective(keys, values); ch = skipWhitespace(read()); JspNode node = _jspBuilder.getCurrentNode(); if (ch == '/') { if ((ch = read()) != '>') throw error(L.l("expected '/>' at '{0}' (for tag '<{1}>' at line {2}). The XML empty tag syntax is: ", badChar(ch), name, String.valueOf(_lineStart))); setLocation(); _jspBuilder.endElement(qname.getName()); } else if (ch != '>') throw error(L.l("expected '>' at '{0}' (for tag '<{1}>' at line {2}). The XML tag syntax is: ", badChar(ch), name, String.valueOf(_lineStart))); // If tagdependent and not XML mode, then read the raw text. else if ("tagdependent".equals(node.getBodyContent()) && ! _isXml) { String tail = ""; for (ch = read(); ch >= 0; ch = read()) { _text.append((char) ch); if (_text.endsWith(tail)) { _text.setLength(_text.length() - tail.length()); addText(); _jspBuilder.endElement(qname.getName()); return; } } throw error(L.l("expected '{0}' at end of file (for tag <{1}> at line {2}). Tags with 'tagdependent' content need close tags.", tail, String.valueOf(_lineStart))); } } /** * Returns the full QName for the JSP page's name. */ private QName getElementQName(String name) { int p = name.lastIndexOf(':'); if (p > 0) { String prefix = name.substring(0, p); String url = Namespace.find(_namespaces, prefix); _prefixes.add(prefix); if (url != null) return new QName(prefix, name.substring(p + 1), url); else return new QName("", name, ""); } else { String url = Namespace.find(_namespaces, ""); if (url != null) return new QName("", name, url); else return new QName("", name, ""); } } /** * Returns the full QName for the JSP page's name. */ private QName getAttributeQName(String name) { int p = name.lastIndexOf(':'); if (p > 0) { String prefix = name.substring(0, p); String url = Namespace.find(_namespaces, prefix); if (url != null) return new QName(prefix, name.substring(p + 1), url); else return new QName("", name, ""); } else return new QName("", name, ""); } /** * Parses the attributes of an element. */ private void parseAttributes(ArrayList names, ArrayList values, ArrayList prefixes, ArrayList uris) throws IOException, JspParseException { int ch = skipWhitespace(read()); while (XmlChar.isNameStart(ch)) { ch = readName(ch); String key = _tag.toString(); readValue(key, ch, _isXml); String value = _value.toString(); if (key.startsWith("xmlns:")) { String prefix = key.substring(6); _jspBuilder.startPrefixMapping(prefix, value); //_parseState.pushNamespace(prefix, value); prefixes.add(prefix); uris.add(value); _namespaces = new Namespace(_namespaces, prefix, value); } else if (key.equals("xmlns")) { _jspBuilder.startPrefixMapping("", value); //_parseState.pushNamespace(prefix, value); //_parseState.pushNamespace("", value); _namespaces = new Namespace(_namespaces, "", value); } else { names.add(getAttributeQName(key)); values.add(value); } ch = skipWhitespace(read()); } unread(ch); } /** * Reads an attribute value. */ private void readValue(String attribute, int ch, boolean isXml) throws IOException, JspParseException { boolean isRuntimeAttribute = false; _value.clear(); ch = skipWhitespace(ch); if (ch != '=') { unread(ch); return; } ch = skipWhitespace(read()); if (ch != '\'' && ch != '"') { if (XmlChar.isNameChar(ch)) { ch = readName(ch); throw error(L.l("'{0}' attribute value must be quoted at '{1}'. JSP attribute syntax is either attr=\"value\" or attr='value'.", attribute, _tag)); } else throw error(L.l("'{0}' attribute value must be quoted at '{1}'. JSP attribute syntax is either attr=\"value\" or attr='value'.", attribute, badChar(ch))); } int end = ch; int lastCh = 0; ch = read(); if (ch != '<') { } else if ((ch = read()) != '%') _value.append('<'); else if ((ch = read()) != '=') _value.append("<%"); else { _value.append("<%"); isRuntimeAttribute = true; } while (ch != -1 && (ch != end || isRuntimeAttribute)) { if (ch == '\\') { switch ((ch = read())) { case '\\': case '\'': case '\"': // jsp/1505 vs jsp/184s // _value.append('\\'); _value.append((char) ch); ch = read(); break; case '>': if (lastCh == '%') { _value.append('>'); ch = read(); } else _value.append('\\'); break; case '%': if (lastCh == '<') { _value.append('%'); ch = read(); } else _value.append('\\'); break; default: _value.append('\\'); break; } } else if (ch == '%' && isRuntimeAttribute) { _value.append('%'); ch = read(); if (ch == '>') isRuntimeAttribute = false; } else if (ch == '&' && isXml) { lastCh = -1; _value.append((char) parseEntity()); ch = read(); } else if (ch == '&') { if ((ch = read()) == 'a') { if ((ch = read()) != 'p') _value.append("&a"); else if ((ch = read()) != 'o') _value.append("&ap"); else if ((ch = read()) != 's') _value.append("&apo"); else if ((ch = read()) != ';') _value.append("&apos"); else { _value.append('\''); ch = read(); } } else if (ch == 'q') { if ((ch = read()) != 'u') _value.append("&q"); else if ((ch = read()) != 'o') _value.append("&qu"); else if ((ch = read()) != 't') _value.append("&quo"); else if ((ch = read()) != ';') _value.append("""); else { _value.append('"'); ch = read(); } } else { _value.append('&'); } } else { _value.append((char) ch); lastCh = ch; ch = read(); } } } /** * Parses an XML entity. */ int parseEntity() throws IOException, JspParseException { int ch = read(); if (_isXml && ch == '#') { int value = 0; for (ch = read(); ch >= '0' && ch <= '9'; ch = read()) value = 10 * value + ch - '0'; if (ch != ';') throw error(L.l("expected ';' at '{0}' in character entity. The XML character entities syntax is &#nn;", badChar(ch))); return (char) value; } CharBuffer cb = CharBuffer.allocate(); for (; ch >= 'a' && ch <= 'z'; ch = read()) { cb.append((char) ch); } if (ch != ';') { log.warning(L.l("expected ';' at '{0}' in entity '&{1}'. The XML entity syntax is &name;", badChar(ch), cb)); } String entity = cb.close(); if (entity.equals("lt")) return '<'; else if (entity.equals("gt")) return '>'; else if (entity.equals("amp")) return '&'; else if (entity.equals("apos")) return '\''; else if (entity.equals("quot")) return '"'; else throw error(L.l("unknown entity '&{0};'. XML only recognizes the special entities <, >, &, ' and "", entity)); } private int parseCloseTag() throws IOException, JspParseException { int ch; if (! XmlChar.isNameStart(ch = read())) { addText("') throw error(L.l("expected '>' at {0}. The XML close tag syntax is .", badChar(ch))); JspNode node = _jspBuilder.getCurrentNode(); String nodeName = node.getTagName(); if (nodeName.equals(name)) { } else if (nodeName.equals("resin-c:when")) { throw error(L.l("#if expects closing #end before (#if at {1}). #if statements require #end before the enclosing tag closes.", name, String.valueOf(node.getStartLine()))); } else if (nodeName.equals("resin-c:otherwise")) { throw error(L.l("#else expects closing #end before (#else at {1}). #if statements require #end before the enclosing tag closes.", name, String.valueOf(node.getStartLine()))); } else { throw error(L.l("expected at . Closing tags must match opened tags.", nodeName, name)); } addText(); setLocation(); _jspBuilder.endElement(name); return read(); } private void processTaglibDirective(ArrayList keys, ArrayList values) throws IOException, JspParseException { int p = keys.indexOf(PREFIX); if (p < 0) throw error(L.l("The taglib directive requires a 'prefix' attribute. 'prefix' is the XML prefix for all tags in the taglib.")); String prefix = values.get(p); if (_prefixes.contains(prefix) && _parseState.getQName(prefix) == null) { throw error(L.l("The taglib prefix '{0}' must be defined before it is used.", prefix)); } if (_localPrefixes.contains(prefix)) throw error(L.l( "<{0}> cannot occur after an action that uses the same prefix: {1}.", JSP_DIRECTIVE_TAGLIB.getName(), prefix)); String uri = null; p = keys.indexOf(URI); if (p >= 0) uri = values.get(p); String tagdir = null; p = keys.indexOf(TAGDIR); if (p >= 0) tagdir = values.get(p); if (uri != null) processTaglib(prefix, uri); else if (tagdir != null) processTaglibDir(prefix, tagdir); } /** * Adds a new known taglib prefix to the current namespace. */ private void processTaglib(String prefix, String uri) throws JspParseException { Taglib taglib = null; int colon = uri.indexOf(':'); int slash = uri.indexOf('/'); String location = null; if (colon > 0 && colon < slash) location = uri; else if (slash == 0) location = uri; else location = _uriPwd + uri; try { taglib = _tagManager.addTaglib(prefix, uri, location); String tldURI = "urn:jsptld:" + uri; _parseState.pushNamespace(prefix, tldURI); _namespaces = new Namespace(_namespaces, prefix, tldURI); return; } catch (JspParseException e) { throw error(e); } catch (Exception e) { log.log(Level.WARNING, e.toString(), e); } if (colon > 0 && colon < slash) throw error(L.l("Unknown taglib '{0}'. Taglibs specified with an absolute URI must either be:\n1) specified in the web.xml\n2) defined in a jar's .tld in META-INF\n3) defined in a .tld in WEB-INF\n4) predefined by Resin", uri)); } /** * Adds a new known tag dir to the current namespace. */ private void processTaglibDir(String prefix, String tagDir) throws JspParseException { Taglib taglib = null; try { taglib = _tagManager.addTaglibDir(prefix, tagDir); String tagURI = "urn:jsptagdir:" + tagDir; _parseState.pushNamespace(prefix, tagURI); _namespaces = new Namespace(_namespaces, prefix, tagURI); return; } catch (JspParseException e) { throw error(e); } catch (Exception e) { log.log(Level.WARNING, e.toString(), e); } } private void processIncludeDirective(ArrayList keys, ArrayList values) throws IOException, JspParseException { int p = keys.indexOf("file"); if (p < 0) throw error(L.l("The include directive requires a 'file' attribute.")); String file = (String) values.get(p); pushInclude(file); } public void pushInclude(String value) throws IOException, JspParseException { pushInclude(value, false); } public void pushInclude(String value, boolean allowDuplicate) throws IOException, JspParseException { if (value.equals("")) throw error("include directive needs 'file' attribute. Use either <%@ include file='myfile.jsp' %> or "); Path include; if (value.length() > 0 && value.charAt(0) == '/') include = _parseState.resolvePath(value); else include = _parseState.resolvePath(_uriPwd + value); String newUrl = _uriPwd; if (value.startsWith("/")) newUrl = value; else newUrl = _uriPwd + value; include.setUserPath(newUrl); String newUrlPwd; int p = newUrl.lastIndexOf('/'); newUrlPwd = newUrl.substring(0, p + 1); if (_jspPath != null && _jspPath.equals(include) && ! allowDuplicate) throw error(L.l("circular include of '{0}' forbidden. A JSP file may not include itself.", include)); for (int i = 0; i < _includes.size(); i++) { Include inc = _includes.get(i); if (inc._stream != null && inc._stream.getPath() != null && inc._stream.getPath().equals(include) && ! allowDuplicate) throw error(L.l("circular include of '{0}'. A JSP file may not include itself.", include)); } try { addInclude(include.openRead(), newUrlPwd); } catch (IOException e) { log.log(Level.WARNING, e.toString(), e); if (include.exists()) throw error(L.l("can't open include of '{0}'. '{1}' exists but it's not readable.", value, include.getNativePath())); else throw error(L.l("can't open include of '{0}'. '{1}' does not exist.", value, include.getNativePath())); } } private void addInclude(ReadStream stream, String newUrlPwd) throws IOException, JspParseException { addText(); readLf(); Include inc = null; if (_stream != null) { inc = new Include(_localPrefixes, _stream, _line, _uriPwd, _parseState.isLocalScriptingInvalid()); _parseState.setLocalScriptingInvalid(false); _includes.add(inc); _localPrefixes = new HashSet(); } _parseState.addDepend(stream.getPath()); try { String encoding = _stream.getEncoding(); if (encoding != null) stream.setEncoding(encoding); } catch (Exception e) { } _stream = stream; _filename = stream.getUserPath(); _jspPath = stream.getPath(); _line = 1; _lineStart = _line; _uriPwd = newUrlPwd; _parseState.setUriPwd(_uriPwd); } /** * Skips whitespace characters. * * @param ch the current character * * @return the first non-whitespace character */ private int skipWhitespace(int ch) throws IOException, JspParseException { for (; XmlChar.isWhitespace(ch); ch = read()) { } return ch; } /** * Skips whitespace to end of line * * @param ch the current character * * @return the first non-whitespace character */ private int skipWhitespaceToEndOfLine(int ch) throws IOException, JspParseException { for (; XmlChar.isWhitespace(ch); ch = read()) { if (ch == '\n') return read(); else if (ch == '\r') { ch = read(); if (ch == '\n') return read(); else return ch; } } return ch; } private void addText(char ch) throws JspParseException { _text.append(ch); } private void addText(String s) throws JspParseException { _text.append(s); } private void addText() throws JspParseException { if (_text.length() > 0) { createText(); } _startText = _charCount; _lineStart = _line; } private void createText() throws JspParseException { String string = _text.toString(); setLocation(_jspPath, _filename, _lineStart); if (_parseState.isTrimWhitespace() && isWhitespace(string)) { } else { _jspBuilder.text(string, _filename, _lineStart, _line); } _lineStart = _line; _text.clear(); _startText = _charCount; } private boolean isWhitespace(String s) { int length = s.length(); for (int i = 0; i < length; i++) { if (! Character.isWhitespace(s.charAt(i))) return false; } return true; } /** * Checks to see if the element name represents a tag. */ private int getTag(String name) throws JspParseException { int p = name.indexOf(':'); if (p < 0) return TAG_UNKNOWN; String prefix = name.substring(0, p); String local = name.substring(p + 1); _prefixes.add(prefix); _localPrefixes.add(prefix); String url = Namespace.find(_namespaces, prefix); if (url != null) return TAG_JSP; else return TAG_UNKNOWN; /* QName qname; if (url != null) qname = new QName(prefix, local, url); else qname = new QName(prefix, local, null); TagInfo tag = _tagManager.getTag(qname); if (tag != null) return TAG_JSP; else return TAG_UNKNOWN; */ } private void unread(int ch) { _peek = ch; } /** * Reads the next character we're in the middle of cr/lf. */ private void readLf() throws IOException, JspParseException { if (_seenCr) { int ch = read(); if (ch != '\n') _peek = ch; } } /** * Reads the next character. */ private int read() throws IOException, JspParseException { int ch; if (_peek >= 0) { ch = _peek; _peek = -1; return ch; } try { if ((ch = _stream.readChar()) >= 0) { _charCount++; if (ch == '\r') { _line++; _charCount = 0; _seenCr = true; } else if (ch == '\n' && _seenCr) { _seenCr = false; _charCount = 0; } else if (ch == '\n') { _line++; _charCount = 0; } else { _seenCr = false; } return ch; } } catch (IOException e) { throw error(e.toString()); } _stream.close(); _seenCr = false; if (_includes.size() > 0) { setLocation(_jspPath, _filename, _line); Include include = _includes.get(_includes.size() - 1); _includes.remove(_includes.size() - 1); _stream = include._stream; _filename = _stream.getUserPath(); _jspPath = _stream.getPath(); _line = include._line; _lineStart = _line; _uriPwd = include._uriPwd; _localPrefixes = include._localPrefixes; _parseState.setUriPwd(_uriPwd); _parseState.setLocalScriptingInvalid(include._oldLocalScriptingDisabled); setLocation(_jspPath, _filename, _line); return read(); } return -1; } void clear(Path appDir, String errorPage) { } /** * Creates an error message adding the filename and line. * * @param e the exception */ public JspParseException error(Exception e) { String message = e.getMessage(); if (e instanceof JspParseException) { log.log(Level.FINE, e.toString(), e); } if (e instanceof JspLineParseException) return (JspLineParseException) e; else if (e instanceof LineCompileException) return new JspLineParseException(e); if (_lineMap == null) return new JspLineParseException(_filename + ":" + _line + ": " + message, e); else { LineMap.Line line = _lineMap.getLine(_line); return new JspLineParseException(line.getSourceFilename() + ":" + line.getSourceLine(_line) + ": " + message, e); } } /** * Creates an error message adding the filename and line. * * @param message the error message */ public JspParseException error(String message) { JspGenerator gen = _jspBuilder.getGenerator(); if (_lineMap == null) { if (gen != null) return new JspLineParseException(_filename + ":" + _line + ": " + message + gen.getSourceLines(_jspPath, _line)); else return new JspLineParseException(_filename + ":" + _line + ": " + message); } else { LineMap.Line line = _lineMap.getLine(_line); return new JspLineParseException(line.getSourceFilename() + ":" + line.getSourceLine(_line) + ": " + message); } } /** * Creates an error message adding the filename and line. * * @param message the error message */ public JspParseException error(String message, Throwable e) { if (_lineMap == null) return new JspLineParseException(_filename + ":" + _line + ": " + message, e); else { LineMap.Line line = _lineMap.getLine(_line); return new JspLineParseException(line.getSourceFilename() + ":" + line.getSourceLine(_line) + ": " + message, e); } } /** * Sets the current location in the original file */ private void setLocation() { setLocation(_jspPath, _filename, _line); } /** * Sets the current location in the original file * * @param filename the filename * @param line the line in the source file */ private void setLocation(Path jspPath, String filename, int line) { if (_lineMap == null) { _jspBuilder.setLocation(jspPath, filename, line); } else { LineMap.Line srcLine = _lineMap.getLine(line); if (srcLine != null) { _jspBuilder.setLocation(jspPath, srcLine.getSourceFilename(), srcLine.getSourceLine(line)); } } } private String badChar(int ch) { if (ch < 0) return "end of file"; else if (ch == '\n' || ch == '\r') return "end of line"; else if (ch >= 0x20 && ch <= 0x7f) return "'" + (char) ch + "'"; else return "'" + (char) ch + "' (\\u" + hex(ch) + ")"; } private String hex(int value) { CharBuffer cb = new CharBuffer(); for (int b = 3; b >= 0; b--) { int v = (value >> (4 * b)) & 0xf; if (v < 10) cb.append((char) (v + '0')); else cb.append((char) (v - 10 + 'a')); } return cb.toString(); } class Include { ReadStream _stream; int _line; String _uriPwd; Set _localPrefixes; boolean _oldLocalScriptingDisabled; Include(Set prefixes, ReadStream stream, int line, String uriPwd, boolean oldLocalScriptingDisabled ) { _stream = stream; _line = line; _uriPwd = uriPwd; _localPrefixes = prefixes; _oldLocalScriptingDisabled = oldLocalScriptingDisabled; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy