improved PHP parser
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / JavaModelManager.java
index 6652089..f860394 100644 (file)
 package net.sourceforge.phpdt.internal.core;
 
 import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
 import java.io.DataInputStream;
+import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.text.NumberFormat;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -25,14 +29,24 @@ import java.util.WeakHashMap;
 import java.util.zip.ZipFile;
 
 import net.sourceforge.phpdt.core.ElementChangedEvent;
+import net.sourceforge.phpdt.core.IClasspathEntry;
 import net.sourceforge.phpdt.core.ICompilationUnit;
 import net.sourceforge.phpdt.core.IElementChangedListener;
 import net.sourceforge.phpdt.core.IJavaElement;
 import net.sourceforge.phpdt.core.IJavaElementDelta;
+import net.sourceforge.phpdt.core.IJavaModel;
 import net.sourceforge.phpdt.core.IJavaProject;
+import net.sourceforge.phpdt.core.IPackageFragment;
+import net.sourceforge.phpdt.core.IPackageFragmentRoot;
+import net.sourceforge.phpdt.core.IParent;
+import net.sourceforge.phpdt.core.IProblemRequestor;
 import net.sourceforge.phpdt.core.IWorkingCopy;
 import net.sourceforge.phpdt.core.JavaCore;
 import net.sourceforge.phpdt.core.JavaModelException;
+import net.sourceforge.phpdt.core.WorkingCopyOwner;
+import net.sourceforge.phpdt.core.compiler.IProblem;
+import net.sourceforge.phpdt.internal.core.builder.PHPBuilder;
+import net.sourceforge.phpdt.internal.core.util.Util;
 import net.sourceforge.phpdt.internal.ui.util.PHPFileUtil;
 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
 
@@ -49,15 +63,16 @@ import org.eclipse.core.resources.IWorkspaceRoot;
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
-import org.eclipse.core.runtime.IPluginDescriptor;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.ISafeRunnable;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.MultiStatus;
 import org.eclipse.core.runtime.Path;
 import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Plugin;
 import org.eclipse.core.runtime.Preferences;
 import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.Preferences.PropertyChangeEvent;
 
 /**
  * The <code>JavaModelManager</code> manages instances of <code>IJavaModel</code>.
@@ -68,12 +83,11 @@ import org.eclipse.core.runtime.Status;
  * the static method <code>JavaModelManager.getJavaModelManager()</code>.
  */
 public class JavaModelManager implements ISaveParticipant {    
-       /**
+       /**
         * Unique handle onto the JavaModel
         */
        final JavaModel javaModel = new JavaModel();
-       
+//     public IndexManager indexManager = new IndexManager();
        /**
         * Classpath variables pool
         */
@@ -81,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 = JavaCore.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);
 
        /**
@@ -134,41 +148,46 @@ public class JavaModelManager implements ISaveParticipant {
        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
+        */
+       protected Map perWorkingCopyInfos = new HashMap(5);
+       /**
         * Returns whether the given full path (for a package) conflicts with the output location
         * of the given project.
         */
-//     public static boolean conflictsWithOutputLocation(IPath folderPath, JavaProject project) {
-//             try {
-//                     IPath outputLocation = project.getOutputLocation();
-//                     if (outputLocation == null) {
-//                             // in doubt, there is a conflict
-//                             return true;
-//                     }
-//                     if (outputLocation.isPrefixOf(folderPath)) {
-//                             // only allow nesting in project's output if there is a corresponding source folder
-//                             // or if the project's output is not used (in other words, if all source folders have their custom output)
-//                             IClasspathEntry[] classpath = project.getResolvedClasspath(true);
-//                             boolean isOutputUsed = false;
-//                             for (int i = 0, length = classpath.length; i < length; i++) {
-//                                     IClasspathEntry entry = classpath[i];
-//                                     if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
-//                                             if (entry.getPath().equals(outputLocation)) {
-//                                                     return false;
-//                                             }
-//                                             if (entry.getOutputLocation() == null) {
-//                                                     isOutputUsed = true;
-//                                             }
-//                                     }
-//                             }
-//                             return isOutputUsed;
-//                     }
-//                     return false;
-//             } catch (JavaModelException e) {
-//                     // in doubt, there is a conflict
-//                     return true;
-//             }
-//     }
-//
+       public static boolean conflictsWithOutputLocation(IPath folderPath, JavaProject project) {
+               try {
+                       IPath outputLocation = project.getOutputLocation();
+                       if (outputLocation == null) {
+                               // in doubt, there is a conflict
+                               return true;
+                       }
+                       if (outputLocation.isPrefixOf(folderPath)) {
+                               // only allow nesting in project's output if there is a corresponding source folder
+                               // or if the project's output is not used (in other words, if all source folders have their custom output)
+                               IClasspathEntry[] classpath = project.getResolvedClasspath(true);
+                               boolean isOutputUsed = false;
+                               for (int i = 0, length = classpath.length; i < length; i++) {
+                                       IClasspathEntry entry = classpath[i];
+                                       if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
+                                               if (entry.getPath().equals(outputLocation)) {
+                                                       return false;
+                                               }
+                                               if (entry.getOutputLocation() == null) {
+                                                       isOutputUsed = true;
+                                               }
+                                       }
+                               }
+                               return isOutputUsed;
+                       }
+                       return false;
+               } catch (JavaModelException e) {
+                       // in doubt, there is a conflict
+                       return true;
+               }
+       }
+
 //     public static IClasspathContainer containerGet(IJavaProject project, IPath containerPath) {     
 //             Map projectContainers = (Map)Containers.get(project);
 //             if (projectContainers == null){
