You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by co...@apache.org on 2001/06/06 07:40:14 UTC

cvs commit: jakarta-tomcat-jasper/jasper34/generator/org/apache/jasper34/parser CoreElement.java DelegatingListener.java JspReader.java Mark.java ParseEventListener.java ParseException.java ParseUtil.java Parser.java

costin      01/06/05 22:40:14

  Added:       jasper34/generator/org/apache/jasper34/parser
                        CoreElement.java DelegatingListener.java
                        JspReader.java Mark.java ParseEventListener.java
                        ParseException.java ParseUtil.java Parser.java
  Log:
  Move the JSP1.1 parser.
  
  Revision  Changes    Path
  1.1                  jakarta-tomcat-jasper/jasper34/generator/org/apache/jasper34/parser/CoreElement.java
  
  Index: CoreElement.java
  ===================================================================
  /*
   * ====================================================================
   * 
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */ 
  package org.apache.jasper34.parser;
  
  import org.apache.jasper34.runtime.JasperException;
  
  /**
   * The core elements we recognize... these are stateless abstractions that
   * represent the parsing action for a core tag. 
   *
   * @author Anil K. Vijendran
   */
  interface CoreElement {
      /**
       * Return true if the input contained the sequence that matched 
       * the action corresponding to this core tag.
       */
      boolean accept(ParseEventListener listener, JspReader reader,
  		   Parser parser) 
  	throws JasperException;
  }
  
  
  
  1.1                  jakarta-tomcat-jasper/jasper34/generator/org/apache/jasper34/parser/DelegatingListener.java
  
  Index: DelegatingListener.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-jasper/jasper34/generator/org/apache/jasper34/parser/DelegatingListener.java,v 1.1 2001/06/06 05:40:14 costin Exp $
   * $Revision: 1.1 $
   * $Date: 2001/06/06 05:40:14 $
   *
   * ====================================================================
   * 
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */ 
  
  package org.apache.jasper34.parser;
  
  import java.util.Hashtable;
  import java.util.Vector;
  
  import javax.servlet.jsp.tagext.TagInfo;
  import javax.servlet.jsp.tagext.TagLibraryInfo;
  
  import org.apache.jasper34.core.*;
  import org.apache.jasper34.jsptree.TagLibraries;
  import org.apache.jasper34.runtime.JasperException;
  
  
  /** 
   * Simple util class.... see usage in Parser.Parser(). Not intended for anything
   * other than that purpose.... 
   *
   * @author Anil K. Vijendran
   */
  final class DelegatingListener implements ParseEventListener {
      ParseEventListener delegate;
      Parser.Action action;
      Mark tmplStart, tmplStop;
      
      DelegatingListener(ParseEventListener delegate, Parser.Action action) {
          this.delegate = delegate;
          this.action = action;
      }
  
      void doAction(Mark start, Mark stop) throws JasperException {
          action.execute(start, stop);
      }
  
      public void setTemplateInfo(Mark start, Mark stop) {
  	this.tmplStart = start;
  	this.tmplStop = stop;
      }
  
      public void beginPageProcessing() throws JasperException {
          delegate.beginPageProcessing();
      }
      
      public void endPageProcessing() throws JasperException {
          delegate.endPageProcessing();
      }
      
      public void handleComment(Mark start, Mark stop) throws JasperException {
          doAction(this.tmplStart, this.tmplStop);
          delegate.handleComment(start, stop);
      }
  
      public void handleDirective(String directive, Mark start, Mark stop, Hashtable attrs) 
  	throws JasperException 
      {
          doAction(this.tmplStart, this.tmplStop);
          delegate.handleDirective(directive, start, stop, attrs);
      }
      
      public void handleDeclaration(Mark start, Mark stop, Hashtable attrs) throws JasperException {
          doAction(this.tmplStart, this.tmplStop);
          delegate.handleDeclaration(start, stop, attrs);
      }
      
      public void handleScriptlet(Mark start, Mark stop, Hashtable attrs) throws JasperException {
          doAction(this.tmplStart, this.tmplStop);
          delegate.handleScriptlet(start, stop, attrs);
      }
      
      public void handleExpression(Mark start, Mark stop, Hashtable attrs) throws JasperException {
          doAction(this.tmplStart, this.tmplStop);
          delegate.handleExpression(start, stop, attrs);
      }
  
      public void handleBean(Mark start, Mark stop, Hashtable attrs) 
  	throws JasperException
      {
          doAction(this.tmplStart, this.tmplStop);
          delegate.handleBean(start, stop, attrs);
      }
      
      public void handleBeanEnd(Mark start, Mark stop, Hashtable attrs) 
  	throws JasperException 
      {
          doAction(this.tmplStart, this.tmplStop);
          delegate.handleBeanEnd(start, stop, attrs);
      }
  
      public void handleGetProperty(Mark start, Mark stop, Hashtable attrs) 
  	throws JasperException 
      {
          doAction(this.tmplStart, this.tmplStop);
          delegate.handleGetProperty(start, stop, attrs);
      }
      
      public void handleSetProperty(Mark start, Mark stop, Hashtable attrs) 
  	throws JasperException 
      {
          doAction(this.tmplStart, this.tmplStop);
          delegate.handleSetProperty(start, stop, attrs);
      }
      
      public void handlePlugin(Mark start, Mark stop, Hashtable attrs,
      				Hashtable param, String fallback) 
          throws JasperException 
      {
          doAction(this.tmplStart, this.tmplStop);
          delegate.handlePlugin(start, stop, attrs, param, fallback);
      }
      
      public void handleCharData(Mark start, Mark stop, char[] chars) throws JasperException {
          delegate.handleCharData(start, stop, chars);
      }
  
      public void handleForward(Mark start, Mark stop, Hashtable attrs, Hashtable param) 
          throws JasperException 
      {
          doAction(this.tmplStart, this.tmplStop);
          delegate.handleForward(start, stop, attrs, param);
      }
  
      public void handleInclude(Mark start, Mark stop, Hashtable attrs, Hashtable param) 
          throws JasperException 
      {
          doAction(this.tmplStart, this.tmplStop);
          delegate.handleInclude(start, stop, attrs, param);
      }
  
      public void handleTagBegin(Mark start, Mark stop, Hashtable attrs, String prefix, 
  			       String shortTagName, TagLibraryInfo tli, 
  			       TagInfo ti)
  	throws JasperException
      {
          doAction(this.tmplStart, this.tmplStop);
          delegate.handleTagBegin(start, stop, attrs, prefix, shortTagName, tli, ti);
      }
      
      public void handleTagEnd(Mark start, Mark stop, String prefix, 
  			     String shortTagName, Hashtable attrs, 
                               TagLibraryInfo tli, TagInfo ti)
  	throws JasperException
      {
          doAction(this.tmplStart, this.tmplStop);
          delegate.handleTagEnd(start, stop, prefix, shortTagName, attrs, tli, ti);
      }
      
      public TagLibraries getTagLibraries() {
          return delegate.getTagLibraries();
      }
  }
  
  
  
  
  1.1                  jakarta-tomcat-jasper/jasper34/generator/org/apache/jasper34/parser/JspReader.java
  
  Index: JspReader.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */ 
  
  package org.apache.jasper34.parser;
  
  import java.io.InputStreamReader;
  import java.io.FileInputStream;
  import java.io.InputStream;
  import java.io.Reader;
  import java.io.CharArrayWriter;
  import java.io.IOException;
  import java.io.FileNotFoundException;
  import java.io.File;
  import java.util.Hashtable;
  import java.util.Vector;
  import java.util.Stack;
  
  // use: getRealPath, getResourceAsStream
  import org.apache.jasper34.core.JspCompilationContext;
  // getString
  import org.apache.jasper34.core.Constants;
  import org.apache.tomcat.util.log.*;
  
  /**
   * JspReader is an input buffer for the JSP parser. It should allow
   * unlimited lookahead and pushback. It also has a bunch of parsing
   * utility methods for understanding htmlesque thingies.
   *
   * @author Anil K. Vijendran
   * @author Anselm Baird-Smith
   * @author Harish Prabandham
   * @author Rajiv Mordani
   * @author Mandar Raje
   */
  public class JspReader {
      protected Mark current  = null;
      String master = null;
  
      Vector sourceFiles = new Vector();
      int currFileId = 0;
      int size = 0;
      
      private JspCompilationContext context;
  
      Log loghelper = Log.getLog("JASPER_LOG", "JspReader");
      //    LogHelper loghelper = new LogHelper("JASPER_LOG", "JspReader");
      
      /*
       * Default encoding used. The JspReader is created with the
       * "top file" encoding. This is then the encoding used for the
       * included files (same translation unit).
       */
      private String encoding = null;
  
      public String getFile(int fileid) {
  	return (String) sourceFiles.elementAt(fileid);
      }
  
      /**
       * Register a new source file.
       * This method is used to implement file inclusion. Each included file
       * gets a uniq identifier (which is the index in the array of source files).
       * @return The index of the now registered file.
       */
      protected int registerSourceFile(String file) {
          if (sourceFiles.contains(file))
              return -1;
  	sourceFiles.addElement(file);
  	this.size++;
  	return sourceFiles.size() - 1;
      }
      
  
      /**
       * Unregister the source file.
       * This method is used to implement file inclusion. Each included file
       * gets a uniq identifier (which is the index in the array of source files).
       * @return The index of the now registered file.
       */
      protected int unregisterSourceFile(String file) {
          if (!sourceFiles.contains(file))
              return -1;
  	sourceFiles.removeElement(file);
  	this.size--;
  	return sourceFiles.size() - 1;
      }
  
      /**
       * Push a new file onto the stack.
       * The name of the file is resolved to a fully qualified filename.
       * @param name The name of the file.
       */
      public void pushFile(String name) 
  	throws ParseException, FileNotFoundException
      {
  	pushFile(name, this.encoding);
      }
  
      /**
       * Push a new file onto the stack.
       * The name of the file is resolved to a fully qualified filename.
       * @param name The name of the file.
       * @param encoding The optional encoding for the file.
       */
      public void pushFile(String name, String encoding) 
  	throws ParseException, FileNotFoundException
      {
          String parent = master == null ?
              null : master.substring(0, master.lastIndexOf("/") + 1);
          boolean isAbsolute = name.startsWith("/");
  
  	if (parent == null || isAbsolute) {
  	    master = name;
  	    pushFile(new File(name), encoding);
  	} else {
  	    master = parent + name;
  	    pushFile(new File(master), encoding);
  	}
      }
  
      /**
       * Push a new file to be parsed onto the stack.
       * @param inputFile The fully qualified path of the file.
       * @param encoding Optional encoding to read the file.
       */
      private void pushFile(File file, String encoding) 
  	throws ParseException, FileNotFoundException 
      {
          // Default encoding if needed:
  	if (encoding == null) {
              encoding = this.encoding;
              // XXX - longer term, this should really be:
  	    //   System.getProperty("file.encoding", "8859_1");
              // but this doesn't work right now, so we stick with ASCII
          }
  
  	// Register the file, and read its content:
  	String longName = (context == null)
  	    ? file.getAbsolutePath()
  	    : context.getRealPath(file.toString());
  
  	if (longName == null)
  	    throw new FileNotFoundException(file.toString());
  
  	int fileid = registerSourceFile(longName);
  	
          if (fileid == -1)
              throw new ParseException(Constants.getString("jsp.error.file.already.registered",
                                                           new Object[] { 
                                                               file 
                                                           }));
  	currFileId = fileid;
                                       
  	InputStreamReader reader = null;
  	try {
              if (context == null)
                  reader = new InputStreamReader(new FileInputStream(file),
                                                 encoding);
              else {
  	        String fileName = context.getRealPath(file.toString());
  		InputStream in = context.getResourceAsStream(file.toString());
                  if (in == null)
                      throw new FileNotFoundException(fileName);
                  
                  try {
                      reader = new InputStreamReader(in, encoding);
                  } catch (Throwable ex) {
                      throw new FileNotFoundException(fileName + ": "+ ex.getMessage());
                  }
              }
              
  	    CharArrayWriter caw   = new CharArrayWriter();
  	    char            buf[] = new char[1024];
  	    for (int i = 0 ; (i = reader.read(buf)) != -1 ; )
  		caw.write(buf, 0, i);
  	    caw.close();
  	    if (current == null) {
  		current = new Mark( this, caw.toCharArray(), fileid, getFile(fileid),
  				    master, encoding );
  	    } else {
  		current.pushStream( caw.toCharArray(), fileid, getFile(fileid),
  				    master, encoding );
  	    }
  
          } catch (FileNotFoundException fnfe) {
              throw fnfe;
  	} catch (Throwable ex) {
  	    // XXX upper level will log 
  	    loghelper.log("Exception parsing file " + file, ex);
  	    // Pop state being constructed:
  	    popFile();
  	    throw new ParseException(Constants.getString("jsp.error.file.cannot.read",
  							new Object[] { file }));
  	} finally {
  	    if ( reader != null ) {
  		try { reader.close(); } catch (Exception any) {}
  	    }
  	}
      }
  
      public boolean popFile() throws ParseException {
  	// Is stack created ? (will happen if the Jsp file we'r looking at is
  	// missing.
  	if (current == null) 
  		return false;
  
  	// Restore parser state:
  	//size--;
  	if (currFileId < 0) {
  	    throw new ParseException(
  		          Constants.getString("jsp.error.no.more.content"));
  	}
  
  	String fName = getFile(currFileId);
  	currFileId = unregisterSourceFile(fName);
  	if (currFileId < -1)
  	    throw new ParseException
  		(Constants.getString("jsp.error.file.not.registered",
  				     new Object[] {fName}));
  
  	boolean r = current.popStream();
  	if (r)
  		master = current.baseDir;
  	return r;
      }
  	
      protected JspReader(String file, JspCompilationContext ctx, String encoding) 
  	throws ParseException, FileNotFoundException
      {
          this.context = ctx;
  	this.encoding = encoding;
  	if (this.encoding == null) this.encoding = "8859_1";
  	pushFile(file, encoding);
      }
  
      public static JspReader createJspReader(String file,
  					    JspCompilationContext ctx,
  					    String encoding) 
  	throws ParseException, FileNotFoundException
      {
  	return new JspReader(file, ctx, encoding);
      }
  
      public boolean hasMoreInput() throws ParseException {
  	if (current.cursor >= current.stream.length) {
  	    while (popFile()) {
  		if (current.cursor < current.stream.length) return true;
  	    }
  	    return false;
  	}
  	return true;
      }
      
      public int nextChar() throws ParseException {
  	if (!hasMoreInput())
  	    return -1;
  	
  	int ch = current.stream[current.cursor];
  
  	current.cursor++;
  	
  	if (ch == '\n') {
  	    current.line++;
  	    current.col = 0;
  	} else {
  	    current.col++;
  	}
  	return ch;
      }
  
      /**
       * Gets Content until the next potential JSP element.  Because all elements
       * begin with a '&lt;' we can just move until we see the next one.
       */
      String nextContent() {
          int cur_cursor = current.cursor;
  	int len = current.stream.length;
   	char ch;
  
  	if (peekChar() == '\n') {
  	    current.line++;
  	    current.col = 0;
  	}
  	else current.col++;
  	
  	// pure obsfuscated genius!
          while ((++current.cursor < len) && 
  	    ((ch = current.stream[current.cursor]) != '<')) {
  
  	    if (ch == '\n') {
  		current.line++;
  		current.col = 0;
  	    } else {
    		current.col++;
  	    }
  	}
  
  	return new String(current.stream, cur_cursor, current.cursor-cur_cursor);
      }
  
      public char[] getChars(Mark start, Mark stop) throws ParseException {
  	Mark oldstart = mark();
  	reset(start);
  	CharArrayWriter caw = new CharArrayWriter();
  	while (!stop.equals(mark()))
  	    caw.write(nextChar());
  	caw.close();
  	reset(oldstart);
  	return caw.toCharArray();
      }
  
      public int peekChar() {
  	return current.stream[current.cursor];
      }
  
      public Mark mark() {
  	return new Mark(current);
      }
  
      public void reset(Mark mark) {
  	current = new Mark(mark);
      }
  
      public boolean matchesIgnoreCase(String string) throws ParseException {
  	Mark mark = mark();
  	int ch = 0;
  	int i = 0;
  	do {
  	    ch = nextChar();
  	    if (Character.toLowerCase((char) ch) != string.charAt(i++)) {
  		reset(mark);
  		return false;
  	    }
  	} while (i < string.length());
  	reset(mark);
  	return true;
      }
  
      public boolean matches(String string) throws ParseException {
  	Mark mark = mark();
  	int ch = 0;
  	int i = 0;
  	do {
  	    ch = nextChar();
  	    if (((char) ch) != string.charAt(i++)) {
  		reset(mark);
  		return false;
  	    }
  	} while (i < string.length());
  	reset(mark);
  	return true;
      }
      
      public void advance(int n) throws ParseException {
  	while (--n >= 0)
  	    nextChar();
      }
  
      public int skipSpaces() throws ParseException {
  	int i = 0;
  	while (isSpace()) {
  	    i++;
  	    nextChar();
  	}
  	return i;
      }
  
      /**
       * Skip until the given string is matched in the stream.
       * When returned, the context is positioned past the end of the match.
       * @param s The String to match.
       * @return A non-null <code>Mark</code> instance if found,
       * <strong>null</strong> otherwise.
       */
      public Mark skipUntil(String limit)
      throws ParseException {
  	Mark ret = null;
  	int limlen = limit.length();
  	int ch;
  	
      skip:
  	for (ret = mark(), ch = nextChar() ; ch != -1 ; ret = mark(), ch = nextChar()) {
  	    
  	    if ( ch == limit.charAt(0) ) {
  		for (int i = 1 ; i < limlen ; i++) {
  		    if (Character.toLowerCase((char) nextChar()) != limit.charAt(i))
  			continue skip;
  		}
  		return ret;
  	    }
  	}
  	return null;
      }
      
      final boolean isSpace() {
  	return peekChar() <= ' ';
      }
  
      /**
       * Parse a space delimited token.
       * If quoted the token will consume all characters up to a matching quote,
       * otherwise, it consumes up to the first delimiter character.
       * @param quoted If <strong>true</strong> accept quoted strings.
       */
  
      public String parseToken(boolean quoted) 
  	throws ParseException
      {
  	StringBuffer stringBuffer = new StringBuffer();
  	skipSpaces();
  	stringBuffer.setLength(0);
  	
  	int ch = peekChar();
  	
  	if (quoted) {
  	    if ( ch == '"' || ch == '\'') {
  
  		char endQuote = ch == '"' ? '"' : '\'';
  		// Consume the open quote: 
  		ch = nextChar();
  		for(ch = nextChar(); ch != -1 && ch != endQuote; ch = nextChar()) {
  		    if (ch == '\\') 
  			ch = nextChar();
  		    stringBuffer.append((char) ch);
  		}
  		// Check end of quote, skip closing quote:
  		if ( ch == -1 ) 
  		    throw new ParseException(mark(), 
  				Constants.getString("jsp.error.quotes.unterminated"));
  	    }
  	    else throw new ParseException(mark(),
  				Constants.getString("jsp.error.attr.quoted"));
  	} else {
  	    if (!isDelimiter())
  		// Read value until delimiter is found:
  		do {
  		    ch = nextChar();
  		    // Take care of the quoting here.
  		    if (ch == '\\') {
  			if (peekChar() == '"' || peekChar() == '\'' ||
  			       peekChar() == '>' || peekChar() == '%')
  			    ch = nextChar();
  		    }
  		    stringBuffer.append((char) ch);
  		} while ( !isDelimiter() );
  	}
  	return stringBuffer.toString();
      }
  
      /**
       * Parse an attribute/value pair, and store it in provided hash table.
       * The attribute/value pair is defined by:
       * <pre>
       * av := spaces token spaces '=' spaces token spaces
       * </pre>
       * Where <em>token</em> is defined by <code>parseToken</code> and
       * <em>spaces</em> is defined by <code>skipSpaces</code>.
       * The name is always considered case insensitive, hence stored in its
       * lower case version.
       * @param into The Hashtable instance to save the result to.
       */
  
      private void parseAttributeValue(Hashtable into)
  	throws ParseException
      {
  	// Get the attribute name:
  	skipSpaces();
  	String name = parseToken(false);
  	// Check for an equal sign:
  	skipSpaces();
  	if ( peekChar() != '=' ) 
  	    throw new ParseException(mark(), Constants.getString("jsp.error.attr.novalue",
  						new Object[] { name }));
  	char ch = (char) nextChar();
  	// Get the attribute value:
  	skipSpaces();
  	String value = parseToken(true);
  	skipSpaces();
  	// Add the binding to the provided hashtable:
  	into.put(name, value);
  	return;
      }
  
      /**
       * Parse some tag attributes for Beans.
       * The stream is assumed to be positioned right after the tag name. The
       * syntax recognized is:
       * <pre>
       * tag-attrs := empty | attr-list ("&gt;" | "--&gt;" | %&gt;)
       * attr-list := empty | av spaces attr-list
       * empty     := spaces 
       * </pre>
       * Where <em>av</em> is defined by <code>parseAttributeValue</code>.
       * @return A Hashtable mapping String instances (variable names) into
       * String instances (variable values).
       */
  
      public Hashtable parseTagAttributesBean() 
  	throws ParseException
      {
        Hashtable values = new Hashtable(11);
  	while ( true ) {
  	    skipSpaces();
  	    int ch = peekChar();
  	    if ( ch == '>' ) {
  		// End of the useBean tag.
  		return values;
  
  	    } else if ( ch == '/' ) {
  		Mark mark = mark();
  		nextChar();
  		// XMLesque Close tags 
  		try {
  		    if ( nextChar() == '>' )
  		      return values;
  		} finally {
  		    reset(mark);
  		}
  	    }
  	    if ( ch == -1 )
  		break;
  	    // Parse as an attribute=value:
  	    parseAttributeValue(values);
  	}
  	// Reached EOF:
  	throw new ParseException(mark(),
  			Constants.getString("jsp.error.tag.attr.unterminated"));
      }
  
      /**
       * Parse some tag attributes.
       * The stream is assumed to be positioned right after the tag name. The
       * syntax recognized is:
       * <pre>
       * tag-attrs := empty | attr-list ("&gt;" | "--&gt;" | %&gt;)
       * attr-list := empty | av spaces attr-list
       * empty     := spaces 
       * </pre>
       * Where <em>av</em> is defined by <code>parseAttributeValue</code>.
       * @return A Hashtable mapping String instances (variable names) into
       * String instances (variable values).
       */
  
      public Hashtable parseTagAttributes() 
  	throws ParseException
      {
  	Hashtable values = new Hashtable(11);
  	while ( true ) {
  	    skipSpaces();
  	    int ch = peekChar();
  	    if ( ch == '>' ) {
  		return values;
  	    }
  	    if ( ch == '-' ) {
  		Mark mark = mark();
  		nextChar();
  		// Close NCSA like attributes "->"
  		try {
  		    if ( nextChar() == '-' && nextChar() == '>' )
  			return values;
  		} finally {
  		    reset(mark);
  		}
  	    } else if ( ch == '%' ) {
  		Mark mark = mark();
  		nextChar();
  		// Close variable like attributes "%>"
  		try {
  		    if ( nextChar() == '>' )
  			return values;
  		} finally {
  		    reset(mark);
  		}
  	    } else if ( ch == '/' ) {
  		Mark mark = mark();
  		nextChar();
  		// XMLesque Close tags 
  		try {
  		    if ( nextChar() == '>' )
  			return values;
  		} finally {
  		    reset(mark);
  		}
  	    }
  	    if ( ch == -1 )
  		break;
  	    // Parse as an attribute=value:
  	    parseAttributeValue(values);
  	}
  	// Reached EOF:
  	throw new ParseException(mark(),
  			Constants.getString("jsp.error.tag.attr.unterminated"));
      }
  
      /**
       * Parse PARAM tag attributes into the given hashtable.
       * Parses the PARAM tag as defined by:
       * <pre>
       * &lt;PARAM tag-attributes %gt;
       * </pre>
       * Two special tag attributes are recognized here:
       * <ol>
       * <li>The <strong>name</strong> attribute,
       * <li>The <strong>value</strong> attribute.
       * </ol>
       * The resulting name, value pair is stored in the provided hash table.
       * @param into Storage for parameter values.
       */
      public void parseParamTag(Hashtable into) 
  	throws ParseException
      {
  	// Really check for a param tag:
  	if ( matches("param") ) {
  	    advance(6);
  	    parseParams (into);
  	} else {
  	    // False alarm, just skip it
  	}
      }
  
      /**
       * Parse jsp:param tag attributes into the given hashtable.
       * Parses the jsp:param tag as defined by:
       * <pre>
       * &lt;jsp:param tag-attributes %gt;
       * </pre>
       * Two special tag attributes are recognized here:
       * <ol>
       * <li>The <strong>name</strong> attribute,
       * <li>The <strong>value</strong> attribute.
       * </ol>
       * The resulting name, value pair is stored in the provided hash table.
       * @param into Storage for parameter values.
       */
      public void parsePluginParamTag(Hashtable into) 
  	throws ParseException
      {
  	// Really check for a param tag:
  	if ( matches("<jsp:param") ) {
  	    advance(11);
  	    parseParams (into);
  	} else {
  	    // False alarm, just skip it
  	}
      }
  
      private void parseParams (Hashtable into) 
          throws ParseException
      {
  	Hashtable attrs = parseTagAttributes();
  	// Check attributes (name and value):
  	String name  = (String) attrs.get("name");
  	String value = (String) attrs.get("value");
  	if ( name == null )
   	    throw new ParseException(mark(), Constants.getString("jsp.error.param.noname"));
  	if ( value == null )
   	    throw new ParseException(mark(), Constants.getString("jsp.error.param.novalue"));
  	// Put that new binding into the params hashatble:
  	String oldval[] = (String[]) into.get(name);
  	if ( oldval == null ) {
  	    String newval[] = new String[1];
  	    newval[0] = value;
  	    into.put(name, newval);
  	} else {
  	    String newval[] = new String[oldval.length+1];
  	    System.arraycopy(oldval, 0, newval, 0, oldval.length);
  	    newval[oldval.length] = value;
  	    into.put(name, newval);
          }
      }
  
      /**
       * Parse utils - Is current character a token delimiter ?
       * Delimiters are currently defined to be =, &gt;, &lt;, ", and ' or any
       * any space character as defined by <code>isSpace</code>.
       * @return A boolean.
       */
      private boolean isDelimiter() throws ParseException {
  	if ( ! isSpace() ) {
  	    int ch = peekChar();
  	    // Look for a single-char work delimiter:
  	    if ( ch == '=' || ch == '>' || ch == '"' || ch == '\'' || ch == '/') 
  		return true;
  	    // Look for an end-of-comment or end-of-tag:		
  	    if ( ch == '-' ) {
  		Mark mark = mark();
  		if ( ((ch = nextChar()) == '>')
  		     || ((ch == '-') && (nextChar() == '>')) ) {
  		    reset(mark);
  		    return true;
  		} else {
  		    reset(mark);
  		    return false;
  		}
  	    }
  	    return false;
  	} else {
  	    return true;
  	}
      }
  }
  
  
  
  
  1.1                  jakarta-tomcat-jasper/jasper34/generator/org/apache/jasper34/parser/Mark.java
  
  Index: Mark.java
  ===================================================================
  /*
   * ====================================================================
   * 
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */ 
  package org.apache.jasper34.parser;
  
  import java.util.Stack;
  
  /**
   * Mark represents a point in the JSP input. 
   *
   * @author Anil K. Vijendran
   */
  public final class Mark {
      int cursor, line, col;	// position within current stream
      int fileid;			// fileid of current stream
      String fileName;            // name of the current file
      String baseDir;		// directory of file for current stream
      char[] stream = null;	// current stream
      Stack includeStack = null;	// stack of stream and stream state of streams
  				//   that have included current stream
      String encoding = null;	// encoding of current file
      private JspReader reader;	// reader that owns this mark 
  				//   (so we can look up fileid's)
  
  
      /**
       * Keep track of parser before parsing an included file.
       * This class keeps track of the parser before we switch to parsing an
       * included file. In other words, it's the parser's continuation to be
       * reinstalled after the included file parsing is done.
       */
      class IncludeState {
  	int cursor, line, col;
  	int fileid;
  	String fileName;
  	String baseDir;
  	String encoding;
  	char[] stream = null;
  
  	IncludeState(int inCursor, int inLine, int inCol, int inFileid, 
  		     String name, String inBaseDir, String inEncoding,
  		     char[] inStream) 
  	{
  	    cursor = inCursor;
  	    line = inLine;
  	    col = inCol;
  	    fileid = inFileid;
  	    fileName = name;
  	    baseDir = inBaseDir;
  	    encoding = inEncoding;
  	    stream = inStream;
  	}
      }
  
  
      /**
      * Creates a new mark
      * @param inReader JspReader this mark belongs to
      * @param inStream current stream for this mark
      * @param inFileid id of requested jsp file
      * @param inEncoding encoding of current file
      * @param inBaseDir base directory of requested jsp file
      */
      Mark(JspReader reader, char[] inStream, int fileid, String name,
  	 String inBaseDir, String inEncoding) 
      {
  	this.reader = reader;
  	this.stream = inStream;
  	this.cursor = this.line = this.col = 0;
  	this.fileid = fileid;
  	this.fileName = name;
  	this.baseDir = inBaseDir;
  	this.encoding = inEncoding;
  	this.includeStack = new Stack();
      }
  	
      Mark(Mark other) {
  	this.reader = other.reader;
  	this.stream = other.stream;
  	this.fileid = other.fileid;
  	this.fileName = other.fileName;
  	this.cursor = other.cursor;
  	this.line = other.line;
  	this.col = other.col;
  	this.baseDir = other.baseDir;
  	this.encoding = other.encoding;
  
  	// clone includeStack without cloning contents
  	includeStack = new Stack();
  	for ( int i=0; i < other.includeStack.size(); i++ ) {
    	    includeStack.addElement( other.includeStack.elementAt(i) );
  	}
      }
  	    
      /** Sets this mark's state to a new stream.
       * It will store the current stream in it's includeStack.
       * @param inStream new stream for mark
       * @param inFileid id of new file from which stream comes from
       * @param inBaseDir directory of file
  	 * @param inEncoding encoding of new file
       */
      void pushStream(char[] inStream, int inFileid, String name,
  			   String inBaseDir, String inEncoding) 
      {
  
  	// store current state in stack
  	includeStack.push(new IncludeState(cursor, line, col, fileid, fileName, baseDir, 
  					   encoding, stream) );
  
  	// set new variables
  	cursor = 0;
  	line = 0;
  	col = 0;
  	fileid = inFileid;
  	fileName = name;
  	baseDir = inBaseDir;
  	encoding = inEncoding;
  	stream = inStream;
      }
  
  
  
      /** Restores this mark's state to a previously stored stream.
       */
      boolean popStream() {
  	// make sure we have something to pop
  	if ( includeStack.size() <= 0 ) return false;
  
  	// get previous state in stack
  	IncludeState state = (IncludeState) includeStack.pop( );
  
  	// set new variables
  	cursor = state.cursor;
  	line = state.line;
  	col = state.col;
  	fileid = state.fileid;
  	fileName = state.fileName;
  	baseDir = state.baseDir;
  	stream = state.stream;
  	return true;
      }
  
      // -------------------- Locator interface --------------------
      
      public int getLineNumber() {
  	return line;
      }
  
      public int getColumnNumber() {
  	return col;
      }
  
      public String getSystemId() {
  	return getFile();
      }
  
      public String getPublicId() {
  	return null;
      }
  
      // -------------------- 
      public String toString() {
  	return getFile()+"("+line+","+col+")";
      }
  
      public String getFile() {
          return this.fileName;
      }
      
      String toShortString() {
          return "("+line+","+col+")";
      }
  
      public boolean equals(Object other) {
  	if (other instanceof Mark) {
  	    Mark m = (Mark) other;
  	    return this.reader == m.reader && this.fileid == m.fileid 
  		&& this.cursor == m.cursor && this.line == m.line 
  		&& this.col == m.col;
  	} 
  	return false;
      }
  }
  
  
  
  
  1.1                  jakarta-tomcat-jasper/jasper34/generator/org/apache/jasper34/parser/ParseEventListener.java
  
  Index: ParseEventListener.java
  ===================================================================
  /*
   * ====================================================================
   * 
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */ 
  package org.apache.jasper34.parser;
  
  import java.util.Hashtable;
  import java.util.Vector;
  
  import javax.servlet.jsp.tagext.TagInfo;
  import javax.servlet.jsp.tagext.TagLibraryInfo;
  // XXX 
  import org.apache.jasper34.jsptree.TagLibraries;
  
  //import org.apache.jasper34.core.*;
  import org.apache.jasper34.runtime.JasperException;
  /**
   * Interface for the JSP code generation backend. At some point should
   * probably try and make this a SAX (XML) listener. 
   *
   * @author Anil K. Vijendran
   */
  public interface ParseEventListener {
      void setTemplateInfo(Mark start, Mark stop);
      void beginPageProcessing() throws JasperException;
  
      void handleComment(Mark start, Mark stop) throws JasperException;
      void handleDirective(String directive, 
  			 Mark start, Mark stop, 
  			 Hashtable attrs) throws JasperException;
      void handleDeclaration(Mark start, Mark stop, Hashtable attrs) throws JasperException;
      void handleScriptlet(Mark start, Mark stop, Hashtable attrs) throws JasperException;
      void handleExpression(Mark start, Mark stop, Hashtable attrs) throws JasperException;
      void handleBean(Mark start, Mark stop, Hashtable attrs) 
  	throws JasperException;
      void handleBeanEnd (Mark start, Mark stop, Hashtable attrs)
  	throws JasperException;
      void handleGetProperty(Mark start, Mark stop, Hashtable attrs) throws JasperException;
      void handleSetProperty(Mark start, Mark stop, Hashtable attrs) throws JasperException;
      void handlePlugin(Mark start, Mark stop, Hashtable attrs, Hashtable param, 
      			String fallback) throws JasperException;
      void handleCharData(Mark start, Mark stop, char[] chars) throws JasperException;
  
  
      /*
       * Custom tag support
       */
      // XXX
      TagLibraries getTagLibraries();
  
      /*
       * start: is either the start position at "<" if content type is JSP or empty, or
       *        is the start of the body after the "/>" if content type is tag dependent
       * stop: can be null if the body contained JSP tags... 
       */
      void handleTagBegin(Mark start, Mark stop, Hashtable attrs, String prefix, String shortTagName,
  			TagLibraryInfo tli, TagInfo ti) 
  	throws JasperException;
  
      void handleTagEnd(Mark start, Mark stop, String prefix, String shortTagName,
  		      Hashtable attrs, TagLibraryInfo tli, TagInfo ti)
  	throws JasperException;
  
      void handleForward(Mark start, Mark stop, Hashtable attrs, Hashtable param)
  	throws JasperException;
      void handleInclude(Mark start, Mark stop, Hashtable attrs, Hashtable param)
  	throws JasperException;
  
      void endPageProcessing() throws JasperException;
  }
  
  
  
  
  1.1                  jakarta-tomcat-jasper/jasper34/generator/org/apache/jasper34/parser/ParseException.java
  
  Index: ParseException.java
  ===================================================================
  /*
   * ====================================================================
   * 
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */ 
  package org.apache.jasper34.parser;
  
  //import org.apache.jasper34.core.*;
  import org.apache.jasper34.runtime.JasperException;
  /**
   * Class for parser exceptions. 
   *
   * @author Anil K. Vijendran
   */
  public class ParseException extends JasperException {
      public ParseException(Mark m, String reason) {
  	super(m + " " + reason);
      }
      public ParseException(String reason) {
  	super(reason);
      }
  }
  
  
  
  1.1                  jakarta-tomcat-jasper/jasper34/generator/org/apache/jasper34/parser/ParseUtil.java
  
  Index: ParseUtil.java
  ===================================================================
  /*
   * ====================================================================
   * 
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */ 
  package org.apache.jasper34.parser;
  
  import java.net.URL;
  
  import java.io.CharArrayWriter;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.FileInputStream;
  import java.util.Hashtable;
  import java.util.Enumeration;
  
  
  import org.apache.jasper34.runtime.JasperException;
  import org.apache.jasper34.core.Constants;
  
  import org.w3c.dom.*;
  import javax.xml.parsers.DocumentBuilder;
  import javax.xml.parsers.DocumentBuilderFactory;
  import javax.xml.parsers.ParserConfigurationException;
  import org.xml.sax.EntityResolver;
  import org.xml.sax.SAXException;
  import org.xml.sax.InputSource;
  
  /** 
   * This class has all the utility method(s).
   * Ideally should move all the bean containers here.
   *
   * @author Mandar Raje.
   * @author Rajiv Mordani.
   */
  public class ParseUtil {
  
      /**
       *  Escape the 5 entities defined by XML.
       */
      public static String escapeXml(String s) {
          if (s == null) return null;
          StringBuffer sb = new StringBuffer();
          for(int i=0; i<s.length(); i++) {
              char c = s.charAt(i);
              if (c == '<') {
                  sb.append("&lt;");
              } else if (c == '>') {
                  sb.append("&gt;");
              } else if (c == '\'') {
                  sb.append("&apos;");
              } else if (c == '&') {
                  sb.append("&amp;");
              } else if (c == '"') {
                  sb.append("&quote;");
              } else {
                  sb.append(c);
              }
          }
          return sb.toString();
      }
  
      public static void checkAttributes (String typeOfTag, Hashtable attrs,
      					ValidAttribute[] validAttributes, Mark start)
  					throws JasperException
      {
  	boolean valid = true;
  	Hashtable temp = (Hashtable)attrs.clone ();
  
  	/**
  	 * First check to see if all the mandatory attributes are present.
  	 * If so only then proceed to see if the other attributes are valid
  	 * for the particular tag.
  	 */
  	String missingAttribute = null;
  
  	for (int i = 0; i < validAttributes.length; i++) {
  	        
  	    if (validAttributes[i].mandatory) {
  	        if (temp.get (validAttributes[i].name) != null) {
  	            temp.remove (validAttributes[i].name);
  		    valid = true;
  		} else {
  		    valid = false;
  		    missingAttribute = validAttributes[i].name;
  		    break;
  		}
  	    }
  	}
  
  	/**
  	 * If mandatory attribute is missing then the exception is thrown.
  	 */
  	if (!valid)
  	    throw new ParseException(start, Constants.getString(
  			"jsp.error.mandatory.attribute", 
                                   new Object[] { typeOfTag, missingAttribute}));
  
  	/**
  	 * Check to see if there are any more attributes for the specified
  	 * tag.
  	 */
  	if (temp.size() == 0)
  	    return;
  
  	/**
  	 * Now check to see if the rest of the attributes are valid too.
  	 */
     	Enumeration enum = temp.keys ();
  	String attribute = null;
  
  	while (enum.hasMoreElements ()) {
  	    valid = false;
  	    attribute = (String) enum.nextElement ();
  	    for (int i = 0; i < validAttributes.length; i++) {
  	        if (attribute.equals(validAttributes[i].name)) {
  		    valid = true;
  		    break;
  		}
  	    }
  	    if (!valid)
  	        throw new ParseException(start, Constants.getString(
  			"jsp.error.invalid.attribute", 
                                   new Object[] { typeOfTag, attribute }));
  	}
      }
      
  
      public static class ValidAttribute {
     	String name;
  	boolean mandatory;
  
  	public ValidAttribute (String name, boolean mandatory) {
  	    this.name = name;
  	    this.mandatory = mandatory;
  	}
  
  	public ValidAttribute (String name) {
  	    this (name, false);
  	}
      }
  }
  
  
  
  
  
  
  
  1.1                  jakarta-tomcat-jasper/jasper34/generator/org/apache/jasper34/parser/Parser.java
  
  Index: Parser.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */ 
  package org.apache.jasper34.parser;
  
  import java.util.Vector;
  import java.util.Hashtable;
  import java.util.Enumeration;
  
  import java.io.CharArrayWriter;
  import org.apache.jasper34.core.Constants;
  import org.apache.jasper34.runtime.JasperException;
  import org.apache.jasper34.jsptree.TagLibraries;
  
  import javax.servlet.jsp.tagext.TagLibraryInfo;
  import javax.servlet.jsp.tagext.TagInfo;
  
  import org.apache.tomcat.util.log.*;
  
  /**
   * The class that parses the JSP input and calls the right methods on
   * the code generator backend. 
   *
   * @author Anil K. Vijendran
   * @author Rajiv Mordani
   */
  public class Parser {
      /**
       * The input source we read from...
       */
      private JspReader reader;
  
      /**
       * The backend that is notified of constructs recognized in the input... 
       */
      private ParseEventListener listener;
  
      /*
       * Char buffer for HTML data
       */
      CharArrayWriter caw;
  
      /*
       * Marker for start and end of the tempate data.
       */
      Mark tmplStart;
      Mark tmplStop;
  
      /*
       * Name of the current file.
       * Useful to preserve the line number information in
       * case of an include.
       */
      String currentFile;
  
      public interface Action {
          void execute(Mark start, Mark stop) throws JasperException;
      }
  
      public Parser(JspReader reader, final ParseEventListener lnr) {
  	this.reader = reader;
  	this.listener = new DelegatingListener(lnr,
                                                 new Action() {
                                                         public void execute(Mark start, Mark stop) 
                                                             throws JasperException 
                                                         {
                                                             Parser.this.flushCharData(start, stop);
                                                         }
                                                     });
  	this.caw = new CharArrayWriter();
  	this.currentFile = reader.mark().getFile();
      }
  
      static final Vector coreElements = new Vector();
  
      /*
       * JSP directives
       */
      static final class Directive implements CoreElement {
  	private static final String OPEN_DIRECTIVE  = "<%@";
  	private static final String CLOSE_DIRECTIVE = "%>";
  
  	static final String[] directives = {
  	  "page",
  	  "include",
  	  "taglib"
  	};
  
  	private static final ParseUtil.ValidAttribute[] pageDvalidAttrs = {
  	    new ParseUtil.ValidAttribute ("language"),
  	    new ParseUtil.ValidAttribute ("extends"),
  	    new ParseUtil.ValidAttribute ("import"),
  	    new ParseUtil.ValidAttribute ("session"),
  	    new ParseUtil.ValidAttribute ("buffer"),
  	    new ParseUtil.ValidAttribute ("autoFlush"),
  	    new ParseUtil.ValidAttribute ("isThreadSafe"),
  	    new ParseUtil.ValidAttribute ("info"),
  	    new ParseUtil.ValidAttribute ("errorPage"),
  	    new ParseUtil.ValidAttribute ("isErrorPage"),
  	    new ParseUtil.ValidAttribute ("contentType")
  	};
  
  	private static final ParseUtil.ValidAttribute[] includeDvalidAttrs = {
  	    new ParseUtil.ValidAttribute ("file", true)
  	};
  
  	private static final ParseUtil.ValidAttribute[] tagDvalidAttrs = {
  	    new ParseUtil.ValidAttribute ("uri", true),
  	    new ParseUtil.ValidAttribute ("prefix", true)
  	};
  
  	public boolean accept(ParseEventListener listener, JspReader reader, 
  			      Parser parser) throws JasperException
  	{
  	    String close;
  	    String open;
  	    
  	    if (reader.matches(OPEN_DIRECTIVE)) {
  		open = OPEN_DIRECTIVE;
  		close = CLOSE_DIRECTIVE;
  	    } else
  		return false;
  
  	    Mark start = reader.mark();
  	    reader.advance(open.length());
  	    reader.skipSpaces();
  	    
  	    // Check which directive it is.
  	    String match = null;
  	    for(int i = 0; i < directives.length; i++)
  		if (reader.matches(directives[i])) {
  		    match = directives[i];
  		    break;
  		}
  	    if (match == null)
  		throw new ParseException(reader.mark(),
  					 Constants.getString("jsp.error.invalid.directive"));
  
  	    reader.advance(match.length());
  
  	    // Parse the attr-val pairs.
  	    Hashtable attrs = reader.parseTagAttributes();
  	    if (match.equals ("page"))
  	        ParseUtil.checkAttributes ("Page directive", attrs, 
  					 pageDvalidAttrs, start);
  	    else if (match.equals("include"))
  	        ParseUtil.checkAttributes ("Include directive", attrs, 
  					 includeDvalidAttrs, start);
  	    else if (match.equals("taglib"))
  	        ParseUtil.checkAttributes ("Taglib directive", attrs, 
  					 tagDvalidAttrs, start);
  	    
  	    // Match close.
  	    reader.skipSpaces();
  	    if (!reader.matches(close))
                  throw new ParseException(reader.mark(), 
                                           Constants.getString("jsp.error.unterminated", 
                                                               new Object[] { open }));
  	    else
  		reader.advance(close.length());
  
  	    Mark stop = reader.mark();
  
  	    listener.setTemplateInfo(parser.tmplStart, parser.tmplStop);
  	    listener.handleDirective(match, start, stop, attrs);
  	    return true;
  	}
  
      }
    
      static {
  	coreElements.addElement(new Directive());
      }
  
      /*
       * Include action
       */
      static final class Include implements CoreElement {
  	private static final String OPEN_INCLUDE = "<jsp:include";
  	private static final String CLOSE_INCLUDE_NO_BODY = "/>";
  	private static final String CLOSE_INCLUDE_BODY = ">";
  	private static final String CLOSE_INCLUDE = "</jsp:include>";
  	private static final String OPEN_INDIVIDUAL_PARAM = "<jsp:param";
  	private static final String CLOSE_INDIVIDUAL_PARAM = "/>";
  
  	private static final ParseUtil.ValidAttribute[] validAttributes = {
              new ParseUtil.ValidAttribute("page", true),
              new ParseUtil.ValidAttribute("flush")
  	};
  
  	public boolean accept(ParseEventListener listener, JspReader reader, 
                                Parser parser) 
  	    throws JasperException 
  	{
  	    if (reader.matches(OPEN_INCLUDE)) {
  		Hashtable param = new Hashtable();
  		Mark start = reader.mark();
  		reader.advance(OPEN_INCLUDE.length());
  		Hashtable attrs = reader.parseTagAttributes();
  		ParseUtil.checkAttributes ("Include", attrs, validAttributes, start);
  		reader.skipSpaces();
  		
  		if (!reader.matches(CLOSE_INCLUDE_NO_BODY)) {
  		    
  		    if (!reader.matches(CLOSE_INCLUDE_BODY))
  			throw new ParseException(reader.mark(), 
  						 Constants.getString
  						 ("jsp.error.unterminated", 
  						  new Object[] { OPEN_INCLUDE }));
  		    reader.advance(CLOSE_INCLUDE_BODY.length());
  
  		    reader.skipSpaces();
  		    if (!reader.matches(CLOSE_INCLUDE)) {
  			
  			// Parse the params.
  			reader.skipSpaces();
  			if (!reader.matches (OPEN_INDIVIDUAL_PARAM))
  			    throw new ParseException (reader.mark(),
  						      Constants.getString
  						      ("jsp.error.paramexpected"));
  
  			//Parse zero or more param tags.
  			while (reader.matches(OPEN_INDIVIDUAL_PARAM)) {
  			
  			    reader.parsePluginParamTag(param);
  			    reader.skipSpaces ();
  			
  			    if (!reader.matches (CLOSE_INDIVIDUAL_PARAM))
  				throw new ParseException (reader.mark(),
  							  Constants.getString
  							  ("jsp.error.unterminated",
  							   new Object[] {OPEN_INDIVIDUAL_PARAM}));
  			    reader.advance (CLOSE_INDIVIDUAL_PARAM.length ());
  			    reader.skipSpaces();
  			}
  		    }
  		    
  		    if (!reader.matches(CLOSE_INCLUDE))
  			throw new ParseException(reader.mark(), 
  						 Constants.getString
  						 ("jsp.error.unterminated", 
  						  new Object[] { OPEN_INCLUDE }));
  		    reader.advance(CLOSE_INCLUDE.length());
  		}
  		else
  		    reader.advance(CLOSE_INCLUDE_NO_BODY.length());
  		Mark stop = reader.mark();
  		listener.setTemplateInfo(parser.tmplStart, parser.tmplStop);		
  		listener.handleInclude(start, stop, attrs, param);
  		return true;
  	    } else
  		return false;
  	}
      }
  
      static {
  	coreElements.addElement(new Include());
      }
    
      /*
       * Forward action
       */
      static final class Forward implements CoreElement {
  	private static final String OPEN_FORWARD = "<jsp:forward";
  	private static final String CLOSE_FORWARD_NO_BODY = "/>";
  	private static final String CLOSE_FORWARD_BODY = ">";
  	private static final String CLOSE_FORWARD = "</jsp:forward>";
  	private static final String OPEN_INDIVIDUAL_PARAM = "<jsp:param";
  	private static final String CLOSE_INDIVIDUAL_PARAM = "/>";
  
  	private static final ParseUtil.ValidAttribute[] validAttributes = {
  	   new ParseUtil.ValidAttribute("page", true)
  	};
  	public boolean accept(ParseEventListener listener, JspReader reader, 
  				Parser parser) 
  	    throws JasperException 
  	{
  	    if (reader.matches(OPEN_FORWARD)) {
  		Mark start = reader.mark();
  		reader.advance(OPEN_FORWARD.length());
  		Hashtable attrs = reader.parseTagAttributes();
  		Hashtable param = new Hashtable();
  	        ParseUtil.checkAttributes ("Forward", attrs, validAttributes, start);
  		reader.skipSpaces();
  		if (!reader.matches(CLOSE_FORWARD_NO_BODY)) {
  		    if (!reader.matches(CLOSE_FORWARD_BODY))
  			throw new ParseException(reader.mark(), 
  						 Constants.getString
  						 ("jsp.error.unterminated", 
  						  new Object[] { OPEN_FORWARD }));
  		    reader.advance(CLOSE_FORWARD_BODY.length());
  		    reader.skipSpaces();
  
  		    if (!reader.matches(CLOSE_FORWARD)) {
  			
  			// Parse the params.
  			reader.skipSpaces();
  			if (!reader.matches (OPEN_INDIVIDUAL_PARAM))
  			    throw new ParseException (reader.mark(),
  						      Constants.getString
  						      ("jsp.error.paramexpected"));
  			// Parse zero or more param tags.
  			while (reader.matches(OPEN_INDIVIDUAL_PARAM)) {
  			    
  			    //Borrow plugin's parse function.
  			    reader.parsePluginParamTag(param);
  			    reader.skipSpaces();
  			    
  			    if (!reader.matches (CLOSE_INDIVIDUAL_PARAM))
  				throw new ParseException (reader.mark(),
  							  Constants.getString
  							  ("jsp.error.unterminated",
  							   new Object[] {OPEN_INDIVIDUAL_PARAM}));
  			    reader.advance (CLOSE_INDIVIDUAL_PARAM.length ());
  			    reader.skipSpaces();
  			}
  		    }
  		    
  		    if (!reader.matches(CLOSE_FORWARD))
  			throw new ParseException(reader.mark(), 
  						 Constants.getString
  						 ("jsp.error.unterminated", 
  						  new Object[] { OPEN_FORWARD }));
  		    reader.advance(CLOSE_FORWARD.length());
  		}
  		else
  		    reader.advance(CLOSE_FORWARD_NO_BODY.length());
  		
  		Mark stop = reader.mark();
  		listener.setTemplateInfo(parser.tmplStart, parser.tmplStop);		
  		listener.handleForward(start, stop, attrs, param);
  		return true;
  	    } else
  		return false;
  	}
      }
  
      static {
  	coreElements.addElement(new Forward());
      }
  
  
      /*
       * Jsp comments <%--  stuff --%>
       */
  
      // declarations
      static final class Comment implements CoreElement {
  
  	private static final String OPEN_COMMENT  = "<%--";
  	private static final String CLOSE_COMMENT = "--%>";
  
  	public boolean accept(ParseEventListener listener, JspReader reader, Parser parser) 
  	    throws JasperException 
  	{
  
  	    if (reader.matches(OPEN_COMMENT)) {
  		reader.advance(OPEN_COMMENT.length());
  		Mark start = reader.mark();
  		Mark stop = reader.skipUntil(CLOSE_COMMENT);
  		if (stop == null)
  		    throw new ParseException(Constants.getString("jsp.error.unterminated", 
                                                                   new Object[] { OPEN_COMMENT }));
  
  		parser.flushCharData(parser.tmplStart, parser.tmplStop);
  		return true;
  	    }
  	    return false;
  	}
      }
  	
      static {
  	coreElements.addElement(new Comment());
      }
  
      /*
       * Scripting elements
       */
      
      // declarations
      static final class Declaration implements CoreElement {
  
  	private static final String OPEN_DECL  = "<%!";
  	private static final String CLOSE_DECL = "%>";
  
          private static final ParseUtil.ValidAttribute[] validAttributes = {
          };
  
  	public boolean accept(ParseEventListener listener, JspReader reader, Parser parser) 
  	    throws JasperException 
  	{
  	    String close, open, end_open = null;
              Hashtable attrs = null;
  	    Mark start;
  				
  	    if (reader.matches(OPEN_DECL)) {
  		open = OPEN_DECL;
  		close = CLOSE_DECL;
  	    } else
  		return false;
  
  	    reader.advance(open.length());
  	    start = reader.mark();
  
              if (end_open != null) {
                  attrs = reader.parseTagAttributes();
  
  		reader.skipSpaces();
  		if (!reader.matches(end_open)) 
  		    throw new ParseException(reader.mark(),
  			Constants.getString("jsp.error.unterminated"));
  	        reader.advance(end_open.length());
  		reader.skipSpaces();
  
  		ParseUtil.checkAttributes("Declaration", attrs, validAttributes, start);
              }
  
  	    Mark stop = reader.skipUntil(close);
  	    if (stop == null)
  		throw new ParseException(Constants.getString("jsp.error.unterminated", 
                                                               new Object[] { open }));
  
  	    listener.setTemplateInfo(parser.tmplStart, parser.tmplStop);	    
  	    listener.handleDeclaration(start, stop, attrs);
  	    return true;
  	}
      }
  	
      static {
  	coreElements.addElement(new Declaration());
      }
      
      
      // expressions
      static final class Expression implements CoreElement {
  
  	private static final String OPEN_EXPR  = "<%=";
  	private static final String CLOSE_EXPR = "%>";
  
          private static final ParseUtil.ValidAttribute[] validAttributes = {
          };
  
  	public boolean accept(ParseEventListener listener, JspReader reader, Parser parser) 
  	    throws JasperException
  	{
  	    String close, open, end_open=null;
              Hashtable attrs = null;
  	    Mark start;
  		
  	    if (reader.matches(OPEN_EXPR)) {
  		open = OPEN_EXPR;
  		close = CLOSE_EXPR;
  	    } else
  		return false;
  
  	    reader.advance(open.length());
  	    start = reader.mark();
  
              if (end_open != null) {
                  attrs = reader.parseTagAttributes();
  
  		reader.skipSpaces();
  		if (!reader.matches(end_open)) 
  		    throw new ParseException(reader.mark(),
  			Constants.getString("jsp.error.unterminated"));
  	        reader.advance(end_open.length());
  		reader.skipSpaces();
  
                  ParseUtil.checkAttributes("Expression", attrs, validAttributes, start);
              }
  
  	    Mark stop = reader.skipUntil(close);
  	    if (stop == null)
  		throw new ParseException(reader.mark(), 
                                           Constants.getString("jsp.error.unterminated", 
                                                                   new Object[] { open }));
  	    listener.setTemplateInfo(parser.tmplStart, parser.tmplStop);	    
  	    listener.handleExpression(start, stop, attrs);
  	    return true;
  	}
      }
  
      static {
  	coreElements.addElement(new Expression());
      }
  
      // scriptlets
      static final class Scriptlet implements CoreElement {
  
  	private static final String OPEN_SCRIPTLET  = "<%";
  	private static final String CLOSE_SCRIPTLET = "%>";
  
          private static final ParseUtil.ValidAttribute[] validAttributes = {
          };
  
  	public boolean accept(ParseEventListener listener, JspReader reader, Parser parser) 
  	    throws JasperException
  	{
  	    String close, open, end_open = null;
              Hashtable attrs = null;
  	    Mark start;
  	    
  	    if (reader.matches(OPEN_SCRIPTLET)) {
  		open = OPEN_SCRIPTLET;
  		close = CLOSE_SCRIPTLET;
  	    } else
  		return false;
  		
  	    reader.advance(open.length());
  	    start = reader.mark();
  
              if (end_open != null) {
                  attrs = reader.parseTagAttributes();
  
  		reader.skipSpaces();
  		if (!reader.matches(end_open)) 
  		    throw new ParseException(reader.mark(),
  			Constants.getString("jsp.error.unterminated"));
  	        reader.advance(end_open.length());
  		reader.skipSpaces();
  
                  ParseUtil.checkAttributes("Scriptlet", attrs, validAttributes, start);
              }
  
  	    Mark stop = reader.skipUntil(close);
  	    if (stop == null)
  		throw new ParseException(reader.mark(), 
                                           Constants.getString("jsp.error.unterminated", 
                                                                   new Object[] { open }));
  	    listener.setTemplateInfo(parser.tmplStart, parser.tmplStop);	    
  	    listener.handleScriptlet(start, stop, attrs);
  	    return true;
  	}
      }
  
      static {
  	coreElements.addElement(new Scriptlet());
      }
  
      /*
       * UseBean
       */
      static final class Bean implements CoreElement {
  
  	private static final String OPEN_BEAN  = "<jsp:useBean";
  	private static final String CLOSE_BEAN = "/>";
  	private static final String CLOSE_BEAN_2 = "</jsp:useBean>";
  	private static final String CLOSE_BEAN_3 = ">";
  
  	private static final ParseUtil.ValidAttribute[] validAttributes = {
  	   new ParseUtil.ValidAttribute("id"),
  	   new ParseUtil.ValidAttribute("scope"),
  	   new ParseUtil.ValidAttribute("class"),
  	   new ParseUtil.ValidAttribute("type"),
  	   new ParseUtil.ValidAttribute("beanName")
  	};
  
  	public boolean accept(ParseEventListener listener, JspReader reader, Parser parser) 
  	    throws JasperException 
  	{
  	    if (reader.matches(OPEN_BEAN)) {
  		Mark start = reader.mark();
  		reader.advance(OPEN_BEAN.length());
  		Hashtable attrs = reader.parseTagAttributesBean();
  	        ParseUtil.checkAttributes ("useBean", attrs, validAttributes, start);
  		reader.skipSpaces();
  		if (!reader.matches(CLOSE_BEAN)) {
  		    if (!reader.matches(CLOSE_BEAN_3))
  			throw new ParseException(reader.mark(),
                                                   Constants.getString("jsp.error.unterminated", 
                                                                   new Object[] { "useBean" }));
  		    reader.advance(CLOSE_BEAN_3.length());
                      Mark stop = reader.mark();
  		    listener.setTemplateInfo(parser.tmplStart, parser.tmplStop);		    
                      listener.handleBean(start, stop, attrs);
  		    int oldSize = reader.size;
  		    parser.parse(CLOSE_BEAN_2);
  		    if (oldSize != reader.size) {
  			throw new ParseException (reader.mark(), 
                                                    Constants.getString("jsp.error.usebean.notinsamefile"));
  		    }
  		    if (!reader.matches(CLOSE_BEAN_2))
  			throw new ParseException(reader.mark(), 
                                                   Constants.getString("jsp.error.unterminated"
  								     , 
                                                                       new Object[] { OPEN_BEAN })
  						 );
  
  		    reader.advance (CLOSE_BEAN_2.length());
  		    
                      listener.handleBeanEnd(start, stop, attrs);
                      return true;
  		} else {
                      reader.advance(CLOSE_BEAN.length());
                      Mark stop = reader.mark();
  		    listener.setTemplateInfo(parser.tmplStart, parser.tmplStop);		    
                      listener.handleBean(start, stop, attrs);
                      listener.handleBeanEnd(start, stop, attrs);
                      return true;
                  }
  	    } else
  		return false;
  	}
      }
  
      static {
  	coreElements.addElement(new Bean());
      }
  
      /*
       * GetProperty
       */
      static final class GetProperty implements CoreElement {
  
  	private static final String OPEN_GETPROPERTY  = "<jsp:getProperty";
  	private static final String CLOSE_GETPROPERTY = "/>";
  	
  	private static final ParseUtil.ValidAttribute[] validAttributes = {
  	   new ParseUtil.ValidAttribute("name", true),
  	   new ParseUtil.ValidAttribute("property", true)
  	};
  
  	public boolean accept(ParseEventListener listener, JspReader reader, Parser parser) 
  	    throws JasperException 
  	{
  	    if (reader.matches(OPEN_GETPROPERTY)) {
  		Mark start = reader.mark();
  		reader.advance(OPEN_GETPROPERTY.length());
  		Hashtable attrs = reader.parseTagAttributes ();
  	        ParseUtil.checkAttributes ("getProperty", attrs, validAttributes, start);
  		reader.skipSpaces();
  		if (!reader.matches(CLOSE_GETPROPERTY))
  		    throw new ParseException(reader.mark(), 
                                               Constants.getString("jsp.error.unterminated", 
                                                                   new Object[] { OPEN_GETPROPERTY }));
  		else
  		    reader.advance(CLOSE_GETPROPERTY.length());
  		Mark stop = reader.mark();
  		listener.setTemplateInfo(parser.tmplStart, parser.tmplStop);		
  		listener.handleGetProperty(start, stop, attrs);
  		return true;
  	    } else
  		return false;
  	}
      }
  
      static {
  	coreElements.addElement(new GetProperty());
      }
      
      /*
       * SetProperty
       */
      static final class SetProperty implements CoreElement {
  
  	private static final String OPEN_SETPROPERTY  = "<jsp:setProperty";
  	private static final String CLOSE_SETPROPERTY = "/>";
  	
  	private static final ParseUtil.ValidAttribute[] validAttributes = {
  	   new ParseUtil.ValidAttribute("name", true),
  	   new ParseUtil.ValidAttribute("property", true),
  	   new ParseUtil.ValidAttribute("value"),
  	   new ParseUtil.ValidAttribute("param")
  	};
  
  	public boolean accept(ParseEventListener listener, JspReader reader, Parser parser) 
  	    throws JasperException 
  	{
  	    if (reader.matches(OPEN_SETPROPERTY)) {
  		Mark start = reader.mark();
  		reader.advance(OPEN_SETPROPERTY.length());
  		Hashtable attrs = reader.parseTagAttributes ();
  	        ParseUtil.checkAttributes ("setProperty", attrs, validAttributes, start);
  		reader.skipSpaces();
  		if (!reader.matches(CLOSE_SETPROPERTY))
  		    throw new ParseException(reader.mark(), 
                                               Constants.getString("jsp.error.unterminated", 
                                                                   new Object[] { OPEN_SETPROPERTY }));
  		else
  		    reader.advance(CLOSE_SETPROPERTY.length());
  		Mark stop = reader.mark();
  		listener.setTemplateInfo(parser.tmplStart, parser.tmplStop);		
  		listener.handleSetProperty(start, stop, attrs);
  		return true;
  	    } else
  		return false;
  	}
      }
  
      static {
  	coreElements.addElement(new SetProperty());
      }
  
      /*
       * User-defined Tags
       */
      static final class Tag implements CoreElement {
          
          private static final String CLOSE_1 = "/>";
          private static final String CLOSE = ">";
          
  	public boolean accept(ParseEventListener listener, JspReader reader, 
                                Parser parser) throws JasperException 
  	{
              if (reader.peekChar() != '<')
                  return false;
  
              Mark start = reader.mark();
              reader.nextChar();
              String tag = reader.parseToken(false);
  
              /*
               * Extract the prefix and the short tag name.
               */
              int i = tag.indexOf(':');
              if (i == -1) {
                  reader.reset(start);
                  return false;
              }
              String prefix = tag.substring(0, i);
              String shortTagName = "";
              if (++i < tag.length()) 
                  shortTagName = tag.substring(i);
              
              /*
               * Check if this is a user-defined tag; otherwise we won't touch this...
               */
  
              TagLibraries libraries = listener.getTagLibraries();
              
              if (!libraries.isUserDefinedTag(prefix, shortTagName)) {
                  reader.reset(start);
                  return false;
              }
  
              if (shortTagName == null)
                  throw new ParseException(start, "Nothing after the :");
  
              
              TagLibraryInfo tli = libraries.getTagLibInfo(prefix);
              TagInfo ti = tli.getTag(shortTagName);
              
              if (ti == null)
                  throw new ParseException(start, "Unable to locate TagInfo for "+tag);
  
  	    String bc = ti.getBodyContent();
  
              Hashtable attrs = reader.parseTagAttributes();
              reader.skipSpaces();
              Mark bodyStart = null;
              Mark bodyStop = null;
  
  	    
  	    
  	    if (reader.matches(CLOSE_1)
  		|| bc.equalsIgnoreCase(TagInfo.BODY_CONTENT_EMPTY)) {
  		if (reader.matches(CLOSE_1))
  		    reader.advance(CLOSE_1.length());
  		else
  		    throw new ParseException(start, "Body is supposed to be empty for "+tag);
  
  		listener.setTemplateInfo(parser.tmplStart, parser.tmplStop);		
  		listener.handleTagBegin(start, reader.mark(), attrs, prefix,
  					shortTagName, tli, ti);
  		listener.handleTagEnd(start, reader.mark(), prefix, 
  				      shortTagName, attrs, tli, ti);
  	    } else { 
  		// Body can be either
  		//     - JSP tags
  		//     - tag dependent stuff
  		if (reader.matches(CLOSE)) {
  		    reader.advance(CLOSE.length());
  		    bodyStart = reader.mark();
  		    listener.setTemplateInfo(parser.tmplStart, parser.tmplStop);		    
  		    listener.handleTagBegin(start, bodyStart, attrs, prefix, 
  					    shortTagName, tli, ti);
                      if (bc.equalsIgnoreCase(TagInfo.BODY_CONTENT_TAG_DEPENDENT) ||
                          bc.equalsIgnoreCase(TagInfo.BODY_CONTENT_JSP)) 
                          {
                              String tagEnd = "</"+tag+">";
                              // Parse until the end of the tag body. 
                              // Then skip the tag end... 
                              parser.parse(tagEnd);
                              try {
                                  reader.advance(tagEnd.length());
                              } catch (ParseException ex) {
                                  throw new ParseException(
                                      start,
                                      Constants.getString("jsp.error.unterminated.user.tag", 
                                          new Object[]{ParseUtil.escapeXml(tagEnd)}));
  			    }
  			    listener.setTemplateInfo(parser.tmplStart, parser.tmplStop);
                              listener.handleTagEnd(parser.tmplStop, reader.mark(), prefix, 
                                                    shortTagName, attrs, tli, ti);
                          } else
                              throw new ParseException(start, 
                                                       "Internal Error: Invalid BODY_CONTENT type");
  		} else 
  		    throw new ParseException(start, 
  					     "Unterminated user-defined tag");
  	    }
              return true;
          }
      }
  
      static {
          coreElements.addElement(new Tag());
      }
      
      
  
      /*
       * Plugin
       */
      static final class Plugin implements CoreElement {
  	private static final String OPEN_PLUGIN  = "<jsp:plugin";
  	private static final String END_OPEN_PLUGIN  = ">";
  	private static final String CLOSE_PLUGIN = "</jsp:plugin>";
  	private static final String OPEN_PARAMS = "<jsp:params>";
  	private static final String CLOSE_PARAMS = "</jsp:params>";
  	private static final String OPEN_INDIVIDUAL_PARAM = "<jsp:param";
  	private static final String CLOSE_INDIVIDUAL_PARAM = "/>";
  	private static final String OPEN_FALLBACK = "<jsp:fallback>";
  	private static final String CLOSE_FALLBACK = "</jsp:fallback>";
  
  	private static final ParseUtil.ValidAttribute[] validAttributes = {
  	   new ParseUtil.ValidAttribute ("type",true),
  	   new ParseUtil.ValidAttribute("code", true),
  	   new ParseUtil.ValidAttribute("codebase"),
  	   new ParseUtil.ValidAttribute("align"),
  	   new ParseUtil.ValidAttribute("archive"),
  	   new ParseUtil.ValidAttribute("height"),
  	   new ParseUtil.ValidAttribute("hspace"),
  	   new ParseUtil.ValidAttribute("jreversion"),
  	   new ParseUtil.ValidAttribute("name"),
  	   new ParseUtil.ValidAttribute("vspace"),
  	   new ParseUtil.ValidAttribute("width"),
  	   new ParseUtil.ValidAttribute("nspluginurl"),
  	   new ParseUtil.ValidAttribute("iepluginurl")
  	};
  
  	public boolean accept(ParseEventListener listener, JspReader reader, 
  				Parser parser) throws JasperException 
  	{
  	    if (reader.matches(OPEN_PLUGIN)) {
  		Mark start = reader.mark();
  		reader.advance(OPEN_PLUGIN.length());
  		Hashtable attrs = reader.parseTagAttributes ();
  		reader.skipSpaces ();
  
  	    if (!reader.matches(END_OPEN_PLUGIN))
  	        throw new ParseException (reader.mark(),
  	                   Constants.getString("jsp.error.plugin.notclosed"));
  	    
  	    reader.advance (END_OPEN_PLUGIN.length ());
  	    reader.skipSpaces ();
  
  		Hashtable param = null;
  		String fallback = null;
  
  	        ParseUtil.checkAttributes ("plugin", attrs, validAttributes, start);
  		if (reader.matches (OPEN_PARAMS)) {
  		    param = new Hashtable ();
  		    boolean paramsClosed = false;
  		    reader.advance (OPEN_PARAMS.length ());
  
  		    /**
  		     * Can have more than one param tag. Hence get all the
  		     * params.
  		     */
  
  		    while (reader.hasMoreInput ()) {
  		        reader.skipSpaces ();
  		        if (reader.matches (CLOSE_PARAMS)) {
  			    paramsClosed = true;
  			    reader.advance (CLOSE_PARAMS.length ());
  			    break;
  			}
  		        if (!reader.matches (OPEN_INDIVIDUAL_PARAM))
  		    	    throw new ParseException (reader.mark(),
  				Constants.getString("jsp.error.paramexpected"));
  
  			reader.parsePluginParamTag(param);
  			reader.skipSpaces ();
  
  		        if (!reader.matches (CLOSE_INDIVIDUAL_PARAM))
  		    	    throw new ParseException (reader.mark(),
  				Constants.getString(
  					"jsp.error.closeindividualparam"));
  			reader.advance (CLOSE_INDIVIDUAL_PARAM.length ());
  		    }
  		    if (!paramsClosed)
  		    	    throw new ParseException (reader.mark(),
  				Constants.getString("jsp.error.closeparams"));
  		    reader.skipSpaces ();
  		}
  		
  		if (reader.matches (OPEN_FALLBACK)) {
  		    reader.advance(OPEN_FALLBACK.length ());
  		    reader.skipSpaces ();
  		    Mark fallBackStart = reader.mark ();
  		    Mark fallBackStop = reader.skipUntil (CLOSE_FALLBACK);
  		    fallback = new String (reader.getChars(fallBackStart,
  		    					         fallBackStop));
  		    reader.skipSpaces ();
  		}
  
  		if (!reader.matches(CLOSE_PLUGIN)) 
  		    throw new ParseException(reader.mark(), 
                                            Constants.getString(
  					  "jsp.error.unterminated", 
                                             new Object[] { OPEN_PLUGIN }));
  
  		reader.advance(CLOSE_PLUGIN.length());
  		Mark stop = reader.mark();
  		listener.setTemplateInfo(parser.tmplStart, parser.tmplStop);		
  		listener.handlePlugin(start, stop, attrs, param, fallback);
  		return true;
  	    } else
  		return false;
  	}
      }
  
      static {
  	coreElements.addElement(new Plugin());
      }
  
      /*
       * Quoting in template text.
       * Entities &apos; and &quote;
       */
      static final class QuoteEscape implements CoreElement {
          /**
           * constants for escapes
           */
          private static String QUOTED_START_TAG = "<\\%";
          private static String QUOTED_END_TAG = "%\\>";
          private static String START_TAG = "<%";
          private static String END_TAG = "%>";
  
  	private static final String APOS = "&apos;";
  	private static final String QUOTE = "&quote;";
          
  	public boolean accept(ParseEventListener listener, JspReader reader, Parser parser) 
              throws JasperException 
  	{
              try {
  		Mark start = reader.mark();
                  if (reader.matches(QUOTED_START_TAG)) {
                      reader.advance(QUOTED_START_TAG.length());
  		    Mark end = reader.mark();
                      parser.caw.write(START_TAG);
                      parser.flushCharData(start, end);
                      return true;
                  } else if (reader.matches(APOS)) {
                      reader.advance(APOS.length());
  		    Mark end = reader.mark();
                      parser.caw.write("\'");
                      parser.flushCharData(start, end);
                      return true;
                  }
                  else if (reader.matches(QUOTE)) {
                      reader.advance(QUOTE.length());
  		    Mark end = reader.mark();
                      parser.caw.write("\"");
                      parser.flushCharData(start, end);
                      return true;
                  }
              } catch (java.io.IOException ex) {
                  System.out.println (ex.getMessage());
              }
              return false;
  	}
      }
      
      static {
  	coreElements.addElement(new QuoteEscape());
      }
  
      void flushCharData(Mark start, Mark stop) throws JasperException {
          char[] array = caw.toCharArray();
          if (array.length != 0) // Avoid unnecessary out.write("") statements...
              listener.handleCharData(start, stop, caw.toCharArray());
          caw = new CharArrayWriter();
      }
  
      public void parse() throws JasperException {
          parse(null);
      }
  
      public void parse(String until) throws JasperException {
          parse(until, null);
      }
      
      public void parse(String until, Class[] accept) throws JasperException {
  
  	boolean noJspElement = false;
  	while (reader.hasMoreInput()) {
              if (until != null && reader.matches(until)) 
                  return;
  
  	    // If the file has changed because of a 'push' or a 'pop'
  	    // we must flush the character data for the old file.
  	    if (!reader.mark().getFile().equals(currentFile)) {
  		flushCharData(tmplStart, tmplStop);
  		currentFile = reader.mark().getFile();
  		tmplStart = reader.mark();
  	    }
  	    
  	    Enumeration e = coreElements.elements(); 
  
              if (accept != null) {
                  Vector v = new Vector();
                  while (e.hasMoreElements()) {
                      CoreElement c = (CoreElement) e.nextElement();
                      for(int i = 0; i < accept.length; i++)
                          if (c.getClass().equals(accept[i]))
                              v.addElement(c);
                  }
                  e = v.elements();
              }
  
  	    boolean accepted = false;
  	    while (e.hasMoreElements()) {
  		CoreElement c = (CoreElement) e.nextElement();
  		Mark m = reader.mark();
  		if (c.accept(listener, reader, this)) {
                      Constants.message("jsp.message.accepted",
                                        new Object[] { c.getClass().getName(), m },
                                        Log.DEBUG);
  		    accepted = true;
  		    noJspElement = false;
  		    break;
  		} 
  	    }
  	    if (!accepted) {
  
  		// This is a hack. "reader.nextContent()" will just return 
  		// after it sees "<" -- not necessarily a JSP element. Using
  		// a boolean we will ensure that tmplStart changes only when
  		// strictly necessary.
  		if (noJspElement == false) {
  		    tmplStart = reader.mark();
  		    noJspElement = true;
  		}
  		String s = reader.nextContent();
  		tmplStop = reader.mark();
  		caw.write(s, 0, s.length());
  	    }
  	}
  	flushCharData(tmplStart, tmplStop);
      }
  }