org.apache.jasper.compiler.JspUtil Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2004 The Apache Software Foundation
*
* Licensed 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.compiler;
import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.el.ELException;
import javax.el.FunctionMapper;
import javax.el.ExpressionFactory;
import org.apache.jasper.Constants;
import org.apache.jasper.JasperException;
import org.apache.jasper.JspCompilationContext;
import org.apache.jasper.runtime.ELContextImpl;
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
* @author Kin-man Chung
*/
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 ExpressionFactory expFactory;
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;
StringBuilder sb = new StringBuilder(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);
}
/**
* 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
* "page", "request", "session", and
* "application"
*/
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();
ArrayList temp = new ArrayList(tempLength);
for (int i = 0; i < tempLength; i++) {
String qName = attrs.getQName(i);
if ((!qName.equals("xmlns")) && (!qName.startsWith("xmlns:")))
temp.add(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.add( 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 = temp.get(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') {
sb.append(">");
} else if (c == '\'') {
sb.append("'");
} else if (c == '&') {
sb.append("&");
} else if (c == '"') {
sb.append(""");
} else {
sb.append(c);
}
}
return sb.toString();
}
/**
* Replaces any occurrences of the character replace with the
* string with.
*/
public static String replace(String name, char replace, String with) {
StringBuilder buf = new StringBuilder();
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;
public ValidAttribute (String name, boolean mandatory)
{
this.name = name;
this.mandatory = mandatory;
}
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 Class object associated with the class or
* interface with the given string name.
*
* The Class object is determined by passing the given string
* name to the Class.forName() method, unless the given string
* name represents a primitive type, in which case it is converted to a
* Class 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.
* @return a String representing a call to the EL interpreter.
*/
public static String interpreterCall(boolean isTagFile,
String expression,
Class expectedType,
String fnmapvar,
String expectedDeferredType,
String expectedReturnType,
String[] expectedParamTypes
)
{
/*
* Determine which context object to use.
*/
String jspCtxt = null;
if (isTagFile)
jspCtxt = "this.getJspContext()";
else
jspCtxt = "_jspx_page_context";
if (expectedType == javax.el.ValueExpression.class) {
if (expectedDeferredType == null) {
expectedDeferredType = "java.lang.Object";
}
return "org.apache.jasper.runtime.PageContextImpl.getValueExpression"
+ "(" + Generator.quote(expression) + ", "
+ "(PageContext)" + jspCtxt + ", "
+ expectedDeferredType + ".class, "
+ fnmapvar + ")";
}
if (expectedType == javax.el.MethodExpression.class) {
if (expectedReturnType == null) {
expectedReturnType = "Void";
}
StringBuilder params = new StringBuilder();
if (expectedParamTypes != null) {
for (int i = 0; i < expectedParamTypes.length; i++) {
if (i > 0) {
params.append(", ");
}
params.append(expectedParamTypes[i] + ".class");
}
}
return "org.apache.jasper.runtime.PageContextImpl.getMethodExpression"
+ "(" + Generator.quote(expression) + ", "
+ "(PageContext)" + jspCtxt + ", "
+ fnmapvar + ", "
+ expectedReturnType + ".class, "
+ "new Class[] {" + params.toString() + "})";
}
/*
* 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";
}
}
targetType = toJavaSourceType(targetType);
StringBuilder call = new StringBuilder(
"(" + targetType + ") "
+ "org.apache.jasper.runtime.PageContextImpl.evaluateExpression"
+ "(" + Generator.quote(expression) + ", "
+ targetType + ".class, "
+ "(PageContext)" + jspCtxt
+ ", " + fnmapvar
+ ")");
/*
* 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 EL expressions within the given string.
* @param where the approximate location of the expressions in the JSP page
* @param expressions a string containing an EL expressions
* @param err an error dispatcher to use
*/
public static void validateExpressions(Mark where,
String expressions,
FunctionMapper functionMapper,
ErrorDispatcher err)
throws JasperException {
try {
ELContextImpl elContext = new ELContextImpl(null);
elContext.setFunctionMapper(functionMapper);
getExpressionFactory().createValueExpression(
elContext,
expressions, Object.class);
}
catch( ELException e ) {
err.jspError(where, "jsp.error.invalid.expression", expressions,
e.toString() );
}
}
public static Object coerce(Class targetType, String value)
throws ELException {
return getExpressionFactory().coerceToType(value, targetType);
}
/**
* Resets the temporary variable name.
* (not thread-safe)
*/
public static void resetTemporaryVariableName() {
tempSequenceNumber = 0;
}
/**
* Generates a new temporary variable name.
* (not thread-safe)
*/
public static String nextTemporaryVariableName() {
return Constants.TEMP_VARIABLE_NAME_PREFIX + (tempSequenceNumber++);
}
public static String coerceToPrimitiveBoolean(String s,
boolean isNamedAttribute) {
if (isNamedAttribute) {
return "org.apache.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.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Boolean.class)";
} else {
if (s == null || s.length() == 0) {
return "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.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.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Byte.class)";
} else {
if (s == null || s.length() == 0) {
return "Byte.valueOf((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.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.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.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.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.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.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Float.class)";
} else {
if (s == null || s.length() == 0) {
return "Float.valueOf(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.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.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Integer.class)";
} else {
if (s == null || s.length() == 0) {
return "Integer.valueOf(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.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.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Short.class)";
} else {
if (s == null || s.length() == 0) {
return "Short.valueOf((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.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.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Long.class)";
} else {
if (s == null || s.length() == 0) {
return "Long.valueOf(0)";
} else {
// Detect format error at translation time
return "new Long(" + Long.valueOf(s).toString() + "l)";
}
}
}
public static String coerceToEnum(String s, String enumClass, boolean isNamedAttribute) {
if (isNamedAttribute) {
return "org.apache.jasper.runtime.JspRuntimeLibrary.coerce("
+ s + "," + enumClass + ".class)";
} else {
if (s == null || s.length() == 0) {
return "null";
} else {
return "Enum.valueOf(" + enumClass + ".class, \"" + s + "\")";
}
}
}
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
*/
public static String getTagHandlerClassName(String path,
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 = "org.apache.jsp.tag.meta.";
begin = index + META_INF_TAGS.length();
} else {
err.jspError("jsp.error.tagfile.illegalPath", path);
}
}
className += makeJavaPackage(path.substring(begin));
return className;
}
/**
* 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,"/");
StringBuilder legalClassNames = new StringBuilder();
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) {
ArrayList comps = new ArrayList();
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] = comps.get(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) {
StringBuilder modifiedIdentifier =
new StringBuilder(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 {
InputStreamReader reader = null;
InputStream in = getInputStream(fname, jarFile, ctxt, err);
try {
reader = new InputStreamReader(in, encoding);
} catch (UnsupportedEncodingException ex) {
err.jspError("jsp.error.unsupported.encoding", encoding);
}
return reader;
}
/**
* Class.getName() return arrays in the form "[[[", where et,
* the element type can be one of ZBCDFIJS or L;
* 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;
}
}
StringBuilder resultType = new StringBuilder(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;
}
StringBuilder buf = new StringBuilder(binaryName);
do {
buf.setCharAt(c.getName().length(), '.');
c = c.getDeclaringClass();
} while ( c != null);
return buf.toString();
}
private static ExpressionFactory getExpressionFactory() {
if (expFactory == null) {
expFactory = ExpressionFactory.newInstance();
}
return expFactory;
}
static Map manifestMap =
new ConcurrentHashMap();
static Manifest nullManifest = new Manifest();
/**
* Given a list of jar files, their manifest attribute Class-path are
* scanned, and jars specified there are added to the list. This is
* carried out recursively.
* Note: This is needed to work around the JDK bug 6725230.
*/
public static List expandClassPath(Listfiles) {
for (int i = 0; i < files.size(); i++) {
String file = files.get(i);
if (! file.endsWith(".jar")) {
continue;
}
Manifest manifest = manifestMap.get(file);
JarFile jarfile = null;
if (manifest == null) {
try {
jarfile = new JarFile(file, false);
manifest = jarfile.getManifest();
if (manifest == null) {
// mark jar file as known to contain no manifest
manifestMap.put(file, nullManifest);
continue;
} else if (! file.contains("/WEB-INF")) {
// Don't cache any jars bundled with the app.
manifestMap.put(file, manifest);
}
} catch (IOException ex) {
// Ignored
continue;
} finally {
try {
if (jarfile != null)
jarfile.close();
} catch (IOException ex) {
// Ignored
}
}
} else if (manifest == nullManifest) {
continue;
}
java.util.jar.Attributes attrs = manifest.getMainAttributes();
String cp = (String) attrs.getValue("Class-Path");
if (cp == null) {
continue;
}
String[] paths = cp.split(" ");
int lastIndex = file.lastIndexOf(File.separatorChar);
String baseDir = "";
if (lastIndex > 0) {
baseDir = file.substring(0, lastIndex+1);
}
for (String path: paths) {
String p;
if (path.startsWith(File.separator)) {
p = path;
} else {
p = baseDir + path;
}
if (! files.contains(p)) {
files.add(p);
}
}
}
return files;
}
}