@@ -276,12 +295,12 @@ public class JavaModelManager implements ISaveParticipant {
        
                if (file.getFileExtension() != null) {
                        String name = file.getName();
-//                     if (Util.isValidCompilationUnitName(name))
-                       if (PHPFileUtil.isPHPFile(file))
+                       if (PHPFileUtil.isValidPHPUnitName(name))
+                       //if (PHPFileUtil.isPHPFile(file))
                                return createCompilationUnitFrom(file, project);
-//                     if (Util.isValidClassFileName(name))
+//                     if (ProjectPrefUtil.isValidClassFileName(name))
 //                             return createClassFileFrom(file, project);
-//                     if (Util.isArchiveFileName(name))
+//                     if (ProjectPrefUtil.isArchiveFileName(name))
 //                             return createJarPackageFragmentRootFrom(file, project);
                }
                return null;
@@ -304,16 +323,14 @@ public class JavaModelManager implements ISaveParticipant {
                if (project == null) {
                        project = JavaCore.create(folder.getProject());
                }
-//     TODO khartlage temp-del
-//             IJavaElement element = determineIfOnClasspath(folder, project);
-//             if (conflictsWithOutputLocation(folder.getFullPath(), (JavaProject)project)
-//                     || (folder.getName().indexOf('.') >= 0 
-//                             && !(element instanceof IPackageFragmentRoot))) {
-//                     return null; // only package fragment roots are allowed with dot names
-//             } else {
-//                     return element;
-//             }
-return null;
+               IJavaElement element = determineIfOnClasspath(folder, project);
+               if (conflictsWithOutputLocation(folder.getFullPath(), (JavaProject)project)
+                       || (folder.getName().indexOf('.') >= 0 
+                               && !(element instanceof IPackageFragmentRoot))) {
+                       return null; // only package fragment roots are allowed with dot names
+               } else {
+                       return element;
+               }
        }
 
        /**
@@ -326,7 +343,7 @@ return null;
 //                     return null;
 //             }
 //             if (project == null) {
-//                     project = JavaCore.create(file.getProject());
+//                     project = PHPCore.create(file.getProject());
 //             }
 //             IPackageFragment pkg = (IPackageFragment) determineIfOnClasspath(file, project);
 //             if (pkg == null) {
@@ -350,22 +367,18 @@ return null;
                if (project == null) {
                        project = JavaCore.create(file.getProject());
                }
-//     TODO khartlage temp-del
-//             IPackageFragment pkg = (IPackageFragment) determineIfOnClasspath(file, project);
-//             if (pkg == null) {
-
+               IPackageFragment pkg = (IPackageFragment) determineIfOnClasspath(file, project);
+               if (pkg == null) {
                        // 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$
-//                     }
-//             }
-//             return pkg.getCompilationUnit(file.getName());
-return null;
+                       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$
+                       }
+               }
+               return pkg.getCompilationUnit(file.getName());
        }
-       
        /**
         * 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
@@ -378,7 +391,7 @@ return null;
 //                     return null;
 //             }
 //             if (project == null) {
-//                     project = JavaCore.create(file.getProject());
+//                     project = PHPCore.create(file.getProject());
 //             }
 //     
 //             // Create a jar package fragment root only if on the classpath
@@ -402,50 +415,51 @@ return null;
         * the package fragment the given resource is located in, or <code>null</code>
         * if the given resource is not on the classpath of the given project.
         */
-//     public static IJavaElement determineIfOnClasspath(
-//             IResource resource,
-//             IJavaProject project) {
-//                     
-//             IPath resourcePath = resource.getFullPath();
-//             try {
-//                     IClasspathEntry[] entries = 
-//                             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())) {
-//                                     // 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;
-//                                     IPath pkgPath = resourcePath.removeFirstSegments(rootPath.segmentCount());
-//                                     if (resource.getType() == IResource.FILE) {
-//                                             // 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)
+       public static IJavaElement determineIfOnClasspath(
+               IResource resource,
+               IJavaProject project) {
+                       
+               IPath resourcePath = resource.getFullPath();
+               try {
+                       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, 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;
+                                       IPath pkgPath = resourcePath.removeFirstSegments(rootPath.segmentCount());
+                                       if (resource.getType() == IResource.FILE) {
+                                               // 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('/', '.');
-//                                             return root.getPackageFragment(pkgName);
-//                                     } else {
-//                                             String pkgName = Util.packageName(pkgPath);
-//                                             if (pkgName == null || JavaConventions.validatePackageName(pkgName).getSeverity() == IStatus.ERROR) {
-//                                                     return null;
-//                                             }
-//                                             return root.getPackageFragment(pkgName);
-//                                     }
-//                             }
-//                     }
-//             } catch (JavaModelException npe) {
-//                     return null;
-//             }
-//             return null;
-//     }
+                                               String pkgName = pkgPath.toString();
+                                               return root.getPackageFragment(pkgName);
+                                       } else {
+                                               String pkgName = Util.packageName(pkgPath);
+                                               if (pkgName == null){// || JavaConventions.validatePackageName(pkgName).getSeverity() == IStatus.ERROR) {
+                                                       return null;
+                                               }
+                                               return root.getPackageFragment(pkgName);
+                                       }
+                               }
+                       }
+               } catch (JavaModelException npe) {
+                       return null;
+               }
+               return null;
+       }
        
        /**
         * The singleton manager
@@ -455,13 +469,20 @@ return null;
        /**
         * Infos cache.
         */
