1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.jasper.compiler;
19
20 import java.io.CharArrayWriter;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.InputStreamReader;
24 import java.io.UnsupportedEncodingException;
25 import java.util.Vector;
26 import java.util.jar.JarFile;
27 import java.util.zip.ZipEntry;
28
29 import javax.el.FunctionMapper;
30 import javax.servlet.jsp.el.ExpressionEvaluator;
31
32
33 import org.apache.el.ExpressionFactoryImpl;
34 import org.apache.jasper.Constants;
35 import org.apache.jasper.JasperException;
36 import org.apache.jasper.JspCompilationContext;
37 import org.apache.jasper.el.ExpressionEvaluatorImpl;
38 import org.xml.sax.Attributes;
39
40 /**
41 * This class has all the utility method(s).
42 * Ideally should move all the bean containers here.
43 *
44 * @author Mandar Raje.
45 * @author Rajiv Mordani.
46 * @author Danno Ferrin
47 * @author Pierre Delisle
48 * @author Shawn Bayern
49 * @author Mark Roth
50 */
51 public class JspUtil {
52
53 private static final String WEB_INF_TAGS = "/WEB-INF/tags/";
54 private static final String META_INF_TAGS = "/META-INF/tags/";
55
56 // Delimiters for request-time expressions (JSP and XML syntax)
57 private static final String OPEN_EXPR = "<%=";
58 private static final String CLOSE_EXPR = "%>";
59 private static final String OPEN_EXPR_XML = "%=";
60 private static final String CLOSE_EXPR_XML = "%";
61
62 private static int tempSequenceNumber = 0;
63
64 //private static ExpressionEvaluatorImpl expressionEvaluator
65 //= new ExpressionEvaluatorImpl();
66
67 //tc6
68 private final static ExpressionEvaluator expressionEvaluator =
69 new ExpressionEvaluatorImpl(new ExpressionFactoryImpl());
70
71 private static final String javaKeywords[] = {
72 "abstract", "assert", "boolean", "break", "byte", "case",
73 "catch", "char", "class", "const", "continue",
74 "default", "do", "double", "else", "enum", "extends",
75 "final", "finally", "float", "for", "goto",
76 "if", "implements", "import", "instanceof", "int",
77 "interface", "long", "native", "new", "package",
78 "private", "protected", "public", "return", "short",
79 "static", "strictfp", "super", "switch", "synchronized",
80 "this", "throws", "transient", "try", "void",
81 "volatile", "while" };
82
83 public static final int CHUNKSIZE = 1024;
84
85 public static char[] removeQuotes(char []chars) {
86 CharArrayWriter caw = new CharArrayWriter();
87 for (int i = 0; i < chars.length; i++) {
88 if (chars[i] == '%' && chars[i+1] == '\\' &&
89 chars[i+2] == '>') {
90 caw.write('%');
91 caw.write('>');
92 i = i + 2;
93 } else {
94 caw.write(chars[i]);
95 }
96 }
97 return caw.toCharArray();
98 }
99
100 public static char[] escapeQuotes (char []chars) {
101 // Prescan to convert %\> to %>
102 String s = new String(chars);
103 while (true) {
104 int n = s.indexOf("%\\>");
105 if (n < 0)
106 break;
107 StringBuffer sb = new StringBuffer(s.substring(0, n));
108 sb.append("%>");
109 sb.append(s.substring(n + 3));
110 s = sb.toString();
111 }
112 chars = s.toCharArray();
113 return (chars);
114
115
116 // Escape all backslashes not inside a Java string literal
117 /*
118 CharArrayWriter caw = new CharArrayWriter();
119 boolean inJavaString = false;
120 for (int i = 0; i < chars.length; i++) {
121 if (chars[i] == '"') inJavaString = !inJavaString;
122 // escape out the escape character
123 if (!inJavaString && (chars[i] == '\\')) caw.write('\\');
124 caw.write(chars[i]);
125 }
126 return caw.toCharArray();
127 */
128 }
129
130 /**
131 * Checks if the token is a runtime expression.
132 * In standard JSP syntax, a runtime expression starts with '<%' and
133 * ends with '%>'. When the JSP document is in XML syntax, a runtime
134 * expression starts with '%=' and ends with '%'.
135 *
136 * @param token The token to be checked
137 * return whether the token is a runtime expression or not.
138 */
139 public static boolean isExpression(String token, boolean isXml) {
140 String openExpr;
141 String closeExpr;
142 if (isXml) {
143 openExpr = OPEN_EXPR_XML;
144 closeExpr = CLOSE_EXPR_XML;
145 } else {
146 openExpr = OPEN_EXPR;
147 closeExpr = CLOSE_EXPR;
148 }
149 if (token.startsWith(openExpr) && token.endsWith(closeExpr)) {
150 return true;
151 } else {
152 return false;
153 }
154 }
155
156 /**
157 * @return the "expression" part of a runtime expression,
158 * taking the delimiters out.
159 */
160 public static String getExpr (String expression, boolean isXml) {
161 String returnString;
162 String openExpr;
163 String closeExpr;
164 if (isXml) {
165 openExpr = OPEN_EXPR_XML;
166 closeExpr = CLOSE_EXPR_XML;
167 } else {
168 openExpr = OPEN_EXPR;
169 closeExpr = CLOSE_EXPR;
170 }
171 int length = expression.length();
172 if (expression.startsWith(openExpr) &&
173 expression.endsWith(closeExpr)) {
174 returnString = expression.substring(
175 openExpr.length(), length - closeExpr.length());
176 } else {
177 returnString = "";
178 }
179 return returnString;
180 }
181
182 /**
183 * Takes a potential expression and converts it into XML form
184 */
185 public static String getExprInXml(String expression) {
186 String returnString;
187 int length = expression.length();
188
189 if (expression.startsWith(OPEN_EXPR)
190 && expression.endsWith(CLOSE_EXPR)) {
191 returnString = expression.substring (1, length - 1);
192 } else {
193 returnString = expression;
194 }
195
196 return escapeXml(returnString.replace(Constants.ESC, '$'));
197 }
198
199 /**
200 * Checks to see if the given scope is valid.
201 *
202 * @param scope The scope to be checked
203 * @param n The Node containing the 'scope' attribute whose value is to be
204 * checked
205 * @param err error dispatcher
206 *
207 * @throws JasperException if scope is not null and different from
208 * "page", "request", "session", and
209 * "application"
210 */
211 public static void checkScope(String scope, Node n, ErrorDispatcher err)
212 throws JasperException {
213 if (scope != null && !scope.equals("page") && !scope.equals("request")
214 && !scope.equals("session") && !scope.equals("application")) {
215 err.jspError(n, "jsp.error.invalid.scope", scope);
216 }
217 }
218
219 /**
220 * Checks if all mandatory attributes are present and if all attributes
221 * present have valid names. Checks attributes specified as XML-style
222 * attributes as well as attributes specified using the jsp:attribute
223 * standard action.
224 */
225 public static void checkAttributes(String typeOfTag,
226 Node n,
227 ValidAttribute[] validAttributes,
228 ErrorDispatcher err)
229 throws JasperException {
230 Attributes attrs = n.getAttributes();
231 Mark start = n.getStart();
232 boolean valid = true;
233
234 // AttributesImpl.removeAttribute is broken, so we do this...
235 int tempLength = (attrs == null) ? 0 : attrs.getLength();
236 Vector temp = new Vector(tempLength, 1);
237 for (int i = 0; i < tempLength; i++) {
238 String qName = attrs.getQName(i);
239 if ((!qName.equals("xmlns")) && (!qName.startsWith("xmlns:")))
240 temp.addElement(qName);
241 }
242
243 // Add names of attributes specified using jsp:attribute
244 Node.Nodes tagBody = n.getBody();
245 if( tagBody != null ) {
246 int numSubElements = tagBody.size();
247 for( int i = 0; i < numSubElements; i++ ) {
248 Node node = tagBody.getNode( i );
249 if( node instanceof Node.NamedAttribute ) {
250 String attrName = node.getAttributeValue( "name" );
251 temp.addElement( attrName );
252 // Check if this value appear in the attribute of the node
253 if (n.getAttributeValue(attrName) != null) {
254 err.jspError(n, "jsp.error.duplicate.name.jspattribute",
255 attrName);
256 }
257 }
258 else {
259 // Nothing can come before jsp:attribute, and only
260 // jsp:body can come after it.
261 break;
262 }
263 }
264 }
265
266 /*
267 * First check to see if all the mandatory attributes are present.
268 * If so only then proceed to see if the other attributes are valid
269 * for the particular tag.
270 */
271 String missingAttribute = null;
272
273 for (int i = 0; i < validAttributes.length; i++) {
274 int attrPos;
275 if (validAttributes[i].mandatory) {
276 attrPos = temp.indexOf(validAttributes[i].name);
277 if (attrPos != -1) {
278 temp.remove(attrPos);
279 valid = true;
280 } else {
281 valid = false;
282 missingAttribute = validAttributes[i].name;
283 break;
284 }
285 }
286 }
287
288 // If mandatory attribute is missing then the exception is thrown
289 if (!valid)
290 err.jspError(start, "jsp.error.mandatory.attribute", typeOfTag,
291 missingAttribute);
292
293 // Check to see if there are any more attributes for the specified tag.
294 int attrLeftLength = temp.size();
295 if (attrLeftLength == 0)
296 return;
297
298 // Now check to see if the rest of the attributes are valid too.
299 String attribute = null;
300
301 for (int j = 0; j < attrLeftLength; j++) {
302 valid = false;
303 attribute = (String) temp.elementAt(j);
304 for (int i = 0; i < validAttributes.length; i++) {
305 if (attribute.equals(validAttributes[i].name)) {
306 valid = true;
307 break;
308 }
309 }
310 if (!valid)
311 err.jspError(start, "jsp.error.invalid.attribute", typeOfTag,
312 attribute);
313 }
314 // XXX *could* move EL-syntax validation here... (sb)
315 }
316
317 public static String escapeQueryString(String unescString) {
318 if ( unescString == null )
319 return null;
320
321 String escString = "";
322 String shellSpChars = "\\\"";
323
324 for(int index=0; index<unescString.length(); index++) {
325 char nextChar = unescString.charAt(index);
326
327 if( shellSpChars.indexOf(nextChar) != -1 )
328 escString += "\\";
329
330 escString += nextChar;
331 }
332 return escString;
333 }
334
335 /**
336 * Escape the 5 entities defined by XML.
337 */
338 public static String escapeXml(String s) {
339 if (s == null) return null;
340 StringBuffer sb = new StringBuffer();
341 for(int i=0; i<s.length(); i++) {
342 char c = s.charAt(i);
343 if (c == '<') {
344 sb.append("<");
345 } else if (c == '>') {
346 sb.append(">");
347 } else if (c == '\'') {
348 sb.append("'");
349 } else if (c == '&') {
350 sb.append("&");
351 } else if (c == '"') {
352 sb.append(""");
353 } else {
354 sb.append(c);
355 }
356 }
357 return sb.toString();
358 }
359
360 /**
361 * Replaces any occurrences of the character <tt>replace</tt> with the
362 * string <tt>with</tt>.
363 */
364 public static String replace(String name, char replace, String with) {
365 StringBuffer buf = new StringBuffer();
366 int begin = 0;
367 int end;
368 int last = name.length();
369
370 while (true) {
371 end = name.indexOf(replace, begin);
372 if (end < 0) {
373 end = last;
374 }
375 buf.append(name.substring(begin, end));
376 if (end == last) {
377 break;
378 }
379 buf.append(with);
380 begin = end + 1;
381 }
382
383 return buf.toString();
384 }
385
386 public static class ValidAttribute {
387 String name;
388 boolean mandatory;
389 boolean rtexprvalue; // not used now
390
391 public ValidAttribute (String name, boolean mandatory,
392 boolean rtexprvalue )
393 {
394 this.name = name;
395 this.mandatory = mandatory;
396 this.rtexprvalue = rtexprvalue;
397 }
398
399 public ValidAttribute (String name, boolean mandatory) {
400 this( name, mandatory, false );
401 }
402
403 public ValidAttribute (String name) {
404 this (name, false);
405 }
406 }
407
408 /**
409 * Convert a String value to 'boolean'.
410 * Besides the standard conversions done by
411 * Boolean.valueOf(s).booleanValue(), the value "yes"
412 * (ignore case) is also converted to 'true'.
413 * If 's' is null, then 'false' is returned.
414 *
415 * @param s the string to be converted
416 * @return the boolean value associated with the string s
417 */
418 public static boolean booleanValue(String s) {
419 boolean b = false;
420 if (s != null) {
421 if (s.equalsIgnoreCase("yes")) {
422 b = true;
423 } else {
424 b = Boolean.valueOf(s).booleanValue();
425 }
426 }
427 return b;
428 }
429
430 /**
431 * Returns the <tt>Class</tt> object associated with the class or
432 * interface with the given string name.
433 *
434 * <p> The <tt>Class</tt> object is determined by passing the given string
435 * name to the <tt>Class.forName()</tt> method, unless the given string
436 * name represents a primitive type, in which case it is converted to a
437 * <tt>Class</tt> object by appending ".class" to it (e.g., "int.class").
438 */
439 public static Class toClass(String type, ClassLoader loader)
440 throws ClassNotFoundException {
441
442 Class c = null;
443 int i0 = type.indexOf('[');
444 int dims = 0;
445 if (i0 > 0) {
446 // This is an array. Count the dimensions
447 for (int i = 0; i < type.length(); i++) {
448 if (type.charAt(i) == '[')
449 dims++;
450 }
451 type = type.substring(0, i0);
452 }
453
454 if ("boolean".equals(type))
455 c = boolean.class;
456 else if ("char".equals(type))
457 c = char.class;
458 else if ("byte".equals(type))
459 c = byte.class;
460 else if ("short".equals(type))
461 c = short.class;
462 else if ("int".equals(type))
463 c = int.class;
464 else if ("long".equals(type))
465 c = long.class;
466 else if ("float".equals(type))
467 c = float.class;
468 else if ("double".equals(type))
469 c = double.class;
470 else if (type.indexOf('[') < 0)
471 c = loader.loadClass(type);
472
473 if (dims == 0)
474 return c;
475
476 if (dims == 1)
477 return java.lang.reflect.Array.newInstance(c, 1).getClass();
478
479 // Array of more than i dimension
480 return java.lang.reflect.Array.newInstance(c, new int[dims]).getClass();
481 }
482
483 /**
484 * Produces a String representing a call to the EL interpreter.
485 * @param expression a String containing zero or more "${}" expressions
486 * @param expectedType the expected type of the interpreted result
487 * @param fnmapvar Variable pointing to a function map.
488 * @param XmlEscape True if the result should do XML escaping
489 * @return a String representing a call to the EL interpreter.
490 */
491 public static String interpreterCall(boolean isTagFile,
492 String expression,
493 Class expectedType,
494 String fnmapvar,
495 boolean XmlEscape )
496 {
497 /*
498 * Determine which context object to use.
499 */
500 String jspCtxt = null;
501 if (isTagFile)
502 jspCtxt = "this.getJspContext()";
503 else
504 jspCtxt = "_jspx_page_context";
505
506 /*
507 * Determine whether to use the expected type's textual name
508 * or, if it's a primitive, the name of its correspondent boxed
509 * type.
510 */
511 String targetType = expectedType.getName();
512 String primitiveConverterMethod = null;
513 if (expectedType.isPrimitive()) {
514 if (expectedType.equals(Boolean.TYPE)) {
515 targetType = Boolean.class.getName();
516 primitiveConverterMethod = "booleanValue";
517 } else if (expectedType.equals(Byte.TYPE)) {
518 targetType = Byte.class.getName();
519 primitiveConverterMethod = "byteValue";
520 } else if (expectedType.equals(Character.TYPE)) {
521 targetType = Character.class.getName();
522 primitiveConverterMethod = "charValue";
523 } else if (expectedType.equals(Short.TYPE)) {
524 targetType = Short.class.getName();
525 primitiveConverterMethod = "shortValue";
526 } else if (expectedType.equals(Integer.TYPE)) {
527 targetType = Integer.class.getName();
528 primitiveConverterMethod = "intValue";
529 } else if (expectedType.equals(Long.TYPE)) {
530 targetType = Long.class.getName();
531 primitiveConverterMethod = "longValue";
532 } else if (expectedType.equals(Float.TYPE)) {
533 targetType = Float.class.getName();
534 primitiveConverterMethod = "floatValue";
535 } else if (expectedType.equals(Double.TYPE)) {
536 targetType = Double.class.getName();
537 primitiveConverterMethod = "doubleValue";
538 }
539 }
540
541 if (primitiveConverterMethod != null) {
542 XmlEscape = false;
543 }
544
545 /*
546 * Build up the base call to the interpreter.
547 */
548 // XXX - We use a proprietary call to the interpreter for now
549 // as the current standard machinery is inefficient and requires
550 // lots of wrappers and adapters. This should all clear up once
551 // the EL interpreter moves out of JSTL and into its own project.
552 // In the future, this should be replaced by code that calls
553 // ExpressionEvaluator.parseExpression() and then cache the resulting
554 // expression objects. The interpreterCall would simply select
555 // one of the pre-cached expressions and evaluate it.
556 // Note that PageContextImpl implements VariableResolver and
557 // the generated Servlet/SimpleTag implements FunctionMapper, so
558 // that machinery is already in place (mroth).
559 targetType = toJavaSourceType(targetType);
560 StringBuffer call = new StringBuffer(
561 "(" + targetType + ") "
562 + "org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate"
563 + "(" + Generator.quote(expression) + ", "
564 + targetType + ".class, "
565 + "(PageContext)" + jspCtxt
566 + ", " + fnmapvar
567 + ", " + XmlEscape
568 + ")");
569
570 /*
571 * Add the primitive converter method if we need to.
572 */
573 if (primitiveConverterMethod != null) {
574 call.insert(0, "(");
575 call.append(")." + primitiveConverterMethod + "()");
576 }
577
578 return call.toString();
579 }
580
581 /**
582 * Validates the syntax of all ${} expressions within the given string.
583 * @param where the approximate location of the expressions in the JSP page
584 * @param expressions a string containing zero or more "${}" expressions
585 * @param err an error dispatcher to use
586 * @deprecated now delegated to the org.apache.el Package
587 */
588 public static void validateExpressions(Mark where,
589 String expressions,
590 Class expectedType,
591 FunctionMapper functionMapper,
592 ErrorDispatcher err)
593 throws JasperException {
594
595 // try {
596 //
597 // JspUtil.expressionEvaluator.parseExpression( expressions,
598 // expectedType, functionMapper );
599 // }
600 // catch( ELParseException e ) {
601 // err.jspError(where, "jsp.error.invalid.expression", expressions,
602 // e.toString() );
603 // }
604 // catch( ELException e ) {
605 // err.jspError(where, "jsp.error.invalid.expression", expressions,
606 // e.toString() );
607 // }
608 }
609
610 /**
611 * Resets the temporary variable name.
612 * (not thread-safe)
613 */
614 public static void resetTemporaryVariableName() {
615 tempSequenceNumber = 0;
616 }
617
618 /**
619 * Generates a new temporary variable name.
620 * (not thread-safe)
621 */
622 public static String nextTemporaryVariableName() {
623 return Constants.TEMP_VARIABLE_NAME_PREFIX + (tempSequenceNumber++);
624 }
625
626 public static String coerceToPrimitiveBoolean(String s,
627 boolean isNamedAttribute) {
628 if (isNamedAttribute) {
629 return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToBoolean(" + s + ")";
630 } else {
631 if (s == null || s.length() == 0)
632 return "false";
633 else
634 return Boolean.valueOf(s).toString();
635 }
636 }
637
638 public static String coerceToBoolean(String s, boolean isNamedAttribute) {
639 if (isNamedAttribute) {
640 return "(Boolean) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Boolean.class)";
641 } else {
642 if (s == null || s.length() == 0) {
643 return "new Boolean(false)";
644 } else {
645 // Detect format error at translation time
646 return "new Boolean(" + Boolean.valueOf(s).toString() + ")";
647 }
648 }
649 }
650
651 public static String coerceToPrimitiveByte(String s,
652 boolean isNamedAttribute) {
653 if (isNamedAttribute) {
654 return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToByte(" + s + ")";
655 } else {
656 if (s == null || s.length() == 0)
657 return "(byte) 0";
658 else
659 return "((byte)" + Byte.valueOf(s).toString() + ")";
660 }
661 }
662
663 public static String coerceToByte(String s, boolean isNamedAttribute) {
664 if (isNamedAttribute) {
665 return "(Byte) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Byte.class)";
666 } else {
667 if (s == null || s.length() == 0) {
668 return "new Byte((byte) 0)";
669 } else {
670 // Detect format error at translation time
671 return "new Byte((byte)" + Byte.valueOf(s).toString() + ")";
672 }
673 }
674 }
675
676 public static String coerceToChar(String s, boolean isNamedAttribute) {
677 if (isNamedAttribute) {
678 return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToChar(" + s + ")";
679 } else {
680 if (s == null || s.length() == 0) {
681 return "(char) 0";
682 } else {
683 char ch = s.charAt(0);
684 // this trick avoids escaping issues
685 return "((char) " + (int) ch + ")";
686 }
687 }
688 }
689
690 public static String coerceToCharacter(String s, boolean isNamedAttribute) {
691 if (isNamedAttribute) {
692 return "(Character) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Character.class)";
693 } else {
694 if (s == null || s.length() == 0) {
695 return "new Character((char) 0)";
696 } else {
697 char ch = s.charAt(0);
698 // this trick avoids escaping issues
699 return "new Character((char) " + (int) ch + ")";
700 }
701 }
702 }
703
704 public static String coerceToPrimitiveDouble(String s,
705 boolean isNamedAttribute) {
706 if (isNamedAttribute) {
707 return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToDouble(" + s + ")";
708 } else {
709 if (s == null || s.length() == 0)
710 return "(double) 0";
711 else
712 return Double.valueOf(s).toString();
713 }
714 }
715
716 public static String coerceToDouble(String s, boolean isNamedAttribute) {
717 if (isNamedAttribute) {
718 return "(Double) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Double.class)";
719 } else {
720 if (s == null || s.length() == 0) {
721 return "new Double(0)";
722 } else {
723 // Detect format error at translation time
724 return "new Double(" + Double.valueOf(s).toString() + ")";
725 }
726 }
727 }
728
729 public static String coerceToPrimitiveFloat(String s,
730 boolean isNamedAttribute) {
731 if (isNamedAttribute) {
732 return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToFloat(" + s + ")";
733 } else {
734 if (s == null || s.length() == 0)
735 return "(float) 0";
736 else
737 return Float.valueOf(s).toString() + "f";
738 }
739 }
740
741 public static String coerceToFloat(String s, boolean isNamedAttribute) {
742 if (isNamedAttribute) {
743 return "(Float) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Float.class)";
744 } else {
745 if (s == null || s.length() == 0) {
746 return "new Float(0)";
747 } else {
748 // Detect format error at translation time
749 return "new Float(" + Float.valueOf(s).toString() + "f)";
750 }
751 }
752 }
753
754 public static String coerceToInt(String s, boolean isNamedAttribute) {
755 if (isNamedAttribute) {
756 return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToInt(" + s + ")";
757 } else {
758 if (s == null || s.length() == 0)
759 return "0";
760 else
761 return Integer.valueOf(s).toString();
762 }
763 }
764
765 public static String coerceToInteger(String s, boolean isNamedAttribute) {
766 if (isNamedAttribute) {
767 return "(Integer) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Integer.class)";
768 } else {
769 if (s == null || s.length() == 0) {
770 return "new Integer(0)";
771 } else {
772 // Detect format error at translation time
773 return "new Integer(" + Integer.valueOf(s).toString() + ")";
774 }
775 }
776 }
777
778 public static String coerceToPrimitiveShort(String s,
779 boolean isNamedAttribute) {
780 if (isNamedAttribute) {
781 return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToShort(" + s + ")";
782 } else {
783 if (s == null || s.length() == 0)
784 return "(short) 0";
785 else
786 return "((short) " + Short.valueOf(s).toString() + ")";
787 }
788 }
789
790 public static String coerceToShort(String s, boolean isNamedAttribute) {
791 if (isNamedAttribute) {
792 return "(Short) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Short.class)";
793 } else {
794 if (s == null || s.length() == 0) {
795 return "new Short((short) 0)";
796 } else {
797 // Detect format error at translation time
798 return "new Short(\"" + Short.valueOf(s).toString() + "\")";
799 }
800 }
801 }
802
803 public static String coerceToPrimitiveLong(String s,
804 boolean isNamedAttribute) {
805 if (isNamedAttribute) {
806 return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToLong(" + s + ")";
807 } else {
808 if (s == null || s.length() == 0)
809 return "(long) 0";
810 else
811 return Long.valueOf(s).toString() + "l";
812 }
813 }
814
815 public static String coerceToLong(String s, boolean isNamedAttribute) {
816 if (isNamedAttribute) {
817 return "(Long) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Long.class)";
818 } else {
819 if (s == null || s.length() == 0) {
820 return "new Long(0)";
821 } else {
822 // Detect format error at translation time
823 return "new Long(" + Long.valueOf(s).toString() + "l)";
824 }
825 }
826 }
827
828 public static InputStream getInputStream(String fname, JarFile jarFile,
829 JspCompilationContext ctxt,
830 ErrorDispatcher err)
831 throws JasperException, IOException {
832
833 InputStream in = null;
834
835 if (jarFile != null) {
836 String jarEntryName = fname.substring(1, fname.length());
837 ZipEntry jarEntry = jarFile.getEntry(jarEntryName);
838 if (jarEntry == null) {
839 err.jspError("jsp.error.file.not.found", fname);
840 }
841 in = jarFile.getInputStream(jarEntry);
842 } else {
843 in = ctxt.getResourceAsStream(fname);
844 }
845
846 if (in == null) {
847 err.jspError("jsp.error.file.not.found", fname);
848 }
849
850 return in;
851 }
852
853 /**
854 * Gets the fully-qualified class name of the tag handler corresponding to
855 * the given tag file path.
856 *
857 * @param path Tag file path
858 * @param err Error dispatcher
859 *
860 * @return Fully-qualified class name of the tag handler corresponding to
861 * the given tag file path
862 */
863 public static String getTagHandlerClassName(String path,
864 ErrorDispatcher err)
865 throws JasperException {
866
867 String className = null;
868 int begin = 0;
869 int index;
870
871 index = path.lastIndexOf(".tag");
872 if (index == -1) {
873 err.jspError("jsp.error.tagfile.badSuffix", path);
874 }
875
876 //It's tempting to remove the ".tag" suffix here, but we can't.
877 //If we remove it, the fully-qualified class name of this tag
878 //could conflict with the package name of other tags.
879 //For instance, the tag file
880 // /WEB-INF/tags/foo.tag
881 //would have fully-qualified class name
882 // org.apache.jsp.tag.web.foo
883 //which would conflict with the package name of the tag file
884 // /WEB-INF/tags/foo/bar.tag
885
886 index = path.indexOf(WEB_INF_TAGS);
887 if (index != -1) {
888 className = "org.apache.jsp.tag.web.";
889 begin = index + WEB_INF_TAGS.length();
890 } else {
891 index = path.indexOf(META_INF_TAGS);
892 if (index != -1) {
893 className = "org.apache.jsp.tag.meta.";
894 begin = index + META_INF_TAGS.length();
895 } else {
896 err.jspError("jsp.error.tagfile.illegalPath", path);
897 }
898 }
899
900 className += makeJavaPackage(path.substring(begin));
901
902 return className;
903 }
904
905 /**
906 * Converts the given path to a Java package or fully-qualified class name
907 *
908 * @param path Path to convert
909 *
910 * @return Java package corresponding to the given path
911 */
912 public static final String makeJavaPackage(String path) {
913 String classNameComponents[] = split(path,"/");
914 StringBuffer legalClassNames = new StringBuffer();
915 for (int i = 0; i < classNameComponents.length; i++) {
916 legalClassNames.append(makeJavaIdentifier(classNameComponents[i]));
917 if (i < classNameComponents.length - 1) {
918 legalClassNames.append('.');
919 }
920 }
921 return legalClassNames.toString();
922 }
923
924 /**
925 * Splits a string into it's components.
926 * @param path String to split
927 * @param pat Pattern to split at
928 * @return the components of the path
929 */
930 private static final String [] split(String path, String pat) {
931 Vector comps = new Vector();
932 int pos = path.indexOf(pat);
933 int start = 0;
934 while( pos >= 0 ) {
935 if(pos > start ) {
936 String comp = path.substring(start,pos);
937 comps.add(comp);
938 }
939 start = pos + pat.length();
940 pos = path.indexOf(pat,start);
941 }
942 if( start < path.length()) {
943 comps.add(path.substring(start));
944 }
945 String [] result = new String[comps.size()];
946 for(int i=0; i < comps.size(); i++) {
947 result[i] = (String)comps.elementAt(i);
948 }
949 return result;
950 }
951
952 /**
953 * Converts the given identifier to a legal Java identifier
954 *
955 * @param identifier Identifier to convert
956 *
957 * @return Legal Java identifier corresponding to the given identifier
958 */
959 public static final String makeJavaIdentifier(String identifier) {
960 StringBuffer modifiedIdentifier =
961 new StringBuffer(identifier.length());
962 if (!Character.isJavaIdentifierStart(identifier.charAt(0))) {
963 modifiedIdentifier.append('_');
964 }
965 for (int i = 0; i < identifier.length(); i++) {
966 char ch = identifier.charAt(i);
967 if (Character.isJavaIdentifierPart(ch) && ch != '_') {
968 modifiedIdentifier.append(ch);
969 } else if (ch == '.') {
970 modifiedIdentifier.append('_');
971 } else {
972 modifiedIdentifier.append(mangleChar(ch));
973 }
974 }
975 if (isJavaKeyword(modifiedIdentifier.toString())) {
976 modifiedIdentifier.append('_');
977 }
978 return modifiedIdentifier.toString();
979 }
980
981 /**
982 * Mangle the specified character to create a legal Java class name.
983 */
984 public static final String mangleChar(char ch) {
985 char[] result = new char[5];
986 result[0] = '_';
987 result[1] = Character.forDigit((ch >> 12) & 0xf, 16);
988 result[2] = Character.forDigit((ch >> 8) & 0xf, 16);
989 result[3] = Character.forDigit((ch >> 4) & 0xf, 16);
990 result[4] = Character.forDigit(ch & 0xf, 16);
991 return new String(result);
992 }
993
994 /**
995 * Test whether the argument is a Java keyword
996 */
997 public static boolean isJavaKeyword(String key) {
998 int i = 0;
999 int j = javaKeywords.length;
1000 while (i < j) {
1001 int k = (i+j)/2;
1002 int result = javaKeywords[k].compareTo(key);
1003 if (result == 0) {
1004 return true;
1005 }
1006 if (result < 0) {
1007 i = k+1;
1008 } else {
1009 j = k;
1010 }
1011 }
1012 return false;
1013 }
1014
1015 /**
1016 * Converts the given Xml name to a legal Java identifier. This is
1017 * slightly more efficient than makeJavaIdentifier in that we only need
1018 * to worry about '.', '-', and ':' in the string. We also assume that
1019 * the resultant string is further concatenated with some prefix string
1020 * so that we don't have to worry about it being a Java key word.
1021 *
1022 * @param name Identifier to convert
1023 *
1024 * @return Legal Java identifier corresponding to the given identifier
1025 */
1026 public static final String makeXmlJavaIdentifier(String name) {
1027 if (name.indexOf('-') >= 0)
1028 name = replace(name, '-', "$1");
1029 if (name.indexOf('.') >= 0)
1030 name = replace(name, '.', "$2");
1031 if (name.indexOf(':') >= 0)
1032 name = replace(name, ':', "$3");
1033 return name;
1034 }
1035
1036 static InputStreamReader getReader(String fname, String encoding,
1037 JarFile jarFile,
1038 JspCompilationContext ctxt,
1039 ErrorDispatcher err)
1040 throws JasperException, IOException {
1041
1042 return getReader(fname, encoding, jarFile, ctxt, err, 0);
1043 }
1044
1045 static InputStreamReader getReader(String fname, String encoding,
1046 JarFile jarFile,
1047 JspCompilationContext ctxt,
1048 ErrorDispatcher err, int skip)
1049 throws JasperException, IOException {
1050
1051 InputStreamReader reader = null;
1052 InputStream in = getInputStream(fname, jarFile, ctxt, err);
1053 for (int i = 0; i < skip; i++) {
1054 in.read();
1055 }
1056 try {
1057 reader = new InputStreamReader(in, encoding);
1058 } catch (UnsupportedEncodingException ex) {
1059 err.jspError("jsp.error.unsupported.encoding", encoding);
1060 }
1061
1062 return reader;
1063 }
1064
1065 /**
1066 * Handles taking input from TLDs
1067 * 'java.lang.Object' -> 'java.lang.Object.class'
1068 * 'int' -> 'int.class'
1069 * 'void' -> 'Void.TYPE'
1070 * 'int[]' -> 'int[].class'
1071 *
1072 * @param type
1073 * @return
1074 */
1075 public static String toJavaSourceTypeFromTld(String type) {
1076 if (type == null || "void".equals(type)) {
1077 return "Void.TYPE";
1078 }
1079 return type + ".class";
1080 }
1081
1082 /**
1083 * Class.getName() return arrays in the form "[[[<et>", where et,
1084 * the element type can be one of ZBCDFIJS or L<classname>;
1085 * It is converted into forms that can be understood by javac.
1086 */
1087 public static String toJavaSourceType(String type) {
1088
1089 if (type.charAt(0) != '[') {
1090 return type;
1091 }
1092
1093 int dims = 1;
1094 String t = null;
1095 for (int i = 1; i < type.length(); i++) {
1096 if (type.charAt(i) == '[') {
1097 dims++;
1098 } else {
1099 switch (type.charAt(i)) {
1100 case 'Z': t = "boolean"; break;
1101 case 'B': t = "byte"; break;
1102 case 'C': t = "char"; break;
1103 case 'D': t = "double"; break;
1104 case 'F': t = "float"; break;
1105 case 'I': t = "int"; break;
1106 case 'J': t = "long"; break;
1107 case 'S': t = "short"; break;
1108 case 'L': t = type.substring(i+1, type.indexOf(';')); break;
1109 }
1110 break;
1111 }
1112 }
1113 StringBuffer resultType = new StringBuffer(t);
1114 for (; dims > 0; dims--) {
1115 resultType.append("[]");
1116 }
1117 return resultType.toString();
1118 }
1119
1120 /**
1121 * Compute the canonical name from a Class instance. Note that a
1122 * simple replacment of '$' with '.' of a binary name would not work,
1123 * as '$' is a legal Java Identifier character.
1124 * @param c A instance of java.lang.Class
1125 * @return The canonical name of c.
1126 */
1127 public static String getCanonicalName(Class c) {
1128
1129 String binaryName = c.getName();
1130 c = c.getDeclaringClass();
1131
1132 if (c == null) {
1133 return binaryName;
1134 }
1135
1136 StringBuffer buf = new StringBuffer(binaryName);
1137 do {
1138 buf.setCharAt(c.getName().length(), '.');
1139 c = c.getDeclaringClass();
1140 } while ( c != null);
1141
1142 return buf.toString();
1143 }
1144 }