Author: kmorin Date: 2009-07-30 16:58:40 +0200 (Thu, 30 Jul 2009) New Revision: 1539 Added: trunk/guix-compiler/src/main/java/org/nuiton/guix/generator/JavaFileParser.java Modified: trunk/guix-compiler/src/main/java/org/nuiton/guix/GuixLauncher.java trunk/guix-compiler/src/main/java/org/nuiton/guix/generator/Generator.java trunk/guix-compiler/src/main/java/org/nuiton/guix/generator/GuixGenerator.java trunk/guix-compiler/src/test/java/org/nuiton/guix/GuixLauncherTest.java trunk/guix-compiler/src/test/java/org/nuiton/guix/compiler/GuixCompilerTest.java Log: Add the acceptation for tags that represents classes defined in the src directory Modified: trunk/guix-compiler/src/main/java/org/nuiton/guix/GuixLauncher.java =================================================================== --- trunk/guix-compiler/src/main/java/org/nuiton/guix/GuixLauncher.java 2009-07-30 14:57:24 UTC (rev 1538) +++ trunk/guix-compiler/src/main/java/org/nuiton/guix/GuixLauncher.java 2009-07-30 14:58:40 UTC (rev 1539) @@ -22,7 +22,6 @@ import java.io.File; import java.io.PrintWriter; import java.io.StringReader; -import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; @@ -71,6 +70,8 @@ private List<File> compiledFiles = new ArrayList<File>(); /** Maps the generator with the generated JavaFile */ private Map<GuixGenerator, JavaFile> generatedFiles = new HashMap<GuixGenerator, JavaFile>(); + /** Source directory of the project */ + private File srcDirectory; /** Directory where to save the generated files */ private File targetDirectory; /** Root package */ @@ -93,10 +94,11 @@ * @param generatorClass class of the generator * @param launcherName name of the class that will launch the application */ - public GuixLauncher(File[] files, File targetDirectory, String rootPackage, + public GuixLauncher(File[] files, File targetDirectory, String rootPackage, File srcDir, File baseDir, String mainClass, Class<? extends GuixGenerator> generatorClass, String launcherName) { // Set up a simple configuration that logs on the console. this.files = files; + this.srcDirectory = srcDir; this.targetDirectory = targetDirectory; this.rootPackage = rootPackage != null ? rootPackage : ""; if (files != null) { @@ -127,7 +129,6 @@ * <code>false</code> otherwise */ public synchronized boolean compile() { - File destDir = targetDirectory; if (files != null) { if (log.isInfoEnabled()) { log.info("Start compiling"); @@ -154,24 +155,19 @@ if (targetDirectory != null) { int dotPos = className.lastIndexOf("."); if (dotPos != -1) { - destDir = new File(targetDirectory, className.substring(0, dotPos).replace('.', File.separatorChar)); classPackage = className.substring(0, dotPos); } else { - destDir = new File(targetDirectory, className); - classPackage = className; + classPackage = ""; } - if (!destDir.exists() && !destDir.mkdirs()) { - if (log.isWarnEnabled()) { - log.warn("couldn't create directory " + destDir); - } - continue; - } } else { - destDir = file.getParentFile(); - classPackage = destDir.getAbsolutePath().replace(File.separatorChar, '.'); + targetDirectory = file.getParentFile(); + classPackage = targetDirectory.getAbsolutePath().replace(File.separatorChar, '.'); } + if(!targetDirectory.exists()) { + targetDirectory.mkdirs(); + } //compile the file GuixCompiler compiler = new GuixCompiler(file, this, classPackage); GuixModelObject rootModelObject = compiler.compile(); @@ -224,7 +220,8 @@ try { //init generator GuixGenerator gen = (GuixGenerator) generatorClass.newInstance(); - gen.setDestDir(destDir); + gen.setSrcDir(srcDirectory); + gen.setDestDir(targetDirectory); gen.setGmo(mo); gen.setLastModification(rootModelObjects.get(mo)); gen.setMainClass((mo.getClassDescriptor().getPackageName() + "." + mo.getClassDescriptor().getName()).equals(mainClass)); Modified: trunk/guix-compiler/src/main/java/org/nuiton/guix/generator/Generator.java =================================================================== --- trunk/guix-compiler/src/main/java/org/nuiton/guix/generator/Generator.java 2009-07-30 14:57:24 UTC (rev 1538) +++ trunk/guix-compiler/src/main/java/org/nuiton/guix/generator/Generator.java 2009-07-30 14:58:40 UTC (rev 1539) @@ -33,6 +33,10 @@ public JavaFile generate(); + public File getSrcDir(); + + public void setSrcDir(File srcDir); + public File getDestDir(); public void setDestDir(File destDir); Modified: trunk/guix-compiler/src/main/java/org/nuiton/guix/generator/GuixGenerator.java =================================================================== --- trunk/guix-compiler/src/main/java/org/nuiton/guix/generator/GuixGenerator.java 2009-07-30 14:57:24 UTC (rev 1538) +++ trunk/guix-compiler/src/main/java/org/nuiton/guix/generator/GuixGenerator.java 2009-07-30 14:58:40 UTC (rev 1539) @@ -36,6 +36,8 @@ /** GuixModelObject which represents the class to generate */ protected GuixModelObject gmo = null; + /** Source directory */ + protected File srcDir = null; /** Destination directory of the generated file */ protected File destDir = null; /** True if the class to generate is teh main class of the application */ @@ -60,6 +62,14 @@ */ public abstract JavaFile generate(); + public File getSrcDir() { + return srcDir; + } + + public void setSrcDir(File srcDir) { + this.srcDir = srcDir; + } + public File getDestDir() { return destDir; } Added: trunk/guix-compiler/src/main/java/org/nuiton/guix/generator/JavaFileParser.java =================================================================== --- trunk/guix-compiler/src/main/java/org/nuiton/guix/generator/JavaFileParser.java (rev 0) +++ trunk/guix-compiler/src/main/java/org/nuiton/guix/generator/JavaFileParser.java 2009-07-30 14:58:40 UTC (rev 1539) @@ -0,0 +1,214 @@ +package org.nuiton.guix.generator; + +import org.nuiton.guix.CompilerException; +import org.nuiton.guix.compiler.GuixCompiler; +import org.nuiton.guix.GuixLauncher; +import org.nuiton.guix.parser.JavaParser; +import org.nuiton.guix.parser.JavaParserTreeConstants; +import org.nuiton.guix.parser.ParseException; +import org.nuiton.guix.parser.SimpleNode; +import org.nuiton.guix.tags.TagManager; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.Reader; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; + +// 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 String className; + private String packageName = null; + private String superclassName = "java.lang.Object"; + private List<JavaMethod> methods = new ArrayList<JavaMethod>(); + private List<JavaField> fields = new ArrayList<JavaField>(); + + + public static JavaFile parseJavaFile(String displayName, Reader src) throws ClassNotFoundException { + // 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 + // Guix doesn't look at any of those for non-Guix classes. + JavaFileParser parser = new JavaFileParser(); + if (log.isDebugEnabled()) { + log.debug("starting parsing : " + displayName); + } + try { + parser.doParse(displayName, src); + } catch (Exception e) { + log.error(e.getMessage()); + throw new RuntimeException(e); + } + JavaFile jf = new JavaFile(Modifier.PUBLIC, JavaFile.CLASS, parser.packageName, + parser.className, parser.superclassName, new ArrayList<String>(), null); + for(JavaMethod jm : parser.methods) { + if(Modifier.isPublic(jm.getModifiers())) { + jf.addMethod(jm); + } + } + for(JavaField jfi : parser.fields) { + if(Modifier.isPublic(jfi.getModifiers())) { + jf.addField(jfi); + } + } + + Class superclass = Class.forName(parser.superclassName); + for(Method m : superclass.getMethods()) { + JavaArgument[] jas = new JavaArgument[m.getParameterTypes().length]; + for(int i = 0 ; i < m.getParameterTypes().length ; i++) { + jas[i] = new JavaArgument(m.getParameterTypes()[i].getName(), "arg" + i); + } + String[] exceptions = new String[m.getExceptionTypes().length]; + for(int i = 0 ; i < m.getExceptionTypes().length ; i++) { + exceptions[i] = m.getExceptionTypes()[i].getName(); + } + jf.addMethod(new JavaMethod(m.getModifiers(), m.getReturnType().getName(), m.getName(), jas, exceptions, "", null)); + } + for(Field f : superclass.getFields()) { + jf.addField(new JavaField(f.getModifiers(), f.getType().getName(), f.getName(), null)); + } + + for (Class i : superclass.getInterfaces()) { + jf.getInterfaces().add(i.getName()); + } + + return jf; + } + + private void doParse(String displayName, Reader src) { + try { + JavaParser p = new JavaParser(src); + p.CompilationUnit(); + SimpleNode node = p.popNode(); + if (node != null) { + scanCompilationUnit(node); + return; + } + 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()); + } + } + + + 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) { + packageName = child.getChild(1).getText().trim(); + } else + if (nodeType == JavaParserTreeConstants.JJTIMPORTDECLARATION) { + String text = child.getText().trim(); + if (text.startsWith("import")) { + text = text.substring("import".length()).trim(); + } + if (text.endsWith(";")) { + text = text.substring(0, text.length() - 1); + } + } else if (nodeType == JavaParserTreeConstants.JJTTYPEDECLARATION) { + scanCompilationUnit(child); + } else + if (nodeType == JavaParserTreeConstants.JJTCLASSORINTERFACEDECLARATION) { + scanClass(child); + } + } + } + + + // scans the main ClassOrInterfaceDeclaration + private void scanClass(SimpleNode node) { + boolean isInterface = node.firstToken.image.equals("interface"); + className = node.firstToken.next.image; + if (packageName != null) + className = packageName + "." + className; + for (int i = 0; i < node.jjtGetNumChildren(); i++) { + SimpleNode child = node.getChild(i); + int nodeType = child.getId(); + 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(); + superclassName = TagManager.resolveClassName(rawName); + if (superclassName == null) { + throw new CompilerException("Could not find class: " + rawName); + } + } + } else + if (nodeType == JavaParserTreeConstants.JJTCLASSORINTERFACEBODY) { + scanClassNode(child); + } + } + } + + + // scans class body nodes + private void scanClassNode(SimpleNode node) { + int nodeType = node.getId(); + if (nodeType == JavaParserTreeConstants.JJTMETHODDECLARATION) { + String returnType = null; + String name = null; + List<JavaArgument> parameterTypes = new ArrayList<JavaArgument>(); + //List<String> parameterNames = new ArrayList<String>(); + for (int i = 0; i < node.jjtGetNumChildren(); i++) { + SimpleNode child = node.getChild(i); + int type = child.getId(); + if (type == JavaParserTreeConstants.JJTRESULTTYPE) + returnType = TagManager.resolveClassName(child.getText().trim()); + else if (type == JavaParserTreeConstants.JJTMETHODDECLARATOR) { + name = child.firstToken.image.trim(); + SimpleNode formalParameters = child.getChild(0); + assert formalParameters.getId() == JavaParserTreeConstants.JJTFORMALPARAMETERS; + for (int j = 0; j < formalParameters.jjtGetNumChildren(); j++) + { + SimpleNode parameter = formalParameters.getChild(j); + String rawParameterType = parameter.getChild(1).getText().trim().replaceAll("\\.\\.\\.", "[]"); + String parameterType = TagManager.resolveClassName(rawParameterType); + if (parameterType == null) { + throw new CompilerException("could not find class '" + rawParameterType + "'"); + } + parameterTypes.add(new JavaArgument(parameterType, "arg" + j)); + //parameterNames.add(parameter.getChild(2).getText().trim()); + } + } + } + methods.add(new JavaMethod(Modifier.PUBLIC, returnType, name, parameterTypes.toArray(new JavaArgument[parameterTypes.size()]), null, "", null)); // TODO: determine the actual modifiers + } else + if (nodeType == JavaParserTreeConstants.JJTCLASSORINTERFACEDECLARATION) { + // TODO: handle inner classes + } else + if (nodeType == JavaParserTreeConstants.JJTCONSTRUCTORDECLARATION) { + // TODO: handle constructors + } else if (nodeType == JavaParserTreeConstants.JJTFIELDDECLARATION) { + String text = node.getText(); + String declaration = text; + int equals = text.indexOf("="); + if (equals != -1) { + declaration = declaration.substring(0, equals); + } + declaration = declaration.trim(); + 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 className = declarationTokens[declarationTokens.length - 2]; + String type = TagManager.resolveClassName(className); + fields.add(new JavaField(Modifier.PUBLIC, type, name, null)); // TODO: determine the actual modifiers + } else { + for (int i = 0; i < node.jjtGetNumChildren(); i++) { + SimpleNode child = node.getChild(i); + scanClassNode(child); + } + } + } +} Modified: trunk/guix-compiler/src/test/java/org/nuiton/guix/GuixLauncherTest.java =================================================================== --- trunk/guix-compiler/src/test/java/org/nuiton/guix/GuixLauncherTest.java 2009-07-30 14:57:24 UTC (rev 1538) +++ trunk/guix-compiler/src/test/java/org/nuiton/guix/GuixLauncherTest.java 2009-07-30 14:58:40 UTC (rev 1539) @@ -41,7 +41,7 @@ public void compileTest() { GuixInitializer.initialize(); - GuixLauncher gl = new GuixLauncher(null,null,null,null,null,null,null); + GuixLauncher gl = new GuixLauncher(null,null,null,null,null,null,null,null); assertTrue(gl.files == null || gl.classNames.length == gl.files.length); assertTrue(gl.compile()); assertTrue(gl.files == null || gl.rootModelObjects.size() == gl.files.length); @@ -50,7 +50,7 @@ File f2 = new File(f.getAbsolutePath()); File dir = new File("target/test"); File dir2 = new File(dir.getAbsolutePath()); - gl = new GuixLauncher(new File[]{f2},dir2,"org.nuiton.guix",f2.getParentFile(), null,null,null); + gl = new GuixLauncher(new File[]{f2},dir2,"org.nuiton.guix",null,f2.getParentFile(),null,null,null); assertTrue(gl.files == null || gl.classNames.length == gl.files.length); assertTrue(gl.compile()); } @@ -60,7 +60,7 @@ */ @Test public void registerClassDescriptorTest() { - GuixLauncher gl = new GuixLauncher(null,null,null,null,null,null,null); + GuixLauncher gl = new GuixLauncher(null,null,null,null,null,null,null,null); assertNull(gl.registerClassDescriptor(null)); assertNull(gl.registerClassDescriptor(new ClassDescriptor(null, null))); Modified: trunk/guix-compiler/src/test/java/org/nuiton/guix/compiler/GuixCompilerTest.java =================================================================== --- trunk/guix-compiler/src/test/java/org/nuiton/guix/compiler/GuixCompilerTest.java 2009-07-30 14:57:24 UTC (rev 1538) +++ trunk/guix-compiler/src/test/java/org/nuiton/guix/compiler/GuixCompilerTest.java 2009-07-30 14:58:40 UTC (rev 1539) @@ -36,7 +36,7 @@ File f = new File("src/test/test.guix"); File f2 = new File(f.getAbsolutePath()); GuixLauncher gl = new GuixLauncher(new File[]{f2}, null, - "org.nuiton.guix",f2.getParentFile(),null,null,null); + "org.nuiton.guix",null,f2.getParentFile(),null,null,null); GuixCompiler gc = new GuixCompiler(null, null,null); assertNull(gc.compile());