Bug 37971 - Non "source-path" ICompilationUnit from IResource in a Builder
Summary: Non "source-path" ICompilationUnit from IResource in a Builder
Status: RESOLVED INVALID
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 2.1   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: 3.0 M1   Edit
Assignee: JDT-Core-Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2003-05-22 09:31 EDT by Timothy Halloran CLA
Modified: 2003-07-18 06:57 EDT (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Timothy Halloran CLA 2003-05-22 09:31:41 EDT
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?
Comment 1 Philipe Mulet CLA 2003-05-22 10:21:44 EDT
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).
Comment 2 Philipe Mulet CLA 2003-05-22 10:24:17 EDT
I agree though that #isStructureKnown shouldn't declare thrown exceptions. Will 
see if we can perform this change for 3.0 (likely yes).
Comment 3 Olivier Thomann CLA 2003-05-22 10:59:55 EDT
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.
Comment 4 Timothy Halloran CLA 2003-05-22 13:08:04 EDT
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)
Comment 5 Timothy Halloran CLA 2003-05-22 13:16:00 EDT
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");
      }