improved PHP parser
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / JavaProject.java
index 21777c4..e3fdb4d 100644 (file)
@@ -49,20 +49,12 @@ import net.sourceforge.phpdt.core.JavaModelException;
 import net.sourceforge.phpdt.core.WorkingCopyOwner;
 import net.sourceforge.phpdt.internal.codeassist.ISearchableNameEnvironment;
 import net.sourceforge.phpdt.internal.compiler.util.ObjectVector;
+import net.sourceforge.phpdt.internal.core.util.MementoTokenizer;
 import net.sourceforge.phpdt.internal.core.util.Util;
 import net.sourceforge.phpdt.internal.corext.Assert;
 import net.sourceforge.phpeclipse.LoadPathEntry;
 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
 
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-
 import org.eclipse.core.resources.ICommand;
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IFolder;
@@ -80,9 +72,6 @@ import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.Path;
 import org.eclipse.core.runtime.Preferences;
 import org.eclipse.core.runtime.QualifiedName;
-import net.sourceforge.phpdt.internal.core.ClasspathEntry;
-import net.sourceforge.phpdt.internal.core.XMLWriter;
-import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
@@ -148,7 +137,11 @@ public class JavaProject
        public static final IClasspathEntry[] INVALID_CLASSPATH = new IClasspathEntry[0];
 
        private static final String CUSTOM_DEFAULT_OPTION_VALUE = "#\r\n\r#custom-non-empty-default-value#\r\n\r#"; //$NON-NLS-1$
