Author: tchemit Date: 2010-05-04 11:49:03 +0200 (Tue, 04 May 2010) New Revision: 1870 Url: http://nuiton.org/repositories/revision/jaxx/1870 Log: improve jarsing of java source files since now we WILL use them instead of jaxx files if java source file is up to date Added: trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyAbstractClass.java trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyClass.java trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyInterface.java trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyInterface2.java trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyInterface3.java Modified: trunk/jaxx-compiler/src/main/java/jaxx/compiler/reflect/ClassDescriptor.java trunk/jaxx-compiler/src/main/java/jaxx/compiler/reflect/ClassDescriptorLoader.java trunk/jaxx-compiler/src/main/java/jaxx/compiler/reflect/JavaFileParser.java trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/JavaFileParserTest.java trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyEnum.java Modified: trunk/jaxx-compiler/src/main/java/jaxx/compiler/reflect/ClassDescriptor.java =================================================================== --- trunk/jaxx-compiler/src/main/java/jaxx/compiler/reflect/ClassDescriptor.java 2010-05-04 09:48:20 UTC (rev 1869) +++ trunk/jaxx-compiler/src/main/java/jaxx/compiler/reflect/ClassDescriptor.java 2010-05-04 09:49:03 UTC (rev 1870) @@ -38,24 +38,47 @@ public abstract class ClassDescriptor { private String name; + private String packageName; + private String superclass; + private String[] interfaces; + private boolean isInterface; + private boolean isArray; + private String componentType; + private JAXXObjectDescriptor jaxxObjectDescriptor; + private ClassLoader classLoader; + private MethodDescriptor[] methodDescriptors; + private FieldDescriptor[] fieldDescriptors; - public abstract MethodDescriptor getDeclaredMethodDescriptor(String name, ClassDescriptor... parameterTypes) throws NoSuchMethodException; + protected FieldDescriptor[] declaredFieldDescriptors; - public abstract FieldDescriptor getDeclaredFieldDescriptor(String name) throws NoSuchFieldException; + public abstract MethodDescriptor getDeclaredMethodDescriptor( + String name, + ClassDescriptor... parameterTypes) throws NoSuchMethodException; - ClassDescriptor(String name, String packageName, String superclass, String[] interfaces, boolean isInterface, - boolean isArray, String componentType, JAXXObjectDescriptor jaxxObjectDescriptor, - ClassLoader classLoader, MethodDescriptor[] methodDescriptors, FieldDescriptor[] fieldDescriptors) { + public abstract FieldDescriptor getDeclaredFieldDescriptor( + String name) throws NoSuchFieldException; + + ClassDescriptor(String name, + String packageName, + String superclass, + String[] interfaces, + boolean isInterface, + boolean isArray, + String componentType, + JAXXObjectDescriptor jaxxObjectDescriptor, + ClassLoader classLoader, + MethodDescriptor[] methodDescriptors, + FieldDescriptor[] fieldDescriptors) { this.name = name; this.packageName = packageName; this.superclass = superclass; @@ -69,6 +92,32 @@ this.fieldDescriptors = fieldDescriptors; } + ClassDescriptor(String name, + String packageName, + String superclass, + String[] interfaces, + boolean isInterface, + boolean isArray, + String componentType, + JAXXObjectDescriptor jaxxObjectDescriptor, + ClassLoader classLoader, + MethodDescriptor[] methodDescriptors, + FieldDescriptor[] fieldDescriptors, + FieldDescriptor[] declaredFieldDescriptors) { + this.name = name; + this.packageName = packageName; + this.superclass = superclass; + this.interfaces = interfaces; + this.isInterface = isInterface; + this.isArray = isArray; + this.componentType = componentType; + this.jaxxObjectDescriptor = jaxxObjectDescriptor; + this.classLoader = classLoader; + this.methodDescriptors = methodDescriptors; + this.fieldDescriptors = fieldDescriptors; + this.declaredFieldDescriptors = declaredFieldDescriptors; + } + public String getName() { return name; } @@ -83,23 +132,16 @@ } public ClassDescriptor getSuperclass() { - try { - return superclass != null ? ClassDescriptorLoader.getClassDescriptor(superclass, getClassLoader()) : null; - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); - } + return getClassDescriptor(superclass); + } public ClassDescriptor[] getInterfaces() { - try { - ClassDescriptor[] result = new ClassDescriptor[interfaces.length]; - for (int i = 0; i < result.length; i++) { - result[i] = ClassDescriptorLoader.getClassDescriptor(interfaces[i], getClassLoader()); - } - return result; - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); + ClassDescriptor[] result = new ClassDescriptor[interfaces.length]; + for (int i = 0; i < result.length; i++) { + result[i] = getClassDescriptor(interfaces[i]); } + return result; } public boolean isInterface() { @@ -111,11 +153,7 @@ } public ClassDescriptor getComponentType() { - try { - return componentType != null ? ClassDescriptorLoader.getClassDescriptor(componentType, getClassLoader()) : null; - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); - } + return getClassDescriptor(componentType); } public ClassLoader getClassLoader() { @@ -126,7 +164,8 @@ return methodDescriptors; } - public MethodDescriptor getMethodDescriptor(String name, ClassDescriptor... parameterTypes) throws NoSuchMethodException { + public MethodDescriptor getMethodDescriptor(String name, + ClassDescriptor... parameterTypes) throws NoSuchMethodException { for (MethodDescriptor methodDescriptor : methodDescriptors) { if (methodDescriptor.getName().equals(name) && methodDescriptor.getParameterTypes().length == parameterTypes.length && Arrays.equals(methodDescriptor.getParameterTypes(), parameterTypes)) { return methodDescriptor; @@ -154,11 +193,11 @@ public boolean isAssignableFrom(ClassDescriptor descriptor) { while (descriptor != null) { - if (descriptor == this) { + if (equals(descriptor)) { return true; } for (ClassDescriptor anInterface : descriptor.getInterfaces()) { - if (anInterface == this) { + if (equals(anInterface)) { return true; } } @@ -171,4 +210,20 @@ public String toString() { return "ClassDescriptor[" + getName() + "]"; } + + protected ClassDescriptor getClassDescriptor(String fqn) { + if (fqn == null) { + return null; + } + + try { + ClassDescriptor result = ClassDescriptorLoader.getClassDescriptor( + fqn, + getClassLoader() + ); + return result; + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } } Modified: trunk/jaxx-compiler/src/main/java/jaxx/compiler/reflect/ClassDescriptorLoader.java =================================================================== --- trunk/jaxx-compiler/src/main/java/jaxx/compiler/reflect/ClassDescriptorLoader.java 2010-05-04 09:48:20 UTC (rev 1869) +++ trunk/jaxx-compiler/src/main/java/jaxx/compiler/reflect/ClassDescriptorLoader.java 2010-05-04 09:49:03 UTC (rev 1870) @@ -93,13 +93,14 @@ JAXXEngine engine = JAXXFactory.isEngineRegistred() ? JAXXFactory.getEngine() : null; - if (engine != null) { SymbolTable symbolTable = engine.getSymbolTable(className); if (symbolTable != null) { + log.info("<<< from symbol table [" + className + "]"); + JAXXCompiler compiler = engine.getJAXXCompiler(className); result = createClassDescriptorFromSymbolTable( @@ -129,6 +130,7 @@ javaFile.toString().startsWith("file:") && javaFile.toString().matches(relativePathPattern)) { javaLastModified = JAXXCompiler.URLtoFile(javaFile).lastModified(); + log.info("[" + className + "] javaFile lastModified " + javaLastModified); } long classLastModified = -1; @@ -137,6 +139,7 @@ classFile.toString().startsWith("file:") && classFile.toString().matches(relativePathPattern)) { classLastModified = JAXXCompiler.URLtoFile(classFile).lastModified(); + log.info("[" + className + "] class lastModified " + classLastModified); } long jaxxLastModified = -1; @@ -156,6 +159,7 @@ if (scriptFilePath.exists()) { jaxxLastModified = Math.max(jaxxLastModified, scriptFilePath.lastModified()); } + log.info("[" + className + "] jaxxFile lastModified " + jaxxLastModified); } if (classLastModified != -1) { @@ -166,6 +170,9 @@ // ok class is up to date, choose it Class<?> javaClass = getClass(className, classLoader); + + log.debug("<<< from class [" + className + "] " + javaClass); + result = createClassDescriptorFromClass(javaClass); descriptors.put(className, result); return result; @@ -174,12 +181,36 @@ // the class is not the last modified resource, do not use it } +// if (jaxxLastModified != -1) { +// +// // there is a jaxx file, use it +// +// File jaxxFilePath = JAXXCompiler.URLtoFile(jaxxFile); +// +// JAXXCompilerFile file = new JAXXCompilerFile(jaxxFilePath, className); +// +// engine.addFileToCompile(jaxxFilePath, className); +// +// JAXXCompiler compiler = engine.getJAXXCompiler(className); +// +// result = createClassDescriptorFromSymbolTable( +// compiler, +// className, +// classLoader +// ); +// // result = createClassDescriptorFromSymbolTable(className, classLoader); +// descriptors.put(className, result); +// return result; +// } + if (javaLastModified != -1) { // there is a java source if (javaLastModified > jaxxLastModified) { + log.info("<<< from java file [" + className + "] " + javaFile); + // ok java source is up to date, choose it result = createClassDescriptorFromJavaSource(javaFile, classLoader); @@ -192,24 +223,13 @@ } -// if (jaxxLastModified != -1) { -// -// // there is a jaxx file, use it -// -// compiler = engine.getJAXXCompiler(className); -// -// result = createClassDescriptorFromSymbolTable( -// compiler, -// className, -// classLoader -// ); -// // result = createClassDescriptorFromSymbolTable(className, classLoader); -// descriptors.put(className, result); -// return result; -// } + // last fall back for class Class<?> javaClass = getClass(className, classLoader); + + log.debug("<<< from class [" + className + "] " + javaClass); + result = createClassDescriptorFromClass(javaClass); if (result != null) { @@ -350,7 +370,8 @@ return null; } - public static Class<?> getClass(String className, ClassLoader classLoader) throws ClassNotFoundException { + public static Class<?> getClass(String className, + ClassLoader classLoader) throws ClassNotFoundException { Class<?> klass = getPrimitiveClass(className); if (klass != null) { return klass; @@ -369,7 +390,10 @@ } } try { - return classLoader != null ? Class.forName(className, true, classLoader) : Class.forName(className); + return classLoader != null ? + Class.forName(className, true, classLoader) : + Class.forName(className); + } catch (ClassNotFoundException e) { // perharps we are in a inner class ? int dotIndex = className.lastIndexOf("."); @@ -565,15 +589,27 @@ for (int i = 0; i < fields.length; i++) { fields[i] = createFieldDescriptor(javaFields[i], javaClass.getClassLoader()); } - return new ClassDescriptor(name, packageName, superclassName, interfaceNames, isInterface, isArray, componentTypeName, jaxxObjectDescriptor, classLoader, methods, fields) { + return new ClassDescriptor(name, packageName, + superclassName, + interfaceNames, + isInterface, + isArray, + componentTypeName, + jaxxObjectDescriptor, + classLoader, + methods, + fields) { @Override public FieldDescriptor getDeclaredFieldDescriptor(String name) throws NoSuchFieldException { - return createFieldDescriptor(javaClass.getDeclaredField(name), javaClass.getClassLoader()); + return createFieldDescriptor(javaClass.getDeclaredField(name), + javaClass.getClassLoader() + ); } @Override - public MethodDescriptor getDeclaredMethodDescriptor(String name, ClassDescriptor... parameterTypes) throws NoSuchMethodException { + public MethodDescriptor getDeclaredMethodDescriptor(String name, + ClassDescriptor... parameterTypes) throws NoSuchMethodException { try { Class<?>[] parameterTypeClasses = new Class[parameterTypes.length]; for (int i = 0; i < parameterTypes.length; i++) { Modified: trunk/jaxx-compiler/src/main/java/jaxx/compiler/reflect/JavaFileParser.java =================================================================== --- trunk/jaxx-compiler/src/main/java/jaxx/compiler/reflect/JavaFileParser.java 2010-05-04 09:48:20 UTC (rev 1869) +++ trunk/jaxx-compiler/src/main/java/jaxx/compiler/reflect/JavaFileParser.java 2010-05-04 09:49:03 UTC (rev 1870) @@ -40,28 +40,51 @@ import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; // TODO: need to unify this implementation with the parsing in ScriptManager + public class JavaFileParser { /** to use log facility, just put in your code: log.info(\"...\"); */ static private final Log log = LogFactory.getLog(JavaFileParser.class); + private JAXXCompiler compiler; + private String className; + private String packageName; + private String superclass = "java.lang.Object"; + + private boolean isEnum; + + private boolean isInterface; + + private Set<String> interfaces = new HashSet<String>(); + private List<MethodDescriptor> methods = new ArrayList<MethodDescriptor>(); + private List<FieldDescriptor> fields = new ArrayList<FieldDescriptor>(); + private List<FieldDescriptor> declaredFields = new ArrayList<FieldDescriptor>(); + public static final String[] EMPTY_STRING_ARRAY = new String[0]; private JavaFileParser(ClassLoader classLoader) { + //FIXME-TC-20100504 : shoudl remove this to make the parser free of jaxx :) + // We could imagine just to offers to the parser a list of namespaces + // (for class resolving)... compiler = JAXXFactory.newDummyCompiler(classLoader); } - public static ClassDescriptor parseJavaFile(String displayName, Reader src, ClassLoader classLoader) throws ClassNotFoundException { + public static ClassDescriptor parseJavaFile( + String displayName, + Reader src, + ClassLoader classLoader) throws ClassNotFoundException { //FIXME-TC20091205 : no now JAXX can look to interfaces, must add them // has some limitations -- it reports all members as public, leaves getDeclaredMethod and getDeclaredField // undefined, and doesn't report interfaces. It's safe to leave those the way they are for now, because @@ -79,6 +102,7 @@ List<MethodDescriptor> publicMethods = parser.methods; List<FieldDescriptor> publicFields = parser.fields; + List<FieldDescriptor> declaredFields = parser.declaredFields; //List/*<MethodDescriptor>*/ declaredMethods = new ArrayList/*<MethodDescriptor>*/(publicMethods); //List/*<FieldDescriptor>*/ declaredFields = new ArrayList/*<FieldDescriptor>*/(publicFields); Iterator<MethodDescriptor> methods = publicMethods.iterator(); @@ -95,21 +119,45 @@ fields.remove(); } } - ClassDescriptor superclassDescriptor = ClassDescriptorLoader.getClassDescriptor(parser.superclass, classLoader); - publicMethods.addAll(Arrays.asList(superclassDescriptor.getMethodDescriptors())); - publicFields.addAll(Arrays.asList(superclassDescriptor.getFieldDescriptors())); + + if (parser.superclass != null) { + //FIXME-TC20100504 This is not good, should add nothing here + // and modify the algorithm of ClassDescriptor to go and seek + // in super classes on interfaces if required. + ClassDescriptor superclassDescriptor = ClassDescriptorLoader.getClassDescriptor(parser.superclass, classLoader); + publicMethods.addAll(Arrays.asList(superclassDescriptor.getMethodDescriptors())); + publicFields.addAll(Arrays.asList(superclassDescriptor.getFieldDescriptors())); + } //FIXME-TC20091205 : now JAXX can look to interfaces, must add them //Set<String> interfaces = new HashSet<String>(); //ClassDescriptor[] superclassInterfaces = superclassDescriptor.getInterfaces(); //for (ClassDescriptor superclassInterface : superclassInterfaces) { //interfaces.add(superclassInterface.getName()); //} - return new ClassDescriptor(parser.className, parser.packageName, parser.superclass, EMPTY_STRING_ARRAY, false, false, null, null, classLoader, - publicMethods.toArray(new MethodDescriptor[publicMethods.size()]), - publicFields.toArray(new FieldDescriptor[publicFields.size()])) { + String[] interfaces = parser.interfaces.toArray(new String[parser.interfaces.size()]); + return new ClassDescriptor(parser.className, + parser.packageName, + parser.superclass, + interfaces, + parser.isInterface, + false, + null, + null, + classLoader, + publicMethods.toArray(new MethodDescriptor[publicMethods.size()]), + publicFields.toArray(new FieldDescriptor[publicFields.size()]), + declaredFields.toArray(new FieldDescriptor[declaredFields.size()]) + ) { +// publicFields.toArray(new FieldDescriptor[publicFields.size()])) { @Override public FieldDescriptor getDeclaredFieldDescriptor(String name) throws NoSuchFieldException { + for (FieldDescriptor descriptor : declaredFieldDescriptors) { + if (name.equals(descriptor.getName())) { + log.info("Using a declared field descriptor [" + name + "] for " + getName()); + return descriptor; + } + } throw new NoSuchFieldException(name); } @@ -132,17 +180,38 @@ throw new CompilerException("Internal error: null node parsing Java file from " + src); } catch (ParseException e) { throw new CompilerException("Error parsing Java source code " + displayName + ": " + e.getMessage()); + } finally { + if (isInterface) { + + // remove super class + superclass = null; + } + + if (isEnum) { + + // super class is always Enum + + superclass = Enum.class.getName(); + } } } private void scanCompilationUnit(SimpleNode node) { for (int i = 0; i < node.jjtGetNumChildren(); i++) { SimpleNode child = node.getChild(i); - int nodeType = child.getId(); - if (nodeType == JavaParserTreeConstants.JJTPACKAGEDECLARATION) { + scanCompilationUnitChild(child); + } + } + + private void scanCompilationUnitChild(SimpleNode child) { + int nodeType = child.getId(); + switch (nodeType) { + case JavaParserTreeConstants.JJTPACKAGEDECLARATION: packageName = child.getChild(1).getText().trim(); compiler.addImport(packageName + ".*"); - } else if (nodeType == JavaParserTreeConstants.JJTIMPORTDECLARATION) { + compiler.addImport("java.lang.*"); + break; + case JavaParserTreeConstants.JJTIMPORTDECLARATION: String text = child.getText().trim(); if (text.startsWith("import")) { text = text.substring("import".length()).trim(); @@ -150,20 +219,29 @@ if (text.endsWith(";")) { text = text.substring(0, text.length() - 1); } + if (log.isDebugEnabled()) { + log.debug("import " + text); + } compiler.addImport(text); - } else if (nodeType == JavaParserTreeConstants.JJTTYPEDECLARATION) { + break; + case JavaParserTreeConstants.JJTTYPEDECLARATION: scanCompilationUnit(child); - //TC-20091204 : add enum support -// } else if (nodeType == JavaParserTreeConstants.JJTCLASSORINTERFACEDECLARATION) { - } else if (nodeType == JavaParserTreeConstants.JJTCLASSORINTERFACEDECLARATION || nodeType== JavaParserTreeConstants.JJTENUMDECLARATION) { + break; + + case JavaParserTreeConstants.JJTCLASSORINTERFACEDECLARATION: + isInterface = child.firstToken.image.equals("interface"); scanClass(child); - } + case JavaParserTreeConstants.JJTENUMDECLARATION: + isEnum = child.firstToken.image.equals("enum"); + scanClass(child); + break; } } // scans the main ClassOrInterfaceDeclaration + private void scanClass(SimpleNode node) { - boolean isInterface = node.firstToken.image.equals("interface"); +// boolean isInterface = node.firstToken.image.equals("interface"); className = node.firstToken.next.image; if (packageName != null) { className = packageName + "." + className; @@ -171,22 +249,133 @@ for (int i = 0; i < node.jjtGetNumChildren(); i++) { SimpleNode child = node.getChild(i); int nodeType = child.getId(); + + if (nodeType == JavaParserTreeConstants.JJTIMPLEMENTSLIST) { + + if (log.isDebugEnabled()) { + log.debug("[" + className + "] Found a implements list " + child + " :: " + child.jjtGetNumChildren()); + } + // obtain interfaces + for (int j = 0; j < child.jjtGetNumChildren(); j++) { + String rawName = child.getChild(j).getText().trim(); + + addInterface(rawName); + } + continue; + } if (nodeType == JavaParserTreeConstants.JJTEXTENDSLIST) { - if (!isInterface) { - assert child.jjtGetNumChildren() == 1 : "expected ExtendsList to have exactly one child for a non-interface class"; - String rawName = child.getChild(0).getText().trim(); - superclass = TagManager.resolveClassName(rawName, compiler); - if (superclass == null) { - throw new CompilerException("Could not find class: " + rawName); + + if (isInterface) { + + // obtain interfaces + for (int j = 0; j < child.jjtGetNumChildren(); j++) { + String rawName = child.getChild(j).getText().trim(); + + addInterface(rawName); } + continue; } + // this is an extends + assert child.jjtGetNumChildren() == 1 : "expected ExtendsList to have exactly one child for a non-interface class"; + String rawName = child.getChild(0).getText().trim(); + superclass = TagManager.resolveClassName(rawName, compiler); + if (superclass == null) { + throw new CompilerException("Could not find class: " + rawName); + } + if (log.isDebugEnabled()) { + log.debug("Set superClass = " + superclass); + } } else if (nodeType == JavaParserTreeConstants.JJTCLASSORINTERFACEBODY) { scanClassNode(child); } } } + protected void addInterface(String rawName) { + if (rawName.contains("<")) { + + // generic type + rawName = rawName.substring(0, rawName.indexOf("<")); + } + if (log.isDebugEnabled()) { + log.debug("[" + className + "] try to obtain type of interface " + rawName); + } + + String myInterface = resolveFullyQualifiedName(rawName); + if (myInterface == null) { + throw new CompilerException("Could not find interface: " + myInterface); + } + if (!interfaces.contains(myInterface)) { + if (log.isDebugEnabled()) { + log.debug("[" + className + "] add interface " + myInterface); + } + interfaces.add(myInterface); + } + } + + protected String resolveFullyQualifiedName(String rawName) { + String result; + + String realRawName = null; + String realParentRawName = null; + if (rawName.contains(".")) { + // this is a inner class + int index = rawName.lastIndexOf("."); + realParentRawName = rawName.substring(0, index); + realRawName = rawName.substring(index + 1); + + log.info("inner class detected ? " + realParentRawName + "//" + realRawName); + } + + log.info("try fqn = " + rawName); + result = TagManager.resolveClassName(rawName, compiler); + + if (result != null) { + // interface is detected fine (fqn was used or in good package ?) + return result; + } + + String suffix = "." + rawName; + + if (realParentRawName != null) { + suffix = "." + realParentRawName; + } + + for (String aClass : compiler.getImportedClasses()) { + + if (aClass.endsWith(suffix)) { + + // found the class as an already knwon class + + if (realRawName != null) { + aClass += "." + realRawName; + } + + return aClass; + } + } + + // try on packages + + Set<String> importedPackages = compiler.getImportedPackages(); + + for (String aClass : importedPackages) { + String fqn = aClass + rawName; + + log.info("try fqn = " + fqn); + result = TagManager.resolveClassName(fqn, compiler); + if (result != null) { + return result; + } + } + + // nothing was found + return null; + } + + // scans class body nodes + private void scanClassNode(SimpleNode node) { int nodeType = node.getId(); if (nodeType == JavaParserTreeConstants.JJTMETHODDECLARATION) { @@ -221,6 +410,7 @@ } else if (nodeType == JavaParserTreeConstants.JJTCONSTRUCTORDECLARATION) { // TODO: handle constructors } else if (nodeType == JavaParserTreeConstants.JJTFIELDDECLARATION) { + String text = node.getText(); String declaration = text; int equals = text.indexOf("="); @@ -228,16 +418,43 @@ declaration = declaration.substring(0, equals); } declaration = declaration.trim(); + + SimpleNode parentNode = node.getParent(); + int modifiers = 0; + if (isInterface) { + modifiers = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL; + } else { + for (int i = 0; i < parentNode.jjtGetNumChildren(); i++) { + SimpleNode child = parentNode.getChild(i); + if (child.getId() == JavaParserTreeConstants.JJTMODIFIERS) { + String modifiersStr = child.getText().trim(); + modifiers = scanModifiers(modifiersStr); + } + } + } + + log.info("field [" + declaration + "] modifiers == " + Modifier.toString(modifiers)); + String[] declarationTokens = declaration.split("\\s"); - //boolean isFinal = Arrays.asList(declarationTokens).contains("final"); - //boolean isStatic = Arrays.asList(declarationTokens).contains("static"); String name = declarationTokens[declarationTokens.length - 1]; if (name.endsWith(";")) { name = name.substring(0, name.length() - 1).trim(); } String cName = declarationTokens[declarationTokens.length - 2]; String type = TagManager.resolveClassName(cName, compiler); - fields.add(new FieldDescriptor(name, Modifier.PUBLIC, type, compiler.getClassLoader())); // TODO: determine the actual modifiers + + FieldDescriptor descriptor = new FieldDescriptor( + name, + modifiers, + type, + compiler.getClassLoader() + ); + if (Modifier.isPublic(modifiers)) { + fields.add(descriptor); + } else { + declaredFields.add(descriptor); + } + } else { for (int i = 0; i < node.jjtGetNumChildren(); i++) { SimpleNode child = node.getChild(i); @@ -245,4 +462,36 @@ } } } + + protected int scanModifiers(String modifiersStr) { + int modifiers = 0; + if (modifiersStr.contains("public")) { + modifiers |= Modifier.PUBLIC; + } + if (modifiersStr.contains("protected")) { + modifiers |= Modifier.PROTECTED; + } + if (modifiersStr.contains("private")) { + modifiers |= Modifier.PRIVATE; + } + if (modifiersStr.contains("static")) { + modifiers |= Modifier.STATIC; + } + if (modifiersStr.contains("final")) { + modifiers |= Modifier.FINAL; + } + if (modifiersStr.contains("volatile")) { + modifiers |= Modifier.VOLATILE; + } + if (modifiersStr.contains("transient")) { + modifiers |= Modifier.TRANSIENT; + } + if (modifiersStr.contains("synchronized")) { + modifiers |= Modifier.SYNCHRONIZED; + } + if (modifiersStr.contains("abstract")) { + modifiers |= Modifier.ABSTRACT; + } + return modifiers; + } } Modified: trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/JavaFileParserTest.java =================================================================== --- trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/JavaFileParserTest.java 2010-05-04 09:48:20 UTC (rev 1869) +++ trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/JavaFileParserTest.java 2010-05-04 09:49:03 UTC (rev 1870) @@ -25,24 +25,29 @@ package jaxx.compiler.reflect; -import jaxx.compiler.CompilerException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; +import org.nuiton.util.ApplicationConfig; -import java.io.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.Serializable; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; public class JavaFileParserTest { + /** log */ + protected static final Log log = + LogFactory.getLog(JavaFileParserTest.class); - /** - * log - */ - protected static final Log log = LogFactory.getLog(JavaFileParserTest.class); - - static File basedir; + /** test source root directory */ static File testSourceRoot; @BeforeClass @@ -50,60 +55,232 @@ // get maven env basedir String basedir = System.getenv("basedir"); if (basedir == null) { + + // says basedir is where we start tests. basedir = new File("").getAbsolutePath(); } - JavaFileParserTest.basedir = new File(basedir); - testSourceRoot = new File(basedir, "src" + File.separator + "test" + File.separator + "java"); + testSourceRoot = new File(new File(basedir), + "src" + File.separator + + "test" + File.separator + "java"); } @Test - public void testParseJavaSourceFile() throws Exception { + public void parseJavaFileParserTest() throws Exception { - Assert.assertTrue(testSourceRoot.exists()); + ClassDescriptor descriptor = getDescriptor(JavaFileParserTest.class); - File src = new File(testSourceRoot, getClass().getName().replaceAll("\\.", File.separator) + ".java"); - Assert.assertTrue(src.exists()); - log.info("trying parsing file " + src); - Reader reader = new FileReader(src); - try { - ClassDescriptor result = JavaFileParser.parseJavaFile("TestParserJava", reader, getClass().getClassLoader()); - Assert.assertNotNull(result); + assertInterfaces(descriptor); + assertSuperClass(descriptor, Object.class); + } - } catch (CompilerException e) { - log.error("could not parse file " + src + " for reason " + e.getMessage(), e); - Assert.fail(e.getMessage()); - } - finally { - reader.close(); - } + @Test + public void parseMyEnum() throws Exception { + + ClassDescriptor descriptor = getDescriptor(MyEnum.class); + + assertInterfaces(descriptor, Serializable.class, MyInterface.class); + assertInterfaces(descriptor, MyEnum.class.getInterfaces()); + assertSuperClass(descriptor, Enum.class); + + assertIsAssignableFrom( + descriptor, + Serializable.class, + MyInterface.class, + Enum.class + ); } @Test - public void testParseEnum() throws ClassNotFoundException, IOException { + public void parseMyInterface() throws Exception { - File src = new File(testSourceRoot, MyEnum.class.getName().replaceAll("\\.", File.separator) + ".java"); + ClassDescriptor descriptor = getDescriptor(MyInterface.class); + + assertInterfaces(descriptor); + assertSuperClass(descriptor, null); + } + + @Test + public void parseMyInterface2() throws Exception { + + ClassDescriptor descriptor = getDescriptor(MyInterface2.class); + + assertInterfaces(descriptor, MyInterface2.class.getInterfaces()); + assertInterfaces(descriptor, ApplicationConfig.OptionDef.class); + assertSuperClass(descriptor, null); + } + + @Test + public void parseMyInterface3() throws Exception { + + ClassDescriptor descriptor = getDescriptor(MyInterface3.class); + + assertInterfaces(descriptor, MyInterface3.class.getInterfaces()); + assertInterfaces(descriptor, ApplicationConfig.OptionDef.class, Iterable.class); + assertSuperClass(descriptor, null); + } + + @Test + public void parseMyAbstractClass() throws Exception { + + ClassDescriptor descriptor = getDescriptor(MyAbstractClass.class); + + assertInterfaces(descriptor, MyAbstractClass.class.getInterfaces()); + assertInterfaces(descriptor, MyInterface.class); + assertSuperClass(descriptor, Object.class); + assertIsAssignableFrom(descriptor, MyInterface.class, Object.class); + } + + @Test + public void parseMyClass() throws Exception { + + ClassDescriptor descriptor = getDescriptor(MyClass.class); + + assertInterfaces(descriptor, MyClass.class.getInterfaces()); + assertInterfaces(descriptor, Serializable.class); + assertSuperClass(descriptor, MyAbstractClass.class); + assertIsAssignableFrom(descriptor, + Serializable.class, + MyInterface.class, + MyAbstractClass.class); + + assertDeclaredField( + descriptor, + "serialVersionUID", + long.class, + Modifier.PRIVATE | Modifier.FINAL | Modifier.STATIC + ); + + assertDeclaredField( + descriptor, + "myPrivateStringField", + String.class, + Modifier.PRIVATE | Modifier.FINAL + ); + + assertDeclaredField( + descriptor, + "myProtectedStringField", + String.class, + Modifier.PROTECTED + ); + + assertField( + descriptor, + "myPublicStringField", + String.class, + Modifier.PUBLIC + ); + + } + + protected void assertField(ClassDescriptor descriptor, + String fieldName, + Class<?> fieldType, + int fieldModifiers) throws NoSuchFieldException { + FieldDescriptor fieldDescriptor = + descriptor.getFieldDescriptor(fieldName); + Assert.assertNotNull(fieldDescriptor); + ClassDescriptor type = fieldDescriptor.getType(); + Assert.assertNotNull(type); + Assert.assertEquals(fieldType.getName(), type.getName()); + int modifiers = fieldDescriptor.getModifiers(); + Assert.assertEquals(fieldModifiers, modifiers); + } + + protected void assertDeclaredField(ClassDescriptor descriptor, + String fieldName, + Class<?> fieldType, + int fieldModifiers) throws NoSuchFieldException { + FieldDescriptor fieldDescriptor = + descriptor.getDeclaredFieldDescriptor(fieldName); + Assert.assertNotNull(fieldDescriptor); + ClassDescriptor type = fieldDescriptor.getType(); + Assert.assertNotNull(type); + Assert.assertEquals(fieldType.getName(), type.getName()); + int modifiers = fieldDescriptor.getModifiers(); + Assert.assertEquals(fieldModifiers, modifiers); + } + + protected ClassDescriptor getDescriptor(Class<?> klass) throws Exception { + + String javaFilePath = + klass.getName().replaceAll("\\.", File.separator) + ".java"; + File src = new File(testSourceRoot, javaFilePath); + Assert.assertTrue(src.exists()); + if (log.isInfoEnabled()) { log.info("file to parse " + src); } - Assert.assertTrue(src.exists()); - InputStream in = new FileInputStream(src); - Assert.assertNotNull(in); + Reader reader = new InputStreamReader(new FileInputStream(src)); try { - Reader reader = new InputStreamReader(in); - ClassDescriptor descriptor = JavaFileParser.parseJavaFile("MyEnum", reader, null); + ClassDescriptor descriptor = JavaFileParser.parseJavaFile( + klass.getSimpleName(), + reader, null); Assert.assertNotNull(descriptor); if (log.isInfoEnabled()) { - log.info("description found : " + descriptor); + log.info("loaded " + descriptor); } - Assert.assertNotNull(descriptor.getName()); + Assert.assertEquals(klass.getName(), descriptor.getName()); + return descriptor; } finally { - in.close(); + reader.close(); + } + } + public static void assertIsAssignableFrom(ClassDescriptor descriptor, + Class<?>... interfaces) { + + for (Class<?> anInterface : interfaces) { + ClassDescriptor descriptor2 = + ClassDescriptorLoader.getClassDescriptor(anInterface); + Assert.assertNotNull(descriptor2); + boolean value = descriptor2.isAssignableFrom(descriptor); + Assert.assertTrue( + anInterface + " should be assignable from " + descriptor, + value + ); } } + public static void assertInterfaces(ClassDescriptor descriptor, + Class<?>... interfaces) { + ClassDescriptor[] descriptors = descriptor.getInterfaces(); + Assert.assertEquals(interfaces.length, descriptors.length); + + List<String> doFind = new ArrayList<String>(); + for (Class<?> anInterface : interfaces) { + doFind.add(anInterface.getName()); + } + + for (ClassDescriptor descriptor1 : descriptors) { + String name = descriptor1.getName(); + Assert.assertTrue(doFind.contains(name)); + doFind.remove(name); + } + Assert.assertTrue( + "The follwing interfaces were not find found : " + doFind, + doFind.isEmpty() + ); + + } + + public static void assertSuperClass(ClassDescriptor descriptor, + Class<?> superClass) { + ClassDescriptor superDescriptor = descriptor.getSuperclass(); + if (superClass == null) { + Assert.assertNull( + "Should be null but was " + superDescriptor, + superDescriptor + ); + } else { + Assert.assertNotNull(superDescriptor); + Assert.assertEquals(superClass.getName(), superDescriptor.getName()); + } + + } + } Added: trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyAbstractClass.java =================================================================== --- trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyAbstractClass.java (rev 0) +++ trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyAbstractClass.java 2010-05-04 09:49:03 UTC (rev 1870) @@ -0,0 +1,14 @@ +package jaxx.compiler.reflect; + +/** + * To test parser on abstract class file. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.0.2 + */ +public abstract class MyAbstractClass implements MyInterface { + + protected void myAbstractMethod() { + + } +} Property changes on: trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyAbstractClass.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Added: trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyClass.java =================================================================== --- trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyClass.java (rev 0) +++ trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyClass.java 2010-05-04 09:49:03 UTC (rev 1870) @@ -0,0 +1,36 @@ +package jaxx.compiler.reflect; + +import java.io.Serializable; + +/** + * To test inheritance. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.0.2 + */ +public class MyClass extends MyAbstractClass implements Serializable { + + private static final long serialVersionUID = 1L; + + private final String myPrivateStringField = "final"; + + protected String myProtectedStringField; + + public String myPublicStringField; + + @Override + public void myMethod() { + } + + public String getMyPrivateStringField() { + return myPrivateStringField; + } + + public String getMyProtectedStringField() { + return myProtectedStringField; + } + + public String getMyPublicStringField() { + return myPublicStringField; + } +} Property changes on: trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyClass.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Modified: trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyEnum.java =================================================================== --- trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyEnum.java 2010-05-04 09:48:20 UTC (rev 1869) +++ trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyEnum.java 2010-05-04 09:49:03 UTC (rev 1870) @@ -24,6 +24,8 @@ */ package jaxx.compiler.reflect; +import java.io.Serializable; + /** * Created: 4 déc. 2009 * @@ -33,7 +35,11 @@ * Mise a jour: $Date$ par : * $Author$ */ -public enum MyEnum { +public enum MyEnum implements Serializable, MyInterface { - A, B + A, B; + + @Override + public void myMethod() { + } } Added: trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyInterface.java =================================================================== --- trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyInterface.java (rev 0) +++ trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyInterface.java 2010-05-04 09:49:03 UTC (rev 1870) @@ -0,0 +1,14 @@ +package jaxx.compiler.reflect; + +/** + * Simple interface to test. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.0.2 + */ +public interface MyInterface { + + String MY_CONSTANT = "constant"; + + void myMethod(); +} Property changes on: trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyInterface.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Added: trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyInterface2.java =================================================================== --- trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyInterface2.java (rev 0) +++ trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyInterface2.java 2010-05-04 09:49:03 UTC (rev 1870) @@ -0,0 +1,16 @@ +package jaxx.compiler.reflect; + +import org.nuiton.util.ApplicationConfig; + +/** + * Simple interface to test. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.0.2 + */ +public interface MyInterface2 extends ApplicationConfig.OptionDef { + + String MY_CONSTANT = "constant"; + + void myMethod(); +} \ No newline at end of file Property changes on: trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyInterface2.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Added: trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyInterface3.java =================================================================== --- trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyInterface3.java (rev 0) +++ trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyInterface3.java 2010-05-04 09:49:03 UTC (rev 1870) @@ -0,0 +1,16 @@ +package jaxx.compiler.reflect; + +import org.nuiton.util.ApplicationConfig.OptionDef; + +/** + * Simple interface to test. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.0.2 + */ +public interface MyInterface3 extends OptionDef, Iterable<MyInterface3> { + + String MY_CONSTANT = "constant"; + + void myMethod(); +} \ No newline at end of file Property changes on: trunk/jaxx-compiler/src/test/java/jaxx/compiler/reflect/MyInterface3.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL