001: /**
002: *
003: * Licensed to the Apache Software Foundation (ASF) under one or more
004: * contributor license agreements. See the NOTICE file distributed with
005: * this work for additional information regarding copyright ownership.
006: * The ASF licenses this file to You under the Apache License, Version 2.0
007: * (the "License"); you may not use this file except in compliance with
008: * the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */package org.apache.geronimo.corba.util;
018:
019: import java.lang.reflect.Array;
020: import java.util.HashMap;
021:
022: /**
023: * Utility class for loading classes by a variety of name variations.
024: * <p/>
025: * Supported names types are:
026: * <p/>
027: * 1) Fully qualified class name (e.g., "java.lang.String", "org.apache.geronimo.corba.util.ClassLoading"
028: * 2) Method signature encoding ("Ljava.lang.String;", "J", "I", etc.)
029: * 3) Primitive type names ("int", "boolean", etc.)
030: * 4) Method array signature strings ("[I", "[Ljava.lang.String")
031: * 5) Arrays using Java code format ("int[]", "java.lang.String[][]")
032: * <p/>
033: * The classes are loaded using the provided class loader. For the basic types, the primitive
034: * reflection types are returned.
035: *
036: * @version $Rev: 450758 $
037: */
038: public class ClassLoading {
039:
040: /**
041: * Table for mapping primitive class names/signatures to the implementing
042: * class object
043: */
044: private static final HashMap PRIMITIVE_CLASS_MAP = new HashMap();
045:
046: /**
047: * Table for mapping primitive classes back to their name signature type, which
048: * allows a reverse mapping to be performed from a class object into a resolvable
049: * signature.
050: */
051: private static final HashMap CLASS_TO_SIGNATURE_MAP = new HashMap();
052:
053: /**
054: * Setup the primitives map. We make any entry for each primitive class using both the
055: * human readable name and the method signature shorthand type.
056: */
057: static {
058: PRIMITIVE_CLASS_MAP.put("boolean", boolean.class);
059: PRIMITIVE_CLASS_MAP.put("Z", boolean.class);
060: PRIMITIVE_CLASS_MAP.put("byte", byte.class);
061: PRIMITIVE_CLASS_MAP.put("B", byte.class);
062: PRIMITIVE_CLASS_MAP.put("char", char.class);
063: PRIMITIVE_CLASS_MAP.put("C", char.class);
064: PRIMITIVE_CLASS_MAP.put("short", short.class);
065: PRIMITIVE_CLASS_MAP.put("S", short.class);
066: PRIMITIVE_CLASS_MAP.put("int", int.class);
067: PRIMITIVE_CLASS_MAP.put("I", int.class);
068: PRIMITIVE_CLASS_MAP.put("long", long.class);
069: PRIMITIVE_CLASS_MAP.put("J", long.class);
070: PRIMITIVE_CLASS_MAP.put("float", float.class);
071: PRIMITIVE_CLASS_MAP.put("F", float.class);
072: PRIMITIVE_CLASS_MAP.put("double", double.class);
073: PRIMITIVE_CLASS_MAP.put("D", double.class);
074: PRIMITIVE_CLASS_MAP.put("void", void.class);
075: PRIMITIVE_CLASS_MAP.put("V", void.class);
076:
077: // Now build a reverse mapping table. The table above has a many-to-one mapping for
078: // class names. To do the reverse, we need to pick just one. As long as the
079: // returned name supports "round tripping" of the requests, this will work fine.
080:
081: CLASS_TO_SIGNATURE_MAP.put(boolean.class, "Z");
082: CLASS_TO_SIGNATURE_MAP.put(byte.class, "B");
083: CLASS_TO_SIGNATURE_MAP.put(char.class, "C");
084: CLASS_TO_SIGNATURE_MAP.put(short.class, "S");
085: CLASS_TO_SIGNATURE_MAP.put(int.class, "I");
086: CLASS_TO_SIGNATURE_MAP.put(long.class, "J");
087: CLASS_TO_SIGNATURE_MAP.put(float.class, "F");
088: CLASS_TO_SIGNATURE_MAP.put(double.class, "D");
089: CLASS_TO_SIGNATURE_MAP.put(void.class, "V");
090: }
091:
092: /**
093: * Load a class that matches the requested name, using the provided class loader context.
094: * <p/>
095: * The class name may be a standard class name, the name of a primitive type Java
096: * reflection class (e.g., "boolean" or "int"), or a type in method type signature
097: * encoding. Array classes in either encoding form are also processed.
098: *
099: * @param className The name of the required class.
100: * @param classLoader The class loader used to resolve the class object.
101: * @return The Class object resolved from "className".
102: * @throws ClassNotFoundException When unable to resolve the class object.
103: * @throws IllegalArgumentException If either argument is null.
104: */
105: public static Class loadClass(String className,
106: ClassLoader classLoader) throws ClassNotFoundException {
107:
108: // the tests require IllegalArgumentExceptions for null values on either of these.
109: if (className == null) {
110: throw new IllegalArgumentException("className is null");
111: }
112:
113: if (classLoader == null) {
114: throw new IllegalArgumentException("classLoader is null");
115: }
116: // The easiest case is a proper class name. We just have the class loader resolve this.
117: // If the class loader throws a ClassNotFoundException, then we need to check each of the
118: // special name encodings we support.
119: try {
120: return classLoader.loadClass(className);
121: } catch (ClassNotFoundException ignore) {
122: // if not found, continue on to the other name forms.
123: }
124:
125: // The second easiest version to resolve is a direct map to a primitive type name
126: // or method signature. Check our name-to-class map for one of those.
127: Class resolvedClass = (Class) PRIMITIVE_CLASS_MAP
128: .get(className);
129: if (resolvedClass != null) {
130: return resolvedClass;
131: }
132:
133: // Class names in method signature have the format "Lfully.resolved.name;",
134: // so if it ends in a semicolon and begins with an "L", this must be in
135: // this format. Have the class loader try to load this. There are no other
136: // options if this fails, so just allow the class loader to throw the
137: // ClassNotFoundException.
138: if (className.endsWith(";") && className.startsWith("L")) {
139: // pick out the name portion
140: String typeName = className.substring(1,
141: className.length() - 1);
142: // and delegate the loading to the class loader.
143: return classLoader.loadClass(typeName);
144: }
145:
146: // All we have left now are the array types. Method signature array types
147: // have a series of leading "[" characters to specify the number of dimensions.
148: // The other array type we handle uses trailing "[]" for the dimensions, just
149: // like the Java language syntax.
150:
151: // first check for the signature form ([[[[type).
152: if (className.charAt(0) == '[') {
153: // we have at least one array marker, now count how many leading '['s we have
154: // to get the dimension count.
155: int count = 0;
156: int nameLen = className.length();
157:
158: while (count < nameLen && className.charAt(count) == '[') {
159: count++;
160: }
161:
162: // pull of the name subtype, which is everything after the last '['
163: String arrayTypeName = className.substring(count, className
164: .length());
165: // resolve the type using a recursive call, which will load any of the primitive signature
166: // types as well as class names.
167: Class arrayType = loadClass(arrayTypeName, classLoader);
168:
169: // Resolving array types require a little more work. The array classes are
170: // created dynamically when the first instance of a given dimension and type is
171: // created. We need to create one using reflection to do this.
172: return getArrayClass(arrayType, count);
173: }
174:
175: // ok, last chance. Now check for an array specification in Java language
176: // syntax. This will be a type name followed by pairs of "[]" to indicate
177: // the number of dimensions.
178: if (className.endsWith("[]")) {
179: // get the base component class name and the arrayDimensions
180: int count = 0;
181: int position = className.length();
182:
183: while (position > 1
184: && className.substring(position - 2, position)
185: .equals("[]")) {
186: // count this dimension
187: count++;
188: // and step back the probe position.
189: position -= 2;
190: }
191:
192: // position now points at the location of the last successful test. This makes it
193: // easy to pick off the class name.
194:
195: String typeName = className.substring(0, position);
196:
197: // load the base type, again, doing this recursively
198: Class arrayType = loadClass(typeName, classLoader);
199: // and turn this into the class object
200: return getArrayClass(arrayType, count);
201: }
202:
203: // We're out of options, just toss an exception over the wall.
204: throw new ClassNotFoundException(className);
205: }
206:
207: /**
208: * Map a class object back to a class name. The returned class object
209: * must be "round trippable", which means
210: * <p/>
211: * type == ClassLoading.loadClass(ClassLoading.getClassName(type), classLoader)
212: * <p/>
213: * must be true. To ensure this, the class name is always returned in
214: * method signature format.
215: *
216: * @param type The class object we convert into name form.
217: * @return A string representation of the class name, in method signature
218: * format.
219: */
220: public static String getClassName(Class type) {
221: StringBuffer name = new StringBuffer();
222:
223: // we test these in reverse order from the resolution steps,
224: // first handling arrays, then primitive types, and finally
225: // "normal" class objects.
226:
227: // First handle arrays. If a class is an array, the type is
228: // element stored at that level. So, for a 2-dimensional array
229: // of ints, the top-level type will be "[I". We need to loop
230: // down the hierarchy until we hit a non-array type.
231: while (type.isArray()) {
232: // add another array indicator at the front of the name,
233: // and continue with the next type.
234: name.append('[');
235: type = type.getComponentType();
236: }
237:
238: // we're down to the base type. If this is a primitive, then
239: // we poke in the single-character type specifier.
240: if (type.isPrimitive()) {
241: name.append((String) CLASS_TO_SIGNATURE_MAP.get(type));
242: }
243: // a "normal" class. This gets expressing using the "Lmy.class.name;" syntax.
244: else {
245: name.append('L');
246: name.append(type.getName());
247: name.append(';');
248: }
249: return name.toString();
250: }
251:
252: private static Class getArrayClass(Class type, int dimension) {
253: // Array.newInstance() requires an array of the requested number of dimensions
254: // that gives the size for each dimension. We just request 0 in each of the
255: // dimentions, which is not unlike a black hole sigularity.
256: int dimensions[] = new int[dimension];
257: // create an instance and return the associated class object.
258: return Array.newInstance(type, dimensions).getClass();
259: }
260: }
|