X-Git-Url: http://git.phpeclipse.com diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/core/JavaModelManager.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/core/JavaModelManager.java index 6652089..39978d1 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/core/JavaModelManager.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/core/JavaModelManager.java @@ -1,20 +1,24 @@ /******************************************************************************* * 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 *******************************************************************************/ 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 JavaModelManager manages instances of IJavaModel. @@ -67,13 +82,12 @@ import org.eclipse.core.runtime.Status; * The single instance of JavaModelManager is available from * the static method JavaModelManager.getJavaModelManager(). */ -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 */ @@ -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 = "####"; //$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); /** @@ -103,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 */ @@ -116,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$ @@ -132,44 +151,49 @@ 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 + */ + 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 IClasspathContainer containerGet(IJavaProject project, IPath containerPath) { + 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){ // return null; @@ -273,15 +297,15 @@ public class JavaModelManager implements ISaveParticipant { if (project == null) { project = JavaCore.create(file.getProject()); } - + 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; @@ -289,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 null if unable to associate the given folder with a Java element. *

* Note that a package fragment root is returned rather than a default package. @@ -304,16 +328,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 +348,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) { @@ -337,9 +359,9 @@ return null; // } // return pkg.getClassFile(file.getName()); // } - + /** - * Creates and returns a compilation unit element for the given .java + * Creates and returns a compilation unit element for the given .java * file, its project being the given project. Returns null if unable * to recognize the compilation unit. */ @@ -350,26 +372,22 @@ 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 - * created as a side effect. + * created as a side effect. * Returns null if unable to create a JAR package fragment root. * (for example, if the JAR file represents a non-Java resource) */ @@ -378,9 +396,9 @@ 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 // IPath resourcePath = file.getFullPath(); // try { @@ -396,57 +414,58 @@ return null; // } // return null; // } - + /** * Returns the package fragment root represented by the resource, or * the package fragment the given resource is located in, or null * 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 +474,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,13 +519,13 @@ return null; /** * Used to convert IResourceDeltas into IJavaElementDeltas. */ - public final DeltaProcessor deltaProcessor = new DeltaProcessor(this); +// public final DeltaProcessor deltaProcessor = new DeltaProcessor(this); /** * Used to update the JavaModel for IJavaElementDeltas. */ -// 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; @@ -509,46 +535,144 @@ return null; * 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(" \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(" \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(""); //$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; 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 */ @@ -557,18 +681,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 +701,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 */ @@ -599,7 +722,7 @@ return null; 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; @@ -645,7 +768,7 @@ return null; } } } - + /** @@ -653,16 +776,17 @@ 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$ -// +// // 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$ @@ -674,7 +798,7 @@ return null; // // 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$ @@ -687,30 +811,75 @@ 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 */ 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$ } @@ -721,19 +890,19 @@ return null; } else { deltaToNotify = customDelta; } - + // 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(); // 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; @@ -762,7 +931,7 @@ return null; 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 ? "" : deltaToNotify.toString()); //$NON-NLS-1$ @@ -777,7 +946,7 @@ return null; 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$ @@ -786,10 +955,10 @@ return null; 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, @@ -804,9 +973,9 @@ return null; 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) { @@ -834,7 +1003,7 @@ return null; } } } - + /** * Flushes all deltas without firing them. */ @@ -861,20 +1030,25 @@ return null; } 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 IJavaElement represented by the + * Returns the IJavaElement represented by the * String memento. */ public IJavaElement getHandleFromMemento(String memento) throws JavaModelException { @@ -901,7 +1075,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 +1108,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. @@ -986,8 +1160,8 @@ return null; } 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. @@ -1003,16 +1177,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(); @@ -1022,19 +1219,19 @@ return null; // 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; -// } + 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); @@ -1047,23 +1244,34 @@ return null; // 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. */ 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 @@ -1072,14 +1280,14 @@ return null; * @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; @@ -1108,7 +1316,7 @@ return null; } 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$ @@ -1123,13 +1331,18 @@ 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$ +// // 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); @@ -1148,7 +1361,7 @@ return null; // if (!cpElement.getNodeName().equalsIgnoreCase("variables")) { //$NON-NLS-1$ // return; // } -// +// // NodeList list= cpElement.getChildNodes(); // int length= list.getLength(); // for (int i= 0; i < length; ++i) { @@ -1157,7 +1370,7 @@ return null; // 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$ // } @@ -1169,12 +1382,12 @@ return null; // if (xmlString != null){ // ResourcesPlugin.getWorkspace().getRoot().setPersistentProperty(qName, null); // flush old one // } -// +// // } -// -// // load variables and containers from preferences into cache -// Preferences preferences = JavaCore.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(); @@ -1183,8 +1396,8 @@ return null; // 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)){ @@ -1224,11 +1437,11 @@ return null; 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); @@ -1264,25 +1477,54 @@ return null; else { 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 +1540,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(); } @@ -1322,7 +1563,7 @@ return null; // 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 { @@ -1336,7 +1577,7 @@ return null; // 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; @@ -1366,39 +1607,39 @@ return null; 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; @@ -1408,11 +1649,50 @@ 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 +1702,12 @@ return null; } } } - + /* + * Resets the temporary cache for newly created elements to null. + */ + public void resetTemporaryCache() { + this.temporaryCache.set(null); + } /** * @see ISaveParticipant */ @@ -1433,57 +1718,100 @@ return null; // passed this point, save actions are non trivial 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) { if (!JavaProject.hasJavaNature(savedProject)) return; // ignore @@ -1509,6 +1837,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 @@ -1519,9 +1877,9 @@ return null; // 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(); @@ -1583,18 +1941,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) { + } } /** @@ -1612,24 +1970,24 @@ return null; 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); } @@ -1644,10 +2002,10 @@ return null; } 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); @@ -1658,7 +2016,7 @@ return null; // 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(); @@ -1666,4 +2024,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); + } + } }