-//     protected JavaModelCache cache = new JavaModelCache();
+       protected JavaModelCache cache = new JavaModelCache();
 
+       /*
+        * Temporary cache of newly opened elements
+        */
+       private ThreadLocal temporaryCache = new ThreadLocal();
        /**
         * Set of elements which are out of sync with their buffers.
         */
        protected Map elementsOutOfSynchWithBuffers = new HashMap(11);
-       
+       /**
+        * Holds the state used for delta processing.
+        */
+       public DeltaProcessingState deltaState = new DeltaProcessingState();
        /**
         * Turns delta firing on/off. By default it is on.
         */
@@ -493,11 +514,11 @@ return null;
        /**
         * Used to convert <code>IResourceDelta</code>s into <code>IJavaElementDelta</code>s.
         */
-       public final DeltaProcessor deltaProcessor = new DeltaProcessor(this);
+//     public final DeltaProcessor deltaProcessor = new DeltaProcessor(this);
        /**
         * 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  
         * This is a cache of the projects before any project addition/deletion has started.
@@ -519,23 +540,121 @@ return null;
        /**
         * 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;
+               ICompilationUnit workingCopy;
+               public PerWorkingCopyInfo(ICompilationUnit workingCopy, IProblemRequestor problemRequestor) {
+                       this.workingCopy = workingCopy;
+                       this.problemRequestor = problemRequestor;
+               }
+               public void acceptProblem(IProblem problem) {
+                       if (this.problemRequestor == null) return;
+                       this.problemRequestor.acceptProblem(problem);
+               }
+               public void beginReporting() {
+                       if (this.problemRequestor == null) return;
+                       this.problemRequestor.beginReporting();
+               }
+               public void endReporting() {
+                       if (this.problemRequestor == null) return;
+                       this.problemRequestor.endReporting();
+               }
+               public ICompilationUnit getWorkingCopy() {
+                       return this.workingCopy;
+               }
+               public boolean isActive() {
+                       return this.problemRequestor != null && this.problemRequestor.isActive();
+               }
+               public String toString() {
+                       StringBuffer buffer = new StringBuffer();
+                       buffer.append("Info for "); //$NON-NLS-1$
+                       buffer.append(((JavaElement)workingCopy).toStringWithAncestors());
+                       buffer.append("\nUse count = "); //$NON-NLS-1$
+                       buffer.append(this.useCount);
+                       buffer.append("\nProblem requestor:\n  "); //$NON-NLS-1$
+                       buffer.append(this.problemRequestor);
+                       return buffer.toString();
+               }
        }
        public static boolean VERBOSE = false;
        public static boolean CP_RESOLVE_VERBOSE = false;
@@ -557,18 +676,17 @@ return null;
                 * @see org.eclipse.core.runtime.Preferences.IPropertyChangeListener#propertyChange(PropertyChangeEvent)
                 */
                public void propertyChange(Preferences.PropertyChangeEvent event) {
-
-                       String propertyName = event.getProperty();
-                       if (propertyName.startsWith(CP_VARIABLE_PREFERENCES_PREFIX)) {
-                               String varName = propertyName.substring(CP_VARIABLE_PREFERENCES_PREFIX.length());
-                               String newValue = (String)event.getNewValue();
-                               if (newValue != null && !(newValue = newValue.trim()).equals(CP_ENTRY_IGNORE)) {
-                                       Variables.put(varName, new Path(newValue));
-                               } else {
-                                       Variables.remove(varName);
-                               }
-                       }
-//             TODO khartlage temp-del
+//                     TODO : jsurfer temp-del
+//                     String propertyName = event.getProperty();
+//                     if (propertyName.startsWith(CP_VARIABLE_PREFERENCES_PREFIX)) {
+//                             String varName = propertyName.substring(CP_VARIABLE_PREFERENCES_PREFIX.length());
+//                             String newValue = (String)event.getNewValue();
+//                             if (newValue != null && !(newValue = newValue.trim()).equals(CP_ENTRY_IGNORE)) {
+//                                     Variables.put(varName, new Path(newValue));
+//                             } else {
+//                                     Variables.remove(varName);
+//                             }
+//                     }
 //                     if (propertyName.startsWith(CP_CONTAINER_PREFERENCES_PREFIX)) {
 //                             recreatePersistedContainer(propertyName, (String)event.getNewValue(), false);
 //                     }
@@ -578,7 +696,7 @@ return null;
        /**
         * Line separator to use throughout the JavaModel for any source edit operation
         */
-       //      public static String LINE_SEPARATOR = System.getProperty("line.separator"); //$NON-NLS-1$
+  public static String LINE_SEPARATOR = System.getProperty("line.separator"); //$NON-NLS-1$
        /**
         * Constructs a new JavaModelManager
         */
@@ -653,7 +771,8 @@ return null;
         */
        public void configurePluginDebugOptions(){
                if(JavaCore.getPlugin().isDebugging()){
-//             TODO khartlage temp-del
+//             TODO jsurfer temp-del
+                       
                        String option = Platform.getDebugOption(BUILDER_DEBUG);
 //                     if(option != null) JavaBuilder.DEBUG = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
 //                     
@@ -687,15 +806,60 @@ return null;
 //                     option = Platform.getDebugOption(SELECTION_DEBUG);
 //                     if(option != null) SelectionEngine.DEBUG = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
 
-                       option = Platform.getDebugOption(SHARED_WC_DEBUG);
-                       if(option != null) CompilationUnit.SHARED_WC_VERBOSE = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
-
                        option = Platform.getDebugOption(ZIP_ACCESS_DEBUG);
                        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)
+        * if its use count was 1. Otherwise, just decrement the use count.
+        * If the working copy is primary, computes the delta between its state and the original compilation unit
+        * and register it.
+        * Close the working copy, its buffer and remove it from the shared working copy table.
+        * Ignore if no per-working copy info existed.
+        * NOTE: it must be synchronized as it may interact with the element info cache (if useCount is decremented to 0), see bug 50667.
+        * Returns the new use count (or -1 if it didn't exist).
+        */
+       public synchronized int discardPerWorkingCopyInfo(CompilationUnit workingCopy) throws JavaModelException {
+               synchronized(perWorkingCopyInfos) {
+                       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;
+                               if (workingCopy.isPrimary()) {
+                                       deltaBuilder = new JavaElementDeltaBuilder(workingCopy);
+                               }
+
+                               // remove per working copy info
+                               workingCopyToInfos.remove(workingCopy);
+                               if (workingCopyToInfos.isEmpty()) {
+                                       this.perWorkingCopyInfos.remove(owner);
+                               }
+
+                               // remove infos + close buffer (since no longer working copy)
+                               removeInfoAndChildren(workingCopy);
+                               workingCopy.closeBuffer();
+
+                               // compute the delta if needed and register it if there are changes
+                               if (deltaBuilder != null) {
+                                       deltaBuilder.buildDeltas();
+                                       if ((deltaBuilder.delta != null) && (deltaBuilder.delta.getAffectedChildren().length > 0)) {
+                                               getDeltaProcessor().registerJavaModelDelta(deltaBuilder.delta);
+                                       }
+                               }
+                               
+                       }
+                       return info.useCount;
+               }
+       }
        
        /**
         * @see ISaveParticipant
@@ -724,7 +888,7 @@ return null;
                        
                // Refresh internal scopes
                if (deltaToNotify != null) {
-//             TODO khartlage temp-del
+//             TODO  temp-del
 //                     Iterator scopes = this.scopes.keySet().iterator();
 //                     while (scopes.hasNext()) {
 //                             AbstractSearchScope scope = (AbstractSearchScope)scopes.next();
@@ -865,7 +1029,9 @@ return null;
        }
        
 
-       
+       public DeltaProcessor getDeltaProcessor() {
+               return this.deltaState.getDeltaProcessor();
+       }
        /** 
         * Returns the set of elements which are out of synch with their buffers.
         */
@@ -873,6 +1039,9 @@ return null;
                return this.elementsOutOfSynchWithBuffers;
        }
 
+//     public IndexManager getIndexManager() {
+//             return this.indexManager;
+//     }
        /**
         * Returns the <code>IJavaElement</code> represented by the 
         * <code>String</code> memento.
@@ -901,7 +1070,7 @@ return null;
                        return proj;
                }
                int rootEnd= memento.indexOf(JavaElement.JEM_PACKAGEFRAGMENT, projectEnd + 1);
-//     TODO khartlage temp-del
+//     TODO  temp-del
 //             if (rootEnd == -1) {
 //                     return model.getHandleFromMementoForRoot(memento, proj, projectEnd, memento.length());
 //             }
@@ -934,9 +1103,9 @@ return null;
        /**
         *  Returns the info for the element.
         */
-//     public Object getInfo(IJavaElement element) {
-//             return this.cache.getInfo(element);
-//     }
+       public Object getInfo(IJavaElement element) {
+               return this.cache.getInfo(element);
+       }
 
        /**
         * Returns the handle to the active Java Model.
@@ -1003,16 +1172,39 @@ return null;
                }
                return info;
        }
+       /*
+        * Returns the per-working copy info for the given working copy at the given path.
+        * If it doesn't exist and if create, add a new per-working copy info with the given problem requestor.
+        * If recordUsage, increment the per-working copy info's use count.
+        * Returns null if it doesn't exist and not create.
+        */
+       public PerWorkingCopyInfo getPerWorkingCopyInfo(CompilationUnit workingCopy,boolean create, boolean recordUsage, IProblemRequestor problemRequestor) {
+               synchronized(perWorkingCopyInfos) { // use the perWorkingCopyInfo collection as its own lock
+                       WorkingCopyOwner owner = workingCopy.owner;
+                       Map workingCopyToInfos = (Map)this.perWorkingCopyInfos.get(owner);
+                       if (workingCopyToInfos == null && create) {
+                               workingCopyToInfos = new HashMap();
+                               this.perWorkingCopyInfos.put(owner, workingCopyToInfos);
+                       }
 
+                       PerWorkingCopyInfo info = workingCopyToInfos == null ? null : (PerWorkingCopyInfo) workingCopyToInfos.get(workingCopy);
+                       if (info == null && create) {
+                               info= new PerWorkingCopyInfo(workingCopy, problemRequestor);
+                               workingCopyToInfos.put(workingCopy, info);
+                       }
+                       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;
-//
-//             ArrayList variableList = new ArrayList(5);
+       public static String[] getRegisteredVariableNames(){
+               
+               Plugin jdtCorePlugin = JavaCore.getPlugin();
+               if (jdtCorePlugin == null) return null;
+
+               ArrayList variableList = new ArrayList(5);
 //             IExtensionPoint extension = jdtCorePlugin.getDescriptor().getExtensionPoint(JavaModelManager.CPVARIABLE_INITIALIZER_EXTPOINT_ID);
 //             if (extension != null) {
 //                     IExtension[] extensions =  extension.getExtensions();
@@ -1024,17 +1216,17 @@ return null;
 //                             }
 //                     }       
 //             }
-//             String[] variableNames = new String[variableList.size()];
-//             variableList.toArray(variableNames);
-//             return variableNames;
-//     }       
+               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 = JavaCore.getPlugin();
+//             Plugin jdtCorePlugin = PHPCore.getPlugin();
 //             if (jdtCorePlugin == null) return null;
 //
 //             ArrayList containerIDList = new ArrayList(5);
@@ -1059,11 +1251,22 @@ return null;
         */
        private File getSerializationFile(IProject project) {
                if (!project.exists()) return null;
-               IPluginDescriptor descr= JavaCore.getJavaCore().getDescriptor();
-               IPath workingLocation= project.getPluginWorkingLocation(descr);
+               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.
+        */
+       public HashMap getTemporaryCache() {
+               HashMap result = (HashMap)this.temporaryCache.get();
+               if (result == null) {
+                       result = new HashMap();
+                       this.temporaryCache.set(result);
+               }
+               return result;
+       }
        /**
         * Returns the open ZipFile at the given location. If the ZipFile
         * does not yet exist, it is created, opened, and added to the cache
@@ -1123,11 +1326,16 @@ return null;
                        }
                }
        }
-
+       /*
+        * Returns whether there is a temporary cache for the current thread.
+        */
+       public boolean hasTemporaryCache() {
+               return this.temporaryCache.get() != null;
+       }
 //     public void loadVariablesAndContainers() throws CoreException {
 //
 //             // backward compatibility, consider persistent property 
-//             QualifiedName qName = new QualifiedName(JavaCore.PLUGIN_ID, "variables"); //$NON-NLS-1$
+//             QualifiedName qName = new QualifiedName(PHPCore.PLUGIN_ID, "variables"); //$NON-NLS-1$
 //             String xmlString = ResourcesPlugin.getWorkspace().getRoot().getPersistentProperty(qName);
 //             
 //             try {
@@ -1173,8 +1381,8 @@ return null;
 //             }
 //             
 //             // load variables and containers from preferences into cache
-//             Preferences preferences = JavaCore.getPlugin().getPluginPreferences();
-//
+//             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();
@@ -1270,19 +1478,48 @@ return null;
         *  Returns the info for this element without
         *  disturbing the cache ordering.
         */ // TODO: should be synchronized, could answer unitialized info or if cache is in middle of rehash, could even answer distinct element info
-//     protected Object peekAtInfo(IJavaElement element) {
-//             return this.cache.peekAtInfo(element);
-//     }
+       protected Object peekAtInfo(IJavaElement element) {
+               return this.cache.peekAtInfo(element);
+       }
 
        /**
         * @see ISaveParticipant
         */
        public void prepareToSave(ISaveContext context) throws CoreException {
        }
-//     protected void putInfo(IJavaElement element, Object info) {
-//             this.cache.putInfo(element, info);
-//     }
-
+       
+       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 
+        * 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.
+        */
+       protected synchronized void putInfos(IJavaElement openedElement, Map newElements) {
+               // remove children
+               Object existingInfo = this.cache.peekAtInfo(openedElement);
+               if (openedElement instanceof IParent && existingInfo instanceof JavaElementInfo) {
+                       IJavaElement[] children = ((JavaElementInfo)existingInfo).getChildren();
+                       for (int i = 0, size = children.length; i < size; ++i) {
+                               JavaElement child = (JavaElement) children[i];
+                               try {
+                                       child.close();
+                               } catch (JavaModelException e) {
+                                       // ignore
+                               }
+                       }
+               }
+       
+               Iterator iterator = newElements.keySet().iterator();
+               while (iterator.hasNext()) {
+                       IJavaElement element = (IJavaElement)iterator.next();
+                       Object info = newElements.get(element);
+                       this.cache.putInfo(element, info);
+               }
+       }
        /**
         * Reads the build state for the relevant project.
         */
@@ -1298,11 +1535,10 @@ return null;
                                        String kind= in.readUTF();
                                        if (!kind.equals("STATE")) //$NON-NLS-1$
                                                throw new IOException(Util.bind("build.wrongFileFormat")); //$NON-NLS-1$
-//                             TODO khartlage temp-del
-//                                     if (in.readBoolean())
-//                                             return JavaBuilder.readState(project, in);
-//                                     if (JavaBuilder.DEBUG)
-//                                             System.out.println("Saved state thinks last build failed for " + project.getName()); //$NON-NLS-1$
+                                       if (in.readBoolean())
+                                               return PHPBuilder.readState(project, in);
+                                       if (PHPBuilder.DEBUG)
+                                               System.out.println("Saved state thinks last build failed for " + project.getName()); //$NON-NLS-1$
                                } finally {
                                        in.close();
                                }
@@ -1409,10 +1645,49 @@ return null;
                }
        }
        
-//     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)
+        * from the cache.
+        * Returns the info for the given element, or null if it was closed.
+        */
+       public synchronized Object removeInfoAndChildren(JavaElement element) throws JavaModelException {
+               Object info = this.cache.peekAtInfo(element);
+               if (info != null) {
+                       boolean wasVerbose = false;
+                       try {
+                               if (VERBOSE) {
+                                       System.out.println("CLOSING Element ("+ Thread.currentThread()+"): " + element.toStringWithAncestors());  //$NON-NLS-1$//$NON-NLS-2$
+                                       wasVerbose = true;
+                                       VERBOSE = false;
+                               }
+                               element.closing(info);
+                               if (element instanceof IParent && info instanceof JavaElementInfo) {
+                                       IJavaElement[] children = ((JavaElementInfo)info).getChildren();
+                                       for (int i = 0, size = children.length; i < size; ++i) {
+                                               JavaElement child = (JavaElement) children[i];
+                                               child.close();
+                                       }
+                               }
+                               this.cache.removeInfo(element);
+                               if (wasVerbose) {
+                                       System.out.println("-> Package cache size = " + this.cache.pkgSize()); //$NON-NLS-1$
+                                       System.out.println("-> Openable cache filling ratio = " + NumberFormat.getInstance().format(this.cache.openableFillingRatio()) + "%"); //$NON-NLS-1$//$NON-NLS-2$
+                               }
+                       } finally {
+                               JavaModelManager.VERBOSE = wasVerbose;
+                       }
+                       return info;
+               }
+               return null;
+       }       
        public void removePerProjectInfo(JavaProject javaProject) {
                synchronized(perProjectInfo) { // use the perProjectInfo collection as its own lock
                        IProject project = javaProject.getProject();
@@ -1422,7 +1697,12 @@ return null;
                        }
                }
        }
-
+       /*
+        * Resets the temporary cache for newly created elements to null.
+        */
+       public void resetTemporaryCache() {
+               this.temporaryCache.set(null);
+       }
        /**
         * @see ISaveParticipant
         */
@@ -1435,54 +1715,97 @@ return null;
                if (context.getKind() == ISaveContext.SNAPSHOT) return;
                
                // save built state
-               // TODO khartlage temp-del
-//             if (info.triedRead) saveBuiltState(info);
+               if (info.triedRead) saveBuiltState(info);
        }
        
        /**
         * Saves the built state for the project.
         */
