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