Added: 
struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/JspUtil.java
URL: 
http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/JspUtil.java?rev=819444&view=auto
==============================================================================
--- 
struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/JspUtil.java
 (added)
+++ 
struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/JspUtil.java
 Mon Sep 28 01:55:26 2009
@@ -0,0 +1,1177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.struts2.jasper.compiler;
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.util.Vector;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+import javax.el.FunctionMapper;
+import javax.servlet.jsp.el.ExpressionEvaluator;
+
+
+import org.apache.struts2.el.ExpressionFactoryImpl;
+import org.apache.struts2.jasper.Constants;
+import org.apache.struts2.jasper.JasperException;
+import org.apache.struts2.jasper.JspCompilationContext;
+import org.apache.struts2.jasper.el.ExpressionEvaluatorImpl;
+import org.xml.sax.Attributes;
+
+/** 
+ * This class has all the utility method(s).
+ * Ideally should move all the bean containers here.
+ *
+ * @author Mandar Raje.
+ * @author Rajiv Mordani.
+ * @author Danno Ferrin
+ * @author Pierre Delisle
+ * @author Shawn Bayern
+ * @author Mark Roth
+ */
+public class JspUtil {
+
+    private static final String WEB_INF_TAGS = "/WEB-INF/tags/";
+    private static final String META_INF_TAGS = "/META-INF/tags/";
+
+    // Delimiters for request-time expressions (JSP and XML syntax)
+    private static final String OPEN_EXPR  = "<%=";
+    private static final String CLOSE_EXPR = "%>";
+    private static final String OPEN_EXPR_XML  = "%=";
+    private static final String CLOSE_EXPR_XML = "%";
+
+    private static int tempSequenceNumber = 0;
+    
+    //private static ExpressionEvaluatorImpl expressionEvaluator
+    //= new ExpressionEvaluatorImpl();
+    
+    //tc6
+    private final static ExpressionEvaluator expressionEvaluator =
+        new ExpressionEvaluatorImpl(new ExpressionFactoryImpl());
+
+    private static final String javaKeywords[] = {
+        "abstract", "assert", "boolean", "break", "byte", "case",
+        "catch", "char", "class", "const", "continue",
+        "default", "do", "double", "else", "enum", "extends",
+        "final", "finally", "float", "for", "goto",
+        "if", "implements", "import", "instanceof", "int",
+        "interface", "long", "native", "new", "package",
+        "private", "protected", "public", "return", "short",
+        "static", "strictfp", "super", "switch", "synchronized",
+        "this", "throws", "transient", "try", "void",
+        "volatile", "while" };
+
+    public static final int CHUNKSIZE = 1024;
+        
+    public static char[] removeQuotes(char []chars) {
+        CharArrayWriter caw = new CharArrayWriter();
+        for (int i = 0; i < chars.length; i++) {
+            if (chars[i] == '%' && chars[i+1] == '\\' &&
+                chars[i+2] == '>') {
+                caw.write('%');
+                caw.write('>');
+                i = i + 2;
+            } else {
+                caw.write(chars[i]);
+            }
+        }
+        return caw.toCharArray();
+    }
+
+    public static char[] escapeQuotes (char []chars) {
+        // Prescan to convert %\> to %>
+        String s = new String(chars);
+        while (true) {
+            int n = s.indexOf("%\\>");
+            if (n < 0)
+                break;
+            StringBuffer sb = new StringBuffer(s.substring(0, n));
+            sb.append("%>");
+            sb.append(s.substring(n + 3));
+            s = sb.toString();
+        }
+        chars = s.toCharArray();
+        return (chars);
+
+
+        // Escape all backslashes not inside a Java string literal
+        /*
+        CharArrayWriter caw = new CharArrayWriter();
+        boolean inJavaString = false;
+        for (int i = 0; i < chars.length; i++) {
+            if (chars[i] == '"') inJavaString = !inJavaString;
+            // escape out the escape character
+            if (!inJavaString && (chars[i] == '\\')) caw.write('\\');
+            caw.write(chars[i]);
+        }
+        return caw.toCharArray();
+        */
+    }
+
+    /**
+     * Checks if the token is a runtime expression.
+     * In standard JSP syntax, a runtime expression starts with '<%' and
+     * ends with '%>'. When the JSP document is in XML syntax, a runtime
+     * expression starts with '%=' and ends with '%'.
+     *
+     * @param token The token to be checked
+     * return whether the token is a runtime expression or not.
+     */
+    public static boolean isExpression(String token, boolean isXml) {
+    String openExpr;
+    String closeExpr;
+    if (isXml) {
+        openExpr = OPEN_EXPR_XML;
+        closeExpr = CLOSE_EXPR_XML;
+    } else {
+        openExpr = OPEN_EXPR;
+        closeExpr = CLOSE_EXPR;
+    }
+    if (token.startsWith(openExpr) && token.endsWith(closeExpr)) {
+        return true;
+    } else {
+        return false;
+    }
+    }
+
+    /**
+     * @return the "expression" part of a runtime expression, 
+     * taking the delimiters out.
+     */
+    public static String getExpr (String expression, boolean isXml) {
+    String returnString;
+    String openExpr;
+    String closeExpr;
+    if (isXml) {
+        openExpr = OPEN_EXPR_XML;
+        closeExpr = CLOSE_EXPR_XML;
+    } else {
+        openExpr = OPEN_EXPR;
+        closeExpr = CLOSE_EXPR;
+    }
+    int length = expression.length();
+    if (expression.startsWith(openExpr) && 
+                expression.endsWith(closeExpr)) {
+        returnString = expression.substring(
+                               openExpr.length(), length - closeExpr.length());
+    } else {
+        returnString = "";
+    }
+    return returnString;
+    }
+
+    /**
+     * Takes a potential expression and converts it into XML form
+     */
+    public static String getExprInXml(String expression) {
+        String returnString;
+        int length = expression.length();
+
+        if (expression.startsWith(OPEN_EXPR) 
+                && expression.endsWith(CLOSE_EXPR)) {
+            returnString = expression.substring (1, length - 1);
+        } else {
+            returnString = expression;
+        }
+
+        return escapeXml(returnString.replace(Constants.ESC, '$'));
+    }
+
+    /**
+     * Checks to see if the given scope is valid.
+     *
+     * @param scope The scope to be checked
+     * @param n The Node containing the 'scope' attribute whose value is to be
+     * checked
+     * @param err error dispatcher
+     *
+     * @throws JasperException if scope is not null and different from
+     * &quot;page&quot;, &quot;request&quot;, &quot;session&quot;, and
+     * &quot;application&quot;
+     */
+    public static void checkScope(String scope, Node n, ErrorDispatcher err)
+            throws JasperException {
+    if (scope != null && !scope.equals("page") && !scope.equals("request")
+        && !scope.equals("session") && !scope.equals("application")) {
+        err.jspError(n, "jsp.error.invalid.scope", scope);
+    }
+    }
+
+    /**
+     * Checks if all mandatory attributes are present and if all attributes
+     * present have valid names.  Checks attributes specified as XML-style
+     * attributes as well as attributes specified using the jsp:attribute
+     * standard action. 
+     */
+    public static void checkAttributes(String typeOfTag,
+                       Node n,
+                       ValidAttribute[] validAttributes,
+                       ErrorDispatcher err)
+                throws JasperException {
+        Attributes attrs = n.getAttributes();
+        Mark start = n.getStart();
+    boolean valid = true;
+
+        // AttributesImpl.removeAttribute is broken, so we do this...
+        int tempLength = (attrs == null) ? 0 : attrs.getLength();
+    Vector temp = new Vector(tempLength, 1);
+        for (int i = 0; i < tempLength; i++) {
+            String qName = attrs.getQName(i);
+            if ((!qName.equals("xmlns")) && (!qName.startsWith("xmlns:")))
+                temp.addElement(qName);
+        }
+
+        // Add names of attributes specified using jsp:attribute
+        Node.Nodes tagBody = n.getBody();
+        if( tagBody != null ) {
+            int numSubElements = tagBody.size();
+            for( int i = 0; i < numSubElements; i++ ) {
+                Node node = tagBody.getNode( i );
+                if( node instanceof Node.NamedAttribute ) {
+                    String attrName = node.getAttributeValue( "name" );
+                    temp.addElement( attrName );
+            // Check if this value appear in the attribute of the node
+            if (n.getAttributeValue(attrName) != null) {
+            err.jspError(n, "jsp.error.duplicate.name.jspattribute",
+                    attrName);
+            }
+                }
+                else {
+                    // Nothing can come before jsp:attribute, and only
+                    // jsp:body can come after it.
+                    break;
+                }
+            }
+        }
+
+    /*
+     * 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++) {
+        int attrPos;    
+        if (validAttributes[i].mandatory) {
+                attrPos = temp.indexOf(validAttributes[i].name);
+        if (attrPos != -1) {
+            temp.remove(attrPos);
+            valid = true;
+        } else {
+            valid = false;
+            missingAttribute = validAttributes[i].name;
+            break;
+        }
+        }
+    }
+
+    // If mandatory attribute is missing then the exception is thrown
+    if (!valid)
+        err.jspError(start, "jsp.error.mandatory.attribute", typeOfTag,
+             missingAttribute);
+
+    // Check to see if there are any more attributes for the specified tag.
+        int attrLeftLength = temp.size();
+    if (attrLeftLength == 0)
+        return;
+
+    // Now check to see if the rest of the attributes are valid too.
+    String attribute = null;
+
+    for (int j = 0; j < attrLeftLength; j++) {
+        valid = false;
+        attribute = (String) temp.elementAt(j);
+        for (int i = 0; i < validAttributes.length; i++) {
+        if (attribute.equals(validAttributes[i].name)) {
+            valid = true;
+            break;
+        }
+        }
+        if (!valid)
+        err.jspError(start, "jsp.error.invalid.attribute", typeOfTag,
+                 attribute);
+    }
+    // XXX *could* move EL-syntax validation here... (sb)
+    }
+    
+    public static String escapeQueryString(String unescString) {
+    if ( unescString == null )
+        return null;
+    
+    String escString    = "";
+    String shellSpChars = "\\\"";
+    
+    for(int index=0; index<unescString.length(); index++) {
+        char nextChar = unescString.charAt(index);
+        
+        if( shellSpChars.indexOf(nextChar) != -1 )
+        escString += "\\";
+        
+        escString += nextChar;
+    }
+    return escString;
+    }
+ 
+    /**
+     *  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("&quot;");
+            } else {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Replaces any occurrences of the character <tt>replace</tt> with the
+     * string <tt>with</tt>.
+     */
+    public static String replace(String name, char replace, String with) {
+    StringBuffer buf = new StringBuffer();
+    int begin = 0;
+    int end;
+    int last = name.length();
+
+    while (true) {
+        end = name.indexOf(replace, begin);
+        if (end < 0) {
+        end = last;
+        }
+        buf.append(name.substring(begin, end));
+        if (end == last) {
+        break;
+        }
+        buf.append(with);
+        begin = end + 1;
+    }
+    
+    return buf.toString();
+    }
+
+    public static class ValidAttribute {
+    String name;
+    boolean mandatory;
+    boolean rtexprvalue;    // not used now
+
+    public ValidAttribute (String name, boolean mandatory,
+            boolean rtexprvalue )
+        {
+        this.name = name;
+        this.mandatory = mandatory;
+            this.rtexprvalue = rtexprvalue;
+        }
+
+       public ValidAttribute (String name, boolean mandatory) {
+            this( name, mandatory, false );
+    }
+
+    public ValidAttribute (String name) {
+        this (name, false);
+    }
+    }
+    
+    /**
+     * Convert a String value to 'boolean'.
+     * Besides the standard conversions done by
+     * Boolean.valueOf(s).booleanValue(), the value "yes"
+     * (ignore case) is also converted to 'true'. 
+     * If 's' is null, then 'false' is returned.
+     *
+     * @param s the string to be converted
+     * @return the boolean value associated with the string s
+     */
+    public static boolean booleanValue(String s) {
+    boolean b = false;
+    if (s != null) {
+        if (s.equalsIgnoreCase("yes")) {
+        b = true;
+        } else {
+        b = Boolean.valueOf(s).booleanValue();
+        }
+    }
+    return b;
+    }
+
+    /**
+     * Returns the <tt>Class</tt> object associated with the class or
+     * interface with the given string name.
+     *
+     * <p> The <tt>Class</tt> object is determined by passing the given string
+     * name to the <tt>Class.forName()</tt> method, unless the given string
+     * name represents a primitive type, in which case it is converted to a
+     * <tt>Class</tt> object by appending ".class" to it (e.g., "int.class").
+     */
+    public static Class toClass(String type, ClassLoader loader)
+        throws ClassNotFoundException {
+
+    Class c = null;
+    int i0 = type.indexOf('[');
+    int dims = 0;
+    if (i0 > 0) {
+        // This is an array.  Count the dimensions
+        for (int i = 0; i < type.length(); i++) {
+        if (type.charAt(i) == '[')
+            dims++;
+        }
+        type = type.substring(0, i0);
+    }
+
+    if ("boolean".equals(type))
+        c = boolean.class;
+    else if ("char".equals(type))
+        c = char.class;
+    else if ("byte".equals(type))
+        c =  byte.class;
+    else if ("short".equals(type))
+        c = short.class;
+    else if ("int".equals(type))
+        c = int.class;
+    else if ("long".equals(type))
+        c = long.class;
+    else if ("float".equals(type))
+        c = float.class;
+    else if ("double".equals(type))
+        c = double.class;
+    else if (type.indexOf('[') < 0)
+        c = loader.loadClass(type);
+
+    if (dims == 0)
+        return c;
+
+    if (dims == 1)
+        return java.lang.reflect.Array.newInstance(c, 1).getClass();
+
+    // Array of more than i dimension
+    return java.lang.reflect.Array.newInstance(c, new int[dims]).getClass();
+    }
+    
+    /**
+     * Produces a String representing a call to the EL interpreter.
+     * @param expression a String containing zero or more "${}" expressions
+     * @param expectedType the expected type of the interpreted result
+     * @param fnmapvar Variable pointing to a function map.
+     * @param XmlEscape True if the result should do XML escaping
+     * @return a String representing a call to the EL interpreter.
+     */
+    public static String interpreterCall(boolean isTagFile,
+                     String expression,
+                                         Class expectedType,
+                                         String fnmapvar,
+                                         boolean XmlEscape ) 
+    {
+        /*
+         * Determine which context object to use.
+         */
+    String jspCtxt = null;
+    if (isTagFile)
+        jspCtxt = "this.getJspContext()";
+    else
+        jspCtxt = "_jspx_page_context";
+
+    /*
+         * Determine whether to use the expected type's textual name
+     * or, if it's a primitive, the name of its correspondent boxed
+     * type.
+         */
+    String targetType = expectedType.getName();
+    String primitiveConverterMethod = null;
+    if (expectedType.isPrimitive()) {
+        if (expectedType.equals(Boolean.TYPE)) {
+        targetType = Boolean.class.getName();
+        primitiveConverterMethod = "booleanValue";
+        } else if (expectedType.equals(Byte.TYPE)) {
+        targetType = Byte.class.getName();
+        primitiveConverterMethod = "byteValue";
+        } else if (expectedType.equals(Character.TYPE)) {
+        targetType = Character.class.getName();
+        primitiveConverterMethod = "charValue";
+        } else if (expectedType.equals(Short.TYPE)) {
+        targetType = Short.class.getName();
+        primitiveConverterMethod = "shortValue";
+        } else if (expectedType.equals(Integer.TYPE)) {
+        targetType = Integer.class.getName();
+        primitiveConverterMethod = "intValue";
+        } else if (expectedType.equals(Long.TYPE)) {
+        targetType = Long.class.getName();
+        primitiveConverterMethod = "longValue";
+        } else if (expectedType.equals(Float.TYPE)) {
+        targetType = Float.class.getName();
+        primitiveConverterMethod = "floatValue";
+        } else if (expectedType.equals(Double.TYPE)) { 
+        targetType = Double.class.getName();
+        primitiveConverterMethod = "doubleValue";
+        }
+    }
+ 
+    if (primitiveConverterMethod != null) {
+        XmlEscape = false;
+    }
+
+    /*
+         * Build up the base call to the interpreter.
+         */
+        // XXX - We use a proprietary call to the interpreter for now
+        // as the current standard machinery is inefficient and requires
+        // lots of wrappers and adapters.  This should all clear up once
+        // the EL interpreter moves out of JSTL and into its own project.
+        // In the future, this should be replaced by code that calls
+        // ExpressionEvaluator.parseExpression() and then cache the resulting
+        // expression objects.  The interpreterCall would simply select
+        // one of the pre-cached expressions and evaluate it.
+        // Note that PageContextImpl implements VariableResolver and
+        // the generated Servlet/SimpleTag implements FunctionMapper, so
+        // that machinery is already in place (mroth).
+    targetType = toJavaSourceType(targetType);
+    StringBuffer call = new StringBuffer(
+             "(" + targetType + ") "
+               + 
"org.apache.struts2.jasper.runtime.PageContextImpl.proprietaryEvaluate"
+               + "(" + Generator.quote(expression) + ", "
+               +       targetType + ".class, "
+           +       "(PageContext)" + jspCtxt 
+               +       ", " + fnmapvar
+           + ", " + XmlEscape
+               + ")");
+ 
+    /*
+         * Add the primitive converter method if we need to.
+         */
+    if (primitiveConverterMethod != null) {
+        call.insert(0, "(");
+        call.append(")." + primitiveConverterMethod + "()");
+    }
+ 
+    return call.toString();
+    }
+
+    /**
+     * Validates the syntax of all ${} expressions within the given string.
+     * @param where the approximate location of the expressions in the JSP page
+     * @param expressions a string containing zero or more "${}" expressions
+     * @param err an error dispatcher to use
+     * @deprecated now delegated to the org.apache.el Package
+     */
+    public static void validateExpressions(Mark where,
+                                           String expressions,
+                                           Class expectedType,
+                                           FunctionMapper functionMapper,
+                                           ErrorDispatcher err)
+            throws JasperException {
+
+//        try {
+//            
+//            JspUtil.expressionEvaluator.parseExpression( expressions, 
+//                expectedType, functionMapper );
+//        }
+//        catch( ELParseException e ) {
+//            err.jspError(where, "jsp.error.invalid.expression", expressions,
+//                e.toString() );
+//        }
+//        catch( ELException e ) {
+//            err.jspError(where, "jsp.error.invalid.expression", expressions,
+//                e.toString() );
+//        }
+    }
+
+    /**
+     * Resets the temporary variable name.
+     * (not thread-safe)
+     * @deprecated
+     */
+    public static void resetTemporaryVariableName() {
+        tempSequenceNumber = 0;
+    }
+
+    /**
+     * Generates a new temporary variable name.
+     * (not thread-safe)
+     * @deprecated
+     */
+    public static String nextTemporaryVariableName() {
+        return Constants.TEMP_VARIABLE_NAME_PREFIX + (tempSequenceNumber++);
+    }
+
+    public static String coerceToPrimitiveBoolean(String s,
+                          boolean isNamedAttribute) {
+    if (isNamedAttribute) {
+        return 
"org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerceToBoolean(" + s + 
")";
+    } else {
+        if (s == null || s.length() == 0)
+        return "false";
+        else
+        return Boolean.valueOf(s).toString();
+    }
+    }
+
+    public static String coerceToBoolean(String s, boolean isNamedAttribute) {
+    if (isNamedAttribute) {
+        return "(Boolean) 
org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", 
Boolean.class)";
+    } else {
+        if (s == null || s.length() == 0) {
+        return "new Boolean(false)";
+        } else {
+        // Detect format error at translation time
+        return "new Boolean(" + Boolean.valueOf(s).toString() + ")";
+        }
+    }
+    }
+
+    public static String coerceToPrimitiveByte(String s,
+                           boolean isNamedAttribute) {
+    if (isNamedAttribute) {
+        return 
"org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerceToByte(" + s + ")";
+    } else {
+        if (s == null || s.length() == 0)
+        return "(byte) 0";
+        else
+        return "((byte)" + Byte.valueOf(s).toString() + ")";
+    }
+    }
+
+    public static String coerceToByte(String s, boolean isNamedAttribute) {
+    if (isNamedAttribute) {
+        return "(Byte) 
org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", 
Byte.class)";
+    } else {
+        if (s == null || s.length() == 0) {
+        return "new Byte((byte) 0)";
+        } else {
+        // Detect format error at translation time
+        return "new Byte((byte)" + Byte.valueOf(s).toString() + ")";
+        }
+    }
+    }
+
+    public static String coerceToChar(String s, boolean isNamedAttribute) {
+    if (isNamedAttribute) {
+        return 
"org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerceToChar(" + s + ")";
+    } else {
+        if (s == null || s.length() == 0) {
+        return "(char) 0";
+        } else {
+        char ch = s.charAt(0);
+        // this trick avoids escaping issues
+        return "((char) " + (int) ch + ")";
+        }
+    }
+    }
+
+    public static String coerceToCharacter(String s, boolean isNamedAttribute) 
{
+    if (isNamedAttribute) {
+        return "(Character) 
org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", 
Character.class)";
+    } else {
+        if (s == null || s.length() == 0) {
+        return "new Character((char) 0)";
+        } else {
+        char ch = s.charAt(0);
+        // this trick avoids escaping issues
+        return "new Character((char) " + (int) ch + ")";
+        }
+    }
+    }
+
+    public static String coerceToPrimitiveDouble(String s,
+                         boolean isNamedAttribute) {
+    if (isNamedAttribute) {
+        return 
"org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerceToDouble(" + s + ")";
+    } else {
+        if (s == null || s.length() == 0)
+        return "(double) 0";
+        else
+        return Double.valueOf(s).toString();
+    }
+    }
+
+    public static String coerceToDouble(String s, boolean isNamedAttribute) {
+    if (isNamedAttribute) {
+        return "(Double) 
org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", 
Double.class)";
+    } else {
+        if (s == null || s.length() == 0) {
+        return "new Double(0)";
+        } else {
+        // Detect format error at translation time
+        return "new Double(" + Double.valueOf(s).toString() + ")";
+        }
+    }
+    }
+
+    public static String coerceToPrimitiveFloat(String s,
+                        boolean isNamedAttribute) {
+    if (isNamedAttribute) {
+        return 
"org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerceToFloat(" + s + ")";
+    } else {
+        if (s == null || s.length() == 0)
+        return "(float) 0";
+        else
+        return Float.valueOf(s).toString() + "f";
+    }
+    }
+
+    public static String coerceToFloat(String s, boolean isNamedAttribute) {
+    if (isNamedAttribute) {
+        return "(Float) 
org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", 
Float.class)";
+    } else {
+        if (s == null || s.length() == 0) {
+        return "new Float(0)";
+        } else {
+        // Detect format error at translation time
+        return "new Float(" + Float.valueOf(s).toString() + "f)";
+        }
+    }
+    }
+
+    public static String coerceToInt(String s, boolean isNamedAttribute) {
+    if (isNamedAttribute) {
+        return 
"org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerceToInt(" + s + ")";
+    } else {
+        if (s == null || s.length() == 0)
+        return "0";
+        else
+        return Integer.valueOf(s).toString();
+    }
+    }
+
+    public static String coerceToInteger(String s, boolean isNamedAttribute) {
+    if (isNamedAttribute) {
+        return "(Integer) 
org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", 
Integer.class)";
+    } else {
+        if (s == null || s.length() == 0) {
+        return "new Integer(0)";
+        } else {
+        // Detect format error at translation time
+        return "new Integer(" + Integer.valueOf(s).toString() + ")";
+        }
+    }
+    }
+
+    public static String coerceToPrimitiveShort(String s,
+                        boolean isNamedAttribute) {
+    if (isNamedAttribute) {
+        return 
"org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerceToShort(" + s + ")";
+    } else {
+        if (s == null || s.length() == 0)
+        return "(short) 0";
+        else
+        return "((short) " + Short.valueOf(s).toString() + ")";
+    }
+    }
+    
+    public static String coerceToShort(String s, boolean isNamedAttribute) {
+    if (isNamedAttribute) {
+        return "(Short) 
org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", 
Short.class)";
+    } else {
+        if (s == null || s.length() == 0) {
+        return "new Short((short) 0)";
+        } else {
+        // Detect format error at translation time
+        return "new Short(\"" + Short.valueOf(s).toString() + "\")";
+        }
+    }
+    }
+    
+    public static String coerceToPrimitiveLong(String s,
+                           boolean isNamedAttribute) {
+    if (isNamedAttribute) {
+        return 
"org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerceToLong(" + s + ")";
+    } else {
+        if (s == null || s.length() == 0)
+        return "(long) 0";
+        else
+        return Long.valueOf(s).toString() + "l";
+    }
+    }
+
+    public static String coerceToLong(String s, boolean isNamedAttribute) {
+    if (isNamedAttribute) {
+        return "(Long) 
org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", 
Long.class)";
+    } else {
+        if (s == null || s.length() == 0) {
+        return "new Long(0)";
+        } else {
+        // Detect format error at translation time
+        return "new Long(" + Long.valueOf(s).toString() + "l)";
+        }
+    }
+    }
+
+    public static InputStream getInputStream(String fname, JarFile jarFile,
+                                             JspCompilationContext ctxt,
+                                             ErrorDispatcher err)
+            throws JasperException, IOException {
+
+        InputStream in = null;
+
+
+        if (jarFile != null) {
+            String jarEntryName = fname.substring(1, fname.length());
+            ZipEntry jarEntry = jarFile.getEntry(jarEntryName);
+            if (jarEntry == null) {
+                err.jspError("jsp.error.file.not.found", fname);
+            }
+            in = jarFile.getInputStream(jarEntry);
+        } else {
+            in = ctxt.getResourceAsStream(fname);
+        }
+
+        if (in == null) {
+            err.jspError("jsp.error.file.not.found", fname);
+        }
+
+        return in;
+    }
+
+    /**
+     * Gets the fully-qualified class name of the tag handler corresponding to
+     * the given tag file path.
+     *
+     * @param path Tag file path
+     * @param err Error dispatcher
+     *
+     * @return Fully-qualified class name of the tag handler corresponding to 
+     * the given tag file path
+     * 
+     * @deprecated Use {...@link #getTagHandlerClassName(String, String,
+     *             ErrorDispatcher)
+     *             See https://issues.apache.org/bugzilla/show_bug.cgi?id=46471
+     */
+    public static String getTagHandlerClassName(String path,
+                        ErrorDispatcher err)
+                throws JasperException {
+        return getTagHandlerClassName(path, null, err);
+    }
+    
+    /**
+     * Gets the fully-qualified class name of the tag handler corresponding to
+     * the given tag file path.
+     * 
+     * @param path
+     *            Tag file path
+     * @param err
+     *            Error dispatcher
+     * 
+     * @return Fully-qualified class name of the tag handler corresponding to
+     *         the given tag file path
+     */
+    public static String getTagHandlerClassName(String path, String urn,
+            ErrorDispatcher err) throws JasperException {
+
+        String className = null;
+        int begin = 0;
+        int index;
+        
+        index = path.lastIndexOf(".tag");
+        if (index == -1) {
+            err.jspError("jsp.error.tagfile.badSuffix", path);
+        }
+
+        //It's tempting to remove the ".tag" suffix here, but we can't.
+        //If we remove it, the fully-qualified class name of this tag
+        //could conflict with the package name of other tags.
+        //For instance, the tag file
+        //    /WEB-INF/tags/foo.tag
+        //would have fully-qualified class name
+        //    org.apache.jsp.tag.web.foo
+        //which would conflict with the package name of the tag file
+        //    /WEB-INF/tags/foo/bar.tag
+
+        index = path.indexOf(WEB_INF_TAGS);
+        if (index != -1) {
+            className = "org.apache.jsp.tag.web.";
+            begin = index + WEB_INF_TAGS.length();
+        } else {
+            index = path.indexOf(META_INF_TAGS);
+            if (index != -1) {
+                className = getClassNameBase(urn);
+                begin = index + META_INF_TAGS.length();
+            } else {
+                err.jspError("jsp.error.tagfile.illegalPath", path);
+            }
+        }
+
+        className += makeJavaPackage(path.substring(begin));
+  
+       return className;
+    }
+
+    private static String getClassNameBase(String urn) {
+        StringBuffer base = new StringBuffer("org.apache.jsp.tag.meta.");
+        if (urn != null) {
+            base.append(makeJavaPackage(urn));
+            base.append('.');
+        }
+        return base.toString();
+    }
+
+    /**
+     * Converts the given path to a Java package or fully-qualified class name
+     *
+     * @param path Path to convert
+     *
+     * @return Java package corresponding to the given path
+     */
+    public static final String makeJavaPackage(String path) {
+        String classNameComponents[] = split(path,"/");
+        StringBuffer legalClassNames = new StringBuffer();
+        for (int i = 0; i < classNameComponents.length; i++) {
+            legalClassNames.append(makeJavaIdentifier(classNameComponents[i]));
+            if (i < classNameComponents.length - 1) {
+                legalClassNames.append('.');
+            }
+        }
+        return legalClassNames.toString();
+    }
+
+    /**
+     * Splits a string into it's components.
+     * @param path String to split
+     * @param pat Pattern to split at
+     * @return the components of the path
+     */
+    private static final String [] split(String path, String pat) {
+        Vector comps = new Vector();
+        int pos = path.indexOf(pat);
+        int start = 0;
+        while( pos >= 0 ) {
+            if(pos > start ) {
+                String comp = path.substring(start,pos);
+                comps.add(comp);
+            }
+            start = pos + pat.length();
+            pos = path.indexOf(pat,start);
+        }
+        if( start < path.length()) {
+            comps.add(path.substring(start));
+        }
+        String [] result = new String[comps.size()];
+        for(int i=0; i < comps.size(); i++) {
+            result[i] = (String)comps.elementAt(i);
+        }
+        return result;
+    }
+            
+    /**
+     * Converts the given identifier to a legal Java identifier
+     *
+     * @param identifier Identifier to convert
+     *
+     * @return Legal Java identifier corresponding to the given identifier
+     */
+    public static final String makeJavaIdentifier(String identifier) {
+        StringBuffer modifiedIdentifier = 
+            new StringBuffer(identifier.length());
+        if (!Character.isJavaIdentifierStart(identifier.charAt(0))) {
+            modifiedIdentifier.append('_');
+        }
+        for (int i = 0; i < identifier.length(); i++) {
+            char ch = identifier.charAt(i);
+            if (Character.isJavaIdentifierPart(ch) && ch != '_') {
+                modifiedIdentifier.append(ch);
+            } else if (ch == '.') {
+                modifiedIdentifier.append('_');
+            } else {
+                modifiedIdentifier.append(mangleChar(ch));
+            }
+        }
+        if (isJavaKeyword(modifiedIdentifier.toString())) {
+            modifiedIdentifier.append('_');
+        }
+        return modifiedIdentifier.toString();
+    }
+    
+    /**
+     * Mangle the specified character to create a legal Java class name.
+     */
+    public static final String mangleChar(char ch) {
+        char[] result = new char[5];
+        result[0] = '_';
+        result[1] = Character.forDigit((ch >> 12) & 0xf, 16);
+        result[2] = Character.forDigit((ch >> 8) & 0xf, 16);
+        result[3] = Character.forDigit((ch >> 4) & 0xf, 16);
+        result[4] = Character.forDigit(ch & 0xf, 16);
+        return new String(result);
+    }
+
+    /**
+     * Test whether the argument is a Java keyword
+     */
+    public static boolean isJavaKeyword(String key) {
+        int i = 0;
+        int j = javaKeywords.length;
+        while (i < j) {
+            int k = (i+j)/2;
+            int result = javaKeywords[k].compareTo(key);
+            if (result == 0) {
+                return true;
+            }
+            if (result < 0) {
+                i = k+1;
+            } else {
+                j = k;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Converts the given Xml name to a legal Java identifier.  This is
+     * slightly more efficient than makeJavaIdentifier in that we only need
+     * to worry about '.', '-', and ':' in the string.  We also assume that
+     * the resultant string is further concatenated with some prefix string
+     * so that we don't have to worry about it being a Java key word.
+     *
+     * @param name Identifier to convert
+     *
+     * @return Legal Java identifier corresponding to the given identifier
+     */
+    public static final String makeXmlJavaIdentifier(String name) {
+        if (name.indexOf('-') >= 0)
+            name = replace(name, '-', "$1");
+        if (name.indexOf('.') >= 0)
+            name = replace(name, '.', "$2");
+        if (name.indexOf(':') >= 0)
+            name = replace(name, ':', "$3");
+        return name;
+    }
+
+    static InputStreamReader getReader(String fname, String encoding,
+            JarFile jarFile,
+            JspCompilationContext ctxt,
+            ErrorDispatcher err)
+    throws JasperException, IOException {
+
+        return getReader(fname, encoding, jarFile, ctxt, err, 0);
+    }
+
+    static InputStreamReader getReader(String fname, String encoding,
+            JarFile jarFile,
+            JspCompilationContext ctxt,
+            ErrorDispatcher err, int skip)
+    throws JasperException, IOException {
+
+        InputStreamReader reader = null;
+        InputStream in = getInputStream(fname, jarFile, ctxt, err);
+        for (int i = 0; i < skip; i++) {
+            in.read();
+        }
+        try {
+            reader = new InputStreamReader(in, encoding);
+        } catch (UnsupportedEncodingException ex) {
+            err.jspError("jsp.error.unsupported.encoding", encoding);
+        }
+
+        return reader;
+    }
+    
+    /**
+     * Handles taking input from TLDs
+     * 'java.lang.Object' -> 'java.lang.Object.class'
+     * 'int' -> 'int.class'
+     * 'void' -> 'Void.TYPE'
+     * 'int[]' -> 'int[].class'
+     * 
+     * @param type
+     * @return
+     */
+    public static String toJavaSourceTypeFromTld(String type) {
+        if (type == null || "void".equals(type)) {
+            return "Void.TYPE";
+        }
+        return type + ".class";
+    }
+
+    /**
+     * Class.getName() return arrays in the form "[[[<et>", where et,
+     * the element type can be one of ZBCDFIJS or L<classname>;
+     * It is converted into forms that can be understood by javac.
+     */
+    public static String toJavaSourceType(String type) {
+
+    if (type.charAt(0) != '[') {
+        return type;
+    }
+
+    int dims = 1;
+    String t = null;
+    for (int i = 1; i < type.length(); i++) {
+        if (type.charAt(i) == '[') {
+        dims++;
+        } else {
+        switch (type.charAt(i)) {
+        case 'Z': t = "boolean"; break;
+        case 'B': t = "byte"; break;
+        case 'C': t = "char"; break;
+        case 'D': t = "double"; break;
+        case 'F': t = "float"; break;
+        case 'I': t = "int"; break;
+        case 'J': t = "long"; break;
+        case 'S': t = "short"; break;
+        case 'L': t = type.substring(i+1, type.indexOf(';')); break;
+        }
+        break;
+        }
+    }
+    StringBuffer resultType = new StringBuffer(t);
+    for (; dims > 0; dims--) {
+        resultType.append("[]");
+    }
+    return resultType.toString();
+    }
+
+    /**
+     * Compute the canonical name from a Class instance.  Note that a
+     * simple replacment of '$' with '.' of a binary name would not work,
+     * as '$' is a legal Java Identifier character.
+     * @param c A instance of java.lang.Class
+     * @return  The canonical name of c.
+     */
+    public static String getCanonicalName(Class c) {
+
+        String binaryName = c.getName();
+        c = c.getDeclaringClass();
+
+        if (c == null) {
+            return binaryName;
+        }
+
+        StringBuffer buf = new StringBuffer(binaryName);
+        do {
+            buf.setCharAt(c.getName().length(), '.');
+            c = c.getDeclaringClass();
+        } while ( c != null);
+
+        return buf.toString();
+    }
+}

Added: 
struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/Localizer.java
URL: 
http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/Localizer.java?rev=819444&view=auto
==============================================================================
--- 
struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/Localizer.java
 (added)
+++ 
struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/Localizer.java
 Mon Sep 28 01:55:26 2009
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.struts2.jasper.compiler;
+
+import java.text.MessageFormat;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * Class responsible for converting error codes to corresponding localized
+ * error messages.
+ *
+ * @author Jan Luehe
+ */
+public class Localizer {
+
+    private static ResourceBundle bundle = null;
+    
+    static {
+        try {
+        bundle = ResourceBundle.getBundle(
+            "org.apache.struts2.jasper.resources.LocalStrings");
+        } catch (Throwable t) {
+            t.printStackTrace();
+        }
+    }
+
+    /*
+     * Returns the localized error message corresponding to the given error
+     * code.
+     *
+     * If the given error code is not defined in the resource bundle for
+     * localized error messages, it is used as the error message.
+     *
+     * @param errCode Error code to localize
+     * 
+     * @return Localized error message
+     */
+    public static String getMessage(String errCode) {
+       String errMsg = errCode;
+       try {
+           errMsg = bundle.getString(errCode);
+       } catch (MissingResourceException e) {
+       }
+       return errMsg;
+    }
+
+    /* 
+     * Returns the localized error message corresponding to the given error
+     * code.
+     *
+     * If the given error code is not defined in the resource bundle for
+     * localized error messages, it is used as the error message.
+     *
+     * @param errCode Error code to localize
+     * @param arg Argument for parametric replacement
+     *
+     * @return Localized error message
+     */
+    public static String getMessage(String errCode, String arg) {
+       return getMessage(errCode, new Object[] {arg});
+    }
+
+    /* 
+     * Returns the localized error message corresponding to the given error
+     * code.
+     *
+     * If the given error code is not defined in the resource bundle for
+     * localized error messages, it is used as the error message.
+     *
+     * @param errCode Error code to localize
+     * @param arg1 First argument for parametric replacement
+     * @param arg2 Second argument for parametric replacement
+     *
+     * @return Localized error message
+     */
+    public static String getMessage(String errCode, String arg1, String arg2) {
+       return getMessage(errCode, new Object[] {arg1, arg2});
+    }
+    
+    /* 
+     * Returns the localized error message corresponding to the given error
+     * code.
+     *
+     * If the given error code is not defined in the resource bundle for
+     * localized error messages, it is used as the error message.
+     *
+     * @param errCode Error code to localize
+     * @param arg1 First argument for parametric replacement
+     * @param arg2 Second argument for parametric replacement
+     * @param arg3 Third argument for parametric replacement
+     *
+     * @return Localized error message
+     */
+    public static String getMessage(String errCode, String arg1, String arg2,
+                                   String arg3) {
+       return getMessage(errCode, new Object[] {arg1, arg2, arg3});
+    }
+
+    /* 
+     * Returns the localized error message corresponding to the given error
+     * code.
+     *
+     * If the given error code is not defined in the resource bundle for
+     * localized error messages, it is used as the error message.
+     *
+     * @param errCode Error code to localize
+     * @param arg1 First argument for parametric replacement
+     * @param arg2 Second argument for parametric replacement
+     * @param arg3 Third argument for parametric replacement
+     * @param arg4 Fourth argument for parametric replacement
+     *
+     * @return Localized error message
+     */
+    public static String getMessage(String errCode, String arg1, String arg2,
+                                   String arg3, String arg4) {
+       return getMessage(errCode, new Object[] {arg1, arg2, arg3, arg4});
+    }
+
+    /*
+     * Returns the localized error message corresponding to the given error
+     * code.
+     *
+     * If the given error code is not defined in the resource bundle for
+     * localized error messages, it is used as the error message.
+     *
+     * @param errCode Error code to localize
+     * @param args Arguments for parametric replacement
+     *
+     * @return Localized error message
+     */
+    public static String getMessage(String errCode, Object[] args) {
+       String errMsg = errCode;
+       try {
+           errMsg = bundle.getString(errCode);
+           if (args != null) {
+               MessageFormat formatter = new MessageFormat(errMsg);
+               errMsg = formatter.format(args);
+           }
+       } catch (MissingResourceException e) {
+       }
+       
+       return errMsg;
+    }
+}

Added: 
struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/Mark.java
URL: 
http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/Mark.java?rev=819444&view=auto
==============================================================================
--- 
struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/Mark.java
 (added)
+++ 
struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/Mark.java
 Mon Sep 28 01:55:26 2009
@@ -0,0 +1,282 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.struts2.jasper.compiler;
+
+import java.util.Stack;
+import java.net.URL;
+import java.net.MalformedURLException;
+import org.apache.struts2.jasper.JspCompilationContext;
+
+/**
+ * Mark represents a point in the JSP input. 
+ *
+ * @author Anil K. Vijendran
+ */
+final class Mark {
+
+    // position within current stream
+    int cursor, line, col;
+
+    // directory of file for current stream
+    String baseDir;
+
+    // current stream
+    char[] stream = null;
+
+    // fileid of current stream
+    private int fileId;
+
+    // name of the current file
+    private String fileName;
+
+    /*
+     * stack of stream and stream state of streams that have included
+     * current stream
+     */
+    private Stack includeStack = null;
+
+    // encoding of current file
+    private String encoding = null;
+
+    // reader that owns this mark (so we can look up fileid's)
+    private JspReader reader;
+
+    private JspCompilationContext ctxt;
+
+    /**
+     * Constructor
+     *
+     * @param reader JspReader this mark belongs to
+     * @param inStream current stream for this mark
+     * @param fileId id of requested jsp file
+     * @param name JSP file name
+     * @param inBaseDir base directory of requested jsp file
+     * @param inEncoding encoding of current file
+     */
+    Mark(JspReader reader, char[] inStream, int fileId, String name,
+         String inBaseDir, String inEncoding) {
+
+        this.reader = reader;
+        this.ctxt = reader.getJspCompilationContext();
+        this.stream = inStream;
+        this.cursor = 0;
+        this.line = 1;
+        this.col = 1;
+        this.fileId = fileId;
+        this.fileName = name;
+        this.baseDir = inBaseDir;
+        this.encoding = inEncoding;
+        this.includeStack = new Stack();
+    }
+
+
+    /**
+     * Constructor
+     */
+    Mark(Mark other) {
+
+        this.reader = other.reader;
+        this.ctxt = other.reader.getJspCompilationContext();
+        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) );
+        }
+    }
+
+
+    /**
+     * Constructor
+     */    
+    Mark(JspCompilationContext ctxt, String filename, int line, int col) {
+
+        this.reader = null;
+        this.ctxt = ctxt;
+        this.stream = null;
+        this.cursor = 0;
+        this.line = line;
+        this.col = col;
+        this.fileId = -1;
+        this.fileName = filename;
+        this.baseDir = "le-basedir";
+        this.encoding = "le-endocing";
+        this.includeStack = null;
+    }
+
+
+    /**
+     * 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 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 = 1;
+        col = 1;
+        fileId = inFileId;
+        fileName = name;
+        baseDir = inBaseDir;
+        encoding = inEncoding;
+        stream = inStream;
+    }
+
+
+    /**
+     * Restores this mark's state to a previously stored stream.
+     * @return The previous Mark instance when the stream was pushed, or null
+     * if there is no previous stream
+     */
+    public Mark popStream() {
+        // make sure we have something to pop
+        if ( includeStack.size() <= 0 ) {
+            return null;
+        }
+
+        // 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 this;
+    }
+
+
+    // -------------------- 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;
+    }
+
+    /**
+     * Gets the URL of the resource with which this Mark is associated
+     *
+     * @return URL of the resource with which this Mark is associated
+     *
+     * @exception MalformedURLException if the resource pathname is incorrect
+     */
+    public URL getURL() throws MalformedURLException {
+        return ctxt.getResource(getFile());
+    }
+
+    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;
+    }
+
+    /**
+     * @return true if this Mark is greather than the <code>other</code>
+     * Mark, false otherwise.
+     */
+    public boolean isGreater(Mark other) {
+
+        boolean greater = false;
+
+        if (this.line > other.line) {
+            greater = true;
+        } else if (this.line == other.line && this.col > other.col) {
+            greater = true;
+        }
+
+        return greater;
+    }
+
+    /**
+     * 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;
+        }
+    }
+
+}
+


Reply via email to