FileDocCategorySizeDatePackage
JspReader.javaAPI DocApache Tomcat 6.0.1420307Fri Jul 20 04:20:34 BST 2007org.apache.jasper.compiler

JspReader

public class JspReader extends Object
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
author
Danno Ferrin
author
Kin-man Chung
author
Shawn Bayern
author
Mark Roth

Fields Summary
private org.apache.juli.logging.Log
log
Logger.
private Mark
current
The current spot in the file.
private String
master
What is this?
private List
sourceFiles
The list of source files.
private int
currFileId
The current file ID (-1 indicates an error or no file).
private int
size
Seems redundant.
private org.apache.jasper.JspCompilationContext
context
The compilation context.
private ErrorDispatcher
err
The Jasper error dispatcher.
private boolean
singleFile
Set to true when using the JspReader on a single file where we read up to the end and reset to the beginning many times. (as in ParserController.figureOutJspDocument()).
Constructors Summary
public JspReader(org.apache.jasper.JspCompilationContext ctxt, String fname, String encoding, JarFile jarFile, ErrorDispatcher err)
Constructor.

param
ctxt The compilation context
param
fname The file name
param
encoding The file encoding
param
jarFile ?
param
err The error dispatcher
throws
JasperException If a Jasper-internal error occurs
throws
FileNotFoundException If the JSP file is not found (or is unreadable)
throws
IOException If an IO-level error occurs, e.g. reading the file


                                                               
      
                      
                      
                      
                      
                

        this(ctxt, fname, encoding,
             JspUtil.getReader(fname, encoding, jarFile, ctxt, err),
             err);
    
public JspReader(org.apache.jasper.JspCompilationContext ctxt, String fname, String encoding, InputStreamReader reader, ErrorDispatcher err)
Constructor: same as above constructor but with initialized reader to the file given.


        this.context = ctxt;
        this.err = err;
        sourceFiles = new Vector();
        currFileId = 0;
        size = 0;
        singleFile = false;
        pushFile(fname, encoding, reader);
    
Methods Summary
java.lang.StringgetFile(int fileid)
Returns the file at the given position in the list.

param
fileid The file position in the list
return
The file at that position, if found, null otherwise

        return (String) sourceFiles.get(fileid);
    
org.apache.jasper.JspCompilationContextgetJspCompilationContext()

return
JSP compilation context with which this JspReader is associated

        return context;
    
java.net.URLgetResource(java.lang.String path)
Gets the URL for the given path name.

param
path Path name
return
URL for the given path name.
exception
MalformedURLException if the path name is not given in the correct form

        return context.getResource(path);
    
java.lang.StringgetText(Mark start, Mark stop)

        Mark oldstart = mark();
        reset(start);
        CharArrayWriter caw = new CharArrayWriter();
        while (!stop.equals(mark()))
            caw.write(nextChar());
        caw.close();
        reset(oldstart);
        return caw.toString();
    
booleanhasMoreInput()
Checks if the current file has more input.

return
True if more reading is possible
throws
JasperException if an error occurs

        if (current.cursor >= current.stream.length) {
            if (singleFile) return false; 
            while (popFile()) {
                if (current.cursor < current.stream.length) return true;
            }
            return false;
        }
        return true;
    
private booleanisDelimiter()
Parse utils - Is current character a token delimiter ? Delimiters are currently defined to be =, >, <, ", and ' or any any space character as defined by isSpace.

return
A boolean.

        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;
        }
    
final booleanisSpace()

        // Note: If this logic changes, also update Node.TemplateText.rtrim()
        return peekChar() <= ' ";
    
Markmark()

        return new Mark(current);
    
booleanmatches(java.lang.String string)
search the stream for a match to a string

param
string The string to match
return
true is one is found, the current position in stream is positioned after the search string, false otherwise, position in stream unchanged.

        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());
        return true;
    
