001: package jaxx.reflect;
002:
003: import java.io.*;
004: import java.lang.reflect.*;
005: import java.net.*;
006: import java.util.*;
007:
008: import jaxx.*;
009: import jaxx.compiler.*;
010: import jaxx.runtime.*;
011:
012: /** Mirrors the class <code>java.lang.ClassLoader</code>. JAXX uses <code>ClassDescriptor</code> instead of <code>Class</code>
013: * almost everywhere so that it can handle circular dependencies (there can't be a <code>Class</code> object for an uncompiled
014: * JAXX or Java source file, and a compiler must be allow references to symbols in uncompiled source files in order to handle
015: * circular dependencies).
016: */
017: public class ClassDescriptorLoader {
018: private static Map/*<String, ClassDescriptor>*/descriptors = new HashMap/*<String, ClassDescriptor>*/();
019:
020: private ClassDescriptorLoader() {
021: }
022:
023: public static synchronized ClassDescriptor getClassDescriptor(
024: String className) throws ClassNotFoundException {
025: return getClassDescriptor(className,
026: ClassDescriptorLoader.class.getClassLoader());
027: }
028:
029: public static synchronized ClassDescriptor getClassDescriptor(
030: String className, ClassLoader classLoader)
031: throws ClassNotFoundException {
032: ClassDescriptor result = (ClassDescriptor) descriptors
033: .get(className);
034: if (result == null) {
035: if (JAXXCompiler.getSymbolTable(className) != null)
036: result = createClassDescriptorFromSymbolTable(
037: className, classLoader);
038: else {
039: if (classLoader == null)
040: classLoader = ClassDescriptorLoader.class
041: .getClassLoader();
042:
043: String relativePath = className.replaceAll("\\.", "/");
044: String relativePathPattern = ".*";// + className + ".*"; // used to ensure that the located resource has the right character cases
045:
046: // find the most recently updated source for the class -- Java source, JAXX source, or compiled class file
047: long javaLastModified = -1;
048: URL javaFile = classLoader.getResource(relativePath
049: + ".java");
050: if (javaFile != null
051: && javaFile.toString().startsWith("file:")
052: && javaFile.toString().matches(
053: relativePathPattern))
054: javaLastModified = JAXXCompiler.URLtoFile(javaFile)
055: .lastModified();
056:
057: long classLastModified = -1;
058: URL classFile = classLoader.getResource(relativePath
059: + ".class");
060: if (classFile != null
061: && classFile.toString().startsWith("file:")
062: && classFile.toString().matches(
063: relativePathPattern))
064: classLastModified = JAXXCompiler.URLtoFile(
065: classFile).lastModified();
066:
067: long jaxxLastModified = -1;
068: URL jaxxFile = classLoader.getResource(relativePath
069: + ".jaxx");
070: if (jaxxFile != null
071: && jaxxFile.toString().startsWith("file:")
072: && jaxxFile.toString().matches(
073: relativePathPattern)) {
074: File jaxxFilePath = JAXXCompiler
075: .URLtoFile(jaxxFile);
076: jaxxLastModified = jaxxFilePath.lastModified();
077: String simplePath = jaxxFilePath.getPath();
078: simplePath = simplePath.substring(0, simplePath
079: .length()
080: - ".jaxx".length());
081: File cssFilePath = new File(simplePath + ".css");
082: if (cssFilePath.exists())
083: jaxxLastModified = Math.max(jaxxLastModified,
084: cssFilePath.lastModified());
085: File scriptFilePath = new File(simplePath
086: + ".script");
087: if (scriptFilePath.exists())
088: jaxxLastModified = Math.max(jaxxLastModified,
089: scriptFilePath.lastModified());
090: }
091:
092: if (jaxxLastModified != -1
093: && JAXXCompiler.getSymbolTable(className) == null)
094: jaxxLastModified = -1; // file has been modified, but wasn't included in this
095: // compilation set so we don't have a symbol table
096:
097: if (javaLastModified != -1 || classLastModified != -1
098: || jaxxLastModified != -1) {
099: if (jaxxLastModified > classLastModified
100: && jaxxLastModified > javaLastModified)
101: result = createClassDescriptorFromSymbolTable(
102: className, classLoader);
103: else if (javaLastModified > classLastModified
104: && javaLastModified > jaxxLastModified)
105: result = createClassDescriptorFromJavaSource(
106: javaFile, classLoader);
107: }
108: // else work off of the class file. This also handles the case where the class is available, but wasn't in a location where
109: // we could check its last modified date (in a JAR, over the network, etc.)
110: if (result == null) {
111: Class javaClass = getClass(className, classLoader);
112: result = createClassDescriptorFromClass(javaClass);
113: }
114: }
115: descriptors.put(className, result);
116: }
117: return result;
118: }
119:
120: public static ClassDescriptor getClassDescriptor(Class javaClass) {
121: try {
122: return getClassDescriptor(javaClass.getName(), javaClass
123: .getClassLoader());
124: } catch (ClassNotFoundException e) {
125: throw new RuntimeException(e);
126: }
127: }
128:
129: public static Class getClass(String className,
130: ClassLoader classLoader) throws ClassNotFoundException {
131: if (className.equals("boolean"))
132: return boolean.class;
133: else if (className.equals("byte"))
134: return byte.class;
135: else if (className.equals("short"))
136: return short.class;
137: else if (className.equals("int"))
138: return int.class;
139: else if (className.equals("long"))
140: return long.class;
141: else if (className.equals("float"))
142: return float.class;
143: else if (className.equals("double"))
144: return double.class;
145: else if (className.equals("char"))
146: return char.class;
147: else {
148: int arrayCount = 0;
149: while (className.endsWith("[]")) {
150: arrayCount++;
151: className = className.substring(0,
152: className.length() - 2);
153: }
154: if (arrayCount > 0) {
155: className = "L" + className + ";";
156: while (arrayCount > 0) {
157: className = "[" + className;
158: arrayCount--;
159: }
160: }
161: try {
162: return classLoader != null ? Class.forName(className,
163: true, classLoader) : Class.forName(className);
164: } catch (NoClassDefFoundError e) {
165: throw new ClassNotFoundException(e.toString());
166: }
167: }
168: }
169:
170: private static MethodDescriptor createMethodDescriptor(
171: Method javaMethod, ClassLoader classLoader) {
172: String methodName = javaMethod.getName();
173: int modifiers = javaMethod.getModifiers();
174: String returnType = javaMethod.getReturnType().getName();
175: Class[] javaParameters = javaMethod.getParameterTypes();
176: String[] parameters = new String[javaParameters.length];
177: for (int i = 0; i < parameters.length; i++)
178: parameters[i] = javaParameters[i].getName();
179: return new MethodDescriptor(methodName, modifiers, returnType,
180: parameters, classLoader);
181: }
182:
183: private static FieldDescriptor createFieldDescriptor(
184: Field javaField, ClassLoader classLoader) {
185: String fieldName = javaField.getName();
186: int modifiers = javaField.getModifiers();
187: String type = javaField.getType().getName();
188: return new FieldDescriptor(fieldName, modifiers, type,
189: classLoader);
190: }
191:
192: private static JAXXObjectDescriptor getJAXXObjectDescriptor(
193: Class jaxxClass) {
194: if (!JAXXObject.class.isAssignableFrom(jaxxClass)
195: || jaxxClass == JAXXObject.class)
196: return null;
197: try {
198: Method getJAXXObjectDescriptor = jaxxClass.getMethod(
199: "$getJAXXObjectDescriptor", new Class[0]);
200: return (JAXXObjectDescriptor) getJAXXObjectDescriptor
201: .invoke(null, new Object[0]);
202: } catch (NoSuchMethodException e) {
203: throw new CompilerException(
204: "Expected JAXXObject "
205: + jaxxClass.getName()
206: + " to have a static method named $getJAXXObjectDescriptor");
207: } catch (IllegalAccessException e) {
208: throw new CompilerException("Expected JAXXObject "
209: + jaxxClass.getName()
210: + "'s $getJAXXObjectDescriptor method to be public");
211: } catch (InvocationTargetException e) {
212: throw new RuntimeException(e);
213: }
214: }
215:
216: private static ClassDescriptor createClassDescriptorFromJavaSource(
217: URL javaSource, ClassLoader classLoader)
218: throws ClassNotFoundException {
219: try {
220: InputStream in = javaSource.openStream();
221: Reader reader = new InputStreamReader(in);
222: ClassDescriptor result = JavaFileParser.parseJavaFile(
223: javaSource.toString(), reader, classLoader);
224: reader.close();
225: return result;
226: } catch (IOException e) {
227: throw new ClassNotFoundException(e.toString());
228: }
229: }
230:
231: private static ClassDescriptor createClassDescriptorFromSymbolTable(
232: String className, ClassLoader classLoader)
233: throws ClassNotFoundException {
234: final JAXXCompiler compiler = JAXXCompiler
235: .getJAXXCompiler(className);
236: final SymbolTable symbolTable = JAXXCompiler
237: .getSymbolTable(className);
238: if (symbolTable == null)
239: throw new CompilerException(
240: "Internal error: no symbol table was generated for class '"
241: + className + "'");
242: ClassDescriptor super class = getClassDescriptor(symbolTable
243: .getSuperclassName(), classLoader);
244: List/*<MethodDescriptor>*/publicMethods = symbolTable
245: .getScriptMethods();
246: List/*<FieldDescriptor>*/publicFields = symbolTable
247: .getScriptFields();
248: List/*<MethodDescriptor>*/declaredMethods = new ArrayList/*<MethodDescriptor>*/(
249: publicMethods);
250: List/*<FieldDescriptor>*/declaredFields = new ArrayList/*<FieldDescriptor>*/(
251: publicFields);
252: Iterator/*<MethodDescriptor>*/methods = publicMethods
253: .iterator();
254: while (methods.hasNext()) {
255: MethodDescriptor method = (MethodDescriptor) methods.next();
256: if (!Modifier.isPublic(method.getModifiers()))
257: methods.remove();
258: }
259: Iterator/*<FieldDescriptor>*/fields = publicFields.iterator();
260: while (fields.hasNext()) {
261: FieldDescriptor field = (FieldDescriptor) fields.next();
262: if (!Modifier.isPublic(field.getModifiers()))
263: fields.remove();
264: }
265: publicMethods.addAll(Arrays.asList(super class
266: .getMethodDescriptors()));
267: publicFields.addAll(Arrays.asList(super class
268: .getFieldDescriptors()));
269: int dotPos = className.lastIndexOf(".");
270: String packageName = dotPos != -1 ? className.substring(0,
271: dotPos) : null;
272: Set/*<String>*/interfaces = new HashSet/*<String>*/();
273: ClassDescriptor[] super classInterfaces = super class
274: .getInterfaces();
275: for (int i = 0; i < super classInterfaces.length; i++)
276: interfaces.add(super classInterfaces[i].getName());
277: interfaces.add(JAXXObject.class.getName());
278: return new ClassDescriptor(className, packageName, symbolTable
279: .getSuperclassName(), (String[]) interfaces
280: .toArray(new String[interfaces.size()]), false, false,
281: null, null, classLoader,
282: (MethodDescriptor[]) publicMethods
283: .toArray(new MethodDescriptor[publicMethods
284: .size()]),
285: (FieldDescriptor[]) publicFields
286: .toArray(new FieldDescriptor[publicFields
287: .size()])) {
288: public FieldDescriptor getDeclaredFieldDescriptor(
289: String name) throws NoSuchFieldException {
290: String type = (String) symbolTable.getClassTagIds()
291: .get(name);
292: if (type != null)
293: return new FieldDescriptor(name,
294: Modifier.PROTECTED, type, compiler
295: .getClassLoader());
296: else
297: throw new NoSuchFieldException(name);
298: }
299:
300: public MethodDescriptor getDeclaredMethodDescriptor(
301: String name, ClassDescriptor[] parameterTypes)
302: throws NoSuchMethodException {
303: throw new NoSuchMethodException(name);
304: }
305:
306: public JAXXObjectDescriptor getJAXXObjectDescriptor() {
307: return compiler.getJAXXObjectDescriptor();
308: }
309: };
310: }
311:
312: private static ClassDescriptor createClassDescriptorFromClass(
313: final Class javaClass) {
314: String name = javaClass.getName();
315: Package p = javaClass.getPackage();
316: String packageName = p != null ? p.getName() : null;
317: Class super class = javaClass.getSuperclass();
318: String super className = super class != null ? super class
319: .getName() : null;
320: Class[] interfaces = javaClass.getInterfaces();
321: String[] interfaceNames = new String[interfaces.length];
322: for (int i = 0; i < interfaces.length; i++)
323: interfaceNames[i] = interfaces[i].getName();
324: boolean isInterface = javaClass.isInterface();
325: boolean isArray = javaClass.isArray();
326: String componentTypeName = isArray ? javaClass
327: .getComponentType().getName() : null;
328: JAXXObjectDescriptor jaxxObjectDescriptor = getJAXXObjectDescriptor(javaClass);
329: ClassLoader classLoader = javaClass.getClassLoader();
330: Method[] javaMethods = javaClass.getMethods();
331: MethodDescriptor[] methods = new MethodDescriptor[javaMethods.length];
332: for (int i = 0; i < methods.length; i++)
333: methods[i] = createMethodDescriptor(javaMethods[i],
334: javaClass.getClassLoader());
335: Field[] javaFields = javaClass.getFields();
336: FieldDescriptor[] fields = new FieldDescriptor[javaFields.length];
337: for (int i = 0; i < fields.length; i++)
338: fields[i] = createFieldDescriptor(javaFields[i], javaClass
339: .getClassLoader());
340: return new ClassDescriptor(name, packageName, super className,
341: interfaceNames, isInterface, isArray,
342: componentTypeName, jaxxObjectDescriptor, classLoader,
343: methods, fields) {
344: public FieldDescriptor getDeclaredFieldDescriptor(
345: String name) throws NoSuchFieldException {
346: return createFieldDescriptor(javaClass
347: .getDeclaredField(name), javaClass
348: .getClassLoader());
349: }
350:
351: public MethodDescriptor getDeclaredMethodDescriptor(
352: String name, ClassDescriptor[] parameterTypes)
353: throws NoSuchMethodException {
354: try {
355: Class[] parameterTypeClasses = new Class[parameterTypes.length];
356: for (int i = 0; i < parameterTypes.length; i++)
357: parameterTypeClasses[i] = Class
358: .forName(parameterTypes[i].getName());
359: return createMethodDescriptor(javaClass
360: .getDeclaredMethod(name,
361: parameterTypeClasses), javaClass
362: .getClassLoader());
363: } catch (ClassNotFoundException e) {
364: throw new RuntimeException(e);
365: }
366: }
367: };
368: }
369: }
|