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