Community
Participate
Working Groups
I've implemented a nature and builder that runs after the Java compiler. The problem I'm seeing is related to generating the AST for a Java compilation unit. The builder provides an IResource resource, which can be changed via IJavaElement javaResource = JavaCore.create(resource); if ((javaResource != null) && (javaResource.getElementType() == IJavaElement.COMPILATION_UNIT)) { ICompilationUnit compUnit = (ICompilationUnit) javaResource; OK this all seems to work EXCEPT if the "foo.java" file is NOT within a source path or is excluded, via a filter, from within a source path of the Java project (i.e., the Java builder doesn't compile it) in that case a call to: CompilationUnit ast = AST.parseCompilationUnit(compUnit, true); results in a runtime exception (varies, null pointer, etc.) I've tried a couple approaches to work around this (as no obvious API is jumping out at me) (1) to see if sturcture exists: boolean hasStructure = false; } try { hasStructure = compUnit.isStructureKnown(); } catch (JavaModelException e) { e.printStackTrace(); } if (hasStructure) { CompilationUnit ast = AST.parseCompilationUnit(compUnit, true); This seems to work but isStructureKnown ALWAYS throws JavaModelException for one of these outside foo.java files. It also seems very slow. (2) to try and see if the compilation unit was within a Java source path via: boolean hasStructure = false; IPackageFragmentRoot pfr = (IPackageFragmentRoot) compUnit.getAncestor( IJavaElement.PACKAGE_FRAGMENT_ROOT); if ((pfr != null) && (!pfr.isArchive())) { LOG.info("found pfr "+ pfr.getElementName()); hasStructure = true; } if (hasStructure) { CompilationUnit ast = AST.parseCompilationUnit(compUnit, true); This just doesn't work. Everything seems to have a package fragment root. I think there should be a way to identify if the resource passed by a builder, once changed into a ICompilationUnit, is actually within a source path the project normally builds so the strangness I'm seeing is avoidable. Either (1) I'm missing some API :-) (2) The IJavaElement for these .java files should NOT be a ICompilationUnit (a bug) (3) The isStructureKnown() should return "false" and not die (a bug) This looks fishy enough I reported it...Thoughts?
JavaCore.create(rsc) doesn't transform anything. It creates a handle onto this resource which denotes a compilation unit. The fact that this unit isn't reachable on the build path is another issue. Handles are quite flexible, and can be created on non-existing units etc... In order to check whether your unit handle is on the build path, why aren't you using: IJavaProject.isOnClasspath ? The fact that the unit outside the path still has a package fragment root corresponds to the fact that we support this mode, and create a fake package fragment root (which doesn't exist for real). This behavior is required to allow opening Java editors on units outside the classpath (which you can still edit).
I agree though that #isStructureKnown shouldn't declare thrown exceptions. Will see if we can perform this change for 3.0 (likely yes).
When I tried: CompilationUnit ast = AST.parseCompilationUnit(compUnit, true); with a compilation unit that is not on the classpath, I got a IllegalArgumentException because the source could not be retrieved. You said you got a NullPointerException. Could you please provide the stack trace? I'd like to investigate this.
Olivier, Yes, that's the most common case and in my most recent code I always get it. I'll try to dig up what I was doing that was throwing the NullPointerException and post if I can find it. INFO [ModalContext] (Majordomo.java:83) - can't generate AST for SimpleService.java java.lang.IllegalArgumentException at org.eclipse.jdt.core.dom.AST.parseCompilationUnit(AST.java:251) at edu.cmu.cs.fluid.javaassure.Majordomo.analyzeResource(Majordomo.java:81) at edu.cmu.cs.fluid.javaassure.Builder.analyzeResource(Builder.java:42) at edu.cmu.cs.fluid.javaassure.Builder.visit(Builder.java:135) at org.eclipse.core.internal.resources.Resource$2.visit(Resource.java:106) at org.eclipse.core.internal.resources.Resource$1.visitElement(Resource.java:50) at org.eclipse.core.internal.watson.ElementTreeIterator.doIteration(ElementTreeIterator.java:76) at org.eclipse.core.internal.watson.ElementTreeIterator.doIteration(ElementTreeIterator.java:80) at org.eclipse.core.internal.watson.ElementTreeIterator.doIteration(ElementTreeIterator.java:80) at org.eclipse.core.internal.watson.ElementTreeIterator.doIteration(ElementTreeIterator.java:80) at org.eclipse.core.internal.watson.ElementTreeIterator.iterate(ElementTreeIterator.java:119) at org.eclipse.core.internal.resources.Resource.accept(Resource.java:60) at org.eclipse.core.internal.resources.Resource.accept(Resource.java:104) at org.eclipse.core.internal.resources.Resource.accept(Resource.java:82) at edu.cmu.cs.fluid.javaassure.Builder.build(Builder.java:62) at org.eclipse.core.internal.events.BuildManager$2.run(BuildManager.java:427) at org.eclipse.core.internal.runtime.InternalPlatform.run(InternalPlatform.java:889) at org.eclipse.core.runtime.Platform.run(Platform.java:413) at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:125) at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:181) at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:191) at org.eclipse.core.internal.events.BuildManager$1.run(BuildManager.java:151) at org.eclipse.core.internal.runtime.InternalPlatform.run(InternalPlatform.java:889) at org.eclipse.core.runtime.Platform.run(Platform.java:413) at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:165) at org.eclipse.core.internal.events.BuildManager.basicBuildLoop(BuildManager.java:243) at org.eclipse.core.internal.events.BuildManager.build(BuildManager.java:212) at org.eclipse.core.internal.resources.Workspace.build(Workspace.java:181) at org.eclipse.ui.actions.GlobalBuildAction$1.run(GlobalBuildAction.java:174) at org.eclipse.jface.operation.ModalContext$ModalContextThread.run(ModalContext.java:101)
The below code seems to work OK (I missed the indirect link to isOnClasspath via the JavaProject -- should the ICompilationUnit have a "boolean isOnClasspath()" convience method? IJavaElement javaResource = JavaCore.create(resource); if ((javaResource != null) && (javaResource.getElementType() == IJavaElement.COMPILATION_UNIT)) { ICompilationUnit compUnit = (ICompilationUnit) javaResource; if (compUnit.getJavaProject().isOnClasspath(compUnit)) { CompilationUnit ast = AST.parseCompilationUnit(compUnit, true); } else { LOG.info( "can't generate AST for " + compUnit.getElementName() + " not on classpath"); }