tomcat-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Alex Cruikshank <a...@epitonic.com>
Subject [PATCH]: state handling in JspReader
Date Sat, 11 Dec 1999 20:52:13 GMT
There appears to be two problems with way JspReader the handles state when 
dealing with the include directive.  The first is the fact that it only 
stores the cursor position (and not the current stream or include stack) 
when doing look-aheads.  This causes big problems when the parser attempts 
to match something at the end of an included stream, sets the stream to the 
previous stream, then resets the cursor to a position in the included 
stream (even though it's now reading in the previous stream).  To verify 
this, put an include directive (<%@ include... %>) at the end of a file 
with no space at the end.  You can also try putting "<j" at the end of a file.

The second problem concerns how it deals with includes in different 
directories.  This problem presents itself when you have doubly nested 
includes in different directories:

/include1.jsp:
...
<%@ include file="dir1/include2.jsp %>
...

/dir1/include2.jsp:
...
<%@ include file="dir2/include3.jsp %>
...

/dir1/dir2/include3.jsp:
This file gets included.

This should work, but JspReader only stores the base directory of the 
original file (the "master" variable), so when it tries to look up 
include3.jsp, it looks in /dir2/include3.jsp instead of 
/dir1/dir2/include3.jsp.  This is clearly incorrect behavior (how is 
include2.jsp supposed to know the base dir of the file that included it?).

SOLUTION:
Both of these problems may be fixed without affecting the interface of 
JspReader and Mark by putting all of JspReader's state into the Mark 
class.  This permits JspReader look-ahead into other streams and still 
return to the correct state simply by creating a new Mark object.  The 
baseDir problem is handled by keeping the base dir each included file in 
the include stack.  The following patches appear to solve the problems with 
JspReader and Mark.


Index: Mark.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-tomcat/src/share/org/apache/jasper/compiler/Mark.java,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 Mark.java
--- Mark.java   1999/10/09 00:20:37     1.1.1.1
+++ Mark.java   1999/12/11 20:44:24
@@ -60,50 +60,162 @@
   */
  package org.apache.jasper.compiler;

+import java.util.Stack;
+
  /**
   * Mark represents a point in the JSP input.
   *
- * @author Anil K. Vijendran
+ * @author Anil K. Vijendran, Alex Cruikshank
   */
-public final class Mark {
-    int cursor, line, col;
-    int fileid;
-    private JspReader reader;
+public final class Mark
+{
+    int cursor, line, col;                     // position within current 
stream
+    int fileid;                                                // fileid 
of current stream
+       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 baseDir;
+               String encoding;
+           char[] stream = null;
+
+               IncludeState( int inCursor, int inLine, int inCol, int 
inFileid, String inBaseDir,
+                               String inEncoding, char[] inStream )
+               {
+                       cursor = inCursor;
+                       line = inLine;
+                       col = inCol;
+                       fileid = inFileid;
+                       baseDir = inBaseDir;
+                       encoding = inEncoding;
+                       stream = inStream;
+               }
+    }

-    Mark(JspReader reader, int fileid) {
-       this.reader = reader;
-       this.cursor = this.line = this.col = 0;
-       this.fileid = fileid;
+
+    /**
+        * 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 string representation of base directory of 
requested jsp file
+        */
+    Mark(JspReader reader, char[] inStream, int fileid, String inBaseDir, 
String inEncoding )
+       {
+               this.reader = reader;
+               this.stream = inStream;
+               this.cursor = this.line = this.col = 0;
+               this.fileid = fileid;
+               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.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) );
+               }
      }