-//     private void saveBuiltState(PerProjectInfo info) throws CoreException {
-//             if (JavaBuilder.DEBUG)
-//                     System.out.println(Util.bind("build.saveStateProgress", info.project.getName())); //$NON-NLS-1$
-//             File file = getSerializationFile(info.project);
-//             if (file == null) return;
-//             long t = System.currentTimeMillis();
-//             try {
-//                     DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
-//                     try {
-//                             out.writeUTF(JavaCore.PLUGIN_ID);
-//                             out.writeUTF("STATE"); //$NON-NLS-1$
-//                             if (info.savedState == null) {
-//                                     out.writeBoolean(false);
-//                             } else {
-//                                     out.writeBoolean(true);
-//                                     JavaBuilder.writeState(info.savedState, out);
-//                             }
-//                     } finally {
-//                             out.close();
-//                     }
-//             } catch (RuntimeException e) {
-//                     try {file.delete();} catch(SecurityException se) {}
-//                     throw new CoreException(
-//                             new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, Platform.PLUGIN_ERROR,
-//                                     Util.bind("build.cannotSaveState", info.project.getName()), e)); //$NON-NLS-1$
-//             } catch (IOException e) {
-//                     try {file.delete();} catch(SecurityException se) {}
-//                     throw new CoreException(
-//                             new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, Platform.PLUGIN_ERROR,
-//                                     Util.bind("build.cannotSaveState", info.project.getName()), e)); //$NON-NLS-1$
-//             }
-//             if (JavaBuilder.DEBUG) {
-//                     t = System.currentTimeMillis() - t;
-//                     System.out.println(Util.bind("build.saveStateComplete", String.valueOf(t))); //$NON-NLS-1$
-//             }
-//     }
-
+       private void saveBuiltState(PerProjectInfo info) throws CoreException {
+               if (PHPBuilder.DEBUG)
+                       System.out.println(Util.bind("build.saveStateProgress", info.project.getName())); //$NON-NLS-1$
+               File file = getSerializationFile(info.project);
+               if (file == null) return;
+               long t = System.currentTimeMillis();
+               try {
+                       DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
+                       try {
+                               out.writeUTF(JavaCore.PLUGIN_ID);
+                               out.writeUTF("STATE"); //$NON-NLS-1$
+                               if (info.savedState == null) {
+                                       out.writeBoolean(false);
+                               } else {
+                                       out.writeBoolean(true);
+                                       PHPBuilder.writeState(info.savedState, out);
+                               }
+                       } finally {
+                               out.close();
+                       }
+               } catch (RuntimeException e) {
+                       try {file.delete();} catch(SecurityException se) {}
+                       throw new CoreException(
+                               new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, Platform.PLUGIN_ERROR,
+                                       Util.bind("build.cannotSaveState", info.project.getName()), e)); //$NON-NLS-1$
+               } catch (IOException e) {
+                       try {file.delete();} catch(SecurityException se) {}
+                       throw new CoreException(
+                               new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, Platform.PLUGIN_ERROR,
+                                       Util.bind("build.cannotSaveState", info.project.getName()), e)); //$NON-NLS-1$
+               }
+               if (PHPBuilder.DEBUG) {
+                       t = System.currentTimeMillis() - t;
+                       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) {
@@ -1509,6 +1832,36 @@ return null;
                        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
@@ -1583,18 +1936,18 @@ return null;
        }
 
        public void shutdown () {
-//     TODO khartlage temp-del
+//     TODO  temp-del
 //             if (this.deltaProcessor.indexManager != null){ // no more indexing
 //                     this.deltaProcessor.indexManager.shutdown();
 //             }
-//             try {
-//                     IJavaModel model = this.getJavaModel();
-//                     if (model != null) {
+               try {
+                       IJavaModel model = this.getJavaModel();
+                       if (model != null) {
 
-//                             model.close();
-//                     }
-//             } catch (JavaModelException e) {
-//             }
+                               model.close();
+                       }
+               } catch (JavaModelException e) {
+               }
        }
 
        /**
@@ -1616,17 +1969,17 @@ return null;
        /**
         * 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);
+               }
+       }
 
 
        
@@ -1666,4 +2019,55 @@ return null;
                preferences.setValue(variableKey, variableString);
                JavaCore.getPlugin().savePluginPreferences();
        }
+       /*
+        * Returns all the working copies which have the given owner.
+        * Adds the working copies of the primary owner if specified.
+        * Returns null if it has none.
+        */
+       public ICompilationUnit[] getWorkingCopies(WorkingCopyOwner owner, boolean addPrimary) {
+               synchronized(perWorkingCopyInfos) {
+                       ICompilationUnit[] primaryWCs = addPrimary && owner != DefaultWorkingCopyOwner.PRIMARY 
+                               ? getWorkingCopies(DefaultWorkingCopyOwner.PRIMARY, false) 
+                               : null;
+                       Map workingCopyToInfos = (Map)perWorkingCopyInfos.get(owner);
+                       if (workingCopyToInfos == null) return primaryWCs;
+                       int primaryLength = primaryWCs == null ? 0 : primaryWCs.length;
+                       int size = workingCopyToInfos.size(); // note size is > 0 otherwise pathToPerWorkingCopyInfos would be null
+                       ICompilationUnit[] result = new ICompilationUnit[primaryLength + size];
+                       if (primaryWCs != null) {
+                               System.arraycopy(primaryWCs, 0, result, 0, primaryLength);
+                       }
+                       Iterator iterator = workingCopyToInfos.values().iterator();
+                       int index = primaryLength;
+                       while(iterator.hasNext()) {
+                               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);
+           }
+       }
 }