Register new file extensions for the php-editor:
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / JavaModelManager.java
index 3410fbc..39978d1 100644 (file)
@@ -1,10 +1,10 @@
 /*******************************************************************************
  * Copyright (c) 2000, 2003 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials 
+ * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Common Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/cpl-v10.html
- * 
+ *
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *******************************************************************************/
@@ -82,13 +82,12 @@ import org.eclipse.core.runtime.Preferences.PropertyChangeEvent;
  * The single instance of <code>JavaModelManager</code> is available from
  * the static method <code>JavaModelManager.getJavaModelManager()</code>.
  */
-public class JavaModelManager implements ISaveParticipant {    
-       /**
+public class JavaModelManager implements ISaveParticipant {
+       /**
         * Unique handle onto the JavaModel
         */
        final JavaModel javaModel = new JavaModel();
-       
+//     public IndexManager indexManager = new IndexManager();
        /**
         * Classpath variables pool
         */
@@ -96,13 +95,13 @@ public class JavaModelManager implements ISaveParticipant {
        public static HashMap PreviousSessionVariables = new HashMap(5);
        public static HashSet OptionNames = new HashSet(20);
        public final static String CP_VARIABLE_PREFERENCES_PREFIX = PHPeclipsePlugin.PLUGIN_ID+".classpathVariable."; //$NON-NLS-1$
-//     public final static String CP_CONTAINER_PREFERENCES_PREFIX = PHPCore.PLUGIN_ID+".classpathContainer."; //$NON-NLS-1$
+       public final static String CP_CONTAINER_PREFERENCES_PREFIX = PHPeclipsePlugin.PLUGIN_ID+".classpathContainer."; //$NON-NLS-1$
        public final static String CP_ENTRY_IGNORE = "##<cp entry ignore>##"; //$NON-NLS-1$
-               
+
        /**
         * Classpath containers pool
         */
-       public static HashMap Containers = new HashMap(5);
+       public static HashMap containers = new HashMap(5);
        public static HashMap PreviousSessionContainers = new HashMap(5);
 
        /**
@@ -118,8 +117,13 @@ public class JavaModelManager implements ISaveParticipant {
        /**
         * Name of the extension point for contributing a source code formatter
         */
-       public static final String FORMATTER_EXTPOINT_ID = "codeFormatter" ; //$NON-NLS-1$
-       
+       public static final String FORMATTER_EXTPOINT_ID = "codeFormatter" ; //$/**
+
+       /**
+        * Value of the content-type for Java source files
+        */
+       public static final String JAVA_SOURCE_CONTENT_TYPE = PHPeclipsePlugin.PLUGIN_ID+".phpSource" ; //$NON-NLS-1$NON-NLS-1$
+
        /**
         * Special value used for recognizing ongoing initialization and breaking initialization cycles
         */
@@ -131,7 +135,7 @@ public class JavaModelManager implements ISaveParticipant {
 //             public IPath getPath() { return null; }
 //             public String toString() { return getDescription(); }
 //     };
-       
+
        private static final String INDEX_MANAGER_DEBUG = PHPeclipsePlugin.PLUGIN_ID + "/debug/indexmanager" ; //$NON-NLS-1$
        private static final String COMPILER_DEBUG = PHPeclipsePlugin.PLUGIN_ID + "/debug/compiler" ; //$NON-NLS-1$
        private static final String JAVAMODEL_DEBUG = PHPeclipsePlugin.PLUGIN_ID + "/debug/javamodel" ; //$NON-NLS-1$
@@ -147,7 +151,7 @@ public class JavaModelManager implements ISaveParticipant {
        private static final String SEARCH_DEBUG = PHPeclipsePlugin.PLUGIN_ID + "/debug/search" ; //$NON-NLS-1$
 
        public final static IWorkingCopy[] NoWorkingCopy = new IWorkingCopy[0];
-       
+
        /**
         * Table from WorkingCopyOwner to a table of ICompilationUnit (working copy handle) to PerWorkingCopyInfo.
         * NOTE: this object itself is used as a lock to synchronize creation/removal of per working copy infos
@@ -189,7 +193,7 @@ public class JavaModelManager implements ISaveParticipant {
                }
        }
 
-//     public static IClasspathContainer containerGet(IJavaProject project, IPath containerPath) {     
+//     public static IClasspathContainer containerGet(IJavaProject project, IPath containerPath) {
 //             Map projectContainers = (Map)Containers.get(project);
 //             if (projectContainers == null){
 //                     return null;
@@ -293,7 +297,7 @@ public class JavaModelManager implements ISaveParticipant {
                if (project == null) {
                        project = JavaCore.create(file.getProject());
                }
-       
+
                if (file.getFileExtension() != null) {
                        String name = file.getName();
                        if (PHPFileUtil.isValidPHPUnitName(name))
@@ -309,7 +313,7 @@ public class JavaModelManager implements ISaveParticipant {
 
        /**
         * Returns the package fragment or package fragment root corresponding to the given folder,
-        * its parent or great parent being the given project. 
+        * its parent or great parent being the given project.
         * or <code>null</code> if unable to associate the given folder with a Java element.
         * <p>
         * Note that a package fragment root is returned rather than a default package.
@@ -326,7 +330,7 @@ public class JavaModelManager implements ISaveParticipant {
                }
                IJavaElement element = determineIfOnClasspath(folder, project);
                if (conflictsWithOutputLocation(folder.getFullPath(), (JavaProject)project)
-                       || (folder.getName().indexOf('.') >= 0 
+                       || (folder.getName().indexOf('.') >= 0
                                && !(element instanceof IPackageFragmentRoot))) {
                        return null; // only package fragment roots are allowed with dot names
                } else {
@@ -355,9 +359,9 @@ public class JavaModelManager implements ISaveParticipant {
 //             }
 //             return pkg.getClassFile(file.getName());
 //     }
-       
+
        /**
-        * Creates and returns a compilation unit element for the given <code>.java</code> 
+        * Creates and returns a compilation unit element for the given <code>.java</code>
         * file, its project being the given project. Returns <code>null</code> if unable
         * to recognize the compilation unit.
         */
@@ -373,7 +377,7 @@ public class JavaModelManager implements ISaveParticipant {
                        // not on classpath - make the root its folder, and a default package
                        IPackageFragmentRoot root = project.getPackageFragmentRoot(file.getParent());
                        pkg = root.getPackageFragment(IPackageFragment.DEFAULT_PACKAGE_NAME);
-                       
+
                        if (VERBOSE){
                                System.out.println("WARNING : creating unit element outside classpath ("+ Thread.currentThread()+"): " + file.getFullPath()); //$NON-NLS-1$//$NON-NLS-2$
                        }
@@ -383,7 +387,7 @@ public class JavaModelManager implements ISaveParticipant {
        /**
         * Creates and returns a handle for the given JAR file, its project being the given project.
         * The Java model associated with the JAR's project may be
-        * created as a side effect. 
+        * created as a side effect.
         * Returns <code>null</code> if unable to create a JAR package fragment root.
         * (for example, if the JAR file represents a non-Java resource)
         */
@@ -394,7 +398,7 @@ public class JavaModelManager implements ISaveParticipant {
 //             if (project == null) {
 //                     project = PHPCore.create(file.getProject());
 //             }
-//     
+//
 //             // Create a jar package fragment root only if on the classpath
 //             IPath resourcePath = file.getFullPath();
 //             try {
@@ -410,7 +414,7 @@ public class JavaModelManager implements ISaveParticipant {
 //             }
 //             return null;
 //     }
-       
+
        /**
         * Returns the package fragment root represented by the resource, or
         * the package fragment the given resource is located in, or <code>null</code>
@@ -419,21 +423,21 @@ public class JavaModelManager implements ISaveParticipant {
        public static IJavaElement determineIfOnClasspath(
                IResource resource,
                IJavaProject project) {
-                       
+
                IPath resourcePath = resource.getFullPath();
                try {
-                       IClasspathEntry[] entries = 
+                       IClasspathEntry[] entries =
                                net.sourceforge.phpdt.internal.compiler.util.Util.isJavaFileName(resourcePath.lastSegment())
                                        ? project.getRawClasspath() // JAVA file can only live inside SRC folder (on the raw path)
                                        : ((JavaProject)project).getResolvedClasspath(true);
-                               
+
                        for (int i = 0; i < entries.length; i++) {
                                IClasspathEntry entry = entries[i];
                                if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) continue;
                                IPath rootPath = entry.getPath();
                                if (rootPath.equals(resourcePath)) {
                                        return project.getPackageFragmentRoot(resource);
-                               } else if (rootPath.isPrefixOf(resourcePath) && !Util.isExcluded(resource, ((ClasspathEntry)entry).fullExclusionPatternChars())) {
+                               } else if (rootPath.isPrefixOf(resourcePath) && !Util.isExcluded(resource, null, ((ClasspathEntry)entry).fullExclusionPatternChars())) {
                                        // given we have a resource child of the root, it cannot be a JAR pkg root
                                        IPackageFragmentRoot root = ((JavaProject) project).getFolderPackageFragmentRoot(rootPath);
                                        if (root == null) return null;
@@ -442,7 +446,7 @@ public class JavaModelManager implements ISaveParticipant {
                                                // if the resource is a file, then remove the last segment which
                                                // is the file name in the package
                                                pkgPath = pkgPath.removeLastSegments(1);
-                                               
+
                                                // don't check validity of package name (see http://bugs.eclipse.org/bugs/show_bug.cgi?id=26706)
 //                                             String pkgName = pkgPath.toString().replace('/', '.');
                                                String pkgName = pkgPath.toString();
@@ -461,7 +465,7 @@ public class JavaModelManager implements ISaveParticipant {
                }
                return null;
        }
-       
+
        /**
         * The singleton manager
         */
@@ -519,9 +523,9 @@ public class JavaModelManager implements ISaveParticipant {
        /**
         * Used to update the JavaModel for <code>IJavaElementDelta</code>s.
         */
-//     private final ModelUpdater modelUpdater =new ModelUpdater();
+       private final ModelUpdater modelUpdater =new ModelUpdater();
        /**
-        * Workaround for bug 15168 circular errors not reported  
+        * Workaround for bug 15168 circular errors not reported
         * This is a cache of the projects before any project addition/deletion has started.
         */
        public IJavaProject[] javaProjectsCache;
@@ -531,34 +535,95 @@ public class JavaModelManager implements ISaveParticipant {
         * NOTE: this object itself is used as a lock to synchronize creation/removal of per project infos
         */
        protected Map perProjectInfo = new HashMap(5);
-       
+
        /**
         * A map from ICompilationUnit to IWorkingCopy
         * of the shared working copies.
         */
        public Map sharedWorkingCopies = new HashMap();
-       
+
        /**
         * A weak set of the known scopes.
         */
-       protected WeakHashMap scopes = new WeakHashMap();
+       protected WeakHashMap searchScopes = new WeakHashMap();
+
+//     public static class PerProjectInfo {
+//             public IProject project;
+//             public Object savedState;
+//             public boolean triedRead;
+//             public IClasspathEntry[] classpath;
+//             public IClasspathEntry[] lastResolvedClasspath;
+//             public Map resolvedPathToRawEntries; // reverse map from resolved path to raw entries
+//             public IPath outputLocation;
+//             public Preferences preferences;
+//             public PerProjectInfo(IProject project) {
+//
+//                     this.triedRead = false;
+//                     this.savedState = null;
+//                     this.project = project;
+//             }
+//     }
 
        public static class PerProjectInfo {
+
                public IProject project;
                public Object savedState;
                public boolean triedRead;
-               public IClasspathEntry[] classpath;
-               public IClasspathEntry[] lastResolvedClasspath;
+               public IClasspathEntry[] rawClasspath;
+               public IClasspathEntry[] resolvedClasspath;
                public Map resolvedPathToRawEntries; // reverse map from resolved path to raw entries
                public IPath outputLocation;
                public Preferences preferences;
+
                public PerProjectInfo(IProject project) {
 
                        this.triedRead = false;
                        this.savedState = null;
                        this.project = project;
                }
+
+               // updating raw classpath need to flush obsoleted cached information about resolved entries
+               public synchronized void updateClasspathInformation(IClasspathEntry[] newRawClasspath) {
+
+                       this.rawClasspath = newRawClasspath;
+                       this.resolvedClasspath = null;
+                       this.resolvedPathToRawEntries = null;
+               }
+               public String toString() {
+                       StringBuffer buffer = new StringBuffer();
+                       buffer.append("Info for "); //$NON-NLS-1$
+                       buffer.append(this.project.getFullPath());
+                       buffer.append("\nRaw classpath:\n"); //$NON-NLS-1$
+                       if (this.rawClasspath == null) {
+                               buffer.append("  <null>\n"); //$NON-NLS-1$
+                       } else {
+                               for (int i = 0, length = this.rawClasspath.length; i < length; i++) {
+                                       buffer.append("  "); //$NON-NLS-1$
+                                       buffer.append(this.rawClasspath[i]);
+                                       buffer.append('\n');
+                               }
+                       }
+                       buffer.append("Resolved classpath:\n"); //$NON-NLS-1$
+                       IClasspathEntry[] resolvedCP = this.resolvedClasspath;
+                       if (resolvedCP == null) {
+                               buffer.append("  <null>\n"); //$NON-NLS-1$
+                       } else {
+                               for (int i = 0, length = resolvedCP.length; i < length; i++) {
+                                       buffer.append("  "); //$NON-NLS-1$
+                                       buffer.append(resolvedCP[i]);
+                                       buffer.append('\n');
+                               }
+                       }
+                       buffer.append("Output location:\n  "); //$NON-NLS-1$
+                       if (this.outputLocation == null) {
+                               buffer.append("<null>"); //$NON-NLS-1$
+                       } else {
+                               buffer.append(this.outputLocation);
+                       }
+                       return buffer.toString();
+               }
        }
+
        public static class PerWorkingCopyInfo implements IProblemRequestor {
                int useCount = 0;
                IProblemRequestor problemRequestor;
@@ -599,15 +664,15 @@ public class JavaModelManager implements ISaveParticipant {
        public static boolean VERBOSE = false;
        public static boolean CP_RESOLVE_VERBOSE = false;
        public static boolean ZIP_ACCESS_VERBOSE = false;
-       
+
        /**
         * A cache of opened zip files per thread.
         * (map from Thread to map of IPath to java.io.ZipFile)
         * NOTE: this object itself is used as a lock to synchronize creation/removal of entries
         */
        private HashMap zipFiles = new HashMap();
-       
-       
+
+
        /**
         * Update the classpath variable cache
         */
@@ -657,7 +722,7 @@ public class JavaModelManager implements ISaveParticipant {
        public void addElementChangedListener(IElementChangedListener listener, int eventMask) {
                for (int i = 0; i < this.elementChangedListenerCount; i++){
                        if (this.elementChangedListeners[i].equals(listener)){
-                               
+
                                // only clone the masks, since we could be in the middle of notifications and one listener decide to change
                                // any event mask of another listeners (yet not notified).
                                int cloneLength = this.elementChangedListenerMasks.length;
@@ -703,7 +768,7 @@ public class JavaModelManager implements ISaveParticipant {
                        }
                }
        }
-       
+
 
 
        /**
@@ -712,16 +777,16 @@ public class JavaModelManager implements ISaveParticipant {
        public void configurePluginDebugOptions(){
                if(JavaCore.getPlugin().isDebugging()){
 //             TODO jsurfer temp-del
-                       
+
                        String option = Platform.getDebugOption(BUILDER_DEBUG);
 //                     if(option != null) JavaBuilder.DEBUG = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
-//                     
+//
 //                     option = Platform.getDebugOption(COMPILER_DEBUG);
 //                     if(option != null) Compiler.DEBUG = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
 //
 //                     option = Platform.getDebugOption(COMPLETION_DEBUG);
 //                     if(option != null) CompletionEngine.DEBUG = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
-//                     
+//
                        option = Platform.getDebugOption(CP_RESOLVE_DEBUG);
                        if(option != null) JavaModelManager.CP_RESOLVE_VERBOSE = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
 
@@ -733,7 +798,7 @@ public class JavaModelManager implements ISaveParticipant {
 //
 //                     option = Platform.getDebugOption(INDEX_MANAGER_DEBUG);
 //                     if(option != null) IndexManager.VERBOSE = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
-                       
+
                        option = Platform.getDebugOption(JAVAMODEL_DEBUG);
                        if(option != null) JavaModelManager.VERBOSE = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
 
@@ -750,7 +815,7 @@ public class JavaModelManager implements ISaveParticipant {
                        if(option != null) JavaModelManager.ZIP_ACCESS_VERBOSE = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
                }
        }
-       
+
 
        /*
         * Discards the per working copy info for the given working copy (making it a compilation unit)
@@ -767,10 +832,10 @@ public class JavaModelManager implements ISaveParticipant {
                        WorkingCopyOwner owner = workingCopy.owner;
                        Map workingCopyToInfos = (Map)this.perWorkingCopyInfos.get(owner);
                        if (workingCopyToInfos == null) return -1;
-                       
+
                        PerWorkingCopyInfo info = (PerWorkingCopyInfo)workingCopyToInfos.get(workingCopy);
                        if (info == null) return -1;
-                       
+
                        if (--info.useCount == 0) {
                                // create the delta builder (this remembers the current content of the working copy)
                                JavaElementDeltaBuilder deltaBuilder = null;
@@ -795,26 +860,26 @@ public class JavaModelManager implements ISaveParticipant {
                                                getDeltaProcessor().registerJavaModelDelta(deltaBuilder.delta);
                                        }
                                }
-                               
+
                        }
                        return info.useCount;
                }
        }
-       
+
        /**
         * @see ISaveParticipant
         */
        public void doneSaving(ISaveContext context){
        }
-       
+
        /**
         * Fire Java Model delta, flushing them after the fact after post_change notification.
-        * If the firing mode has been turned off, this has no effect. 
+        * If the firing mode has been turned off, this has no effect.
         */
        public void fire(IJavaElementDelta customDelta, int eventType) {
 
                if (!this.isFiring) return;
-               
+
                if (DeltaProcessor.VERBOSE && (eventType == DEFAULT_CHANGE_EVENT || eventType == ElementChangedEvent.PRE_AUTO_BUILD)) {
                        System.out.println("-----------------------------------------------------------------------------------------------------------------------");//$NON-NLS-1$
                }
@@ -825,7 +890,7 @@ public class JavaModelManager implements ISaveParticipant {
                } else {
                        deltaToNotify = customDelta;
                }
-                       
+
                // Refresh internal scopes
                if (deltaToNotify != null) {
 //             TODO  temp-del
@@ -835,9 +900,9 @@ public class JavaModelManager implements ISaveParticipant {
 //                             scope.processDelta(deltaToNotify);
 //                     }
                }
-                       
+
                // Notification
-       
+
                // Important: if any listener reacts to notification by updating the listeners list or mask, these lists will
                // be duplicated, so it is necessary to remember original lists in a variable (since field values may change under us)
                IElementChangedListener[] listeners = this.elementChangedListeners;
@@ -866,7 +931,7 @@ public class JavaModelManager implements ISaveParticipant {
                IElementChangedListener[] listeners,
                int[] listenerMask,
                int listenerCount) {
-                       
+
                if (DeltaProcessor.VERBOSE){
                        System.out.println("FIRING PRE_AUTO_BUILD Delta ["+Thread.currentThread()+"]:"); //$NON-NLS-1$//$NON-NLS-2$
                        System.out.println(deltaToNotify == null ? "<NONE>" : deltaToNotify.toString()); //$NON-NLS-1$
@@ -881,7 +946,7 @@ public class JavaModelManager implements ISaveParticipant {
                IElementChangedListener[] listeners,
                int[] listenerMask,
                int listenerCount) {
-                       
+
                // post change deltas
                if (DeltaProcessor.VERBOSE){
                        System.out.println("FIRING POST_CHANGE Delta ["+Thread.currentThread()+"]:"); //$NON-NLS-1$//$NON-NLS-2$
@@ -890,10 +955,10 @@ public class JavaModelManager implements ISaveParticipant {
                if (deltaToNotify != null) {
                        // flush now so as to keep listener reactions to post their own deltas for subsequent iteration
                        this.flush();
-                       
+
                        notifyListeners(deltaToNotify, ElementChangedEvent.POST_CHANGE, listeners, listenerMask, listenerCount);
-               } 
-       }               
+               }
+       }
        private void fireReconcileDelta(
                IElementChangedListener[] listeners,
                int[] listenerMask,
@@ -908,9 +973,9 @@ public class JavaModelManager implements ISaveParticipant {
                if (deltaToNotify != null) {
                        // flush now so as to keep listener reactions to post their own deltas for subsequent iteration
                        this.reconcileDeltas = new HashMap();
-               
+
                        notifyListeners(deltaToNotify, ElementChangedEvent.POST_RECONCILE, listeners, listenerMask, listenerCount);
-               } 
+               }
        }
 
        public void notifyListeners(IJavaElementDelta deltaToNotify, int eventType, IElementChangedListener[] listeners, int[] listenerMask, int listenerCount) {
@@ -938,7 +1003,7 @@ public class JavaModelManager implements ISaveParticipant {
                        }
                }
        }
-       
+
        /**
         * Flushes all deltas without firing them.
         */
@@ -965,22 +1030,25 @@ public class JavaModelManager implements ISaveParticipant {
                                } catch (IOException e) {
                                }
                        }
-               }       
+               }
        }
-       
+
 
        public DeltaProcessor getDeltaProcessor() {
                return this.deltaState.getDeltaProcessor();
        }
-       /** 
+       /**
         * Returns the set of elements which are out of synch with their buffers.
         */
        protected Map getElementsOutOfSynchWithBuffers() {
                return this.elementsOutOfSynchWithBuffers;
        }
 
+//     public IndexManager getIndexManager() {
+//             return this.indexManager;
+//     }
        /**
-        * Returns the <code>IJavaElement</code> represented by the 
+        * Returns the <code>IJavaElement</code> represented by the
         * <code>String</code> memento.
         */
        public IJavaElement getHandleFromMemento(String memento) throws JavaModelException {
@@ -1092,8 +1160,8 @@ public class JavaModelManager implements ISaveParticipant {
                        }
                        return info;
                }
-       }       
-       
+       }
+
        /*
         * Returns  the per-project info for the given project.
         * If the info doesn't exist, check for the project existence and create the info.
@@ -1132,12 +1200,12 @@ public class JavaModelManager implements ISaveParticipant {
                        if (info != null && recordUsage) info.useCount++;
                        return info;
                }
-       }       
+       }
        /**
         * Returns the name of the variables for which an CP variable initializer is registered through an extension point
         */
        public static String[] getRegisteredVariableNames(){
-               
+
                Plugin jdtCorePlugin = JavaCore.getPlugin();
                if (jdtCorePlugin == null) return null;
 
@@ -1151,18 +1219,18 @@ public class JavaModelManager implements ISaveParticipant {
 //                                     String varAttribute = configElements[j].getAttribute("variable"); //$NON-NLS-1$
 //                                     if (varAttribute != null) variableList.add(varAttribute);
 //                             }
-//                     }       
+//                     }
 //             }
                String[] variableNames = new String[variableList.size()];
                variableList.toArray(variableNames);
                return variableNames;
-       }       
+       }
 
        /**
         * Returns the name of the container IDs for which an CP container initializer is registered through an extension point
         */
 //     public static String[] getRegisteredContainerIDs(){
-//             
+//
 //             Plugin jdtCorePlugin = PHPCore.getPlugin();
 //             if (jdtCorePlugin == null) return null;
 //
@@ -1176,12 +1244,12 @@ public class JavaModelManager implements ISaveParticipant {
 //                                     String idAttribute = configElements[j].getAttribute("id"); //$NON-NLS-1$
 //                                     if (idAttribute != null) containerIDList.add(idAttribute);
 //                             }
-//                     }       
+//                     }
 //             }
 //             String[] containerIDs = new String[containerIDList.size()];
 //             containerIDList.toArray(containerIDs);
 //             return containerIDs;
-//     }       
+//     }
 
        /**
         * Returns the File to use for saving and restoring the last built state for the given project.
@@ -1191,6 +1259,7 @@ public class JavaModelManager implements ISaveParticipant {
                IPath workingLocation = project.getWorkingLocation(JavaCore.PLUGIN_ID);
                return workingLocation.append("state.dat").toFile(); //$NON-NLS-1$
        }
+
        /*
         * Returns the temporary cache for newly opened elements for the current thread.
         * Creates it if not already created.
@@ -1211,14 +1280,14 @@ public class JavaModelManager implements ISaveParticipant {
         * @exception CoreException If unable to create/open the ZipFile
         */
        public ZipFile getZipFile(IPath path) throws CoreException {
-                       
+
                synchronized(this.zipFiles) { // TODO:  use PeThreadObject which does synchronization
                        Thread currentThread = Thread.currentThread();
                        HashMap map = null;
                        ZipFile zipFile;
-                       if ((map = (HashMap)this.zipFiles.get(currentThread)) != null 
+                       if ((map = (HashMap)this.zipFiles.get(currentThread)) != null
                                        && (zipFile = (ZipFile)map.get(path)) != null) {
-                                       
+
                                return zipFile;
                        }
                        String fileSystemPath= null;
@@ -1247,7 +1316,7 @@ public class JavaModelManager implements ISaveParticipant {
                        } else {
                                fileSystemPath= path.toOSString();
                        }
-       
+
                        try {
                                if (ZIP_ACCESS_VERBOSE) {
                                        System.out.println("(" + currentThread + ") [JavaModelManager.getZipFile(IPath)] Creating ZipFile on " + fileSystemPath ); //$NON-NLS-1$ //$NON-NLS-2$
@@ -1270,10 +1339,10 @@ public class JavaModelManager implements ISaveParticipant {
        }
 //     public void loadVariablesAndContainers() throws CoreException {
 //
-//             // backward compatibility, consider persistent property 
+//             // backward compatibility, consider persistent property
 //             QualifiedName qName = new QualifiedName(PHPCore.PLUGIN_ID, "variables"); //$NON-NLS-1$
 //             String xmlString = ResourcesPlugin.getWorkspace().getRoot().getPersistentProperty(qName);
-//             
+//
 //             try {
 //                     if (xmlString != null){
 //                             StringReader reader = new StringReader(xmlString);
@@ -1292,7 +1361,7 @@ public class JavaModelManager implements ISaveParticipant {
 //                             if (!cpElement.getNodeName().equalsIgnoreCase("variables")) { //$NON-NLS-1$
 //                                     return;
 //                             }
-//                             
+//
 //                             NodeList list= cpElement.getChildNodes();
 //                             int length= list.getLength();
 //                             for (int i= 0; i < length; ++i) {
@@ -1301,7 +1370,7 @@ public class JavaModelManager implements ISaveParticipant {
 //                                     if (type == Node.ELEMENT_NODE) {
 //                                             Element element= (Element) node;
 //                                             if (element.getNodeName().equalsIgnoreCase("variable")) { //$NON-NLS-1$
-//                                                     variablePut( 
+//                                                     variablePut(
 //                                                             element.getAttribute("name"), //$NON-NLS-1$
 //                                                             new Path(element.getAttribute("path"))); //$NON-NLS-1$
 //                                             }
@@ -1313,12 +1382,12 @@ public class JavaModelManager implements ISaveParticipant {
 //                     if (xmlString != null){
 //                             ResourcesPlugin.getWorkspace().getRoot().setPersistentProperty(qName, null); // flush old one
 //                     }
-//                     
+//
 //             }
-//             
-//             // load variables and containers from preferences into cache
-//             Preferences preferences = PHPCore.getPlugin().getPluginPreferences();
 //
+//             // load variables and containers from preferences into cache
+//             Preferences preferences = PHPeclipsePlugin.getDefault().getPluginPreferences();
+
 //             // only get variable from preferences not set to their default
 //             String[] propertyNames = preferences.propertyNames();
 //             int variablePrefixLength = CP_VARIABLE_PREFERENCES_PREFIX.length();
@@ -1327,8 +1396,8 @@ public class JavaModelManager implements ISaveParticipant {
 //                     if (propertyName.startsWith(CP_VARIABLE_PREFERENCES_PREFIX)){
 //                             String varName = propertyName.substring(variablePrefixLength);
 //                             IPath varPath = new Path(preferences.getString(propertyName).trim());
-//                             
-//                             Variables.put(varName, varPath); 
+//
+//                             Variables.put(varName, varPath);
 //                             PreviousSessionVariables.put(varName, varPath);
 //                     }
 //                     if (propertyName.startsWith(CP_CONTAINER_PREFERENCES_PREFIX)){
@@ -1368,11 +1437,11 @@ public class JavaModelManager implements ISaveParticipant {
        public IJavaElementDelta mergeDeltas(Collection deltas) {
                if (deltas.size() == 0) return null;
                if (deltas.size() == 1) return (IJavaElementDelta)deltas.iterator().next();
-               
+
                if (DeltaProcessor.VERBOSE) {
                        System.out.println("MERGING " + deltas.size() + " DELTAS ["+Thread.currentThread()+"]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                }
-               
+
                Iterator iterator = deltas.iterator();
                IJavaElement javaModel = this.getJavaModel();
                JavaElementDelta rootDelta = new JavaElementDelta(javaModel);
@@ -1408,7 +1477,7 @@ public class JavaModelManager implements ISaveParticipant {
                else {
                        return null;
                }
-       }       
+       }
 
        /**
         *  Returns the info for this element without
@@ -1423,14 +1492,14 @@ public class JavaModelManager implements ISaveParticipant {
         */
        public void prepareToSave(ISaveContext context) throws CoreException {
        }
-       
+
        protected void putInfo(IJavaElement element, Object info) {
                this.cache.putInfo(element, info);
        }
        /*
         * Puts the infos in the given map (keys are IJavaElements and values are JavaElementInfos)
         * in the Java model cache in an atomic way.
-        * First checks that the info for the opened element (or one of its ancestors) has not been 
+        * First checks that the info for the opened element (or one of its ancestors) has not been
         * added to the cache. If it is the case, another thread has opened the element (or one of
         * its ancestors). So returns without updating the cache.
         */
@@ -1448,7 +1517,7 @@ public class JavaModelManager implements ISaveParticipant {
                                }
                        }
                }
-       
+
                Iterator iterator = newElements.keySet().iterator();
                while (iterator.hasNext()) {
                        IJavaElement element = (IJavaElement)iterator.next();
@@ -1494,7 +1563,7 @@ public class JavaModelManager implements ISaveParticipant {
 //                     final String projectName = propertyName.substring(containerPrefixLength, index).trim();
 //                     JavaProject project = (JavaProject)getJavaModelManager().getJavaModel().getJavaProject(projectName);
 //                     final IPath containerPath = new Path(propertyName.substring(index+1).trim());
-//                     
+//
 //                     if (containerString == null || containerString.equals(CP_ENTRY_IGNORE)) {
 //                             containerPut(project, containerPath, null);
 //                     } else {
@@ -1508,7 +1577,7 @@ public class JavaModelManager implements ISaveParticipant {
 //                                                     return "Persisted container ["+containerPath+" for project ["+ projectName+"]"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
 //                                             }
 //                                             public int getKind() {
-//                                                     return 0; 
+//                                                     return 0;
 //                                             }
 //                                             public IPath getPath() {
 //                                                     return containerPath;
@@ -1538,39 +1607,39 @@ public class JavaModelManager implements ISaveParticipant {
        protected void registerJavaModelDelta(IJavaElementDelta delta) {
                this.javaModelDeltas.add(delta);
        }
-       
+
        /**
         * Remembers the given scope in a weak set
         * (so no need to remove it: it will be removed by the garbage collector)
         */
 //     public void rememberScope(AbstractSearchScope scope) {
 //             // NB: The value has to be null so as to not create a strong reference on the scope
-//             this.scopes.put(scope, null); 
+//             this.scopes.put(scope, null);
 //     }
 
        /**
         * removeElementChangedListener method comment.
         */
        public void removeElementChangedListener(IElementChangedListener listener) {
-               
+
                for (int i = 0; i < this.elementChangedListenerCount; i++){
-                       
+
                        if (this.elementChangedListeners[i].equals(listener)){
-                               
+
                                // need to clone defensively since we might be in the middle of listener notifications (#fire)
                                int length = this.elementChangedListeners.length;
                                IElementChangedListener[] newListeners = new IElementChangedListener[length];
                                System.arraycopy(this.elementChangedListeners, 0, newListeners, 0, i);
                                int[] newMasks = new int[length];
                                System.arraycopy(this.elementChangedListenerMasks, 0, newMasks, 0, i);
-                               
+
                                // copy trailing listeners
                                int trailingLength = this.elementChangedListenerCount - i - 1;
                                if (trailingLength > 0){
                                        System.arraycopy(this.elementChangedListeners, i+1, newListeners, i, trailingLength);
                                        System.arraycopy(this.elementChangedListenerMasks, i+1, newMasks, i, trailingLength);
                                }
-                               
+
                                // update manager listener state (#fire need to iterate over original listeners through a local variable to hold onto
                                // the original ones)
                                this.elementChangedListeners = newListeners;
@@ -1580,9 +1649,14 @@ public class JavaModelManager implements ISaveParticipant {
                        }
                }
        }
-       
-//     PROTECTED VOID REMOVEINFO(IJAVAELEMENT ELEMENT) {
-//             THIS.CACHE.REMOVEINFO(ELEMENT);
+
+       /**
+        * Remembers the given scope in a weak set
+        * (so no need to remove it: it will be removed by the garbage collector)
+        */
+//     public void rememberScope(AbstractSearchScope scope) {
+//             // NB: The value has to be null so as to not create a strong reference on the scope
+//             this.searchScopes.put(scope, null);
 //     }
        /*
         * Removes all cached info for the given element (including all children)
@@ -1618,7 +1692,7 @@ public class JavaModelManager implements ISaveParticipant {
                        return info;
                }
                return null;
-       }       
+       }
        public void removePerProjectInfo(JavaProject javaProject) {
                synchronized(perProjectInfo) { // use the perProjectInfo collection as its own lock
                        IProject project = javaProject.getProject();
@@ -1644,11 +1718,11 @@ public class JavaModelManager implements ISaveParticipant {
 
                // passed this point, save actions are non trivial
                if (context.getKind() == ISaveContext.SNAPSHOT) return;
-               
+
                // save built state
                if (info.triedRead) saveBuiltState(info);
        }
-       
+
        /**
         * Saves the built state for the project.
         */
@@ -1688,12 +1762,56 @@ public class JavaModelManager implements ISaveParticipant {
                        System.out.println(Util.bind("build.saveStateComplete", String.valueOf(t))); //$NON-NLS-1$
                }
        }
-
+       private synchronized Map containerClone(IJavaProject project) {
+               Map originalProjectContainers = (Map)this.containers.get(project);
+               if (originalProjectContainers == null) return null;
+               Map projectContainers = new HashMap(originalProjectContainers.size());
+               projectContainers.putAll(originalProjectContainers);
+               return projectContainers;
+       }
        /**
         * @see ISaveParticipant
         */
        public void saving(ISaveContext context) throws CoreException {
-       
+
+           // save container values on snapshot/full save
+               Preferences preferences = JavaCore.getPlugin().getPluginPreferences();
+               IJavaProject[] projects = getJavaModel().getJavaProjects();
+               for (int i = 0, length = projects.length; i < length; i++) {
+                   IJavaProject project = projects[i];
+                       // clone while iterating (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=59638)
+                       Map projectContainers = containerClone(project);
+                       if (projectContainers == null) continue;
+                       for (Iterator keys = projectContainers.keySet().iterator(); keys.hasNext();) {
+                           IPath containerPath = (IPath) keys.next();
+//                         IClasspathContainer container = (IClasspathContainer) projectContainers.get(containerPath);
+                               String containerKey = CP_CONTAINER_PREFERENCES_PREFIX+project.getElementName() +"|"+containerPath;//$NON-NLS-1$
+                               String containerString = CP_ENTRY_IGNORE;
+//                             try {
+//                                     if (container != null) {
+//                                             containerString = ((JavaProject)project).encodeClasspath(container.getClasspathEntries(), null, false);
+//                                     }
+//                             } catch(JavaModelException e){
+//                                     // could not encode entry: leave it as CP_ENTRY_IGNORE
+//                             }
+                               preferences.setDefault(containerKey, CP_ENTRY_IGNORE); // use this default to get rid of removed ones
+                               preferences.setValue(containerKey, containerString);
+                       }
+               }
+               JavaCore.getPlugin().savePluginPreferences();
+
+//             if (context.getKind() == ISaveContext.FULL_SAVE) {
+//                     // will need delta since this save (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38658)
+//                     context.needDelta();
+//
+//                     // clean up indexes on workspace full save
+//                     // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=52347)
+//                     IndexManager manager = this.indexManager;
+//                     if (manager != null) {
+//                             manager.cleanUpIndexes();
+//                     }
+//             }
+
                IProject savedProject = context.getProject();
                if (savedProject != null) {
                        if (!JavaProject.hasJavaNature(savedProject)) return; // ignore
@@ -1719,6 +1837,36 @@ public class JavaModelManager implements ISaveParticipant {
                        throw new CoreException(new MultiStatus(JavaCore.PLUGIN_ID, IStatus.ERROR, stats, Util.bind("build.cannotSaveStates"), null)); //$NON-NLS-1$
                }
        }
+       /**
+        * @see ISaveParticipant
+        */
+//     public void saving(ISaveContext context) throws CoreException {
+//
+//             IProject savedProject = context.getProject();
+//             if (savedProject != null) {
+//                     if (!JavaProject.hasJavaNature(savedProject)) return; // ignore
+//                     PerProjectInfo info = getPerProjectInfo(savedProject, true /* create info */);
+//                     saveState(info, context);
+//                     return;
+//             }
+//
+//             ArrayList vStats= null; // lazy initialized
+//             for (Iterator iter =  perProjectInfo.values().iterator(); iter.hasNext();) {
+//                     try {
+//                             PerProjectInfo info = (PerProjectInfo) iter.next();
+//                             saveState(info, context);
+//                     } catch (CoreException e) {
+//                             if (vStats == null)
+//                                     vStats= new ArrayList();
+//                             vStats.add(e.getStatus());
+//                     }
+//             }
+//             if (vStats != null) {
+//                     IStatus[] stats= new IStatus[vStats.size()];
+//                     vStats.toArray(stats);
+//                     throw new CoreException(new MultiStatus(JavaCore.PLUGIN_ID, IStatus.ERROR, stats, Util.bind("build.cannotSaveStates"), null)); //$NON-NLS-1$
+//             }
+//     }
 
        /**
         * Record the order in which to build the java projects (batch build). This order is based
@@ -1729,9 +1877,9 @@ public class JavaModelManager implements ISaveParticipant {
                // optional behaviour
                // possible value of index 0 is Compute
                if (!JavaCore.COMPUTE.equals(JavaCore.getOption(JavaCore.CORE_JAVA_BUILD_ORDER))) return; // cannot be customized at project level
-               
+
                if (javaBuildOrder == null || javaBuildOrder.length <= 1) return;
-               
+
                IWorkspace workspace = ResourcesPlugin.getWorkspace();
                IWorkspaceDescription description = workspace.getDescription();
                String[] wksBuildOrder = description.getBuildOrder();
@@ -1822,24 +1970,24 @@ public class JavaModelManager implements ISaveParticipant {
        public void stopDeltas() {
                this.isFiring= false;
        }
-       
+
        /**
         * Update Java Model given some delta
         */
-//     public void updateJavaModel(IJavaElementDelta customDelta) {
-//
-//             if (customDelta == null){
-//                     for (int i = 0, length = this.javaModelDeltas.size(); i < length; i++){
-//                             IJavaElementDelta delta = (IJavaElementDelta)this.javaModelDeltas.get(i);
-//                             this.modelUpdater.processJavaDelta(delta);
-//                     }
-//             } else {
-//                     this.modelUpdater.processJavaDelta(customDelta);
-//             }
-//     }
+       public void updateJavaModel(IJavaElementDelta customDelta) {
+
+               if (customDelta == null){
+                       for (int i = 0, length = this.javaModelDeltas.size(); i < length; i++){
+                               IJavaElementDelta delta = (IJavaElementDelta)this.javaModelDeltas.get(i);
+                               this.modelUpdater.processJavaDelta(delta);
+                       }
+               } else {
+                       this.modelUpdater.processJavaDelta(customDelta);
+               }
+       }
+
 
 
-       
        public static IPath variableGet(String variableName){
                return (IPath)Variables.get(variableName);
        }
@@ -1854,10 +2002,10 @@ public class JavaModelManager implements ISaveParticipant {
                }
                return result;
        }
-       
-       public static void variablePut(String variableName, IPath variablePath){                
 
-               // update cache - do not only rely on listener refresh          
+       public static void variablePut(String variableName, IPath variablePath){
+
+               // update cache - do not only rely on listener refresh
                if (variablePath == null) {
                        Variables.remove(variableName);
                        PreviousSessionVariables.remove(variableName);
@@ -1868,7 +2016,7 @@ public class JavaModelManager implements ISaveParticipant {
                // do not write out intermediate initialization value
                if (variablePath == JavaModelManager.VariableInitializationInProgress){
                        return;
-               } 
+               }
                Preferences preferences = JavaCore.getPlugin().getPluginPreferences();
                String variableKey = CP_VARIABLE_PREFERENCES_PREFIX+variableName;
                String variableString = variablePath == null ? CP_ENTRY_IGNORE : variablePath.toString();
@@ -1883,8 +2031,8 @@ public class JavaModelManager implements ISaveParticipant {
         */
        public ICompilationUnit[] getWorkingCopies(WorkingCopyOwner owner, boolean addPrimary) {
                synchronized(perWorkingCopyInfos) {
-                       ICompilationUnit[] primaryWCs = addPrimary && owner != DefaultWorkingCopyOwner.PRIMARY 
-                               ? getWorkingCopies(DefaultWorkingCopyOwner.PRIMARY, false) 
+                       ICompilationUnit[] primaryWCs = addPrimary && owner != DefaultWorkingCopyOwner.PRIMARY
+                               ? getWorkingCopies(DefaultWorkingCopyOwner.PRIMARY, false)
                                : null;
                        Map workingCopyToInfos = (Map)perWorkingCopyInfos.get(owner);
                        if (workingCopyToInfos == null) return primaryWCs;
@@ -1900,6 +2048,31 @@ public class JavaModelManager implements ISaveParticipant {
                                result[index++] = ((JavaModelManager.PerWorkingCopyInfo)iterator.next()).getWorkingCopy();
                        }
                        return result;
-               }               
+               }
+       }
+
+       /*
+        * A HashSet that contains the IJavaProject whose classpath is being resolved.
+        */
+       private ThreadLocal classpathsBeingResolved = new ThreadLocal();
+
+       private HashSet getClasspathBeingResolved() {
+           HashSet result = (HashSet) this.classpathsBeingResolved.get();
+           if (result == null) {
+               result = new HashSet();
+               this.classpathsBeingResolved.set(result);
+           }
+           return result;
+       }
+       public boolean isClasspathBeingResolved(IJavaProject project) {
+           return getClasspathBeingResolved().contains(project);
+       }
+
+       public void setClasspathBeingResolved(IJavaProject project, boolean classpathIsResolved) {
+           if (classpathIsResolved) {
+               getClasspathBeingResolved().add(project);
+           } else {
+               getClasspathBeingResolved().remove(project);
+           }
        }
 }