### Eclipse Workspace Patch 1.0 #P org.eclipse.jdt.core Index: model/org/eclipse/jdt/internal/core/JavaProject.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java,v retrieving revision 1.370 diff -u -r1.370 JavaProject.java --- model/org/eclipse/jdt/internal/core/JavaProject.java 2 May 2006 15:01:33 -0000 1.370 +++ model/org/eclipse/jdt/internal/core/JavaProject.java 3 May 2007 14:01:08 -0000 @@ -63,6 +63,7 @@ import org.eclipse.jdt.core.eval.IEvaluationContext; import org.eclipse.jdt.internal.compiler.util.ObjectVector; import org.eclipse.jdt.internal.compiler.util.SuffixConstants; +import org.eclipse.jdt.internal.core.JavaProjectElementInfo.ProjectCache; import org.eclipse.jdt.internal.core.eval.EvaluationContextWrapper; import org.eclipse.jdt.internal.core.util.MementoTokenizer; import org.eclipse.jdt.internal.core.util.Messages; @@ -1945,6 +1946,10 @@ public IProject getProject() { return this.project; } + + public ProjectCache getProjectCache() throws JavaModelException { + return ((JavaProjectElementInfo) getElementInfo()).getProjectCache(this); + } /** * @see IJavaProject Index: model/org/eclipse/jdt/internal/core/JavaProjectElementInfo.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProjectElementInfo.java,v retrieving revision 1.40 diff -u -r1.40 JavaProjectElementInfo.java --- model/org/eclipse/jdt/internal/core/JavaProjectElementInfo.java 29 Mar 2006 03:08:47 -0000 1.40 +++ model/org/eclipse/jdt/internal/core/JavaProjectElementInfo.java 3 May 2007 14:01:08 -0000 @@ -18,6 +18,7 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.jdt.core.*; +import org.eclipse.jdt.internal.core.util.HashSetOfArray; import org.eclipse.jdt.internal.core.util.Util; import org.eclipse.jdt.internal.core.util.HashtableOfArrayToObject; @@ -33,13 +34,14 @@ /* package */ class JavaProjectElementInfo extends OpenableElementInfo { + + static final IPackageFragmentRoot[] NO_ROOTS = new IPackageFragmentRoot[0]; static class ProjectCache { - ProjectCache(IPackageFragmentRoot[] allPkgFragmentRootsCache, HashtableOfArrayToObject allPkgFragmentsCache, HashtableOfArrayToObject isPackageCache, Map rootToResolvedEntries) { + ProjectCache(IPackageFragmentRoot[] allPkgFragmentRootsCache, Map rootToResolvedEntries, Map pkgFragmentsCaches) { this.allPkgFragmentRootsCache = allPkgFragmentRootsCache; - this.allPkgFragmentsCache = allPkgFragmentsCache; - this.isPackageCache = isPackageCache; this.rootToResolvedEntries = rootToResolvedEntries; + this.pkgFragmentsCaches = pkgFragmentsCaches; } /* @@ -49,15 +51,16 @@ /* * A cache of all package fragments in this project. - * (a map from String[] (the package name) to IPackageFragmentRoot[] (the package fragment roots that contain a package fragment with this name) + * (a map from String[] (the package name) to IPackageFragmentRoot[] (the package fragment roots that contain a package fragment with this name)) */ public HashtableOfArrayToObject allPkgFragmentsCache; /* - * A set of package names (String[]) that are known to be packages. + * A cache of package fragments for each package fragment root of this project + * (a map from IPackageFragmentRoot to a set of String[] (the package name)) */ - public HashtableOfArrayToObject isPackageCache; - + public Map pkgFragmentsCaches; + public Map rootToResolvedEntries; } @@ -72,13 +75,12 @@ * Adds the given name and its super names to the given set * (e.g. for {"a", "b", "c"}, adds {"a", "b", "c"}, {"a", "b"}, and {"a"}) */ - public static void addNames(String[] name, HashtableOfArrayToObject set) { - set.put(name, name); - int length = name.length; - for (int i = length-1; i > 0; i--) { - String[] superName = new String[i]; - System.arraycopy(name, 0, superName, 0, i); - set.put(superName, superName); + static void addSuperPackageNames(String[] pkgName, HashtableOfArrayToObject packageFragments) { + for (int i = pkgName.length-1; i > 0; i--) { + if (packageFragments.getKey(pkgName, i) == null) { + System.arraycopy(pkgName, 0, pkgName = new String[i], 0, i); + packageFragments.put(pkgName, NO_ROOTS); + } } } @@ -204,45 +206,22 @@ roots = new IPackageFragmentRoot[0]; reverseMap.clear(); } - HashtableOfArrayToObject fragmentsCache = new HashtableOfArrayToObject(); - HashtableOfArrayToObject isPackageCache = new HashtableOfArrayToObject(); - for (int i = 0, length = roots.length; i < length; i++) { + + HashMap rootInfos = JavaModelManager.getJavaModelManager().deltaState.roots; + HashMap pkgFragmentsCaches = new HashMap(); + int length = roots.length; + for (int i = 0; i < length; i++) { IPackageFragmentRoot root = roots[i]; - IJavaElement[] frags = null; - try { - if (root.isArchive() && !root.isOpen()) { - JarPackageFragmentRootInfo info = new JarPackageFragmentRootInfo(); - ((JarPackageFragmentRoot) root).computeChildren(info, new HashMap()); - frags = info.children; - } else - frags = root.getChildren(); - } catch (JavaModelException e) { - // root doesn't exist: ignore - continue; - } - for (int j = 0, length2 = frags.length; j < length2; j++) { - PackageFragment fragment= (PackageFragment) frags[j]; - String[] pkgName = fragment.names; - Object existing = fragmentsCache.get(pkgName); - if (existing == null) { - fragmentsCache.put(pkgName, root); - // cache whether each package and its including packages (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=119161) - // are actual packages - addNames(pkgName, isPackageCache); - } else { - if (existing instanceof PackageFragmentRoot) { - fragmentsCache.put(pkgName, new IPackageFragmentRoot[] {(PackageFragmentRoot) existing, root}); - } else { - IPackageFragmentRoot[] entry= (IPackageFragmentRoot[]) existing; - IPackageFragmentRoot[] copy= new IPackageFragmentRoot[entry.length + 1]; - System.arraycopy(entry, 0, copy, 0, entry.length); - copy[entry.length]= root; - fragmentsCache.put(pkgName, copy); - } - } + DeltaProcessor.RootInfo rootInfo = (DeltaProcessor.RootInfo) rootInfos.get(root.getPath()); + if (rootInfo == null || rootInfo.project.equals(project)) { + // compute fragment cache + HashSetOfArray fragmentsCache = new HashSetOfArray(); + initializePackageNames(root, fragmentsCache); + pkgFragmentsCaches.put(root, fragmentsCache); } } - cache = new ProjectCache(roots, fragmentsCache, isPackageCache, reverseMap); + + cache = new ProjectCache(roots, reverseMap, pkgFragmentsCaches); this.projectCache = cache; } return cache; @@ -258,6 +237,24 @@ } return this.nonJavaResources; } + + private void initializePackageNames(IPackageFragmentRoot root, HashSetOfArray fragmentsCache) { + IJavaElement[] frags = null; + try { + if (!root.isOpen()) { + PackageFragmentRootInfo info = root.isArchive() ? new JarPackageFragmentRootInfo() : new PackageFragmentRootInfo(); + ((PackageFragmentRoot) root).computeChildren(info, new HashMap()); + frags = info.children; + } else + frags = root.getChildren(); + } catch (JavaModelException e) { + // root doesn't exist: ignore + return; + } + for (int j = 0, length2 = frags.length; j < length2; j++) { + fragmentsCache.add(((PackageFragment) frags[j]).names); + } + } /* * Returns whether the given path is a classpath entry or an output location. @@ -284,7 +281,62 @@ */ NameLookup newNameLookup(JavaProject project, ICompilationUnit[] workingCopies) { ProjectCache cache = getProjectCache(project); - return new NameLookup(cache.allPkgFragmentRootsCache, cache.allPkgFragmentsCache, cache.isPackageCache, workingCopies, cache.rootToResolvedEntries); + HashtableOfArrayToObject allPkgFragmentsCache = cache.allPkgFragmentsCache; + if (allPkgFragmentsCache == null) { + HashMap rootInfos = JavaModelManager.getJavaModelManager().deltaState.roots; + IPackageFragmentRoot[] allRoots = cache.allPkgFragmentRootsCache; + int length = allRoots.length; + allPkgFragmentsCache = new HashtableOfArrayToObject(); + for (int i = 0; i < length; i++) { + IPackageFragmentRoot root = allRoots[i]; + DeltaProcessor.RootInfo rootInfo = (DeltaProcessor.RootInfo) rootInfos.get(root.getPath()); + JavaProject rootProject = rootInfo == null ? project : rootInfo.project; + HashSetOfArray fragmentsCache; + if (rootProject.equals(project)) { + // retrieve package fragments cache from this project + fragmentsCache = (HashSetOfArray) cache.pkgFragmentsCaches.get(root); + } else { + // retrieve package fragments cache from the root's project + ProjectCache rootProjectCache; + try { + rootProjectCache = rootProject.getProjectCache(); + } catch (JavaModelException e) { + // project doesn't exit + continue; + } + fragmentsCache = (HashSetOfArray) rootProjectCache.pkgFragmentsCaches.get(root); + } + if (fragmentsCache == null) { // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=183833 + fragmentsCache = new HashSetOfArray(); + initializePackageNames(root, fragmentsCache); + } + Object[][] set = fragmentsCache.set; + for (int j = 0, length2 = set.length; j < length2; j++) { + String[] pkgName = (String[]) set[j]; + if (pkgName == null) + continue; + Object existing = allPkgFragmentsCache.get(pkgName); + if (existing == null || existing == NO_ROOTS) { + allPkgFragmentsCache.put(pkgName, root); + // ensure super packages (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=119161) + // are also in the map + addSuperPackageNames(pkgName, allPkgFragmentsCache); + } else { + if (existing instanceof PackageFragmentRoot) { + allPkgFragmentsCache.put(pkgName, new IPackageFragmentRoot[] {(PackageFragmentRoot) existing, root}); + } else { + IPackageFragmentRoot[] roots = (IPackageFragmentRoot[]) existing; + int rootLength = roots.length; + System.arraycopy(roots, 0, roots = new IPackageFragmentRoot[rootLength+1], 0, rootLength); + roots[rootLength] = root; + allPkgFragmentsCache.put(pkgName, roots); + } + } + } + } + cache.allPkgFragmentsCache = allPkgFragmentsCache; + } + return new NameLookup(cache.allPkgFragmentRootsCache, cache.allPkgFragmentsCache, workingCopies, cache.rootToResolvedEntries); } /* Index: model/org/eclipse/jdt/internal/core/NameLookup.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/NameLookup.java,v retrieving revision 1.110 diff -u -r1.110 NameLookup.java --- model/org/eclipse/jdt/internal/core/NameLookup.java 29 Mar 2006 03:08:47 -0000 1.110 +++ model/org/eclipse/jdt/internal/core/NameLookup.java 3 May 2007 14:01:08 -0000 @@ -125,12 +125,6 @@ */ protected HashtableOfArrayToObject packageFragments; - /* - * A set of names (String[]) that are known to be package names. - * Value is not null for known package. - */ - protected HashtableOfArrayToObject isPackageCache; - /** * Reverse map from root path to corresponding resolved CP entry * (so as to be able to figure inclusion/exclusion rules) @@ -149,7 +143,6 @@ public NameLookup( IPackageFragmentRoot[] packageFragmentRoots, HashtableOfArrayToObject packageFragments, - HashtableOfArrayToObject isPackage, ICompilationUnit[] workingCopies, Map rootToResolvedEntries) { long start = -1; @@ -163,12 +156,10 @@ this.packageFragmentRoots = packageFragmentRoots; if (workingCopies == null) { this.packageFragments = packageFragments; - this.isPackageCache = isPackage; } else { // clone tables as we're adding packages from working copies try { this.packageFragments = (HashtableOfArrayToObject) packageFragments.clone(); - this.isPackageCache = (HashtableOfArrayToObject) isPackage.clone(); } catch (CloneNotSupportedException e1) { // ignore (implementation of HashtableOfArrayToObject supports cloning) } @@ -213,11 +204,11 @@ IPackageFragmentRoot root = (IPackageFragmentRoot) pkg.getParent(); String[] pkgName = pkg.names; Object existing = this.packageFragments.get(pkgName); - if (existing == null) { + if (existing == null || existing == JavaProjectElementInfo.NO_ROOTS) { this.packageFragments.put(pkgName, root); - // cache whether each package and its including packages (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=119161) - // are actual packages - JavaProjectElementInfo.addNames(pkgName, this.isPackageCache); + // ensure super packages (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=119161) + // are also in the map + JavaProjectElementInfo.addSuperPackageNames(pkgName, this.packageFragments); } else { if (existing instanceof PackageFragmentRoot) { if (!existing.equals(root)) @@ -778,7 +769,7 @@ } public boolean isPackage(String[] pkgName) { - return this.isPackageCache.get(pkgName) != null; + return this.packageFragments.get(pkgName) != null; } /** Index: model/org/eclipse/jdt/internal/core/util/HashSetOfArray.java =================================================================== RCS file: model/org/eclipse/jdt/internal/core/util/HashSetOfArray.java diff -N model/org/eclipse/jdt/internal/core/util/HashSetOfArray.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ model/org/eclipse/jdt/internal/core/util/HashSetOfArray.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.util; + +/** + * HashSet of Object[] + */ +public final class HashSetOfArray implements Cloneable { + + // to avoid using Enumerations, walk the individual tables skipping nulls + public Object[][] set; + + public int elementSize; // number of elements in the table + int threshold; + + public HashSetOfArray() { + this(13); + } + + public HashSetOfArray(int size) { + + this.elementSize = 0; + this.threshold = size; // size represents the expected number of elements + int extraRoom = (int) (size * 1.75f); + if (this.threshold == extraRoom) + extraRoom++; + this.set = new Object[extraRoom][]; + } + + public Object clone() throws CloneNotSupportedException { + HashSetOfArray result = (HashSetOfArray) super.clone(); + result.elementSize = this.elementSize; + result.threshold = this.threshold; + + int length = this.set.length; + result.set = new Object[length][]; + System.arraycopy(this.set, 0, result.set, 0, length); + + return result; + } + + public boolean contains(Object[] array) { + int length = this.set.length; + int index = hashCode(array) % length; + int arrayLength = array.length; + Object[] currentArray; + while ((currentArray = this.set[index]) != null) { + if (currentArray.length == arrayLength && Util.equalArraysOrNull(currentArray, array)) + return true; + if (++index == length) { + index = 0; + } + } + return false; + } + + private int hashCode(Object[] element) { + return hashCode(element, element.length); + } + + private int hashCode(Object[] element, int length) { + int hash = 0; + for (int i = length-1; i >= 0; i--) + hash = Util.combineHashCodes(hash, element[i].hashCode()); + return hash & 0x7FFFFFFF; + } + + public Object add(Object[] array) { + int length = this.set.length; + int index = hashCode(array) % length; + int arrayLength = array.length; + Object[] currentArray; + while ((currentArray = this.set[index]) != null) { + if (currentArray.length == arrayLength && Util.equalArraysOrNull(currentArray, array)) + return this.set[index] = array; + if (++index == length) { + index = 0; + } + } + this.set[index] = array; + + // assumes the threshold is never equal to the size of the table + if (++this.elementSize > threshold) + rehash(); + return array; + } + + public Object remove(Object[] array) { + int length = this.set.length; + int index = hashCode(array) % length; + int arrayLength = array.length; + Object[] currentArray; + while ((currentArray = this.set[index]) != null) { + if (currentArray.length == arrayLength && Util.equalArraysOrNull(currentArray, array)) { + Object existing = this.set[index]; + this.elementSize--; + this.set[index] = null; + rehash(); + return existing; + } + if (++index == length) { + index = 0; + } + } + return null; + } + + private void rehash() { + + HashSetOfArray newHashSet = new HashSetOfArray(elementSize * 2); // double the number of expected elements + Object[] currentArray; + for (int i = this.set.length; --i >= 0;) + if ((currentArray = this.set[i]) != null) + newHashSet.add(currentArray); + + this.set = newHashSet.set; + this.threshold = newHashSet.threshold; + } + + public int size() { + return elementSize; + } + + public String toString() { + StringBuffer buffer = new StringBuffer(); + Object[] element; + for (int i = 0, length = this.set.length; i < length; i++) + if ((element = this.set[i]) != null) { + buffer.append('{'); + for (int j = 0, length2 = element.length; j < length2; j++) { + buffer.append(element[j]); + if (j != length2-1) + buffer.append(", "); //$NON-NLS-1$ + } + buffer.append("}"); //$NON-NLS-1$ + if (i != length-1) + buffer.append('\n'); + } + return buffer.toString(); + } +}