booleanmatchesETag(java.lang.String tagName)

        Mark mark = mark();

        if (!matches("</" + tagName))
            return false;
        skipSpaces();
        if (nextChar() == '>")
            return true;

        reset(mark);
        return false;
    
booleanmatchesETagWithoutLessThan(java.lang.String tagName)

       Mark mark = mark();

       if (!matches("/" + tagName))
           return false;
       skipSpaces();
       if (nextChar() == '>")
           return true;

       reset(mark);
       return false;
    
booleanmatchesIgnoreCase(java.lang.String string)

        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;
    
booleanmatchesOptionalSpacesFollowedBy(java.lang.String s)
Looks ahead to see if there are optional spaces followed by the given String. If so, true is returned and those spaces and characters are skipped. If not, false is returned and the position is restored to where we were before.

        Mark mark = mark();

        skipSpaces();
        boolean result = matches( s );
        if( !result ) {
            reset( mark );
        }

        return result;
    
intnextChar()

        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;
    
java.lang.StringparseToken(boolean quoted)
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 true accept quoted strings.

        StringBuffer stringBuffer = new StringBuffer();
        skipSpaces();
        stringBuffer.setLength(0);
        
        if (!hasMoreInput()) {
            return "";
        }

        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) {
                    err.jspError(mark(), "jsp.error.quotes.unterminated");
                }
            } else {
                err.jspError(mark(), "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();
    
intpeekChar()

        if (!hasMoreInput())
            return -1;
        return current.stream[current.cursor];
    
private booleanpopFile()
Pop a file from the file stack. The field "current" is retored to the value to point to the previous files, if any, and is set to null otherwise.

return
true is there is a previous file on the stack. false otherwise.


        // Is stack created ? (will happen if the Jsp file we're looking at is
        // missing.
        if (current == null || currFileId < 0) {
            return false;
        }

        // Restore parser state:
        String fName = getFile(currFileId);
        currFileId = unregisterSourceFile(fName);
        if (currFileId < -1) {
            err.jspError("jsp.error.file.not.registered", fName);
        }

        Mark previous = current.popStream();
        if (previous != null) {
            master = current.baseDir;
            current = previous;
            return true;
        }
        // Note that although the current file is undefined here, "current"
        // is not set to null just for convience, for it maybe used to
        // set the current (undefined) position.
        return false;
    
voidpushChar()
Back up the current cursor by one char, assumes current.cursor > 0, and that the char to be pushed back is not '\n'.

        current.cursor--;
        current.col--;
    
private voidpushFile(java.lang.String file, java.lang.String encoding, java.io.InputStreamReader reader)
Push a file (and its associated Stream) on the file stack. THe current position in the current file is remembered.


        // Register the file
        String longName = file;

        int fileid = registerSourceFile(longName);

        if (fileid == -1) {
            // Bugzilla 37407: http://issues.apache.org/bugzilla/show_bug.cgi?id=37407
            if(reader != null) {
                try {
                    reader.close();
                } catch (Exception any) {
                    if(log.isDebugEnabled()) {
                        log.debug("Exception closing reader: ", any);
                    }
                }
            }

            err.jspError("jsp.error.file.already.registered", file);
        }

        currFileId = fileid;

        try {
            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),
                                   longName, encoding);
            }
        } catch (Throwable ex) {
            log.error("Exception parsing file ", ex);
            // Pop state being constructed:
            popFile();
            err.jspError("jsp.error.file.cannot.read", file);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (Exception any) {
                    if(log.isDebugEnabled()) {
                        log.debug("Exception closing reader: ", any);
                    }
                }
            }
        }
    
private intregisterSourceFile(java.lang.String file)
Register a new source file. This method is used to implement file inclusion. Each included file gets a unique identifier (which is the index in the array of source files).

return
The index of the now registered file.

        if (sourceFiles.contains(file)) {
            return -1;
        }

        sourceFiles.add(file);
        this.size++;

        return sourceFiles.size() - 1;
    
voidreset(Mark mark)

        current = new Mark(mark);
    
voidsetSingleFile(boolean val)

        singleFile = val;
    
intskipSpaces()

        int i = 0;
        while (hasMoreInput() && isSpace()) {
            i++;
            nextChar();
        }
        return i;
    
MarkskipUntil(java.lang.String limit)
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 Mark instance (positioned immediately before the search string) if found, null otherwise.

        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)) {
                Mark restart = mark();
                for (int i = 1 ; i < limlen ; i++) {
                    if (peekChar() == limit.charAt(i))
                        nextChar();
                    else {
                        reset(restart);
                        continue skip;
                    }
                }
                return ret;
            }
        }
        return null;
    
MarkskipUntilETag(java.lang.String tag)
Skip until the given end tag is matched in the stream. When returned, the context is positioned past the end of the tag.

param
tag The name of the tag whose ETag () to match.
return
A non-null Mark instance (positioned immediately before the ETag) if found, null otherwise.

        Mark ret = skipUntil("</" + tag);
        if (ret != null) {
            skipSpaces();
            if (nextChar() != '>")
                ret = null;
        }
        return ret;
    
MarkskipUntilIgnoreEsc(java.lang.String limit)
Skip until the given string is matched in the stream, but ignoring chars initially escaped by a '\'. When returned, the context is positioned past the end of the match.

param
s The String to match.
return
A non-null Mark instance (positioned immediately before the search string) if found, null otherwise.

        Mark ret = null;
        int limlen = limit.length();
        int ch;
        int prev = 'x";        // Doesn't matter
        
    skip:
        for (ret = mark(), ch = nextChar() ; ch != -1 ;
                 ret = mark(), prev = ch, ch = nextChar()) {            
            if (ch == '\\" && prev == '\\") {
                ch = 0;                // Double \ is not an escape char anymore
            }
            else if (ch == limit.charAt(0) && prev != '\\") {
                for (int i = 1 ; i < limlen ; i++) {
                    if (peekChar() == limit.charAt(i))
                        nextChar();
                    else
                        continue skip;
                }
                return ret;
            }
        }
        return null;
    
private intunregisterSourceFile(java.lang.String file)
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.

        if (!sourceFiles.contains(file)) {
            return -1;
        }

        sourceFiles.remove(file);
        this.size--;
        return sourceFiles.size() - 1;