-    Mark(Mark other) {
-       this.reader = other.reader;
-       this.fileid = other.fileid;
-       this.cursor = other.cursor;
-       this.line = other.line;
-       this.col = other.col;
+    /** 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
+     */
+    public void pushStream( char[] inStream, int inFileid, String 
inBaseDir, String inEncoding )
+    {
+               // store current state in stack
+               includeStack.push( new IncludeState( cursor, line, col, 
fileid, baseDir, encoding, stream ) );
+
+               // set new variables
+               cursor = 0;
+               line = 0;
+               col = 0;
+               fileid = inFileid;
+               baseDir = inBaseDir;
+               encoding = inEncoding;
+               stream = inStream;
      }
+
+
+
+    /** Restores this mark's state to a previously stored stream.
+     */
+    public 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;
+               baseDir = state.baseDir;
+               stream = state.stream;
+        return true;
+    }
+
+

-    public String toString() {
-       return getFile()+"("+line+","+col+")";
+    public String toString()
+       {
+               return getFile()+"("+line+","+col+")";
      }

-    public String getFile() {
+    public String getFile()
+       {
          return reader.getFile(fileid);
      }

-    public String toShortString() {
+    public 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;
+    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;
      }
  }








Index: JspReader.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-tomcat/src/share/org/apache/jasper/compiler/JspReade 
r.java,v
retrieving revision 1.4
diff -u -r1.4 JspReader.java
--- JspReader.java      1999/11/23 19:27:16     1.4
+++ JspReader.java      1999/12/11 20:45:26
@@ -84,37 +84,13 @@
   * @author Rajiv Mordani
   */
  public class JspReader {
-    protected char stream[] = null;
      protected Mark current  = null;
-    String master = null;

      Vector sourceFiles = new Vector();
-    Stack includeStack;
      int size = 0;

      private ServletContext context;

-    /**
-     * 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 {
-       Mark current;
-       char stream[] = null;
-
-       void restore() {
-           JspReader.this.current = current;
-           JspReader.this.stream = stream;
-       }
-
-       IncludeState() {
-           this.current = JspReader.this.current;
-           this.stream = JspReader.this.stream;
-       }
-
-    }

      public String getFile(int fileid) {
         return (String) sourceFiles.elementAt(fileid);
@@ -141,17 +117,16 @@
      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)
+        if ((current == null) || (current.baseDir == null) || (isAbsolute))
+        {
              pushFile(new File(name), encoding);
+        }
          else
-            pushFile(new File(parent + name), encoding);
-
-        if (master == null)
-            master = name;
+        {
+            pushFile(new File(current.baseDir + "/" + name), encoding);
+        }
      }

      /**
@@ -160,83 +135,98 @@
       * @param encoding Optional encoding to read the file.
       */
      private void pushFile(File file, String encoding)
-       throws ParseException, FileNotFoundException
+        throws ParseException, FileNotFoundException
      {
          // Default encoding if needed:
-       if (encoding == null)
-           encoding = System.getProperty("file.encoding", "8859_1");
-       // Register the file, and read its content:
-       int fileid    = registerSourceFile(file.getAbsolutePath());
-       Reader reader = null;
-       try {
+        if (encoding == null)
+        encoding = System.getProperty("file.encoding", "8859_1");
+
+        // Register the file, and read its content:
+        int fileid    = registerSourceFile(file.getAbsolutePath());
+        Reader reader = null;
+        try
+        {
              if (context == null)
-                reader = new InputStreamReader(new FileInputStream(file),
-                                               encoding);
-            else {
-               // Need to do this as the getResourceAsStream is relative
-               // to docBase and if we use it on Win32 then File uses \
-               // and not / which causes a problem.
-               String fileName = file.toString();
-               if (File.separatorChar == '\\')
-                   fileName = fileName.replace (File.separatorChar, '/');
+            {
+                reader = new InputStreamReader(new FileInputStream(file), 
encoding);
+            }
+            else
+            {
+                // Need to do this as the getResourceAsStream is relative
+                // to docBase and if we use it on Win32 then File uses \
+                // and not / which causes a problem.
+                String fileName = file.toString();
+                if (File.separatorChar == '\\')
+                {
+                    fileName = fileName.replace (File.separatorChar, '/');
+                }
+
                  InputStream in = context.getResourceAsStream(fileName);
                  if (in == null)
+                {
                      throw new FileNotFoundException(fileName);
+                }

-                try {
+                try
+                {
                      reader = new InputStreamReader(in);
-                } catch (Exception ex) {
-                    throw new FileNotFoundException(fileName + ": "+ 
ex.getMessage());
+                }
+                catch (Exception 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();
-           IncludeState state = new IncludeState();
-           //      System.out.println("Pushing... "+this.current);
-           this.stream = caw.toCharArray();
-           this.current = new Mark(this, fileid);
-           // Prepare a new include state:
-           if (includeStack == null) {
-               // Initial file, prepare for include files:
-               includeStack = new Stack();
-           } else {
-               includeStack.push(state);
-           }
-        } catch (FileNotFoundException fnfe) {
+            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, 
file.getParent(), encoding );
+            }
+            else
+            {
+                current.pushStream( caw.toCharArray(), fileid, 
file.getParent(), encoding );
+            }
+        }
+        catch (FileNotFoundException fnfe)
+        {
              throw fnfe;
-       } catch (Throwable ex) {
+        }
+        catch (Throwable ex)
+        {
              System.err.println("STACK TRACE: ");
              ex.printStackTrace();
-           // 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) {}
-           }
-       }
+            // 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() {
-       // Is stack created ? (will happen if the Jsp file we'r looking at is
-       // missing.
-       if(includeStack == null)
-               return false;

-       // Any thing left ?
-       if ( includeStack.size() == 0 )
-           return false;
-       // Restore parser state:
-       size--;
-       IncludeState state = (IncludeState) includeStack.pop();
-       //      System.out.println("Popping back... "+state.current);
-       state.restore();
-       return true;
+    public boolean popFile()
+    {
+        // Is stack created or empty? (will happen if the Jsp file we'r 
looking at is
+        // missing.
+        if ( current == null ) return false;
+
+       // Restore parser state: (left in here for legacy reasons,
+        // should use mark.includeState.size() + 1 instead)
+        size--;
+
+        return current.popStream();
      }

      protected JspReader(String file, ServletContext ctx)
@@ -253,8 +243,14 @@
      }

      public boolean hasMoreInput() {
-       if (current.cursor >= stream.length)
-           return popFile();
+       if (current.cursor >= current.stream.length)
+       {
+           while (popFile())
+            {
+                if ( current.cursor < current.stream.length) return true;
+            }
+           return false;
+       }
         return true;
      }

@@ -262,7 +258,7 @@
         if (!hasMoreInput())
             return -1;

-       int ch = stream[current.cursor];
+       int ch = current.stream[current.cursor];

         current.cursor++;

@@ -287,7 +283,7 @@
      }

      public int peekChar() {
-       return stream[current.cursor];
+       return current.stream[current.cursor];
      }

      public Mark mark() {
@@ -300,14 +296,14 @@

      public boolean matchesIgnoreCase(String string) {
         Mark mark = mark();
-       int ch = nextChar();
+       int ch = 0;
         int i = 0;
         do {
+            ch = nextChar();
             if (Character.toLowerCase((char) ch) != string.charAt(i++)) {
                 reset(mark);
                 return false;
             }
-           ch = nextChar();
         } while (i < string.length());
         reset(mark);
         return true;
@@ -315,14 +311,14 @@

      public boolean matches(String string) {
         Mark mark = mark();
-       int ch = nextChar();
+       int ch = 0;
         int i = 0;
         do {
+            ch = nextChar();
             if (((char) ch) != string.charAt(i++)) {
                 reset(mark);
                 return false;
             }
-           ch = nextChar();
         } while (i < string.length());
         reset(mark);
         return true;


==============================
Alex Cruikshank
Software Engineer
Epitonic.com
alex@epitonic.com


Mime
View raw message