001: /*
002: * Janino - An embedded Java[TM] compiler
003: *
004: * Copyright (c) 2006, Arno Unkrig
005: * All rights reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: * 2. Redistributions in binary form must reproduce the above
014: * copyright notice, this list of conditions and the following
015: * disclaimer in the documentation and/or other materials
016: * provided with the distribution.
017: * 3. The name of the author may not be used to endorse or promote
018: * products derived from this software without specific prior
019: * written permission.
020: *
021: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
022: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
023: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
024: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
025: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
026: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
027: * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
028: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
029: * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
030: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
031: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
032: */
033:
034: package org.codehaus.janino;
035:
036: /**
037: * Helper class that defines useful methods for handling "field descriptors"
038: * (JVMS 4.3.2) and "method descriptors" (JVMS 4.3.3).<p>
039: * Typical descriptors are:
040: * <ul>
041: * <li><code>I</code> Integer
042: * <li><code>[I</code> Array of integer
043: * <li><code>Lpkg1/pkg2/Cls;</code> Class
044: * <li><code>Lpkg1/pkg2/Outer$Inner;</code> Member class
045: * </ul>
046: */
047: public class Descriptor {
048: public static boolean isReference(String d) {
049: return d.length() > 1;
050: }
051:
052: public static boolean isClassOrInterfaceReference(String d) {
053: return d.charAt(0) == 'L';
054: }
055:
056: public static boolean isArrayReference(String d) {
057: return d.charAt(0) == '[';
058: }
059:
060: public static String getComponentDescriptor(String d) {
061: if (d.charAt(0) != '[')
062: throw new RuntimeException(
063: "Cannot determine component descriptor from non-array descriptor \""
064: + d + "\"");
065: return d.substring(1);
066: }
067:
068: public static short size(String d) {
069: if (d.equals(Descriptor.VOID))
070: return 0;
071: if (Descriptor.hasSize1(d))
072: return 1;
073: if (Descriptor.hasSize2(d))
074: return 2;
075: throw new RuntimeException("No size defined for type \""
076: + Descriptor.toString(d) + "\"");
077: }
078:
079: public static boolean hasSize1(String d) {
080: if (d.length() == 1)
081: return "BCFISZ".indexOf(d) != -1;
082: return Descriptor.isReference(d);
083: }
084:
085: public static boolean hasSize2(String d) {
086: return d.equals(Descriptor.LONG) || d.equals(Descriptor.DOUBLE);
087: }
088:
089: // Pretty-print.
090: public static String toString(String d) {
091: int idx = 0;
092: StringBuffer sb = new StringBuffer();
093: if (d.charAt(0) == '(') {
094: ++idx;
095: sb.append("(");
096: while (idx < d.length() && d.charAt(idx) != ')') {
097: if (idx != 1)
098: sb.append(", ");
099: idx = Descriptor.toString(d, idx, sb);
100: }
101: if (idx >= d.length())
102: throw new RuntimeException("Invalid descriptor \"" + d
103: + "\"");
104: sb.append(") => ");
105: ++idx;
106: }
107: Descriptor.toString(d, idx, sb);
108: return sb.toString();
109: }
110:
111: private static int toString(String d, int idx, StringBuffer sb) {
112: int dimensions = 0;
113: while (idx < d.length() && d.charAt(idx) == '[') {
114: ++dimensions;
115: ++idx;
116: }
117: if (idx >= d.length())
118: throw new RuntimeException("Invalid descriptor \"" + d
119: + "\"");
120: switch (d.charAt(idx)) {
121: case 'L': {
122: int idx2 = d.indexOf(';', idx);
123: if (idx2 == -1)
124: throw new RuntimeException("Invalid descriptor \"" + d
125: + "\"");
126: sb.append(d.substring(idx + 1, idx2).replace('/', '.'));
127: idx = idx2;
128: }
129: break;
130: case 'V':
131: sb.append("void");
132: break;
133: case 'B':
134: sb.append("byte");
135: break;
136: case 'C':
137: sb.append("char");
138: break;
139: case 'D':
140: sb.append("double");
141: break;
142: case 'F':
143: sb.append("float");
144: break;
145: case 'I':
146: sb.append("int");
147: break;
148: case 'J':
149: sb.append("long");
150: break;
151: case 'S':
152: sb.append("short");
153: break;
154: case 'Z':
155: sb.append("boolean");
156: break;
157: default:
158: throw new RuntimeException("Invalid descriptor \"" + d
159: + "\"");
160: }
161: for (; dimensions > 0; --dimensions)
162: sb.append("[]");
163: return idx + 1;
164: }
165:
166: /**
167: * Convert a class name as defined by "Class.getName()" into a
168: * descriptor.
169: */
170: public static String fromClassName(String className) {
171: if (className.equals("void"))
172: return Descriptor.VOID;
173: if (className.equals("byte"))
174: return Descriptor.BYTE;
175: if (className.equals("char"))
176: return Descriptor.CHAR;
177: if (className.equals("double"))
178: return Descriptor.DOUBLE;
179: if (className.equals("float"))
180: return Descriptor.FLOAT;
181: if (className.equals("int"))
182: return Descriptor.INT;
183: if (className.equals("long"))
184: return Descriptor.LONG;
185: if (className.equals("short"))
186: return Descriptor.SHORT;
187: if (className.equals("boolean"))
188: return Descriptor.BOOLEAN;
189: if (className.startsWith("["))
190: return className.replace('.', '/');
191: return 'L' + className.replace('.', '/') + ';';
192: }
193:
194: /**
195: * Convert a class name in the "internal form" as described in JVMS 4.2 into a descriptor.
196: * <p>
197: * Also implement the encoding of array types as described in JVMS 4.4.1.
198: */
199: public static String fromInternalForm(String internalForm) {
200: if (internalForm.charAt(0) == '[')
201: return internalForm;
202: return 'L' + internalForm + ';';
203: }
204:
205: /**
206: * Convert a field descriptor into a class name as defined by {@link
207: * Class#getName()}.
208: */
209: public static String toClassName(String d) {
210: if (d.length() == 1) {
211: if (d.equals(Descriptor.VOID))
212: return "void";
213: if (d.equals(Descriptor.BYTE))
214: return "byte";
215: if (d.equals(Descriptor.CHAR))
216: return "char";
217: if (d.equals(Descriptor.DOUBLE))
218: return "double";
219: if (d.equals(Descriptor.FLOAT))
220: return "float";
221: if (d.equals(Descriptor.INT))
222: return "int";
223: if (d.equals(Descriptor.LONG))
224: return "long";
225: if (d.equals(Descriptor.SHORT))
226: return "short";
227: if (d.equals(Descriptor.BOOLEAN))
228: return "boolean";
229: } else {
230: char firstChar = d.charAt(0);
231: if (firstChar == 'L' && d.endsWith(";")) {
232:
233: // Class or interface -- convert "Ljava/lang/String;" to "java.lang.String".
234: return d.substring(1, d.length() - 1).replace('/', '.');
235: }
236: if (firstChar == '[') {
237:
238: // Array type -- convert "[Ljava/lang/String;" to "[Ljava.lang.String;".
239: return d.replace('/', '.');
240: }
241: }
242: throw new RuntimeException("(Invalid field descriptor \"" + d
243: + "\")");
244: }
245:
246: /**
247: * Convert a descriptor into the "internal form" as defined by JVMS 4.2.
248: */
249: public static String toInternalForm(String d) {
250: if (d.charAt(0) != 'L')
251: throw new RuntimeException(
252: "Attempt to convert non-class descriptor \"" + d
253: + "\" into internal form");
254: return d.substring(1, d.length() - 1);
255: }
256:
257: public static boolean isPrimitive(String d) {
258: return d.length() == 1
259: && "VBCDFIJSZ".indexOf(d.charAt(0)) != -1;
260: }
261:
262: public static boolean isPrimitiveNumeric(String d) {
263: return d.length() == 1 && "BDFIJSC".indexOf(d.charAt(0)) != -1;
264: }
265:
266: /**
267: * Returns the package name of a class or interface reference descriptor,
268: * or <code>null</code> if the class or interface is declared in the
269: * default package.
270: */
271: public static String getPackageName(String d) {
272: if (d.charAt(0) != 'L')
273: throw new RuntimeException(
274: "Attempt to get package name of non-class descriptor \""
275: + d + "\"");
276: int idx = d.lastIndexOf('/');
277: return idx == -1 ? null : d.substring(1, idx).replace('/', '.');
278: }
279:
280: /**
281: * Check whether two reference types are declared in the same package.
282: */
283: public static boolean areInSamePackage(String d1, String d2) {
284: String packageName1 = Descriptor.getPackageName(d1);
285: String packageName2 = Descriptor.getPackageName(d2);
286: return packageName1 == null ? packageName2 == null
287: : packageName1.equals(packageName2);
288: }
289:
290: public final static String VOID = "V";
291: public final static String BYTE = "B";
292: public final static String CHAR = "C";
293: public final static String DOUBLE = "D";
294: public final static String FLOAT = "F";
295: public final static String INT = "I";
296: public final static String LONG = "J";
297: public final static String SHORT = "S";
298: public final static String BOOLEAN = "Z";
299: public final static String OBJECT = "Ljava/lang/Object;";
300: public final static String STRING = "Ljava/lang/String;";
301: public final static String CLASS = "Ljava/lang/Class;";
302: public final static String THROWABLE = "Ljava/lang/Throwable;";
303: public final static String RUNTIME_EXCEPTION = "Ljava/lang/RuntimeException;";
304: public final static String ERROR = "Ljava/lang/Error;";
305: public final static String CLONEABLE = "Ljava/lang/Cloneable;";
306: public final static String SERIALIZABLE = "Ljava/io/Serializable;";
307: }
|