You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by mu...@apache.org on 2009/09/28 02:43:39 UTC

svn commit: r819435 [17/23] - in /struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper: ./ compiler/ compiler/tagplugin/ el/ runtime/ security/ servlet/ tagplugins/ tagplugins/jstl/ tagplugins/jstl/core/ util/ xmlparser/

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/JspFactoryImpl.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/JspFactoryImpl.java?rev=819435&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/JspFactoryImpl.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/JspFactoryImpl.java Mon Sep 28 00:43:34 2009
@@ -0,0 +1,202 @@
+/*
+ * 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.jasper.runtime;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.jsp.JspApplicationContext;
+import javax.servlet.jsp.JspEngineInfo;
+import javax.servlet.jsp.JspFactory;
+import javax.servlet.jsp.PageContext;
+
+import org.apache.jasper.Constants;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
+/**
+ * Implementation of JspFactory.
+ *
+ * @author Anil K. Vijendran
+ */
+public class JspFactoryImpl extends JspFactory {
+
+    // Logger
+    private Log log = LogFactory.getLog(JspFactoryImpl.class);
+
+    private static final String SPEC_VERSION = "2.1";
+    private static final boolean USE_POOL = 
+        Boolean.valueOf(System.getProperty("org.apache.jasper.runtime.JspFactoryImpl.USE_POOL", "true")).booleanValue();
+    private static final int POOL_SIZE = 
+        Integer.valueOf(System.getProperty("org.apache.jasper.runtime.JspFactoryImpl.POOL_SIZE", "8")).intValue();
+
+    private ThreadLocal<PageContextPool> localPool = new ThreadLocal<PageContextPool>();
+
+    public PageContext getPageContext(Servlet servlet, ServletRequest request,
+            ServletResponse response, String errorPageURL, boolean needsSession,
+            int bufferSize, boolean autoflush) {
+
+        if( Constants.IS_SECURITY_ENABLED ) {
+            PrivilegedGetPageContext dp = new PrivilegedGetPageContext(
+                    (JspFactoryImpl)this, servlet, request, response, errorPageURL,
+                    needsSession, bufferSize, autoflush);
+            return (PageContext)AccessController.doPrivileged(dp);
+        } else {
+            return internalGetPageContext(servlet, request, response,
+                    errorPageURL, needsSession,
+                    bufferSize, autoflush);
+        }
+    }
+
+    public void releasePageContext(PageContext pc) {
+        if( pc == null )
+            return;
+        if( Constants.IS_SECURITY_ENABLED ) {
+            PrivilegedReleasePageContext dp = new PrivilegedReleasePageContext(
+                    (JspFactoryImpl)this,pc);
+            AccessController.doPrivileged(dp);
+        } else {
+            internalReleasePageContext(pc);
+        }
+    }
+
+    public JspEngineInfo getEngineInfo() {
+        return new JspEngineInfo() {
+            public String getSpecificationVersion() {
+                return SPEC_VERSION;
+            }
+        };
+    }
+
+    private PageContext internalGetPageContext(Servlet servlet, ServletRequest request,
+            ServletResponse response, String errorPageURL, boolean needsSession,
+            int bufferSize, boolean autoflush) {
+        try {
+            PageContext pc;
+            if (USE_POOL) {
+                PageContextPool pool = localPool.get();
+                if (pool == null) {
+                    pool = new PageContextPool();
+                    localPool.set(pool);
+                }
+                pc = pool.get();
+                if (pc == null) {
+                    pc = new PageContextImpl();
+                }
+            } else {
+                pc = new PageContextImpl();
+            }
+            pc.initialize(servlet, request, response, errorPageURL, 
+                    needsSession, bufferSize, autoflush);
+            return pc;
+        } catch (Throwable ex) {
+            /* FIXME: need to do something reasonable here!! */
+            log.fatal("Exception initializing page context", ex);
+            return null;
+        }
+    }
+
+    private void internalReleasePageContext(PageContext pc) {
+        pc.release();
+        if (USE_POOL && (pc instanceof PageContextImpl)) {
+            localPool.get().put(pc);
+        }
+    }
+
+    private class PrivilegedGetPageContext implements PrivilegedAction {
+
+        private JspFactoryImpl factory;
+        private Servlet servlet;
+        private ServletRequest request;
+        private ServletResponse response;
+        private String errorPageURL;
+        private boolean needsSession;
+        private int bufferSize;
+        private boolean autoflush;
+
+        PrivilegedGetPageContext(JspFactoryImpl factory, Servlet servlet,
+                ServletRequest request, ServletResponse response, String errorPageURL,
+                boolean needsSession, int bufferSize, boolean autoflush) {
+            this.factory = factory;
+            this.servlet = servlet;
+            this.request = request;
+            this.response = response;
+            this.errorPageURL = errorPageURL;
+            this.needsSession = needsSession;
+            this.bufferSize = bufferSize;
+            this.autoflush = autoflush;
+        }
+
+        public Object run() {
+            return factory.internalGetPageContext(servlet, request, response,
+                    errorPageURL, needsSession, bufferSize, autoflush);
+        }
+    }
+
+    private class PrivilegedReleasePageContext implements PrivilegedAction {
+
+        private JspFactoryImpl factory;
+        private PageContext pageContext;
+
+        PrivilegedReleasePageContext(JspFactoryImpl factory,
+                PageContext pageContext) {
+            this.factory = factory;
+            this.pageContext = pageContext;
+        }
+
+        public Object run() {
+            factory.internalReleasePageContext(pageContext);
+            return null;
+        }
+    }
+
+    protected final class PageContextPool  {
+
+        private PageContext[] pool;
+
+        private int current = -1;
+
+        public PageContextPool() {
+            this.pool = new PageContext[POOL_SIZE];
+        }
+
+        public void put(PageContext o) {
+            if (current < (POOL_SIZE - 1)) {
+                current++;
+                pool[current] = o;
+            }
+        }
+
+        public PageContext get() {
+            PageContext item = null;
+            if (current >= 0) {
+                item = pool[current];
+                current--;
+            }
+            return item;
+        }
+
+    }
+
+    public JspApplicationContext getJspApplicationContext(ServletContext context) {
+        return JspApplicationContextImpl.getInstance(context);
+    }
+}

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/JspFragmentHelper.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/JspFragmentHelper.java?rev=819435&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/JspFragmentHelper.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/JspFragmentHelper.java Mon Sep 28 00:43:34 2009
@@ -0,0 +1,65 @@
+/*
+ * 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.jasper.runtime;
+
+import javax.servlet.jsp.JspContext;
+import javax.servlet.jsp.PageContext;
+import javax.servlet.jsp.tagext.JspFragment;
+import javax.servlet.jsp.tagext.JspTag;
+
+/**
+ * Helper class from which all Jsp Fragment helper classes extend.
+ * This class allows for the emulation of numerous fragments within
+ * a single class, which in turn reduces the load on the class loader
+ * since there are potentially many JspFragments in a single page.
+ * <p>
+ * The class also provides various utility methods for JspFragment
+ * implementations.
+ *
+ * @author Mark Roth
+ */
+public abstract class JspFragmentHelper 
+    extends JspFragment 
+{
+    
+    protected int discriminator;
+    protected JspContext jspContext;
+    protected PageContext _jspx_page_context;
+    protected JspTag parentTag;
+
+    public JspFragmentHelper( int discriminator, JspContext jspContext, 
+        JspTag parentTag ) 
+    {
+        this.discriminator = discriminator;
+        this.jspContext = jspContext;
+        this._jspx_page_context = null;
+        if( jspContext instanceof PageContext ) {
+            _jspx_page_context = (PageContext)jspContext;
+        }
+        this.parentTag = parentTag;
+    }
+    
+    public JspContext getJspContext() {
+        return this.jspContext;
+    }
+    
+    public JspTag getParentTag() {
+        return this.parentTag;
+    }
+    
+}

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/JspRuntimeLibrary.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/JspRuntimeLibrary.java?rev=819435&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/JspRuntimeLibrary.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/JspRuntimeLibrary.java Mon Sep 28 00:43:34 2009
@@ -0,0 +1,1047 @@
+/*
+ * 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.jasper.runtime;
+
+import java.beans.PropertyEditor;
+import java.beans.PropertyEditorManager;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.Enumeration;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.PageContext;
+import javax.servlet.jsp.tagext.BodyContent;
+
+import org.apache.jasper.Constants;
+import org.apache.jasper.JasperException;
+import org.apache.jasper.compiler.Localizer;
+
+/**
+ * Bunch of util methods that are used by code generated for useBean,
+ * getProperty and setProperty.  
+ *
+ * The __begin, __end stuff is there so that the JSP engine can
+ * actually parse this file and inline them if people don't want
+ * runtime dependencies on this class. However, I'm not sure if that
+ * works so well right now. It got forgotten at some point. -akv
+ *
+ * @author Mandar Raje
+ * @author Shawn Bayern
+ */
+public class JspRuntimeLibrary {
+    
+    private static final String SERVLET_EXCEPTION
+	= "javax.servlet.error.exception";
+    private static final String JSP_EXCEPTION
+	= "javax.servlet.jsp.jspException";
+
+    protected static class PrivilegedIntrospectHelper
+	implements PrivilegedExceptionAction {
+
+	private Object bean;
+	private String prop;
+	private String value;
+	private ServletRequest request;
+	private String param;
+	private boolean ignoreMethodNF;
+
+        PrivilegedIntrospectHelper(Object bean, String prop,
+                                   String value, ServletRequest request,
+                                   String param, boolean ignoreMethodNF)
+        {
+	    this.bean = bean;
+	    this.prop = prop;
+	    this.value = value;
+            this.request = request;
+	    this.param = param;
+	    this.ignoreMethodNF = ignoreMethodNF;
+        }
+         
+        public Object run() throws JasperException {
+	    internalIntrospecthelper(
+                bean,prop,value,request,param,ignoreMethodNF);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value of the javax.servlet.error.exception request
+     * attribute value, if present, otherwise the value of the
+     * javax.servlet.jsp.jspException request attribute value.
+     *
+     * This method is called at the beginning of the generated servlet code
+     * for a JSP error page, when the "exception" implicit scripting language
+     * variable is initialized.
+     */
+    public static Throwable getThrowable(ServletRequest request) {
+	Throwable error = (Throwable) request.getAttribute(SERVLET_EXCEPTION);
+	if (error == null) {
+	    error = (Throwable) request.getAttribute(JSP_EXCEPTION);
+	    if (error != null) {
+		/*
+		 * The only place that sets JSP_EXCEPTION is
+		 * PageContextImpl.handlePageException(). It really should set
+		 * SERVLET_EXCEPTION, but that would interfere with the 
+		 * ErrorReportValve. Therefore, if JSP_EXCEPTION is set, we
+		 * need to set SERVLET_EXCEPTION.
+		 */
+		request.setAttribute(SERVLET_EXCEPTION, error);
+	    }
+	}
+
+	return error;
+    }
+
+    public static boolean coerceToBoolean(String s) {
+	if (s == null || s.length() == 0)
+	    return false;
+	else
+	    return Boolean.valueOf(s).booleanValue();
+    }
+
+    public static byte coerceToByte(String s) {
+	if (s == null || s.length() == 0)
+	    return (byte) 0;
+	else
+	    return Byte.valueOf(s).byteValue();
+    }
+
+    public static char coerceToChar(String s) {
+	if (s == null || s.length() == 0) {
+	    return (char) 0;
+	} else {
+	    // this trick avoids escaping issues
+	    return (char)(int) s.charAt(0);
+	}
+    }
+
+    public static double coerceToDouble(String s) {
+	if (s == null || s.length() == 0)
+	    return (double) 0;
+	else
+	    return Double.valueOf(s).doubleValue();
+    }
+
+    public static float coerceToFloat(String s) {
+	if (s == null || s.length() == 0)
+	    return (float) 0;
+	else
+	    return Float.valueOf(s).floatValue();
+    }
+
+    public static int coerceToInt(String s) {
+	if (s == null || s.length() == 0)
+	    return 0;
+	else
+	    return Integer.valueOf(s).intValue();
+    }
+
+    public static short coerceToShort(String s) {
+	if (s == null || s.length() == 0)
+	    return (short) 0;
+	else
+	    return Short.valueOf(s).shortValue();
+    }
+
+    public static long coerceToLong(String s) {
+	if (s == null || s.length() == 0)
+	    return (long) 0;
+	else
+	    return Long.valueOf(s).longValue();
+    }
+
+    public static Object coerce(String s, Class target) {
+
+	boolean isNullOrEmpty = (s == null || s.length() == 0);
+
+	if (target == Boolean.class) {
+	    if (isNullOrEmpty) {
+		s = "false";
+	    }
+	    return new Boolean(s);
+	} else if (target == Byte.class) {
+	    if (isNullOrEmpty)
+		return new Byte((byte) 0);
+	    else
+		return new Byte(s);
+	} else if (target == Character.class) {
+	    if (isNullOrEmpty)
+		return new Character((char) 0);
+	    else 
+		return new Character(s.charAt(0));
+	} else if (target == Double.class) {
+	    if (isNullOrEmpty)
+		return new Double(0);
+	    else
+		return new Double(s);
+	} else if (target == Float.class) {
+	    if (isNullOrEmpty)
+		return new Float(0);
+	    else
+		return new Float(s);
+	} else if (target == Integer.class) {
+	    if (isNullOrEmpty)
+		return new Integer(0);
+	    else
+		return new Integer(s);
+	} else if (target == Short.class) {
+	    if (isNullOrEmpty)
+		return new Short((short) 0);
+	    else
+		return new Short(s);
+	} else if (target == Long.class) {
+	    if (isNullOrEmpty)
+		return new Long(0);
+	    else
+		return new Long(s);
+	} else {
+	    return null;
+	}
+    }
+
+   // __begin convertMethod
+    public static Object convert(String propertyName, String s, Class t,
+				 Class propertyEditorClass) 
+       throws JasperException 
+    {
+        try {
+            if (s == null) {
+                if (t.equals(Boolean.class) || t.equals(Boolean.TYPE))
+                    s = "false";
+                else
+                    return null;
+            }
+	    if (propertyEditorClass != null) {
+		return getValueFromBeanInfoPropertyEditor(
+				    t, propertyName, s, propertyEditorClass);
+	    } else if ( t.equals(Boolean.class) || t.equals(Boolean.TYPE) ) {
+                if (s.equalsIgnoreCase("on") || s.equalsIgnoreCase("true"))
+                    s = "true";
+                else
+                    s = "false";
+                return new Boolean(s);
+            } else if ( t.equals(Byte.class) || t.equals(Byte.TYPE) ) {
+                return new Byte(s);
+            } else if (t.equals(Character.class) || t.equals(Character.TYPE)) {
+                return s.length() > 0 ? new Character(s.charAt(0)) : null;
+            } else if ( t.equals(Short.class) || t.equals(Short.TYPE) ) {
+                return new Short(s);
+            } else if ( t.equals(Integer.class) || t.equals(Integer.TYPE) ) {
+                return new Integer(s);
+            } else if ( t.equals(Float.class) || t.equals(Float.TYPE) ) {
+                return new Float(s);
+            } else if ( t.equals(Long.class) || t.equals(Long.TYPE) ) {
+                return new Long(s);
+            } else if ( t.equals(Double.class) || t.equals(Double.TYPE) ) {
+                return new Double(s);
+            } else if ( t.equals(String.class) ) {
+                return s;
+            } else if ( t.equals(java.io.File.class) ) {
+                return new java.io.File(s);
+            } else if (t.getName().equals("java.lang.Object")) {
+                return new Object[] {s};
+	    } else {
+		return getValueFromPropertyEditorManager(
+                                            t, propertyName, s);
+            }
+        } catch (Exception ex) {
+            throw new JasperException(ex);
+        }
+    }
+    // __end convertMethod
+
+    // __begin introspectMethod
+    public static void introspect(Object bean, ServletRequest request)
+                                  throws JasperException
+    {
+	Enumeration e = request.getParameterNames();
+	while ( e.hasMoreElements() ) {
+	    String name  = (String) e.nextElement();
+	    String value = request.getParameter(name);
+	    introspecthelper(bean, name, value, request, name, true);
+	}
+    }
+    // __end introspectMethod
+    
+    // __begin introspecthelperMethod
+    public static void introspecthelper(Object bean, String prop,
+                                        String value, ServletRequest request,
+                                        String param, boolean ignoreMethodNF)
+                                        throws JasperException
+    {
+        if( Constants.IS_SECURITY_ENABLED ) {
+            try {
+                PrivilegedIntrospectHelper dp =
+		    new PrivilegedIntrospectHelper(
+			bean,prop,value,request,param,ignoreMethodNF);
+                AccessController.doPrivileged(dp);
+            } catch( PrivilegedActionException pe) {
+                Exception e = pe.getException();
+                throw (JasperException)e;
+            }
+        } else {
+            internalIntrospecthelper(
+		bean,prop,value,request,param,ignoreMethodNF);
+        }
+    }
+
+    private static void internalIntrospecthelper(Object bean, String prop,
+					String value, ServletRequest request,
+					String param, boolean ignoreMethodNF) 
+					throws JasperException
+    {
+        Method method = null;
+        Class type = null;
+        Class propertyEditorClass = null;
+	try {
+	    java.beans.BeanInfo info
+		= java.beans.Introspector.getBeanInfo(bean.getClass());
+	    if ( info != null ) {
+		java.beans.PropertyDescriptor pd[]
+		    = info.getPropertyDescriptors();
+		for (int i = 0 ; i < pd.length ; i++) {
+		    if ( pd[i].getName().equals(prop) ) {
+			method = pd[i].getWriteMethod();
+			type   = pd[i].getPropertyType();
+			propertyEditorClass = pd[i].getPropertyEditorClass();
+			break;
+		    }
+		}
+	    }
+	    if ( method != null ) {
+		if (type.isArray()) {
+                    if (request == null) {
+			throw new JasperException(
+		            Localizer.getMessage("jsp.error.beans.setproperty.noindexset"));
+                    }
+		    Class t = type.getComponentType();
+		    String[] values = request.getParameterValues(param);
+		    //XXX Please check.
+		    if(values == null) return;
+		    if(t.equals(String.class)) {
+			method.invoke(bean, new Object[] { values });
+		    } else {
+			Object tmpval = null;
+			createTypedArray (prop, bean, method, values, t,
+					  propertyEditorClass); 
+		    }
+		} else {
+		    if(value == null || (param != null && value.equals(""))) return;
+		    Object oval = convert(prop, value, type, propertyEditorClass);
+		    if ( oval != null )
+			method.invoke(bean, new Object[] { oval });
+		}
+	    }
+	} catch (Exception ex) {
+	    throw new JasperException(ex);
+	}
+        if (!ignoreMethodNF && (method == null)) {
+            if (type == null) {
+		throw new JasperException(
+                    Localizer.getMessage("jsp.error.beans.noproperty",
+					 prop,
+					 bean.getClass().getName()));
+            } else {
+		throw new JasperException(
+	            Localizer.getMessage("jsp.error.beans.nomethod.setproperty",
+					 prop,
+					 type.getName(),
+					 bean.getClass().getName()));
+            }
+        }
+    }
+    // __end introspecthelperMethod
+    
+    //-------------------------------------------------------------------
+    // functions to convert builtin Java data types to string.
+    //-------------------------------------------------------------------
+    // __begin toStringMethod
+    public static String toString(Object o) {
+        return String.valueOf(o);
+    }
+
+    public static String toString(byte b) {
+        return new Byte(b).toString();
+    }
+
+    public static String toString(boolean b) {
+        return new Boolean(b).toString();
+    }
+
+    public static String toString(short s) {
+        return new Short(s).toString();
+    }
+
+    public static String toString(int i) {
+        return new Integer(i).toString();
+    }
+
+    public static String toString(float f) {
+        return new Float(f).toString();
+    }
+
+    public static String toString(long l) {
+        return new Long(l).toString();
+    }
+
+    public static String toString(double d) {
+        return new Double(d).toString();
+    }
+
+    public static String toString(char c) {
+        return new Character(c).toString();
+    }
+    // __end toStringMethod
+
+
+    /**
+     * Create a typed array.
+     * This is a special case where params are passed through
+     * the request and the property is indexed.
+     */
+    public static void createTypedArray(String propertyName,
+					Object bean,
+					Method method,
+					String[] values,
+					Class t,
+					Class propertyEditorClass)
+	        throws JasperException {
+
+	try {
+	    if (propertyEditorClass != null) {
+		Object[] tmpval = new Integer[values.length];
+		for (int i=0; i<values.length; i++) {
+		    tmpval[i] = getValueFromBeanInfoPropertyEditor(
+                            t, propertyName, values[i], propertyEditorClass);
+		}
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(Integer.class)) {
+		Integer []tmpval = new Integer[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] =  new Integer (values[i]);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(Byte.class)) {
+		Byte[] tmpval = new Byte[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = new Byte (values[i]);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(Boolean.class)) {
+		Boolean[] tmpval = new Boolean[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = new Boolean (values[i]);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(Short.class)) {
+		Short[] tmpval = new Short[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = new Short (values[i]);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(Long.class)) {
+		Long[] tmpval = new Long[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = new Long (values[i]);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(Double.class)) {
+		Double[] tmpval = new Double[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = new Double (values[i]);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(Float.class)) {
+		Float[] tmpval = new Float[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = new Float (values[i]);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(Character.class)) {
+		Character[] tmpval = new Character[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = new Character(values[i].charAt(0));
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(int.class)) {
+		int []tmpval = new int[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = Integer.parseInt (values[i]);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(byte.class)) {
+		byte[] tmpval = new byte[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = Byte.parseByte (values[i]);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(boolean.class)) {
+		boolean[] tmpval = new boolean[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = (Boolean.valueOf(values[i])).booleanValue();
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(short.class)) {
+		short[] tmpval = new short[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = Short.parseShort (values[i]);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(long.class)) {
+		long[] tmpval = new long[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = Long.parseLong (values[i]);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(double.class)) {
+		double[] tmpval = new double[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = Double.valueOf(values[i]).doubleValue();
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(float.class)) {
+		float[] tmpval = new float[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = Float.valueOf(values[i]).floatValue();
+		method.invoke (bean, new Object[] {tmpval});
+	    } else if (t.equals(char.class)) {
+		char[] tmpval = new char[values.length];
+		for (int i = 0 ; i < values.length; i++)
+		    tmpval[i] = values[i].charAt(0);
+		method.invoke (bean, new Object[] {tmpval});
+	    } else {
+		Object[] tmpval = new Integer[values.length];
+		for (int i=0; i<values.length; i++) {
+		    tmpval[i] =  
+			getValueFromPropertyEditorManager(
+                                            t, propertyName, values[i]);
+		}
+		method.invoke (bean, new Object[] {tmpval});
+	    }
+	} catch (Exception ex) {
+            throw new JasperException ("error in invoking method", ex);
+	}
+    }
+
+    /**
+     * Escape special shell characters.
+     * @param unescString The string to shell-escape
+     * @return The escaped shell string.
+     */
+
+    public static String escapeQueryString(String unescString) {
+    if ( unescString == null )
+        return null;
+   
+    String escString    = "";
+    String shellSpChars = "&;`'\"|*?~<>^()[]{}$\\\n";
+   
+    for(int index=0; index<unescString.length(); index++) {
+        char nextChar = unescString.charAt(index);
+
+        if( shellSpChars.indexOf(nextChar) != -1 )
+        escString += "\\";
+
+        escString += nextChar;
+    }
+    return escString;
+    }
+
+    /**
+     * Decode an URL formatted string.
+     * @param encoded The string to decode.
+     * @return The decoded string.
+     */
+
+    public static String decode(String encoded) {
+        // speedily leave if we're not needed
+    if (encoded == null) return null;
+        if (encoded.indexOf('%') == -1 && encoded.indexOf('+') == -1)
+        return encoded;
+
+    //allocate the buffer - use byte[] to avoid calls to new.
+        byte holdbuffer[] = new byte[encoded.length()];
+
+        char holdchar;
+        int bufcount = 0;
+
+        for (int count = 0; count < encoded.length(); count++) {
+        char cur = encoded.charAt(count);
+            if (cur == '%') {
+            holdbuffer[bufcount++] =
+          (byte)Integer.parseInt(encoded.substring(count+1,count+3),16);
+                if (count + 2 >= encoded.length())
+                    count = encoded.length();
+                else
+                    count += 2;
+            } else if (cur == '+') {
+        holdbuffer[bufcount++] = (byte) ' ';
+        } else {
+            holdbuffer[bufcount++] = (byte) cur;
+            }
+        }
+	// REVISIT -- remedy for Deprecated warning.
+    //return new String(holdbuffer,0,0,bufcount);
+    return new String(holdbuffer,0,bufcount);
+    }
+
+    // __begin lookupReadMethodMethod
+    public static Object handleGetProperty(Object o, String prop)
+    throws JasperException {
+        if (o == null) {
+	    throw new JasperException(
+	            Localizer.getMessage("jsp.error.beans.nullbean"));
+        }
+	Object value = null;
+        try {
+            Method method = getReadMethod(o.getClass(), prop);
+	    value = method.invoke(o, (Object[]) null);
+        } catch (Exception ex) {
+	    throw new JasperException (ex);
+        }
+        return value;
+    }
+    // __end lookupReadMethodMethod
+
+    // handles <jsp:setProperty> with EL expression for 'value' attribute
+/** Use proprietaryEvaluate
+    public static void handleSetPropertyExpression(Object bean,
+        String prop, String expression, PageContext pageContext,
+        VariableResolver variableResolver, FunctionMapper functionMapper )
+	throws JasperException
+    {
+	try {
+            Method method = getWriteMethod(bean.getClass(), prop);
+	    method.invoke(bean, new Object[] { 
+		pageContext.getExpressionEvaluator().evaluate(
+		    expression,
+		    method.getParameterTypes()[0],
+                    variableResolver,
+                    functionMapper,
+                    null )
+	    });
+	} catch (Exception ex) {
+	    throw new JasperException(ex);
+	}
+    }
+**/
+    public static void handleSetPropertyExpression(Object bean,
+        String prop, String expression, PageContext pageContext,
+	ProtectedFunctionMapper functionMapper )
+        throws JasperException
+    {
+        try {
+            Method method = getWriteMethod(bean.getClass(), prop);
+            method.invoke(bean, new Object[] {
+                PageContextImpl.proprietaryEvaluate(
+                    expression,
+                    method.getParameterTypes()[0],
+		    pageContext,
+                    functionMapper,
+                    false )
+            });
+        } catch (Exception ex) {
+            throw new JasperException(ex);
+        }
+    }
+
+    public static void handleSetProperty(Object bean, String prop,
+					 Object value)
+	throws JasperException
+    {
+	try {
+            Method method = getWriteMethod(bean.getClass(), prop);
+	    method.invoke(bean, new Object[] { value });
+	} catch (Exception ex) {
+	    throw new JasperException(ex);
+	}
+    }
+    
+    public static void handleSetProperty(Object bean, String prop,
+					 int value)
+	throws JasperException
+    {
+	try {
+            Method method = getWriteMethod(bean.getClass(), prop);
+	    method.invoke(bean, new Object[] { new Integer(value) });
+	} catch (Exception ex) {
+	    throw new JasperException(ex);
+	}	
+    }
+    
+    public static void handleSetProperty(Object bean, String prop,
+					 short value)
+	throws JasperException
+    {
+	try {
+            Method method = getWriteMethod(bean.getClass(), prop);
+	    method.invoke(bean, new Object[] { new Short(value) });
+	} catch (Exception ex) {
+	    throw new JasperException(ex);
+	}	
+    }
+    
+    public static void handleSetProperty(Object bean, String prop,
+					 long value)
+	throws JasperException
+    {
+	try {
+            Method method = getWriteMethod(bean.getClass(), prop);
+	    method.invoke(bean, new Object[] { new Long(value) });
+	} catch (Exception ex) {
+	    throw new JasperException(ex);
+	}	
+    } 
+    
+    public static void handleSetProperty(Object bean, String prop,
+					 double value)
+	throws JasperException
+    {
+	try {
+            Method method = getWriteMethod(bean.getClass(), prop);
+	    method.invoke(bean, new Object[] { new Double(value) });
+	} catch (Exception ex) {
+	    throw new JasperException(ex);
+	}	
+    }
+    
+    public static void handleSetProperty(Object bean, String prop,
+					 float value)
+	throws JasperException
+    {
+	try {
+            Method method = getWriteMethod(bean.getClass(), prop);
+	    method.invoke(bean, new Object[] { new Float(value) });
+	} catch (Exception ex) {
+	    throw new JasperException(ex);
+	}	
+    }
+    
+    public static void handleSetProperty(Object bean, String prop,
+					 char value)
+	throws JasperException
+    {
+	try {
+            Method method = getWriteMethod(bean.getClass(), prop);
+	    method.invoke(bean, new Object[] { new Character(value) });
+	} catch (Exception ex) {
+	    throw new JasperException(ex);
+	}	
+    }
+
+    public static void handleSetProperty(Object bean, String prop,
+					 byte value)
+	throws JasperException
+    {
+	try {
+            Method method = getWriteMethod(bean.getClass(), prop);
+	    method.invoke(bean, new Object[] { new Byte(value) });
+	} catch (Exception ex) {
+	    throw new JasperException(ex);
+	}	
+    }
+    
+    public static void handleSetProperty(Object bean, String prop,
+					 boolean value)
+	throws JasperException
+    {
+	try {
+            Method method = getWriteMethod(bean.getClass(), prop);
+	    method.invoke(bean, new Object[] { new Boolean(value) });
+	} catch (Exception ex) {
+	    throw new JasperException(ex);
+	}	
+    }
+    
+    public static Method getWriteMethod(Class beanClass, String prop)
+    throws JasperException {
+	Method method = null;	
+        Class type = null;
+	try {
+	    java.beans.BeanInfo info
+                = java.beans.Introspector.getBeanInfo(beanClass);
+	    if ( info != null ) {
+		java.beans.PropertyDescriptor pd[]
+		    = info.getPropertyDescriptors();
+		for (int i = 0 ; i < pd.length ; i++) {
+		    if ( pd[i].getName().equals(prop) ) {
+			method = pd[i].getWriteMethod();
+			type   = pd[i].getPropertyType();
+			break;
+		    }
+		}
+            } else {        
+                // just in case introspection silently fails.
+                throw new JasperException(
+                    Localizer.getMessage("jsp.error.beans.nobeaninfo",
+					 beanClass.getName()));
+            }
+        } catch (Exception ex) {
+            throw new JasperException (ex);
+        }
+        if (method == null) {
+            if (type == null) {
+		throw new JasperException(
+                        Localizer.getMessage("jsp.error.beans.noproperty",
+					     prop,
+					     beanClass.getName()));
+            } else {
+		throw new JasperException(
+		    Localizer.getMessage("jsp.error.beans.nomethod.setproperty",
+					 prop,
+					 type.getName(),
+					 beanClass.getName()));
+            }
+        }
+        return method;
+    }
+
+    public static Method getReadMethod(Class beanClass, String prop)
+	    throws JasperException {
+
+        Method method = null;        
+        Class type = null;
+        try {
+            java.beans.BeanInfo info
+                = java.beans.Introspector.getBeanInfo(beanClass);
+            if ( info != null ) {
+                java.beans.PropertyDescriptor pd[]
+                    = info.getPropertyDescriptors();
+                for (int i = 0 ; i < pd.length ; i++) {
+                    if ( pd[i].getName().equals(prop) ) {
+                        method = pd[i].getReadMethod();
+                        type   = pd[i].getPropertyType();
+                        break;
+                    }
+                }
+            } else {        
+                // just in case introspection silently fails.
+		throw new JasperException(
+                    Localizer.getMessage("jsp.error.beans.nobeaninfo",
+					 beanClass.getName()));
+	    }
+	} catch (Exception ex) {
+	    throw new JasperException (ex);
+	}
+        if (method == null) {
+            if (type == null) {
+		throw new JasperException(
+                    Localizer.getMessage("jsp.error.beans.noproperty", prop,
+					 beanClass.getName()));
+            } else {
+		throw new JasperException(
+                    Localizer.getMessage("jsp.error.beans.nomethod", prop,
+					 beanClass.getName()));
+            }
+        }
+
+	return method;
+    }
+
+    //*********************************************************************
+    // PropertyEditor Support
+
+    public static Object getValueFromBeanInfoPropertyEditor(
+		           Class attrClass, String attrName, String attrValue,
+			   Class propertyEditorClass) 
+	throws JasperException 
+    {
+	try {
+	    PropertyEditor pe = (PropertyEditor)propertyEditorClass.newInstance();
+	    pe.setAsText(attrValue);
+	    return pe.getValue();
+	} catch (Exception ex) {
+	    throw new JasperException(
+                Localizer.getMessage("jsp.error.beans.property.conversion",
+				     attrValue, attrClass.getName(), attrName,
+				     ex.getMessage()));
+	}
+    }
+
+    public static Object getValueFromPropertyEditorManager(
+	             Class attrClass, String attrName, String attrValue) 
+	throws JasperException 
+    {
+	try {
+	    PropertyEditor propEditor = 
+		PropertyEditorManager.findEditor(attrClass);
+	    if (propEditor != null) {
+		propEditor.setAsText(attrValue);
+		return propEditor.getValue();
+	    } else {
+		throw new IllegalArgumentException(
+                    Localizer.getMessage("jsp.error.beans.propertyeditor.notregistered"));
+	    }
+	} catch (IllegalArgumentException ex) {
+	    throw new JasperException(
+                Localizer.getMessage("jsp.error.beans.property.conversion",
+				     attrValue, attrClass.getName(), attrName,
+				     ex.getMessage()));
+	}
+    }
+
+
+    // ************************************************************************
+    // General Purpose Runtime Methods
+    // ************************************************************************
+
+
+    /**
+     * Convert a possibly relative resource path into a context-relative
+     * resource path that starts with a '/'.
+     *
+     * @param request The servlet request we are processing
+     * @param relativePath The possibly relative resource path
+     */
+    public static String getContextRelativePath(ServletRequest request,
+                                                String relativePath) {
+
+        if (relativePath.startsWith("/"))
+            return (relativePath);
+        if (!(request instanceof HttpServletRequest))
+            return (relativePath);
+        HttpServletRequest hrequest = (HttpServletRequest) request;
+        String uri = (String)
+            request.getAttribute("javax.servlet.include.servlet_path");
+        if (uri != null) {
+            String pathInfo = (String)
+                request.getAttribute("javax.servlet.include.path_info");
+            if (pathInfo == null) {
+                if (uri.lastIndexOf('/') >= 0) 
+                    uri = uri.substring(0, uri.lastIndexOf('/'));
+            }
+        }
+        else {
+            uri = hrequest.getServletPath();
+            if (uri.lastIndexOf('/') >= 0) 
+                uri = uri.substring(0, uri.lastIndexOf('/'));
+        }
+        return uri + '/' + relativePath;
+
+    }
+
+
+    /**
+     * Perform a RequestDispatcher.include() operation, with optional flushing
+     * of the response beforehand.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are processing
+     * @param relativePath The relative path of the resource to be included
+     * @param out The Writer to whom we are currently writing
+     * @param flush Should we flush before the include is processed?
+     *
+     * @exception IOException if thrown by the included servlet
+     * @exception ServletException if thrown by the included servlet
+     */
+    public static void include(ServletRequest request,
+                               ServletResponse response,
+                               String relativePath,
+                               JspWriter out,
+                               boolean flush)
+        throws IOException, ServletException {
+
+        if (flush && !(out instanceof BodyContent))
+            out.flush();
+
+        // FIXME - It is tempting to use request.getRequestDispatcher() to
+        // resolve a relative path directly, but Catalina currently does not
+        // take into account whether the caller is inside a RequestDispatcher
+        // include or not.  Whether Catalina *should* take that into account
+        // is a spec issue currently under review.  In the mean time,
+        // replicate Jasper's previous behavior
+
+        String resourcePath = getContextRelativePath(request, relativePath);
+        RequestDispatcher rd = request.getRequestDispatcher(resourcePath);
+
+        rd.include(request,
+                   new ServletResponseWrapperInclude(response, out));
+
+    }
+
+    /**
+     * URL encodes a string, based on the supplied character encoding.
+     * This performs the same function as java.next.URLEncode.encode
+     * in J2SDK1.4, and should be removed if the only platform supported
+     * is 1.4 or higher.
+     * @param s The String to be URL encoded.
+     * @param enc The character encoding 
+     * @return The URL encoded String
+     */
+    public static String URLEncode(String s, String enc) {
+
+	if (s == null) {
+	    return "null";
+	}
+
+	if (enc == null) {
+	    enc = "ISO-8859-1";	// The default request encoding 
+	}
+
+	StringBuffer out = new StringBuffer(s.length());
+	ByteArrayOutputStream buf = new ByteArrayOutputStream();
+	OutputStreamWriter writer = null;
+	try {
+	    writer = new OutputStreamWriter(buf, enc);
+	} catch (java.io.UnsupportedEncodingException ex) {
+	    // Use the default encoding?
+	    writer = new OutputStreamWriter(buf);
+	}
+	
+	for (int i = 0; i < s.length(); i++) {
+	    int c = s.charAt(i);
+	    if (c == ' ') {
+		out.append('+');
+	    } else if (isSafeChar(c)) {
+		out.append((char)c);
+	    } else {
+		// convert to external encoding before hex conversion
+		try {
+		    writer.write(c);
+		    writer.flush();
+		} catch(IOException e) {
+		    buf.reset();
+		    continue;
+		}
+		byte[] ba = buf.toByteArray();
+		for (int j = 0; j < ba.length; j++) {
+		    out.append('%');
+		    // Converting each byte in the buffer
+		    out.append(Character.forDigit((ba[j]>>4) & 0xf, 16));
+		    out.append(Character.forDigit(ba[j] & 0xf, 16));
+		}
+		buf.reset();
+	    }
+	}
+	return out.toString();
+    }
+
+    private static boolean isSafeChar(int c) {
+	if (c >= 'a' && c <= 'z') {
+	    return true;
+	}
+	if (c >= 'A' && c <= 'Z') {
+	    return true;
+	}
+	if (c >= '0' && c <= '9') {
+	    return true;
+	}
+	if (c == '-' || c == '_' || c == '.' || c == '!' ||
+	    c == '~' || c == '*' || c == '\'' || c == '(' || c == ')') {
+	    return true;
+	}
+	return false;
+    }
+
+}

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/JspSourceDependent.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/JspSourceDependent.java?rev=819435&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/JspSourceDependent.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/JspSourceDependent.java Mon Sep 28 00:43:34 2009
@@ -0,0 +1,39 @@
+/*
+ * 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.jasper.runtime;
+
+/**
+ * Interface for tracking the source files dependencies, for the purpose
+ * of compiling out of date pages.  This is used for
+ * 1) files that are included by page directives
+ * 2) files that are included by include-prelude and include-coda in jsp:config
+ * 3) files that are tag files and referenced
+ * 4) TLDs referenced
+ */
+
+public interface JspSourceDependent {
+
+   /**
+    * Returns a list of files names that the current page has a source
+    * dependency on.
+    */
+    // FIXME: Type used is Object due to very weird behavior 
+    // with Eclipse JDT 3.1 in Java 5 mode
+    public Object getDependants();
+
+}

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/JspWriterImpl.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/JspWriterImpl.java?rev=819435&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/JspWriterImpl.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/JspWriterImpl.java Mon Sep 28 00:43:34 2009
@@ -0,0 +1,590 @@
+/*
+ * 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.jasper.runtime;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import javax.servlet.ServletResponse;
+import javax.servlet.jsp.JspWriter;
+
+import org.apache.jasper.Constants;
+import org.apache.jasper.compiler.Localizer;
+import org.apache.jasper.security.SecurityUtil;
+
+/**
+ * Write text to a character-output stream, buffering characters so as
+ * to provide for the efficient writing of single characters, arrays,
+ * and strings. 
+ *
+ * Provide support for discarding for the output that has been 
+ * buffered. 
+ * 
+ * This needs revisiting when the buffering problems in the JSP spec
+ * are fixed -akv 
+ *
+ * @author Anil K. Vijendran
+ */
+public class JspWriterImpl extends JspWriter {
+    
+    private Writer out;
+    private ServletResponse response;    
+    private char cb[];
+    private int nextChar;
+    private boolean flushed = false;
+    private boolean closed = false;
+    
+    public JspWriterImpl() {
+        super( Constants.DEFAULT_BUFFER_SIZE, true );
+    }
+    
+    /**
+     * Create a buffered character-output stream that uses a default-sized
+     * output buffer.
+     *
+     * @param  response  A Servlet Response
+     */
+    public JspWriterImpl(ServletResponse response) {
+        this(response, Constants.DEFAULT_BUFFER_SIZE, true);
+    }
+    
+    /**
+     * Create a new buffered character-output stream that uses an output
+     * buffer of the given size.
+     *
+     * @param  response A Servlet Response
+     * @param  sz   	Output-buffer size, a positive integer
+     *
+     * @exception  IllegalArgumentException  If sz is <= 0
+     */
+    public JspWriterImpl(ServletResponse response, int sz, 
+            boolean autoFlush) {
+        super(sz, autoFlush);
+        if (sz < 0)
+            throw new IllegalArgumentException("Buffer size <= 0");
+        this.response = response;
+        cb = sz == 0 ? null : new char[sz];
+        nextChar = 0;
+    }
+    
+    void init( ServletResponse response, int sz, boolean autoFlush ) {
+        this.response= response;
+        if( sz > 0 && ( cb == null || sz > cb.length ) )
+            cb=new char[sz];
+        nextChar = 0;
+        this.autoFlush=autoFlush;
+        this.bufferSize=sz;
+    }
+    
+    /** Package-level access
+     */
+    void recycle() {
+        flushed = false;
+        closed = false;
+        out = null;
+        nextChar = 0;
+        response = null;
+    }
+    
+    /**
+     * Flush the output buffer to the underlying character stream, without
+     * flushing the stream itself.  This method is non-private only so that it
+     * may be invoked by PrintStream.
+     */
+    protected final void flushBuffer() throws IOException {
+        if (bufferSize == 0)
+            return;
+        flushed = true;
+        ensureOpen();
+        if (nextChar == 0)
+            return;
+        initOut();
+        out.write(cb, 0, nextChar);
+        nextChar = 0;
+    }
+    
+    private void initOut() throws IOException {
+        if (out == null) {
+            out = response.getWriter();
+        }
+    }
+    
+    private String getLocalizeMessage(final String message){
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            return (String)AccessController.doPrivileged(new PrivilegedAction(){
+                public Object run(){
+                    return Localizer.getMessage(message); 
+                }
+            });
+        } else {
+            return Localizer.getMessage(message);
+        }
+    }
+    
+    /**
+     * Discard the output buffer.
+     */
+    public final void clear() throws IOException {
+        if ((bufferSize == 0) && (out != null))
+            // clear() is illegal after any unbuffered output (JSP.5.5)
+            throw new IllegalStateException(
+                    getLocalizeMessage("jsp.error.ise_on_clear"));
+        if (flushed)
+            throw new IOException(
+                    getLocalizeMessage("jsp.error.attempt_to_clear_flushed_buffer"));
+        ensureOpen();
+        nextChar = 0;
+    }
+    
+    public void clearBuffer() throws IOException {
+        if (bufferSize == 0)
+            throw new IllegalStateException(
+                    getLocalizeMessage("jsp.error.ise_on_clear"));
+        ensureOpen();
+        nextChar = 0;
+    }
+    
+    private final void bufferOverflow() throws IOException {
+        throw new IOException(getLocalizeMessage("jsp.error.overflow"));
+    }
+    
+    /**
+     * Flush the stream.
+     *
+     */
+    public void flush()  throws IOException {
+        flushBuffer();
+        if (out != null) {
+            out.flush();
+        }
+    }
+    
+    /**
+     * Close the stream.
+     *
+     */
+    public void close() throws IOException {
+        if (response == null || closed)
+            // multiple calls to close is OK
+            return;
+        flush();
+        if (out != null)
+            out.close();
+        out = null;
+        closed = true;
+    }
+    
+    /**
+     * @return the number of bytes unused in the buffer
+     */
+    public int getRemaining() {
+        return bufferSize - nextChar;
+    }
+    
+    /** check to make sure that the stream has not been closed */
+    private void ensureOpen() throws IOException {
+        if (response == null || closed)
+            throw new IOException("Stream closed");
+    }
+    
+    
+    /**
+     * Write a single character.
+     */
+    public void write(int c) throws IOException {
+        ensureOpen();
+        if (bufferSize == 0) {
+            initOut();
+            out.write(c);
+        }
+        else {
+            if (nextChar >= bufferSize)
+                if (autoFlush)
+                    flushBuffer();
+                else
+                    bufferOverflow();
+            cb[nextChar++] = (char) c;
+        }
+    }
+    
+    /**
+     * Our own little min method, to avoid loading java.lang.Math if we've run
+     * out of file descriptors and we're trying to print a stack trace.
+     */
+    private int min(int a, int b) {
+        if (a < b) return a;
+        return b;
+    }
+    
+    /**
+     * Write a portion of an array of characters.
+     *
+     * <p> Ordinarily this method stores characters from the given array into
+     * this stream's buffer, flushing the buffer to the underlying stream as
+     * needed.  If the requested length is at least as large as the buffer,
+     * however, then this method will flush the buffer and write the characters
+     * directly to the underlying stream.  Thus redundant
+     * <code>DiscardableBufferedWriter</code>s will not copy data unnecessarily.
+     *
+     * @param  cbuf  A character array
+     * @param  off   Offset from which to start reading characters
+     * @param  len   Number of characters to write
+     */
+    public void write(char cbuf[], int off, int len) 
+    throws IOException 
+    {
+        ensureOpen();
+        
+        if (bufferSize == 0) {
+            initOut();
+            out.write(cbuf, off, len);
+            return;
+        }
+        
+        if ((off < 0) || (off > cbuf.length) || (len < 0) ||
+                ((off + len) > cbuf.length) || ((off + len) < 0)) {
+            throw new IndexOutOfBoundsException();
+        } else if (len == 0) {
+            return;
+        } 
+        
+        if (len >= bufferSize) {
+            /* If the request length exceeds the size of the output buffer,
+             flush the buffer and then write the data directly.  In this
+             way buffered streams will cascade harmlessly. */
+            if (autoFlush)
+                flushBuffer();
+            else
+                bufferOverflow();
+            initOut();
+            out.write(cbuf, off, len);
+            return;
+        }
+        
+        int b = off, t = off + len;
+        while (b < t) {
+            int d = min(bufferSize - nextChar, t - b);
+            System.arraycopy(cbuf, b, cb, nextChar, d);
+            b += d;
+            nextChar += d;
+            if (nextChar >= bufferSize) 
+                if (autoFlush)
+                    flushBuffer();
+                else
+                    bufferOverflow();
+        }
+        
+    }
+    
+    /**
+     * Write an array of characters.  This method cannot be inherited from the
+     * Writer class because it must suppress I/O exceptions.
+     */
+    public void write(char buf[]) throws IOException {
+        write(buf, 0, buf.length);
+    }
+    
+    /**
+     * Write a portion of a String.
+     *
+     * @param  s     String to be written
+     * @param  off   Offset from which to start reading characters
+     * @param  len   Number of characters to be written
+     */
+    public void write(String s, int off, int len) throws IOException {
+        ensureOpen();
+        if (bufferSize == 0) {
+            initOut();
+            out.write(s, off, len);
+            return;
+        }
+        int b = off, t = off + len;
+        while (b < t) {
+            int d = min(bufferSize - nextChar, t - b);
+            s.getChars(b, b + d, cb, nextChar);
+            b += d;
+            nextChar += d;
+            if (nextChar >= bufferSize) 
+                if (autoFlush)
+                    flushBuffer();
+                else
+                    bufferOverflow();
+        }
+    }
+    
+    /**
+     * Write a string.  This method cannot be inherited from the Writer class
+     * because it must suppress I/O exceptions.
+     */
+    public void write(String s) throws IOException {
+        // Simple fix for Bugzilla 35410
+        // Calling the other write function so as to init the buffer anyways
+        if(s == null) {
+            write(s, 0, 0);
+        } else {
+            write(s, 0, s.length());
+        }
+    }
+    
+    
+    static String lineSeparator = System.getProperty("line.separator");
+    
+    /**
+     * Write a line separator.  The line separator string is defined by the
+     * system property <tt>line.separator</tt>, and is not necessarily a single
+     * newline ('\n') character.
+     *
+     * @exception  IOException  If an I/O error occurs
+     */
+    
+    public void newLine() throws IOException {
+        write(lineSeparator);
+    }
+    
+    
+    /* Methods that do not terminate lines */
+    
+    /**
+     * Print a boolean value.  The string produced by <code>{@link
+     * java.lang.String#valueOf(boolean)}</code> is translated into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the <code>{@link
+     * #write(int)}</code> method.
+     *
+     * @param      b   The <code>boolean</code> to be printed
+     */
+    public void print(boolean b) throws IOException {
+        write(b ? "true" : "false");
+    }
+    
+    /**
+     * Print a character.  The character is translated into one or more bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the <code>{@link
+     * #write(int)}</code> method.
+     *
+     * @param      c   The <code>char</code> to be printed
+     */
+    public void print(char c) throws IOException {
+        write(String.valueOf(c));
+    }
+    
+    /**
+     * Print an integer.  The string produced by <code>{@link
+     * java.lang.String#valueOf(int)}</code> is translated into bytes according
+     * to the platform's default character encoding, and these bytes are
+     * written in exactly the manner of the <code>{@link #write(int)}</code>
+     * method.
+     *
+     * @param      i   The <code>int</code> to be printed
+     */
+    public void print(int i) throws IOException {
+        write(String.valueOf(i));
+    }
+    
+    /**
+     * Print a long integer.  The string produced by <code>{@link
+     * java.lang.String#valueOf(long)}</code> is translated into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the <code>{@link #write(int)}</code>
+     * method.
+     *
+     * @param      l   The <code>long</code> to be printed
+     */
+    public void print(long l) throws IOException {
+        write(String.valueOf(l));
+    }
+    
+    /**
+     * Print a floating-point number.  The string produced by <code>{@link
+     * java.lang.String#valueOf(float)}</code> is translated into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the <code>{@link #write(int)}</code>
+     * method.
+     *
+     * @param      f   The <code>float</code> to be printed
+     */
+    public void print(float f) throws IOException {
+        write(String.valueOf(f));
+    }
+    
+    /**
+     * Print a double-precision floating-point number.  The string produced by
+     * <code>{@link java.lang.String#valueOf(double)}</code> is translated into
+     * bytes according to the platform's default character encoding, and these
+     * bytes are written in exactly the manner of the <code>{@link
+     * #write(int)}</code> method.
+     *
+     * @param      d   The <code>double</code> to be printed
+     */
+    public void print(double d) throws IOException {
+        write(String.valueOf(d));
+    }
+    
+    /**
+     * Print an array of characters.  The characters are converted into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the <code>{@link #write(int)}</code>
+     * method.
+     *
+     * @param      s   The array of chars to be printed
+     *
+     * @throws  NullPointerException  If <code>s</code> is <code>null</code>
+     */
+    public void print(char s[]) throws IOException {
+        write(s);
+    }
+    
+    /**
+     * Print a string.  If the argument is <code>null</code> then the string
+     * <code>"null"</code> is printed.  Otherwise, the string's characters are
+     * converted into bytes according to the platform's default character
+     * encoding, and these bytes are written in exactly the manner of the
+     * <code>{@link #write(int)}</code> method.
+     *
+     * @param      s   The <code>String</code> to be printed
+     */
+    public void print(String s) throws IOException {
+        if (s == null) {
+            s = "null";
+        }
+        write(s);
+    }
+    
+    /**
+     * Print an object.  The string produced by the <code>{@link
+     * java.lang.String#valueOf(Object)}</code> method is translated into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the <code>{@link #write(int)}</code>
+     * method.
+     *
+     * @param      obj   The <code>Object</code> to be printed
+     */
+    public void print(Object obj) throws IOException {
+        write(String.valueOf(obj));
+    }
+    
+    /* Methods that do terminate lines */
+    
+    /**
+     * Terminate the current line by writing the line separator string.  The
+     * line separator string is defined by the system property
+     * <code>line.separator</code>, and is not necessarily a single newline
+     * character (<code>'\n'</code>).
+     *
+     * Need to change this from PrintWriter because the default
+     * println() writes  to the sink directly instead of through the
+     * write method...  
+     */
+    public void println() throws IOException {
+        newLine();
+    }
+    
+    /**
+     * Print a boolean value and then terminate the line.  This method behaves
+     * as though it invokes <code>{@link #print(boolean)}</code> and then
+     * <code>{@link #println()}</code>.
+     */
+    public void println(boolean x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print a character and then terminate the line.  This method behaves as
+     * though it invokes <code>{@link #print(char)}</code> and then <code>{@link
+     * #println()}</code>.
+     */
+    public void println(char x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print an integer and then terminate the line.  This method behaves as
+     * though it invokes <code>{@link #print(int)}</code> and then <code>{@link
+     * #println()}</code>.
+     */
+    public void println(int x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print a long integer and then terminate the line.  This method behaves
+     * as though it invokes <code>{@link #print(long)}</code> and then
+     * <code>{@link #println()}</code>.
+     */
+    public void println(long x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print a floating-point number and then terminate the line.  This method
+     * behaves as though it invokes <code>{@link #print(float)}</code> and then
+     * <code>{@link #println()}</code>.
+     */
+    public void println(float x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print a double-precision floating-point number and then terminate the
+     * line.  This method behaves as though it invokes <code>{@link
+     * #print(double)}</code> and then <code>{@link #println()}</code>.
+     */
+    public void println(double x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print an array of characters and then terminate the line.  This method
+     * behaves as though it invokes <code>{@link #print(char[])}</code> and then
+     * <code>{@link #println()}</code>.
+     */
+    public void println(char x[]) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print a String and then terminate the line.  This method behaves as
+     * though it invokes <code>{@link #print(String)}</code> and then
+     * <code>{@link #println()}</code>.
+     */
+    public void println(String x) throws IOException {
+        print(x);
+        println();
+    }
+    
+    /**
+     * Print an Object and then terminate the line.  This method behaves as
+     * though it invokes <code>{@link #print(Object)}</code> and then
+     * <code>{@link #println()}</code>.
+     */
+    public void println(Object x) throws IOException {
+        print(x);
+        println();
+    }
+    
+}