-       
+       /*
+        * Value of project's resolved classpath while it is being resolved
+        */
+       private static final IClasspathEntry[] RESOLUTION_IN_PROGRESS = new IClasspathEntry[0];
+
        /**
         * Returns a canonicalized path from the given external path.
         * Note that the return path contains the same number of segments
@@ -438,57 +431,117 @@ public class JavaProject
 //     }
        
 
-
        /**
         * Internal computation of an expanded classpath. It will eliminate duplicates, and produce copies
         * of exported classpath entries to avoid possible side-effects ever after.
         */                     
        private void computeExpandedClasspath(
-               JavaProject initialProject, 
+               JavaProject initialProject,  
                boolean ignoreUnresolvedVariable,
                boolean generateMarkerOnError,
-               HashSet visitedProjects, 
-               ObjectVector accumulatedEntries) throws JavaModelException {
+               HashSet rootIDs,
+               ObjectVector accumulatedEntries,
+               Map preferredClasspaths,
+               Map preferredOutputs) throws JavaModelException {
                
-               if (visitedProjects.contains(this)){
+               String projectRootId = this.rootID();
+               if (rootIDs.contains(projectRootId)){
                        return; // break cycles if any
                }
-               visitedProjects.add(this);
+               rootIDs.add(projectRootId);
 
-               if (generateMarkerOnError && !this.equals(initialProject)){
-                       generateMarkerOnError = false;
-               }
+               IClasspathEntry[] preferredClasspath = preferredClasspaths != null ? (IClasspathEntry[])preferredClasspaths.get(this) : null;
+               IPath preferredOutput = preferredOutputs != null ? (IPath)preferredOutputs.get(this) : null;
                IClasspathEntry[] immediateClasspath = 
-                       getResolvedClasspath(ignoreUnresolvedVariable, generateMarkerOnError);
+                       preferredClasspath != null 
+                               ? getResolvedClasspath(preferredClasspath, preferredOutput, ignoreUnresolvedVariable, generateMarkerOnError, null)
+                               : getResolvedClasspath(ignoreUnresolvedVariable, generateMarkerOnError, false/*don't returnResolutionInProgress*/);
                        
                IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
+               boolean isInitialProject = this.equals(initialProject);
                for (int i = 0, length = immediateClasspath.length; i < length; i++){
-                       IClasspathEntry entry = immediateClasspath[i];
-
-                       boolean isInitialProject = this.equals(initialProject);
+                       ClasspathEntry entry = (ClasspathEntry) immediateClasspath[i];
                        if (isInitialProject || entry.isExported()){
+                               String rootID = entry.rootID();
+                               if (rootIDs.contains(rootID)) {
+                                       continue;
+                               }
                                
                                accumulatedEntries.add(entry);
                                
                                // recurse in project to get all its indirect exports (only consider exported entries from there on)                            
-                               if (entry.getEntryKind() == ClasspathEntry.CPE_PROJECT) {
+                               if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
                                        IResource member = workspaceRoot.findMember(entry.getPath()); 
                                        if (member != null && member.getType() == IResource.PROJECT){ // double check if bound to project (23977)
                                                IProject projRsc = (IProject) member;
                                                if (JavaProject.hasJavaNature(projRsc)) {                               
-                                                       JavaProject project = (JavaProject) JavaCore.create(projRsc);
-                                                       project.computeExpandedClasspath(
+                                                       JavaProject javaProject = (JavaProject) JavaCore.create(projRsc);
+                                                       javaProject.computeExpandedClasspath(
                                                                initialProject, 
                                                                ignoreUnresolvedVariable, 
-                                                               generateMarkerOnError,
-                                                               visitedProjects, 
-                                                               accumulatedEntries);
+                                                               false /* no marker when recursing in prereq*/,
+                                                               rootIDs,
+                                                               accumulatedEntries,
+                                                               preferredClasspaths,
+                                                               preferredOutputs);
                                                }
                                        }
+                               } else {
+                                       rootIDs.add(rootID);
                                }
                        }                       
                }
        }
+       /**
+        * Internal computation of an expanded classpath. It will eliminate duplicates, and produce copies
+        * of exported classpath entries to avoid possible side-effects ever after.
+        */                     
+//     private void computeExpandedClasspath(
+//             JavaProject initialProject, 
+//             boolean ignoreUnresolvedVariable,
+//             boolean generateMarkerOnError,
+//             HashSet visitedProjects, 
+//             ObjectVector accumulatedEntries) throws JavaModelException {
+//             
+//             if (visitedProjects.contains(this)){
+//                     return; // break cycles if any
+//             }
+//             visitedProjects.add(this);
+//
+//             if (generateMarkerOnError && !this.equals(initialProject)){
+//                     generateMarkerOnError = false;
+//             }
+//             IClasspathEntry[] immediateClasspath = 
+//                     getResolvedClasspath(ignoreUnresolvedVariable, generateMarkerOnError);
+//                     
+//             IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
+//             for (int i = 0, length = immediateClasspath.length; i < length; i++){
+//                     IClasspathEntry entry = immediateClasspath[i];
+//
+//                     boolean isInitialProject = this.equals(initialProject);
+//                     if (isInitialProject || entry.isExported()){
+//                             
+//                             accumulatedEntries.add(entry);
+//                             
+//                             // recurse in project to get all its indirect exports (only consider exported entries from there on)                            
+//                             if (entry.getEntryKind() == ClasspathEntry.CPE_PROJECT) {
+//                                     IResource member = workspaceRoot.findMember(entry.getPath()); 
+//                                     if (member != null && member.getType() == IResource.PROJECT){ // double check if bound to project (23977)
+//                                             IProject projRsc = (IProject) member;
+//                                             if (JavaProject.hasJavaNature(projRsc)) {                               
+//                                                     JavaProject project = (JavaProject) JavaCore.create(projRsc);
+//                                                     project.computeExpandedClasspath(
+//                                                             initialProject, 
+//                                                             ignoreUnresolvedVariable, 
+//                                                             generateMarkerOnError,
+//                                                             visitedProjects, 
+//                                                             accumulatedEntries);
+//                                             }
+//                                     }
+//                             }
+//                     }                       
+//             }
+//     }
        
        /**
         * Returns (local/all) the package fragment roots identified by the given project's classpath.
@@ -591,7 +644,7 @@ public class JavaProject
                                                }
                                        } else {
                                                // external target - only JARs allowed
-//                                             if (((java.io.File)target).isFile() && (Util.isArchiveFileName(entryPath.lastSegment()))) {
+//                                             if (((java.io.File)target).isFile() && (ProjectPrefUtil.isArchiveFileName(entryPath.lastSegment()))) {
 //                                                     accumulatedRoots.add(
 //                                                             new JarPackageFragmentRoot(entryPath, this));
 //                                                     rootIDs.add(rootID);
@@ -712,7 +765,7 @@ public class JavaProject
                        switch (innerMostEntry.getEntryKind()) {
                                case IClasspathEntry.CPE_SOURCE:
                                        // .class files are not visible in source folders 
-                                       return true; //!net.sourceforge.phpdt.internal.compiler.util.Util.isClassFileName(fullPath.lastSegment());
+                                       return true; //!net.sourceforge.phpdt.internal.compiler.util.ProjectPrefUtil.isClassFileName(fullPath.lastSegment());
                                case IClasspathEntry.CPE_LIBRARY:
                                        // .java files are not visible in library folders
                                        return !net.sourceforge.phpdt.internal.compiler.util.Util.isJavaFileName(fullPath.lastSegment());
@@ -915,7 +968,7 @@ public class JavaProject
         * This is the project bin folder
         */
        protected IPath defaultOutputLocation() throws JavaModelException {
-               return getProject().getFullPath().append("bin"); //$NON-NLS-1$
+               return null; //getProject().getFullPath().append("bin"); //$NON-NLS-1$
        }
 
        /**
@@ -1313,38 +1366,95 @@ public class JavaProject
         * This is a helper method returning the expanded classpath for the project, as a list of classpath entries, 
         * where all classpath variable entries have been resolved and substituted with their final target entries.
         * All project exports have been appended to project entries.
+        * @param ignoreUnresolvedVariable boolean
+        * @return IClasspathEntry[]
+        * @throws JavaModelException
         */
        public IClasspathEntry[] getExpandedClasspath(boolean ignoreUnresolvedVariable) throws JavaModelException {
                        
-                       return getExpandedClasspath(ignoreUnresolvedVariable, false);
+                       return getExpandedClasspath(ignoreUnresolvedVariable, false/*don't create markers*/, null, null);
        }
                
+       /*
+        * @see JavaElement
+        */
+       public IJavaElement getHandleFromMemento(String token, MementoTokenizer memento, WorkingCopyOwner owner) {
+               switch (token.charAt(0)) {
+                       case JEM_COUNT:
+                               return getHandleUpdatingCountFromMemento(memento, owner);
+                       case JEM_PACKAGEFRAGMENTROOT:
+                               String rootPath = IPackageFragmentRoot.DEFAULT_PACKAGEROOT_PATH;
+                               token = null;
+                               while (memento.hasMoreTokens()) {
+                                       token = memento.nextToken();
+                                       char firstChar = token.charAt(0);
+                                       if (firstChar != JEM_PACKAGEFRAGMENT && firstChar != JEM_COUNT) {
+                                               rootPath += token;
+                                       } else {
+                                               break;
+                                       }
+                               }
+                               JavaElement root = (JavaElement)getPackageFragmentRoot(new Path(rootPath));
+                               if (token != null && token.charAt(0) == JEM_PACKAGEFRAGMENT) {
+                                       return root.getHandleFromMemento(token, memento, owner);
+                               } else {
+                                       return root.getHandleFromMemento(memento, owner);
+                               }
+               }
+               return null;
+       }
+
+       /**
+        * Returns the <code>char</code> that marks the start of this handles
+        * contribution to a memento.
+        */
+       protected char getHandleMementoDelimiter() {
+
+               return JEM_JAVAPROJECT;
+       }
+       
        /**
         * Internal variant which can create marker on project for invalid entries,
         * it will also perform classpath expansion in presence of project prerequisites
         * exporting their entries.
+        * @param ignoreUnresolvedVariable boolean
+        * @param generateMarkerOnError boolean
+        * @param preferredClasspaths Map
+        * @param preferredOutputs Map
+        * @return IClasspathEntry[]
+        * @throws JavaModelException
         */
        public IClasspathEntry[] getExpandedClasspath(
                boolean ignoreUnresolvedVariable,
-               boolean generateMarkerOnError) throws JavaModelException {
+               boolean generateMarkerOnError,
+               Map preferredClasspaths,
+               Map preferredOutputs) throws JavaModelException {
        
                ObjectVector accumulatedEntries = new ObjectVector();           
-               computeExpandedClasspath(this, ignoreUnresolvedVariable, generateMarkerOnError, new HashSet(5), accumulatedEntries);
+               computeExpandedClasspath(this, ignoreUnresolvedVariable, generateMarkerOnError, new HashSet(5), accumulatedEntries, preferredClasspaths, preferredOutputs);
                
                IClasspathEntry[] expandedPath = new IClasspathEntry[accumulatedEntries.size()];
                accumulatedEntries.copyInto(expandedPath);
 
                return expandedPath;
        }
-
-       /**
-        * Returns the <code>char</code> that marks the start of this handles
-        * contribution to a memento.
-        */
-       protected char getHandleMementoDelimiter() {
-
-               return JEM_JAVAPROJECT;
-       }
+//     /**
+//      * Internal variant which can create marker on project for invalid entries,
+//      * it will also perform classpath expansion in presence of project prerequisites
+//      * exporting their entries.
+//      */
+//     public IClasspathEntry[] getExpandedClasspath(
+//             boolean ignoreUnresolvedVariable,
+//             boolean generateMarkerOnError) throws JavaModelException {
+//     
+//             ObjectVector accumulatedEntries = new ObjectVector();           
+//             computeExpandedClasspath(this, ignoreUnresolvedVariable, generateMarkerOnError, new HashSet(5), accumulatedEntries);
+//             
+//             IClasspathEntry[] expandedPath = new IClasspathEntry[accumulatedEntries.size()];
+//             accumulatedEntries.copyInto(expandedPath);
+//
+//             return expandedPath;
+//     }
 
        /**
         * Find the specific Java command amongst the build spec of a given description
@@ -1417,7 +1527,7 @@ public class JavaProject
 //     }
 
        /**
-        * @see org.eclipse.jdt.core.IJavaProject#getOption(String, boolean)
+        * @see net.sourceforge.phpdt.core.IJavaProject#getOption(String, boolean)
         */     
        public String getOption(String optionName, boolean inheritJavaCoreOptions) {
                
@@ -1433,7 +1543,7 @@ public class JavaProject
        }
        
        /**
-        * @see org.eclipse.jdt.core.IJavaProject#getOptions(boolean)
+        * @see net.sourceforge.phpdt.core.IJavaProject#getOptions(boolean)
         */
        public Map getOptions(boolean inheritJavaCoreOptions) {
                
@@ -1468,21 +1578,48 @@ public class JavaProject
        /**
         * @see IJavaProject
         */
+//     public IPath getOutputLocation() throws JavaModelException {
+//
+//             JavaModelManager.PerProjectInfo perProjectInfo = JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(project);
+//             IPath outputLocation = perProjectInfo.outputLocation;
+//             if (outputLocation != null) return outputLocation;
+//
+//             // force to read classpath - will position output location as well
+//             this.getRawClasspath();
+//             outputLocation = perProjectInfo.outputLocation;
+//             if (outputLocation == null) {
+//                     return defaultOutputLocation();
+//             }
+//             return outputLocation;
+//     }
+       /**
+        * @see IJavaProject
+        */
        public IPath getOutputLocation() throws JavaModelException {
+               // Do not create marker but log problems while getting output location
+               return this.getOutputLocation(false, true);
+       }
+       
+       /**
+        * @param createMarkers boolean
+        * @param logProblems boolean
+        * @return IPath
+        * @throws JavaModelException
+        */
+       public IPath getOutputLocation(boolean createMarkers, boolean logProblems) throws JavaModelException {
 
-               JavaModelManager.PerProjectInfo perProjectInfo = JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(project);
+               JavaModelManager.PerProjectInfo perProjectInfo = getPerProjectInfo();
                IPath outputLocation = perProjectInfo.outputLocation;
                if (outputLocation != null) return outputLocation;
 
                // force to read classpath - will position output location as well
-               this.getRawClasspath();
+               this.getRawClasspath(createMarkers, logProblems);
                outputLocation = perProjectInfo.outputLocation;
                if (outputLocation == null) {
                        return defaultOutputLocation();
                }
                return outputLocation;
        }
-
        /**
         * @return A handle to the package fragment root identified by the given path.
         * This method is handle-only and the element may or may not exist. Returns
@@ -1504,7 +1641,7 @@ public class JavaProject
                        default:
                                // a path ending with .jar/.zip is still ambiguous and could still resolve to a source/lib folder 
                                // thus will try to guess based on existing resource
-//                             if (Util.isArchiveFileName(path.lastSegment())) {
+//                             if (ProjectPrefUtil.isArchiveFileName(path.lastSegment())) {
 //                                     IResource resource = getProject().getWorkspace().getRoot().findMember(path); 
 //                                     if (resource != null && resource.getType() == IResource.FOLDER){
 //                                             return getPackageFragmentRoot(resource);
@@ -1533,7 +1670,7 @@ public class JavaProject
 
                switch (resource.getType()) {
                        case IResource.FILE:
-//                             if (Util.isArchiveFileName(resource.getName())) {
+//                             if (ProjectPrefUtil.isArchiveFileName(resource.getName())) {
 //                                     return new JarPackageFragmentRoot(resource, this);
 //                             } else {
                                        return null;
@@ -1650,6 +1787,10 @@ public class JavaProject
                return this.getProject().getFullPath();
        }
        
+       public JavaModelManager.PerProjectInfo getPerProjectInfo() throws JavaModelException {
+               return JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(this.project);
+       }
+       
        /**
         * @see IJavaProject
         */
@@ -1657,7 +1798,19 @@ public class JavaProject
 
                return project;
        }
-       
+       /**
+        * Sets the underlying kernel project of this Java project,
+        * and fills in its parent and name.
+        * Called by IProject.getNature().
+        *
+        * @see IProjectNature#setProject(IProject)
+        */
+       public void setProject(IProject project) {
+
+               this.project = project;
+               this.parent = JavaModelManager.getJavaModelManager().getJavaModel();
+               this.name = project.getName();
+       }
        protected IProject getProject(String name) {
                return PHPeclipsePlugin.getWorkspace().getRoot().getProject(name);
        }
@@ -1694,13 +1847,61 @@ public class JavaProject
        /**
         * @see IJavaProject
         */
+//     public IClasspathEntry[] getRawClasspath() throws JavaModelException {
+//
+//             JavaModelManager.PerProjectInfo perProjectInfo = JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(project);
+//             IClasspathEntry[] classpath = perProjectInfo.classpath;
+//             if (classpath != null) return classpath;
+//             classpath = this.readClasspathFile(false/*don't create markers*/, true/*log problems*/);
+//             
+//             // extract out the output location
+//             IPath outputLocation = null;
+//             if (classpath != null && classpath.length > 0) {
+//                     IClasspathEntry entry = classpath[classpath.length - 1];
+//                     if (entry.getContentKind() == ClasspathEntry.K_OUTPUT) {
+//                             outputLocation = entry.getPath();
+//                             IClasspathEntry[] copy = new IClasspathEntry[classpath.length - 1];
+//                             System.arraycopy(classpath, 0, copy, 0, copy.length);
+//                             classpath = copy;
+//                     }
+//             }
+//             if (classpath == null) {
+//                     return defaultClasspath();
+//             }
+//             /* Disable validate: classpath can contain CP variables and container that need to be resolved 
+//             if (classpath != INVALID_CLASSPATH
+//                             && !JavaConventions.validateClasspath(this, classpath, outputLocation).isOK()) {
+//                     classpath = INVALID_CLASSPATH;
+//             }
+//             */
+//             perProjectInfo.classpath = classpath;
+//             perProjectInfo.outputLocation = outputLocation;
+//             return classpath;
+//     }
+       /**
+        * @see IJavaProject
+        */
        public IClasspathEntry[] getRawClasspath() throws JavaModelException {
+               // Do not create marker but log problems while getting raw classpath
+               return getRawClasspath(false, true);
+       }
 
-               JavaModelManager.PerProjectInfo perProjectInfo = JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(project);
-               IClasspathEntry[] classpath = perProjectInfo.classpath;
-               if (classpath != null) return classpath;
-               classpath = this.readClasspathFile(false/*don't create markers*/, true/*log problems*/);
-               
+       /*
+        * Internal variant allowing to parameterize problem creation/logging
+        */
+       public IClasspathEntry[] getRawClasspath(boolean createMarkers, boolean logProblems) throws JavaModelException {
+
+               JavaModelManager.PerProjectInfo perProjectInfo = null;
+               IClasspathEntry[] classpath;
+               if (createMarkers) {
+                       this.flushClasspathProblemMarkers(false/*cycle*/, true/*format*/);
+                       classpath = this.readClasspathFile(createMarkers, logProblems);
+               } else {
+                       perProjectInfo = getPerProjectInfo();
+                       classpath = perProjectInfo.rawClasspath;
+                       if (classpath != null) return classpath;
+                       classpath = this.readClasspathFile(createMarkers, logProblems);
+               }
                // extract out the output location
                IPath outputLocation = null;
                if (classpath != null && classpath.length > 0) {
@@ -1721,11 +1922,13 @@ public class JavaProject
                        classpath = INVALID_CLASSPATH;
                }
                */
-               perProjectInfo.classpath = classpath;
-               perProjectInfo.outputLocation = outputLocation;
+               if (!createMarkers) {
+                       perProjectInfo.rawClasspath = classpath;
+                       perProjectInfo.outputLocation = outputLocation;
+               }
                return classpath;
        }
-
+       
        /**
         * @see IJavaProject#getRequiredProjectNames
         */
@@ -1754,39 +1957,108 @@ public class JavaProject
                boolean ignoreUnresolvedEntry,
                boolean generateMarkerOnError)
                throws JavaModelException {
+         return 
+               getResolvedClasspath(
+                       ignoreUnresolvedEntry, 
+                       generateMarkerOnError,
+                       true // returnResolutionInProgress
+               );
+//             JavaModelManager manager = JavaModelManager.getJavaModelManager();
+//             JavaModelManager.PerProjectInfo perProjectInfo = manager.getPerProjectInfoCheckExistence(project);
+//             
+//             // reuse cache if not needing to refresh markers or checking bound variables
+//             if (ignoreUnresolvedEntry && !generateMarkerOnError && perProjectInfo != null){
+//                     // resolved path is cached on its info
+//                     IClasspathEntry[] infoPath = perProjectInfo.lastResolvedClasspath;
+//                     if (infoPath != null) return infoPath;
+//             }
+//             Map reverseMap = perProjectInfo == null ? null : new HashMap(5);
+//             IClasspathEntry[] resolvedPath = getResolvedClasspath(
+//                     getRawClasspath(), 
+//                     generateMarkerOnError ? getOutputLocation() : null, 
+//                     ignoreUnresolvedEntry, 
+//                     generateMarkerOnError,
+//                     reverseMap);
+//
+//             if (perProjectInfo != null){
+//                     if (perProjectInfo.classpath == null // .classpath file could not be read
+//                             && generateMarkerOnError 
+//                             && JavaProject.hasJavaNature(project)) {
+//                                     this.createClasspathProblemMarker(new JavaModelStatus(
+//                                             IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT,
+//                                             Util.bind("classpath.cannotReadClasspathFile", this.getElementName()))); //$NON-NLS-1$
+//                             }
+//
+//                     perProjectInfo.lastResolvedClasspath = resolvedPath;
+//                     perProjectInfo.resolvedPathToRawEntries = reverseMap;
+//             }
+//             return resolvedPath;
+       }
+       /*
+        * Internal variant which can create marker on project for invalid entries
+        * and caches the resolved classpath on perProjectInfo.
+        * If requested, return a special classpath (RESOLUTION_IN_PROGRESS) if the classpath is being resolved.
+        */
+       public IClasspathEntry[] getResolvedClasspath(
+               boolean ignoreUnresolvedEntry,
+               boolean generateMarkerOnError,
+               boolean returnResolutionInProgress)
+               throws JavaModelException {
 
-               JavaModelManager manager = JavaModelManager.getJavaModelManager();
-               JavaModelManager.PerProjectInfo perProjectInfo = manager.getPerProjectInfoCheckExistence(project);
-               
-               // reuse cache if not needing to refresh markers or checking bound variables
-               if (ignoreUnresolvedEntry && !generateMarkerOnError && perProjectInfo != null){
-                       // resolved path is cached on its info
-                       IClasspathEntry[] infoPath = perProjectInfo.lastResolvedClasspath;
-                       if (infoPath != null) return infoPath;
+           JavaModelManager manager = JavaModelManager.getJavaModelManager();
+               JavaModelManager.PerProjectInfo perProjectInfo = null;
+               if (ignoreUnresolvedEntry && !generateMarkerOnError) {
+                       perProjectInfo = getPerProjectInfo();
+                       if (perProjectInfo != null) {
+                               // resolved path is cached on its info
+                               IClasspathEntry[] infoPath = perProjectInfo.resolvedClasspath;
+                               if (infoPath != null) {
+                                       return infoPath;
+                               } else if  (returnResolutionInProgress && manager.isClasspathBeingResolved(this)) {
+                                       if (JavaModelManager.CP_RESOLVE_VERBOSE) {
+                                               Util.verbose(
+                                                       "CPResolution: reentering raw classpath resolution, will use empty classpath instead" + //$NON-NLS-1$
+                                                       "       project: " + getElementName() + '\n' + //$NON-NLS-1$
+                                                       "       invocation stack trace:"); //$NON-NLS-1$
+                                               new Exception("<Fake exception>").printStackTrace(System.out); //$NON-NLS-1$
+                                       }                                               
+                                   return RESOLUTION_IN_PROGRESS;
+                               }
+                       }
                }
                Map reverseMap = perProjectInfo == null ? null : new HashMap(5);
-               IClasspathEntry[] resolvedPath = getResolvedClasspath(
-                       getRawClasspath(), 
-                       generateMarkerOnError ? getOutputLocation() : null, 
-                       ignoreUnresolvedEntry, 
-                       generateMarkerOnError,
-                       reverseMap);
+               IClasspathEntry[] resolvedPath = null;
+               boolean nullOldResolvedCP = perProjectInfo != null && perProjectInfo.resolvedClasspath == null;
+               try {
+                       // protect against misbehaving clients (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=61040)
+                       if (nullOldResolvedCP) manager.setClasspathBeingResolved(this, true);
+                       resolvedPath = getResolvedClasspath(
+                               getRawClasspath(generateMarkerOnError, !generateMarkerOnError), 
+                               generateMarkerOnError ? getOutputLocation() : null, 
+                               ignoreUnresolvedEntry, 
+                               generateMarkerOnError,
+                               reverseMap);
+               } finally {
+                       if (nullOldResolvedCP) perProjectInfo.resolvedClasspath = null;
+               }
 
                if (perProjectInfo != null){
-                       if (perProjectInfo.classpath == null // .classpath file could not be read
+                       if (perProjectInfo.rawClasspath == null // .classpath file could not be read
                                && generateMarkerOnError 
-                               && JavaProject.hasJavaNature(project)) {
+                               && JavaProject.hasJavaNature(this.project)) {
+                                       // flush .classpath format markers (bug 39877), but only when file cannot be read (bug 42366)
+                                       this.flushClasspathProblemMarkers(false, true);
                                        this.createClasspathProblemMarker(new JavaModelStatus(
                                                IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT,
                                                Util.bind("classpath.cannotReadClasspathFile", this.getElementName()))); //$NON-NLS-1$
-                               }
+                       }
 
-                       perProjectInfo.lastResolvedClasspath = resolvedPath;
+                       perProjectInfo.resolvedClasspath = resolvedPath;
                        perProjectInfo.resolvedPathToRawEntries = reverseMap;
+                       manager.setClasspathBeingResolved(this, false);
                }
                return resolvedPath;
        }
-       
        /**
         * Internal variant which can process any arbitrary classpath
         */
@@ -1894,11 +2166,12 @@ public class JavaProject
        public ISearchableNameEnvironment getSearchableNameEnvironment()
                throws JavaModelException {
 
-               JavaProjectElementInfo info = getJavaProjectElementInfo();
-               if (info.getSearchableEnvironment() == null) {
-                       info.setSearchableEnvironment(new SearchableEnvironment(this));
-               }
-               return info.getSearchableEnvironment();
+//             JavaProjectElementInfo info = getJavaProjectElementInfo();
+//             if (info.getSearchableEnvironment() == null) {
+//                     info.setSearchableEnvironment(new SearchableEnvironment(this));
+//             }
+//             return info.getSearchableEnvironment();
+         return null;
        }
 
        /**
@@ -2055,7 +2328,7 @@ public class JavaProject
                for (int i = 0; i < classpath.length; i++) {
                        IClasspathEntry entry = classpath[i];
                        if (entry.getPath().isPrefixOf(path) 
-                                       && !Util.isExcluded(path, ((ClasspathEntry)entry).fullExclusionPatternChars())) {
+                                       && !Util.isExcluded(path, null,((ClasspathEntry)entry).fullExclusionPatternChars(),true)) {
                                return true;
                        }
                }
@@ -2075,7 +2348,9 @@ public class JavaProject
                return this.isOnClasspath(path);
        }
 
-
+       private IPath getPluginWorkingLocation() {
+               return this.project.getWorkingLocation(JavaCore.PLUGIN_ID);
+       }       
        /*
         * load preferences from a shareable format (VCM-wise)
         */
@@ -2084,7 +2359,7 @@ public class JavaProject
                Preferences preferences = new Preferences();
                
 //             File prefFile = getProject().getLocation().append(PREF_FILENAME).toFile();
-               IPath projectMetaLocation = getProject().getPluginWorkingLocation(JavaCore.getPlugin().getDescriptor());
+               IPath projectMetaLocation = getPluginWorkingLocation();
                if (projectMetaLocation != null) {
                        File prefFile = projectMetaLocation.append(PREF_FILENAME).toFile();
                        if (prefFile.exists()) { // load preferences from file
@@ -2124,7 +2399,7 @@ public class JavaProject
 //             throws JavaModelException {
 //
 //             if (region == null) {
-//                     throw new IllegalArgumentException(Util.bind("hierarchy.nullRegion"));//$NON-NLS-1$
+//                     throw new IllegalArgumentException(ProjectPrefUtil.bind("hierarchy.nullRegion"));//$NON-NLS-1$
 //             }
 //             CreateTypeHierarchyOperation op =
 //                     new CreateTypeHierarchyOperation(null, region, this, true);
@@ -2142,10 +2417,10 @@ public class JavaProject
 //             throws JavaModelException {
 //
 //             if (type == null) {
-//                     throw new IllegalArgumentException(Util.bind("hierarchy.nullFocusType"));//$NON-NLS-1$
+//                     throw new IllegalArgumentException(ProjectPrefUtil.bind("hierarchy.nullFocusType"));//$NON-NLS-1$
 //             }
 //             if (region == null) {
-//                     throw new IllegalArgumentException(Util.bind("hierarchy.nullRegion"));//$NON-NLS-1$
+//                     throw new IllegalArgumentException(ProjectPrefUtil.bind("hierarchy.nullRegion"));//$NON-NLS-1$
 //             }
 //             CreateTypeHierarchyOperation op =
 //                     new CreateTypeHierarchyOperation(type, region, this, true);
@@ -2276,13 +2551,13 @@ public class JavaProject
                }
        }
 
+
        /**
         * Save project custom preferences to shareable file (.jprefs)
         */
        private void savePreferences(Preferences preferences) {
                
-               IProject project = getProject();
-               if (!JavaProject.hasJavaNature(project)) return; // ignore
+               if (!JavaProject.hasJavaNature(this.project)) return; // ignore
                
                if (preferences == null || (!preferences.needsSaving() && preferences.propertyNames().length != 0)) {
                        // nothing to save
@@ -2292,8 +2567,8 @@ public class JavaProject
                // preferences need to be saved
                // the preferences file is located in the plug-in's state area
                // at a well-known name (.jprefs)
-//             File prefFile = getProject().getLocation().append(PREF_FILENAME).toFile();
-               File prefFile = project.getPluginWorkingLocation(JavaCore.getPlugin().getDescriptor()).append(PREF_FILENAME).toFile();
+//             File prefFile = this.project.getLocation().append(PREF_FILENAME).toFile();
+               File prefFile = getPluginWorkingLocation().append(PREF_FILENAME).toFile();
                if (preferences.propertyNames().length == 0) {
                        // there are no preference settings
                        // rather than write an empty file, just delete any existing file
@@ -2320,7 +2595,6 @@ public class JavaProject
                        }
                }
        }
-
        /**
         * Update the Java command in the build spec (replace existing one if present,
         * add one first if none).
@@ -2355,7 +2629,7 @@ public class JavaProject
        }
 
        /**
-        * @see org.eclipse.jdt.core.IJavaProject#setOptions(Map)
+        * @see net.sourceforge.phpdt.core.IJavaProject#setOptions(Map)
         */
        public void setOptions(Map newOptions) {
 
@@ -2403,20 +2677,6 @@ public class JavaProject
        }
 
        /**
-        * Sets the underlying kernel project of this Java project,
-        * and fills in its parent and name.
-        * Called by IProject.getNature().
-        *
-        * @see IProjectNature#setProject
-        */
-       public void setProject(IProject project) {
-
-               project = project;
-               parent = JavaModelManager.getJavaModelManager().getJavaModel();
-               name = project.getName();
-       }
-
-       /**
         * @see IJavaProject
         */
        public void setRawClasspath(
@@ -2493,21 +2753,21 @@ public class JavaProject
         *
         * @exception NotPresentException if this project does not exist.
         */
-       protected void setRawClasspath0(IClasspathEntry[] rawEntries)
-               throws JavaModelException {
-
-               JavaModelManager.PerProjectInfo info = JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(project);
-       
-               synchronized (info) {
-                       if (rawEntries != null) {
-                               info.classpath = rawEntries;
-                       }
-                       
-                       // clear cache of resolved classpath
-                       info.lastResolvedClasspath = null;
-                       info.resolvedPathToRawEntries = null;
-               }
-       }
+//     protected void setRawClasspath0(IClasspathEntry[] rawEntries)
+//             throws JavaModelException {
+//
+//             JavaModelManager.PerProjectInfo info = JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(project);
+//     
+//             synchronized (info) {
+//                     if (rawEntries != null) {
+//                             info.classpath = rawEntries;
+//                     }
+//                     
+//                     // clear cache of resolved classpath
+//                     info.lastResolvedClasspath = null;
+//                     info.resolvedPathToRawEntries = null;
+//             }
+//     }
 
        /**
         * Record a shared persistent property onto a project.