0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: package org.apache.jasper.compiler;
0019:
0020: import java.io.CharArrayWriter;
0021: import java.io.IOException;
0022: import java.io.InputStream;
0023: import java.io.InputStreamReader;
0024: import java.io.UnsupportedEncodingException;
0025: import java.util.Vector;
0026: import java.util.jar.JarFile;
0027: import java.util.zip.ZipEntry;
0028:
0029: import javax.el.FunctionMapper;
0030: import javax.servlet.jsp.el.ExpressionEvaluator;
0031:
0032: import org.apache.el.ExpressionFactoryImpl;
0033: import org.apache.jasper.Constants;
0034: import org.apache.jasper.JasperException;
0035: import org.apache.jasper.JspCompilationContext;
0036: import org.apache.jasper.el.ExpressionEvaluatorImpl;
0037: import org.xml.sax.Attributes;
0038:
0039: /**
0040: * This class has all the utility method(s).
0041: * Ideally should move all the bean containers here.
0042: *
0043: * @author Mandar Raje.
0044: * @author Rajiv Mordani.
0045: * @author Danno Ferrin
0046: * @author Pierre Delisle
0047: * @author Shawn Bayern
0048: * @author Mark Roth
0049: */
0050: public class JspUtil {
0051:
0052: private static final String WEB_INF_TAGS = "/WEB-INF/tags/";
0053: private static final String META_INF_TAGS = "/META-INF/tags/";
0054:
0055: // Delimiters for request-time expressions (JSP and XML syntax)
0056: private static final String OPEN_EXPR = "<%=";
0057: private static final String CLOSE_EXPR = "%>";
0058: private static final String OPEN_EXPR_XML = "%=";
0059: private static final String CLOSE_EXPR_XML = "%";
0060:
0061: private static int tempSequenceNumber = 0;
0062:
0063: //private static ExpressionEvaluatorImpl expressionEvaluator
0064: //= new ExpressionEvaluatorImpl();
0065:
0066: //tc6
0067: private final static ExpressionEvaluator expressionEvaluator = new ExpressionEvaluatorImpl(
0068: new ExpressionFactoryImpl());
0069:
0070: private static final String javaKeywords[] = { "abstract",
0071: "assert", "boolean", "break", "byte", "case", "catch",
0072: "char", "class", "const", "continue", "default", "do",
0073: "double", "else", "enum", "extends", "final", "finally",
0074: "float", "for", "goto", "if", "implements", "import",
0075: "instanceof", "int", "interface", "long", "native", "new",
0076: "package", "private", "protected", "public", "return",
0077: "short", "static", "strictfp", "super", "switch",
0078: "synchronized", "this", "throws", "transient", "try",
0079: "void", "volatile", "while" };
0080:
0081: public static final int CHUNKSIZE = 1024;
0082:
0083: public static char[] removeQuotes(char[] chars) {
0084: CharArrayWriter caw = new CharArrayWriter();
0085: for (int i = 0; i < chars.length; i++) {
0086: if (chars[i] == '%' && chars[i + 1] == '\\'
0087: && chars[i + 2] == '>') {
0088: caw.write('%');
0089: caw.write('>');
0090: i = i + 2;
0091: } else {
0092: caw.write(chars[i]);
0093: }
0094: }
0095: return caw.toCharArray();
0096: }
0097:
0098: public static char[] escapeQuotes(char[] chars) {
0099: // Prescan to convert %\> to %>
0100: String s = new String(chars);
0101: while (true) {
0102: int n = s.indexOf("%\\>");
0103: if (n < 0)
0104: break;
0105: StringBuffer sb = new StringBuffer(s.substring(0, n));
0106: sb.append("%>");
0107: sb.append(s.substring(n + 3));
0108: s = sb.toString();
0109: }
0110: chars = s.toCharArray();
0111: return (chars);
0112:
0113: // Escape all backslashes not inside a Java string literal
0114: /*
0115: CharArrayWriter caw = new CharArrayWriter();
0116: boolean inJavaString = false;
0117: for (int i = 0; i < chars.length; i++) {
0118: if (chars[i] == '"') inJavaString = !inJavaString;
0119: // escape out the escape character
0120: if (!inJavaString && (chars[i] == '\\')) caw.write('\\');
0121: caw.write(chars[i]);
0122: }
0123: return caw.toCharArray();
0124: */
0125: }
0126:
0127: /**
0128: * Checks if the token is a runtime expression.
0129: * In standard JSP syntax, a runtime expression starts with '<%' and
0130: * ends with '%>'. When the JSP document is in XML syntax, a runtime
0131: * expression starts with '%=' and ends with '%'.
0132: *
0133: * @param token The token to be checked
0134: * return whether the token is a runtime expression or not.
0135: */
0136: public static boolean isExpression(String token, boolean isXml) {
0137: String openExpr;
0138: String closeExpr;
0139: if (isXml) {
0140: openExpr = OPEN_EXPR_XML;
0141: closeExpr = CLOSE_EXPR_XML;
0142: } else {
0143: openExpr = OPEN_EXPR;
0144: closeExpr = CLOSE_EXPR;
0145: }
0146: if (token.startsWith(openExpr) && token.endsWith(closeExpr)) {
0147: return true;
0148: } else {
0149: return false;
0150: }
0151: }
0152:
0153: /**
0154: * @return the "expression" part of a runtime expression,
0155: * taking the delimiters out.
0156: */
0157: public static String getExpr(String expression, boolean isXml) {
0158: String returnString;
0159: String openExpr;
0160: String closeExpr;
0161: if (isXml) {
0162: openExpr = OPEN_EXPR_XML;
0163: closeExpr = CLOSE_EXPR_XML;
0164: } else {
0165: openExpr = OPEN_EXPR;
0166: closeExpr = CLOSE_EXPR;
0167: }
0168: int length = expression.length();
0169: if (expression.startsWith(openExpr)
0170: && expression.endsWith(closeExpr)) {
0171: returnString = expression.substring(openExpr.length(),
0172: length - closeExpr.length());
0173: } else {
0174: returnString = "";
0175: }
0176: return returnString;
0177: }
0178:
0179: /**
0180: * Takes a potential expression and converts it into XML form
0181: */
0182: public static String getExprInXml(String expression) {
0183: String returnString;
0184: int length = expression.length();
0185:
0186: if (expression.startsWith(OPEN_EXPR)
0187: && expression.endsWith(CLOSE_EXPR)) {
0188: returnString = expression.substring(1, length - 1);
0189: } else {
0190: returnString = expression;
0191: }
0192:
0193: return escapeXml(returnString.replace(Constants.ESC, '$'));
0194: }
0195:
0196: /**
0197: * Checks to see if the given scope is valid.
0198: *
0199: * @param scope The scope to be checked
0200: * @param n The Node containing the 'scope' attribute whose value is to be
0201: * checked
0202: * @param err error dispatcher
0203: *
0204: * @throws JasperException if scope is not null and different from
0205: * "page", "request", "session", and
0206: * "application"
0207: */
0208: public static void checkScope(String scope, Node n,
0209: ErrorDispatcher err) throws JasperException {
0210: if (scope != null && !scope.equals("page")
0211: && !scope.equals("request") && !scope.equals("session")
0212: && !scope.equals("application")) {
0213: err.jspError(n, "jsp.error.invalid.scope", scope);
0214: }
0215: }
0216:
0217: /**
0218: * Checks if all mandatory attributes are present and if all attributes
0219: * present have valid names. Checks attributes specified as XML-style
0220: * attributes as well as attributes specified using the jsp:attribute
0221: * standard action.
0222: */
0223: public static void checkAttributes(String typeOfTag, Node n,
0224: ValidAttribute[] validAttributes, ErrorDispatcher err)
0225: throws JasperException {
0226: Attributes attrs = n.getAttributes();
0227: Mark start = n.getStart();
0228: boolean valid = true;
0229:
0230: // AttributesImpl.removeAttribute is broken, so we do this...
0231: int tempLength = (attrs == null) ? 0 : attrs.getLength();
0232: Vector temp = new Vector(tempLength, 1);
0233: for (int i = 0; i < tempLength; i++) {
0234: String qName = attrs.getQName(i);
0235: if ((!qName.equals("xmlns"))
0236: && (!qName.startsWith("xmlns:")))
0237: temp.addElement(qName);
0238: }
0239:
0240: // Add names of attributes specified using jsp:attribute
0241: Node.Nodes tagBody = n.getBody();
0242: if (tagBody != null) {
0243: int numSubElements = tagBody.size();
0244: for (int i = 0; i < numSubElements; i++) {
0245: Node node = tagBody.getNode(i);
0246: if (node instanceof Node.NamedAttribute) {
0247: String attrName = node.getAttributeValue("name");
0248: temp.addElement(attrName);
0249: // Check if this value appear in the attribute of the node
0250: if (n.getAttributeValue(attrName) != null) {
0251: err
0252: .jspError(
0253: n,
0254: "jsp.error.duplicate.name.jspattribute",
0255: attrName);
0256: }
0257: } else {
0258: // Nothing can come before jsp:attribute, and only
0259: // jsp:body can come after it.
0260: break;
0261: }
0262: }
0263: }
0264:
0265: /*
0266: * First check to see if all the mandatory attributes are present.
0267: * If so only then proceed to see if the other attributes are valid
0268: * for the particular tag.
0269: */
0270: String missingAttribute = null;
0271:
0272: for (int i = 0; i < validAttributes.length; i++) {
0273: int attrPos;
0274: if (validAttributes[i].mandatory) {
0275: attrPos = temp.indexOf(validAttributes[i].name);
0276: if (attrPos != -1) {
0277: temp.remove(attrPos);
0278: valid = true;
0279: } else {
0280: valid = false;
0281: missingAttribute = validAttributes[i].name;
0282: break;
0283: }
0284: }
0285: }
0286:
0287: // If mandatory attribute is missing then the exception is thrown
0288: if (!valid)
0289: err.jspError(start, "jsp.error.mandatory.attribute",
0290: typeOfTag, missingAttribute);
0291:
0292: // Check to see if there are any more attributes for the specified tag.
0293: int attrLeftLength = temp.size();
0294: if (attrLeftLength == 0)
0295: return;
0296:
0297: // Now check to see if the rest of the attributes are valid too.
0298: String attribute = null;
0299:
0300: for (int j = 0; j < attrLeftLength; j++) {
0301: valid = false;
0302: attribute = (String) temp.elementAt(j);
0303: for (int i = 0; i < validAttributes.length; i++) {
0304: if (attribute.equals(validAttributes[i].name)) {
0305: valid = true;
0306: break;
0307: }
0308: }
0309: if (!valid)
0310: err.jspError(start, "jsp.error.invalid.attribute",
0311: typeOfTag, attribute);
0312: }
0313: // XXX *could* move EL-syntax validation here... (sb)
0314: }
0315:
0316: public static String escapeQueryString(String unescString) {
0317: if (unescString == null)
0318: return null;
0319:
0320: String escString = "";
0321: String shellSpChars = "\\\"";
0322:
0323: for (int index = 0; index < unescString.length(); index++) {
0324: char nextChar = unescString.charAt(index);
0325:
0326: if (shellSpChars.indexOf(nextChar) != -1)
0327: escString += "\\";
0328:
0329: escString += nextChar;
0330: }
0331: return escString;
0332: }
0333:
0334: /**
0335: * Escape the 5 entities defined by XML.
0336: */
0337: public static String escapeXml(String s) {
0338: if (s == null)
0339: return null;
0340: StringBuffer sb = new StringBuffer();
0341: for (int i = 0; i < s.length(); i++) {
0342: char c = s.charAt(i);
0343: if (c == '<') {
0344: sb.append("<");
0345: } else if (c == '>') {
0346: sb.append(">");
0347: } else if (c == '\'') {
0348: sb.append("'");
0349: } else if (c == '&') {
0350: sb.append("&");
0351: } else if (c == '"') {
0352: sb.append(""");
0353: } else {
0354: sb.append(c);
0355: }
0356: }
0357: return sb.toString();
0358: }
0359:
0360: /**
0361: * Replaces any occurrences of the character <tt>replace</tt> with the
0362: * string <tt>with</tt>.
0363: */
0364: public static String replace(String name, char replace, String with) {
0365: StringBuffer buf = new StringBuffer();
0366: int begin = 0;
0367: int end;
0368: int last = name.length();
0369:
0370: while (true) {
0371: end = name.indexOf(replace, begin);
0372: if (end < 0) {
0373: end = last;
0374: }
0375: buf.append(name.substring(begin, end));
0376: if (end == last) {
0377: break;
0378: }
0379: buf.append(with);
0380: begin = end + 1;
0381: }
0382:
0383: return buf.toString();
0384: }
0385:
0386: public static class ValidAttribute {
0387: String name;
0388: boolean mandatory;
0389: boolean rtexprvalue; // not used now
0390:
0391: public ValidAttribute(String name, boolean mandatory,
0392: boolean rtexprvalue) {
0393: this .name = name;
0394: this .mandatory = mandatory;
0395: this .rtexprvalue = rtexprvalue;
0396: }
0397:
0398: public ValidAttribute(String name, boolean mandatory) {
0399: this (name, mandatory, false);
0400: }
0401:
0402: public ValidAttribute(String name) {
0403: this (name, false);
0404: }
0405: }
0406:
0407: /**
0408: * Convert a String value to 'boolean'.
0409: * Besides the standard conversions done by
0410: * Boolean.valueOf(s).booleanValue(), the value "yes"
0411: * (ignore case) is also converted to 'true'.
0412: * If 's' is null, then 'false' is returned.
0413: *
0414: * @param s the string to be converted
0415: * @return the boolean value associated with the string s
0416: */
0417: public static boolean booleanValue(String s) {
0418: boolean b = false;
0419: if (s != null) {
0420: if (s.equalsIgnoreCase("yes")) {
0421: b = true;
0422: } else {
0423: b = Boolean.valueOf(s).booleanValue();
0424: }
0425: }
0426: return b;
0427: }
0428:
0429: /**
0430: * Returns the <tt>Class</tt> object associated with the class or
0431: * interface with the given string name.
0432: *
0433: * <p> The <tt>Class</tt> object is determined by passing the given string
0434: * name to the <tt>Class.forName()</tt> method, unless the given string
0435: * name represents a primitive type, in which case it is converted to a
0436: * <tt>Class</tt> object by appending ".class" to it (e.g., "int.class").
0437: */
0438: public static Class toClass(String type, ClassLoader loader)
0439: throws ClassNotFoundException {
0440:
0441: Class c = null;
0442: int i0 = type.indexOf('[');
0443: int dims = 0;
0444: if (i0 > 0) {
0445: // This is an array. Count the dimensions
0446: for (int i = 0; i < type.length(); i++) {
0447: if (type.charAt(i) == '[')
0448: dims++;
0449: }
0450: type = type.substring(0, i0);
0451: }
0452:
0453: if ("boolean".equals(type))
0454: c = boolean.class;
0455: else if ("char".equals(type))
0456: c = char.class;
0457: else if ("byte".equals(type))
0458: c = byte.class;
0459: else if ("short".equals(type))
0460: c = short.class;
0461: else if ("int".equals(type))
0462: c = int.class;
0463: else if ("long".equals(type))
0464: c = long.class;
0465: else if ("float".equals(type))
0466: c = float.class;
0467: else if ("double".equals(type))
0468: c = double.class;
0469: else if (type.indexOf('[') < 0)
0470: c = loader.loadClass(type);
0471:
0472: if (dims == 0)
0473: return c;
0474:
0475: if (dims == 1)
0476: return java.lang.reflect.Array.newInstance(c, 1).getClass();
0477:
0478: // Array of more than i dimension
0479: return java.lang.reflect.Array.newInstance(c, new int[dims])
0480: .getClass();
0481: }
0482:
0483: /**
0484: * Produces a String representing a call to the EL interpreter.
0485: * @param expression a String containing zero or more "${}" expressions
0486: * @param expectedType the expected type of the interpreted result
0487: * @param fnmapvar Variable pointing to a function map.
0488: * @param XmlEscape True if the result should do XML escaping
0489: * @return a String representing a call to the EL interpreter.
0490: */
0491: public static String interpreterCall(boolean isTagFile,
0492: String expression, Class expectedType, String fnmapvar,
0493: boolean XmlEscape) {
0494: /*
0495: * Determine which context object to use.
0496: */
0497: String jspCtxt = null;
0498: if (isTagFile)
0499: jspCtxt = "this.getJspContext()";
0500: else
0501: jspCtxt = "_jspx_page_context";
0502:
0503: /*
0504: * Determine whether to use the expected type's textual name
0505: * or, if it's a primitive, the name of its correspondent boxed
0506: * type.
0507: */
0508: String targetType = expectedType.getName();
0509: String primitiveConverterMethod = null;
0510: if (expectedType.isPrimitive()) {
0511: if (expectedType.equals(Boolean.TYPE)) {
0512: targetType = Boolean.class.getName();
0513: primitiveConverterMethod = "booleanValue";
0514: } else if (expectedType.equals(Byte.TYPE)) {
0515: targetType = Byte.class.getName();
0516: primitiveConverterMethod = "byteValue";
0517: } else if (expectedType.equals(Character.TYPE)) {
0518: targetType = Character.class.getName();
0519: primitiveConverterMethod = "charValue";
0520: } else if (expectedType.equals(Short.TYPE)) {
0521: targetType = Short.class.getName();
0522: primitiveConverterMethod = "shortValue";
0523: } else if (expectedType.equals(Integer.TYPE)) {
0524: targetType = Integer.class.getName();
0525: primitiveConverterMethod = "intValue";
0526: } else if (expectedType.equals(Long.TYPE)) {
0527: targetType = Long.class.getName();
0528: primitiveConverterMethod = "longValue";
0529: } else if (expectedType.equals(Float.TYPE)) {
0530: targetType = Float.class.getName();
0531: primitiveConverterMethod = "floatValue";
0532: } else if (expectedType.equals(Double.TYPE)) {
0533: targetType = Double.class.getName();
0534: primitiveConverterMethod = "doubleValue";
0535: }
0536: }
0537:
0538: if (primitiveConverterMethod != null) {
0539: XmlEscape = false;
0540: }
0541:
0542: /*
0543: * Build up the base call to the interpreter.
0544: */
0545: // XXX - We use a proprietary call to the interpreter for now
0546: // as the current standard machinery is inefficient and requires
0547: // lots of wrappers and adapters. This should all clear up once
0548: // the EL interpreter moves out of JSTL and into its own project.
0549: // In the future, this should be replaced by code that calls
0550: // ExpressionEvaluator.parseExpression() and then cache the resulting
0551: // expression objects. The interpreterCall would simply select
0552: // one of the pre-cached expressions and evaluate it.
0553: // Note that PageContextImpl implements VariableResolver and
0554: // the generated Servlet/SimpleTag implements FunctionMapper, so
0555: // that machinery is already in place (mroth).
0556: targetType = toJavaSourceType(targetType);
0557: StringBuffer call = new StringBuffer(
0558: "("
0559: + targetType
0560: + ") "
0561: + "org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate"
0562: + "(" + Generator.quote(expression) + ", "
0563: + targetType + ".class, " + "(PageContext)"
0564: + jspCtxt + ", " + fnmapvar + ", " + XmlEscape
0565: + ")");
0566:
0567: /*
0568: * Add the primitive converter method if we need to.
0569: */
0570: if (primitiveConverterMethod != null) {
0571: call.insert(0, "(");
0572: call.append(")." + primitiveConverterMethod + "()");
0573: }
0574:
0575: return call.toString();
0576: }
0577:
0578: /**
0579: * Validates the syntax of all ${} expressions within the given string.
0580: * @param where the approximate location of the expressions in the JSP page
0581: * @param expressions a string containing zero or more "${}" expressions
0582: * @param err an error dispatcher to use
0583: * @deprecated now delegated to the org.apache.el Package
0584: */
0585: public static void validateExpressions(Mark where,
0586: String expressions, Class expectedType,
0587: FunctionMapper functionMapper, ErrorDispatcher err)
0588: throws JasperException {
0589:
0590: // try {
0591: //
0592: // JspUtil.expressionEvaluator.parseExpression( expressions,
0593: // expectedType, functionMapper );
0594: // }
0595: // catch( ELParseException e ) {
0596: // err.jspError(where, "jsp.error.invalid.expression", expressions,
0597: // e.toString() );
0598: // }
0599: // catch( ELException e ) {
0600: // err.jspError(where, "jsp.error.invalid.expression", expressions,
0601: // e.toString() );
0602: // }
0603: }
0604:
0605: /**
0606: * Resets the temporary variable name.
0607: * (not thread-safe)
0608: */
0609: public static void resetTemporaryVariableName() {
0610: tempSequenceNumber = 0;
0611: }
0612:
0613: /**
0614: * Generates a new temporary variable name.
0615: * (not thread-safe)
0616: */
0617: public static String nextTemporaryVariableName() {
0618: return Constants.TEMP_VARIABLE_NAME_PREFIX
0619: + (tempSequenceNumber++);
0620: }
0621:
0622: public static String coerceToPrimitiveBoolean(String s,
0623: boolean isNamedAttribute) {
0624: if (isNamedAttribute) {
0625: return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToBoolean("
0626: + s + ")";
0627: } else {
0628: if (s == null || s.length() == 0)
0629: return "false";
0630: else
0631: return Boolean.valueOf(s).toString();
0632: }
0633: }
0634:
0635: public static String coerceToBoolean(String s,
0636: boolean isNamedAttribute) {
0637: if (isNamedAttribute) {
0638: return "(Boolean) org.apache.jasper.runtime.JspRuntimeLibrary.coerce("
0639: + s + ", Boolean.class)";
0640: } else {
0641: if (s == null || s.length() == 0) {
0642: return "new Boolean(false)";
0643: } else {
0644: // Detect format error at translation time
0645: return "new Boolean(" + Boolean.valueOf(s).toString()
0646: + ")";
0647: }
0648: }
0649: }
0650:
0651: public static String coerceToPrimitiveByte(String s,
0652: boolean isNamedAttribute) {
0653: if (isNamedAttribute) {
0654: return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToByte("
0655: + s + ")";
0656: } else {
0657: if (s == null || s.length() == 0)
0658: return "(byte) 0";
0659: else
0660: return "((byte)" + Byte.valueOf(s).toString() + ")";
0661: }
0662: }
0663:
0664: public static String coerceToByte(String s, boolean isNamedAttribute) {
0665: if (isNamedAttribute) {
0666: return "(Byte) org.apache.jasper.runtime.JspRuntimeLibrary.coerce("
0667: + s + ", Byte.class)";
0668: } else {
0669: if (s == null || s.length() == 0) {
0670: return "new Byte((byte) 0)";
0671: } else {
0672: // Detect format error at translation time
0673: return "new Byte((byte)" + Byte.valueOf(s).toString()
0674: + ")";
0675: }
0676: }
0677: }
0678:
0679: public static String coerceToChar(String s, boolean isNamedAttribute) {
0680: if (isNamedAttribute) {
0681: return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToChar("
0682: + s + ")";
0683: } else {
0684: if (s == null || s.length() == 0) {
0685: return "(char) 0";
0686: } else {
0687: char ch = s.charAt(0);
0688: // this trick avoids escaping issues
0689: return "((char) " + (int) ch + ")";
0690: }
0691: }
0692: }
0693:
0694: public static String coerceToCharacter(String s,
0695: boolean isNamedAttribute) {
0696: if (isNamedAttribute) {
0697: return "(Character) org.apache.jasper.runtime.JspRuntimeLibrary.coerce("
0698: + s + ", Character.class)";
0699: } else {
0700: if (s == null || s.length() == 0) {
0701: return "new Character((char) 0)";
0702: } else {
0703: char ch = s.charAt(0);
0704: // this trick avoids escaping issues
0705: return "new Character((char) " + (int) ch + ")";
0706: }
0707: }
0708: }
0709:
0710: public static String coerceToPrimitiveDouble(String s,
0711: boolean isNamedAttribute) {
0712: if (isNamedAttribute) {
0713: return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToDouble("
0714: + s + ")";
0715: } else {
0716: if (s == null || s.length() == 0)
0717: return "(double) 0";
0718: else
0719: return Double.valueOf(s).toString();
0720: }
0721: }
0722:
0723: public static String coerceToDouble(String s,
0724: boolean isNamedAttribute) {
0725: if (isNamedAttribute) {
0726: return "(Double) org.apache.jasper.runtime.JspRuntimeLibrary.coerce("
0727: + s + ", Double.class)";
0728: } else {
0729: if (s == null || s.length() == 0) {
0730: return "new Double(0)";
0731: } else {
0732: // Detect format error at translation time
0733: return "new Double(" + Double.valueOf(s).toString()
0734: + ")";
0735: }
0736: }
0737: }
0738:
0739: public static String coerceToPrimitiveFloat(String s,
0740: boolean isNamedAttribute) {
0741: if (isNamedAttribute) {
0742: return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToFloat("
0743: + s + ")";
0744: } else {
0745: if (s == null || s.length() == 0)
0746: return "(float) 0";
0747: else
0748: return Float.valueOf(s).toString() + "f";
0749: }
0750: }
0751:
0752: public static String coerceToFloat(String s,
0753: boolean isNamedAttribute) {
0754: if (isNamedAttribute) {
0755: return "(Float) org.apache.jasper.runtime.JspRuntimeLibrary.coerce("
0756: + s + ", Float.class)";
0757: } else {
0758: if (s == null || s.length() == 0) {
0759: return "new Float(0)";
0760: } else {
0761: // Detect format error at translation time
0762: return "new Float(" + Float.valueOf(s).toString()
0763: + "f)";
0764: }
0765: }
0766: }
0767:
0768: public static String coerceToInt(String s, boolean isNamedAttribute) {
0769: if (isNamedAttribute) {
0770: return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToInt("
0771: + s + ")";
0772: } else {
0773: if (s == null || s.length() == 0)
0774: return "0";
0775: else
0776: return Integer.valueOf(s).toString();
0777: }
0778: }
0779:
0780: public static String coerceToInteger(String s,
0781: boolean isNamedAttribute) {
0782: if (isNamedAttribute) {
0783: return "(Integer) org.apache.jasper.runtime.JspRuntimeLibrary.coerce("
0784: + s + ", Integer.class)";
0785: } else {
0786: if (s == null || s.length() == 0) {
0787: return "new Integer(0)";
0788: } else {
0789: // Detect format error at translation time
0790: return "new Integer(" + Integer.valueOf(s).toString()
0791: + ")";
0792: }
0793: }
0794: }
0795:
0796: public static String coerceToPrimitiveShort(String s,
0797: boolean isNamedAttribute) {
0798: if (isNamedAttribute) {
0799: return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToShort("
0800: + s + ")";
0801: } else {
0802: if (s == null || s.length() == 0)
0803: return "(short) 0";
0804: else
0805: return "((short) " + Short.valueOf(s).toString() + ")";
0806: }
0807: }
0808:
0809: public static String coerceToShort(String s,
0810: boolean isNamedAttribute) {
0811: if (isNamedAttribute) {
0812: return "(Short) org.apache.jasper.runtime.JspRuntimeLibrary.coerce("
0813: + s + ", Short.class)";
0814: } else {
0815: if (s == null || s.length() == 0) {
0816: return "new Short((short) 0)";
0817: } else {
0818: // Detect format error at translation time
0819: return "new Short(\"" + Short.valueOf(s).toString()
0820: + "\")";
0821: }
0822: }
0823: }
0824:
0825: public static String coerceToPrimitiveLong(String s,
0826: boolean isNamedAttribute) {
0827: if (isNamedAttribute) {
0828: return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToLong("
0829: + s + ")";
0830: } else {
0831: if (s == null || s.length() == 0)
0832: return "(long) 0";
0833: else
0834: return Long.valueOf(s).toString() + "l";
0835: }
0836: }
0837:
0838: public static String coerceToLong(String s, boolean isNamedAttribute) {
0839: if (isNamedAttribute) {
0840: return "(Long) org.apache.jasper.runtime.JspRuntimeLibrary.coerce("
0841: + s + ", Long.class)";
0842: } else {
0843: if (s == null || s.length() == 0) {
0844: return "new Long(0)";
0845: } else {
0846: // Detect format error at translation time
0847: return "new Long(" + Long.valueOf(s).toString() + "l)";
0848: }
0849: }
0850: }
0851:
0852: public static InputStream getInputStream(String fname,
0853: JarFile jarFile, JspCompilationContext ctxt,
0854: ErrorDispatcher err) throws JasperException, IOException {
0855:
0856: InputStream in = null;
0857:
0858: if (jarFile != null) {
0859: String jarEntryName = fname.substring(1, fname.length());
0860: ZipEntry jarEntry = jarFile.getEntry(jarEntryName);
0861: if (jarEntry == null) {
0862: err.jspError("jsp.error.file.not.found", fname);
0863: }
0864: in = jarFile.getInputStream(jarEntry);
0865: } else {
0866: in = ctxt.getResourceAsStream(fname);
0867: }
0868:
0869: if (in == null) {
0870: err.jspError("jsp.error.file.not.found", fname);
0871: }
0872:
0873: return in;
0874: }
0875:
0876: /**
0877: * Gets the fully-qualified class name of the tag handler corresponding to
0878: * the given tag file path.
0879: *
0880: * @param path Tag file path
0881: * @param err Error dispatcher
0882: *
0883: * @return Fully-qualified class name of the tag handler corresponding to
0884: * the given tag file path
0885: */
0886: public static String getTagHandlerClassName(String path,
0887: ErrorDispatcher err) throws JasperException {
0888:
0889: String className = null;
0890: int begin = 0;
0891: int index;
0892:
0893: index = path.lastIndexOf(".tag");
0894: if (index == -1) {
0895: err.jspError("jsp.error.tagfile.badSuffix", path);
0896: }
0897:
0898: //It's tempting to remove the ".tag" suffix here, but we can't.
0899: //If we remove it, the fully-qualified class name of this tag
0900: //could conflict with the package name of other tags.
0901: //For instance, the tag file
0902: // /WEB-INF/tags/foo.tag
0903: //would have fully-qualified class name
0904: // org.apache.jsp.tag.web.foo
0905: //which would conflict with the package name of the tag file
0906: // /WEB-INF/tags/foo/bar.tag
0907:
0908: index = path.indexOf(WEB_INF_TAGS);
0909: if (index != -1) {
0910: className = "org.apache.jsp.tag.web.";
0911: begin = index + WEB_INF_TAGS.length();
0912: } else {
0913: index = path.indexOf(META_INF_TAGS);
0914: if (index != -1) {
0915: className = "org.apache.jsp.tag.meta.";
0916: begin = index + META_INF_TAGS.length();
0917: } else {
0918: err.jspError("jsp.error.tagfile.illegalPath", path);
0919: }
0920: }
0921:
0922: className += makeJavaPackage(path.substring(begin));
0923:
0924: return className;
0925: }
0926:
0927: /**
0928: * Converts the given path to a Java package or fully-qualified class name
0929: *
0930: * @param path Path to convert
0931: *
0932: * @return Java package corresponding to the given path
0933: */
0934: public static final String makeJavaPackage(String path) {
0935: String classNameComponents[] = split(path, "/");
0936: StringBuffer legalClassNames = new StringBuffer();
0937: for (int i = 0; i < classNameComponents.length; i++) {
0938: legalClassNames
0939: .append(makeJavaIdentifier(classNameComponents[i]));
0940: if (i < classNameComponents.length - 1) {
0941: legalClassNames.append('.');
0942: }
0943: }
0944: return legalClassNames.toString();
0945: }
0946:
0947: /**
0948: * Splits a string into it's components.
0949: * @param path String to split
0950: * @param pat Pattern to split at
0951: * @return the components of the path
0952: */
0953: private static final String[] split(String path, String pat) {
0954: Vector comps = new Vector();
0955: int pos = path.indexOf(pat);
0956: int start = 0;
0957: while (pos >= 0) {
0958: if (pos > start) {
0959: String comp = path.substring(start, pos);
0960: comps.add(comp);
0961: }
0962: start = pos + pat.length();
0963: pos = path.indexOf(pat, start);
0964: }
0965: if (start < path.length()) {
0966: comps.add(path.substring(start));
0967: }
0968: String[] result = new String[comps.size()];
0969: for (int i = 0; i < comps.size(); i++) {
0970: result[i] = (String) comps.elementAt(i);
0971: }
0972: return result;
0973: }
0974:
0975: /**
0976: * Converts the given identifier to a legal Java identifier
0977: *
0978: * @param identifier Identifier to convert
0979: *
0980: * @return Legal Java identifier corresponding to the given identifier
0981: */
0982: public static final String makeJavaIdentifier(String identifier) {
0983: StringBuffer modifiedIdentifier = new StringBuffer(identifier
0984: .length());
0985: if (!Character.isJavaIdentifierStart(identifier.charAt(0))) {
0986: modifiedIdentifier.append('_');
0987: }
0988: for (int i = 0; i < identifier.length(); i++) {
0989: char ch = identifier.charAt(i);
0990: if (Character.isJavaIdentifierPart(ch) && ch != '_') {
0991: modifiedIdentifier.append(ch);
0992: } else if (ch == '.') {
0993: modifiedIdentifier.append('_');
0994: } else {
0995: modifiedIdentifier.append(mangleChar(ch));
0996: }
0997: }
0998: if (isJavaKeyword(modifiedIdentifier.toString())) {
0999: modifiedIdentifier.append('_');
1000: }
1001: return modifiedIdentifier.toString();
1002: }
1003:
1004: /**
1005: * Mangle the specified character to create a legal Java class name.
1006: */
1007: public static final String mangleChar(char ch) {
1008: char[] result = new char[5];
1009: result[0] = '_';
1010: result[1] = Character.forDigit((ch >> 12) & 0xf, 16);
1011: result[2] = Character.forDigit((ch >> 8) & 0xf, 16);
1012: result[3] = Character.forDigit((ch >> 4) & 0xf, 16);
1013: result[4] = Character.forDigit(ch & 0xf, 16);
1014: return new String(result);
1015: }
1016:
1017: /**
1018: * Test whether the argument is a Java keyword
1019: */
1020: public static boolean isJavaKeyword(String key) {
1021: int i = 0;
1022: int j = javaKeywords.length;
1023: while (i < j) {
1024: int k = (i + j) / 2;
1025: int result = javaKeywords[k].compareTo(key);
1026: if (result == 0) {
1027: return true;
1028: }
1029: if (result < 0) {
1030: i = k + 1;
1031: } else {
1032: j = k;
1033: }
1034: }
1035: return false;
1036: }
1037:
1038: /**
1039: * Converts the given Xml name to a legal Java identifier. This is
1040: * slightly more efficient than makeJavaIdentifier in that we only need
1041: * to worry about '.', '-', and ':' in the string. We also assume that
1042: * the resultant string is further concatenated with some prefix string
1043: * so that we don't have to worry about it being a Java key word.
1044: *
1045: * @param name Identifier to convert
1046: *
1047: * @return Legal Java identifier corresponding to the given identifier
1048: */
1049: public static final String makeXmlJavaIdentifier(String name) {
1050: if (name.indexOf('-') >= 0)
1051: name = replace(name, '-', "$1");
1052: if (name.indexOf('.') >= 0)
1053: name = replace(name, '.', "$2");
1054: if (name.indexOf(':') >= 0)
1055: name = replace(name, ':', "$3");
1056: return name;
1057: }
1058:
1059: static InputStreamReader getReader(String fname, String encoding,
1060: JarFile jarFile, JspCompilationContext ctxt,
1061: ErrorDispatcher err) throws JasperException, IOException {
1062:
1063: return getReader(fname, encoding, jarFile, ctxt, err, 0);
1064: }
1065:
1066: static InputStreamReader getReader(String fname, String encoding,
1067: JarFile jarFile, JspCompilationContext ctxt,
1068: ErrorDispatcher err, int skip) throws JasperException,
1069: IOException {
1070:
1071: InputStreamReader reader = null;
1072: InputStream in = getInputStream(fname, jarFile, ctxt, err);
1073: for (int i = 0; i < skip; i++) {
1074: in.read();
1075: }
1076: try {
1077: reader = new InputStreamReader(in, encoding);
1078: } catch (UnsupportedEncodingException ex) {
1079: err.jspError("jsp.error.unsupported.encoding", encoding);
1080: }
1081:
1082: return reader;
1083: }
1084:
1085: /**
1086: * Handles taking input from TLDs
1087: * 'java.lang.Object' -> 'java.lang.Object.class'
1088: * 'int' -> 'int.class'
1089: * 'void' -> 'Void.TYPE'
1090: * 'int[]' -> 'int[].class'
1091: *
1092: * @param type
1093: * @return
1094: */
1095: public static String toJavaSourceTypeFromTld(String type) {
1096: if (type == null || "void".equals(type)) {
1097: return "Void.TYPE";
1098: }
1099: return type + ".class";
1100: }
1101:
1102: /**
1103: * Class.getName() return arrays in the form "[[[<et>", where et,
1104: * the element type can be one of ZBCDFIJS or L<classname>;
1105: * It is converted into forms that can be understood by javac.
1106: */
1107: public static String toJavaSourceType(String type) {
1108:
1109: if (type.charAt(0) != '[') {
1110: return type;
1111: }
1112:
1113: int dims = 1;
1114: String t = null;
1115: for (int i = 1; i < type.length(); i++) {
1116: if (type.charAt(i) == '[') {
1117: dims++;
1118: } else {
1119: switch (type.charAt(i)) {
1120: case 'Z':
1121: t = "boolean";
1122: break;
1123: case 'B':
1124: t = "byte";
1125: break;
1126: case 'C':
1127: t = "char";
1128: break;
1129: case 'D':
1130: t = "double";
1131: break;
1132: case 'F':
1133: t = "float";
1134: break;
1135: case 'I':
1136: t = "int";
1137: break;
1138: case 'J':
1139: t = "long";
1140: break;
1141: case 'S':
1142: t = "short";
1143: break;
1144: case 'L':
1145: t = type.substring(i + 1, type.indexOf(';'));
1146: break;
1147: }
1148: break;
1149: }
1150: }
1151: StringBuffer resultType = new StringBuffer(t);
1152: for (; dims > 0; dims--) {
1153: resultType.append("[]");
1154: }
1155: return resultType.toString();
1156: }
1157:
1158: /**
1159: * Compute the canonical name from a Class instance. Note that a
1160: * simple replacment of '$' with '.' of a binary name would not work,
1161: * as '$' is a legal Java Identifier character.
1162: * @param c A instance of java.lang.Class
1163: * @return The canonical name of c.
1164: */
1165: public static String getCanonicalName(Class c) {
1166:
1167: String binaryName = c.getName();
1168: c = c.getDeclaringClass();
1169:
1170: if (c == null) {
1171: return binaryName;
1172: }
1173:
1174: StringBuffer buf = new StringBuffer(binaryName);
1175: do {
1176: buf.setCharAt(c.getName().length(), '.');
1177: c = c.getDeclaringClass();
1178: } while (c != null);
1179:
1180: return buf.toString();
1181: }
1182: }
|