Various optimizations - see summary in 1GHFO1M
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DataTree.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DataTree.java index d836e45..8c1a0e2 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DataTree.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DataTree.java
@@ -185,10 +185,10 @@ public DataTreeNode findNodeAt (IPath key) { AbstractDataTreeNode node = this.getRootNode(); - String[] segments = key.segments(); - for (int i = 0; i < segments.length; i++) { + int keyLength = key.segmentCount(); + for (int i = 0; i < keyLength; i++) { try { - node = node.childAt(segments[i]); + node = node.childAt(key.segment(i)); } catch (ObjectNotFoundException notFound) { return null; } @@ -252,9 +252,9 @@ DataTreeNode node = this.findNodeAt(key); if (node == null) { - return new DataTreeLookup(key, false, null); + return DataTreeLookup.newLookup(key, false, null); } else { - return new DataTreeLookup(key, true, node.getData()); + return DataTreeLookup.newLookup(key, true, node.getData()); } } /**
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DataTreeLookup.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DataTreeLookup.java index d85145c..57ab138 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DataTreeLookup.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DataTreeLookup.java
@@ -1,28 +1,62 @@ package org.eclipse.core.internal.dtree; - /* * (c) Copyright IBM Corp. 2000, 2001. * All Rights Reserved. */ - import org.eclipse.core.runtime.IPath; /** - * The result of doing a lookup() in a data tree. + * The result of doing a lookup() in a data tree. Uses an instance + * pool that assumes no more than POOL_SIZE instance will ever be + * needed concurrently. Reclaims and reuses instances on an LRU basis. */ public class DataTreeLookup { public IPath key; public boolean isPresent; public Object data; public boolean foundInFirstDelta; -public DataTreeLookup(IPath nodeKey, boolean isPresent, Object data) { - this.key = nodeKey; - this.isPresent = isPresent; - this.data = data; + private static final int POOL_SIZE = 100; + /** + * The array of lookup instances available for use. + */ + private static DataTreeLookup[] instancePool; + /** + * The index of the next available lookup instance. + */ + private static int nextFree = 0; + static { + instancePool = new DataTreeLookup[POOL_SIZE]; + //fill the pool with objects + for (int i = 0; i < POOL_SIZE; i++) { + instancePool[i] = new DataTreeLookup(); + } + } +/** + * Constructors for internal use only. Use factory methods. + */ +private DataTreeLookup() { } -public DataTreeLookup(IPath nodeKey, boolean isPresent, Object data, boolean foundInFirstDelta) { - this.key = nodeKey; - this.isPresent = isPresent; - this.data = data; - this.foundInFirstDelta = foundInFirstDelta; +/** + * Factory method for creating a new lookup object. + */ +public static DataTreeLookup newLookup(IPath nodeKey, boolean isPresent, Object data) { + DataTreeLookup instance = instancePool[nextFree]; + nextFree = ++nextFree % POOL_SIZE; + instance.key = nodeKey; + instance.isPresent = isPresent; + instance.data = data; + instance.foundInFirstDelta = false; + return instance; } +/** + * Factory method for creating a new lookup object. + */ +public static DataTreeLookup newLookup(IPath nodeKey, boolean isPresent, Object data, boolean foundInFirstDelta) { + DataTreeLookup instance = instancePool[nextFree]; + nextFree = ++nextFree % POOL_SIZE; + instance.key = nodeKey; + instance.isPresent = isPresent; + instance.data = data; + instance.foundInFirstDelta = foundInFirstDelta; + return instance; } +} \ No newline at end of file
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DeltaDataTree.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DeltaDataTree.java index 3a03354..4ab0ba0 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DeltaDataTree.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/dtree/DeltaDataTree.java
@@ -33,7 +33,6 @@ */ public class DeltaDataTree extends AbstractDataTree { - private AbstractDataTreeNode rootNode; private DeltaDataTree parent; @@ -399,9 +398,9 @@ public AbstractDataTreeNode findNodeAt (IPath key) { AbstractDataTreeNode node = rootNode; - String[] segments = key.segments(); - for (int i = 0; i < segments.length; i++) { - node = node.childAtOrNull(segments[i]); + int segmentCount = key.segmentCount(); + for (int i = 0; i < segmentCount; i++) { + node = node.childAtOrNull(key.segment(i)); if (node == null) return null; } @@ -478,12 +477,12 @@ */ AbstractDataTreeNode[] childNodes = null; - String[] keyNames = parentKey.segments(); + int keyLength = parentKey.segmentCount(); for (DeltaDataTree tree = this; tree != null; tree = tree.parent) { AbstractDataTreeNode node = tree.rootNode; boolean complete = !node.isDelta(); - for (int i = 0; i < keyNames.length; i++) { - node = node.childAtOrNull(keyNames[i]); + for (int i = 0; i < keyLength; i++) { + node = node.childAtOrNull(parentKey.segment(i)); if (node == null) { break; } @@ -550,12 +549,12 @@ * report error if node is missing or has been deleted */ - String[] keyNames = key.segments(); + int keyLength = key.segmentCount(); for (DeltaDataTree tree = this; tree != null; tree = tree.parent) { AbstractDataTreeNode node = tree.rootNode; boolean complete = !node.isDelta(); - for (int i = 0; i < keyNames.length; i++) { - node = node.childAtOrNull(keyNames[i]); + for (int i = 0; i < keyLength; i++) { + node = node.childAtOrNull(key.segment(i)); if (node == null) { break; } @@ -661,12 +660,12 @@ * @param key key of node for which we want to retrieve data. */ public DataTreeLookup lookup(IPath key) { - String[] keyNames = key.segments(); + int keyLength = key.segmentCount(); for (DeltaDataTree tree = this; tree != null; tree = tree.parent) { AbstractDataTreeNode node = tree.rootNode; boolean complete = !node.isDelta(); - for (int i = 0; i < keyNames.length; i++) { - node = node.childAtOrNull(keyNames[i]); + for (int i = 0; i < keyLength; i++) { + node = node.childAtOrNull(key.segment(i)); if (node == null) { break; } @@ -674,7 +673,7 @@ } if (node != null) { if (node.hasData()) { - return new DataTreeLookup(key, true, node.getData(), tree==this); + return DataTreeLookup.newLookup(key, true, node.getData(), tree==this); } else if (node.isDeleted()) { break; } @@ -684,7 +683,7 @@ break; } } - return new DataTreeLookup(key, false, null); + return DataTreeLookup.newLookup(key, false, null); } /** * Converts this tree's representation to be a complete tree, not a delta. @@ -789,12 +788,12 @@ * if the node is not found or if it has been deleted */ protected AbstractDataTreeNode searchNodeAt (IPath key) { - String[] keyNames = key.segments(); + int keyLength = key.segmentCount(); for (DeltaDataTree tree = this; tree != null; tree = tree.parent) { AbstractDataTreeNode node = tree.rootNode; boolean complete = !node.isDelta(); - for (int i = 0; i < keyNames.length; i++) { - node = node.childAtOrNull(keyNames[i]); + for (int i = 0; i < keyLength; i++) { + node = node.childAtOrNull(key.segment(i)); if (node == null) { break; }
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/events/NodeIDMap.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/events/NodeIDMap.java new file mode 100644 index 0000000..c13197d --- /dev/null +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/events/NodeIDMap.java
@@ -0,0 +1,259 @@ +package org.eclipse.core.internal.events; + +import org.eclipse.core.runtime.IPath; + +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ + + +/** + * A specialized map that maps Node IDs to their old and new paths. + * Used for calculating moves during resource change notification. + */ +public class NodeIDMap { + protected static final int MINIMUM_SIZE = 10; + protected int elementCount = 0; + protected long[] ids; + protected IPath[] oldPaths; + protected IPath[] newPaths; +/** + * Creates a new node ID map of default capacity. + */ +public NodeIDMap() { + this(MINIMUM_SIZE); +} +/** + * Creates a new node ID map with the given capacity. + */ +public NodeIDMap(int capacity) { + super(); + int size = Math.max(MINIMUM_SIZE, capacity * 2); + this.ids = new long[size]; + this.oldPaths = new IPath[size]; + this.newPaths = new IPath[size]; +} +/** + * Returns true if the given element is contained in the map, + * and false otherwise. + */ +public boolean contains(long id) { + return getIndex(id) >= 0; +} +/** + * The array isn't large enough so double its size and rehash + * all its current values. + */ +protected void expand() { + int newLength = ids.length * 2; + long[] grownIds = new long[newLength]; + IPath[] grownOldPaths = new IPath[newLength]; + IPath[] grownNewPaths = new IPath[newLength]; + int maxArrayIndex = newLength-1; + for (int i = 0; i < ids.length; i++) { + long id = ids[i]; + if (id != 0) { + int hash = hashFor(id) % newLength; + while (grownIds[hash] != 0) { + hash++; + if (hash > maxArrayIndex) + hash = 0; + } + grownIds[hash] = id; + grownOldPaths[hash] = oldPaths[i]; + grownNewPaths[hash] = newPaths[i]; + } + } + ids = grownIds; + oldPaths = grownOldPaths; + newPaths = grownNewPaths; +} +/** + * Returns the index of the given element in the map. If not + * found, returns -1. + */ +private int getIndex(long searchID) { + int hash = hashFor(searchID) % ids.length; + + // search the last half of the array + for (int i = hash; i < ids.length; i++) { + if (ids[i] == searchID) + return i; + } + + // search the beginning of the array + for (int i = 0; i < hash - 1; i++) { + if (ids[i] == searchID) + return i; + } + + // marker info not found so return null + return -1; +} +/** + * Returns the new path location for the given ID, or null + * if no new path is available. + */ +public IPath getNewPath(long nodeID) { + int index = getIndex(nodeID); + if (index == -1) + return null; + return newPaths[index]; +} +/** + * Returns the old path location for the given ID, or null + * if no old path is available. + */ +public IPath getOldPath(long nodeID) { + int index = getIndex(nodeID); + if (index == -1) + return null; + return oldPaths[index]; +} + +private int hashFor(long id) { + return Math.abs((int) id); +} +/** + * Returns true if there are no elements in the map, and + * false otherwise. + */ +public boolean isEmpty() { + return elementCount == 0; +} +/** + * Adds the given path mappings to the map. If either oldPath + * or newPath is null, they are ignored (old map values are not overwritten). + */ +private void put(long id, IPath oldPath, IPath newPath) { + if (oldPath == null && newPath == null) + return; + int hash = hashFor(id) % ids.length; + + // search for an empty slot at the end of the array + for (int i = hash; i < ids.length; i++) { + if (ids[i] == id) { + //replace value for existing entry + if (oldPath != null) + oldPaths[i] = oldPath; + if (newPath != null) + newPaths[i] = newPath; + return; + } + if (ids[i] == 0) { + //add a new entry to the map + ids[i] = id; + if (oldPath != null) + oldPaths[i] = oldPath; + if (newPath != null) + newPaths[i] = newPath; + elementCount++; + // grow if necessary + if (shouldGrow()) + expand(); + return; + } + } + + // search for an empty slot at the beginning of the array + for (int i = 0; i < hash - 1; i++) { + if (ids[i] == id) { + //replace value for existing entry + if (oldPath != null) + oldPaths[i] = oldPath; + if (newPath != null) + newPaths[i] = newPath; + return; + } + if (ids[i] == 0) { + //add a new entry to the map + ids[i] = id; + if (oldPath != null) + oldPaths[i] = oldPath; + if (newPath != null) + newPaths[i] = newPath; + elementCount++; + // grow if necessary + if (shouldGrow()) + expand(); + return; + } + } + + // if we didn't find a free slot, then try again with the expanded set + expand(); + put(id, oldPath, newPath); +} +/** + * Adds an entry for a node's old path + */ +public void putOldPath(long id, IPath path) { + put(id, path, null); +} +/** + * Adds an entry for a node's old path + */ +public void putNewPath(long id, IPath path) { + put(id, null, path); +} +/** + * The element at the given index has been removed so move + * elements to keep the set properly hashed. + */ +protected void rehashTo(int anIndex) { + + int target = anIndex; + int index = anIndex + 1; + if (index >= ids.length) + index = 0; + long id = ids[index]; + IPath oldPath = oldPaths[index]; + IPath newPath = newPaths[index]; + while (id != 0) { + int hashIndex = hashFor(id) % ids.length; + boolean match; + if (index < target) + match = !(hashIndex > target || hashIndex <= index); + else + match = !(hashIndex > target && hashIndex <= index); + if (match) { + ids[target] = id; + oldPaths[target] = oldPath; + newPaths[target] = newPath; + target = index; + } + index++; + if (index >= ids.length) + index = 0; + id = ids[index]; + oldPath = oldPaths[index]; + newPath = newPaths[index]; + } + ids[target] = 0; + oldPaths[target] = null; + newPaths[target] = null; +} +/** + * Removes the entry from the map with the given node ID. Does + * nothing if no such node exists in the map. + */ +public void remove(long idToRemove) { + int hash = hashFor(idToRemove) % ids.length; + + int indexToRemove = getIndex(idToRemove); + if (indexToRemove < 0) + return; + rehashTo(indexToRemove); + elementCount--; +} +private boolean shouldGrow() { + return elementCount > ids.length * 0.75; +} +/** + * Returns the number of elements currently stored in the map. + */ +public int size() { + return elementCount; +} +} \ No newline at end of file
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/events/ResourceDelta.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/events/ResourceDelta.java index 3fcd04c..62a942c 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/events/ResourceDelta.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/events/ResourceDelta.java
@@ -5,20 +5,19 @@ * All Rights Reserved. */ -import org.eclipse.core.resources.*; -import org.eclipse.core.runtime.*; -import org.eclipse.core.internal.dtree.*; -import org.eclipse.core.internal.events.ResourceDeltaInfo; -import org.eclipse.core.internal.resources.IMarkerSetElement; -import org.eclipse.core.internal.resources.MarkerSet; -import org.eclipse.core.internal.resources.ResourceInfo; -import org.eclipse.core.internal.utils.Assert; import java.util.*; +import org.eclipse.core.internal.resources.*; +import org.eclipse.core.internal.utils.Assert; +import org.eclipse.core.resources.*; +import org.eclipse.core.runtime.*; +/** + * Concrete implementation of the IResourceDelta interface. Each ResourceDelta + * object represents changes that have occurred between two states of the + * resource tree. + */ public class ResourceDelta extends PlatformObject implements IResourceDelta { protected IPath path; - protected IPath movedToPath; - protected IPath movedFromPath; protected ResourceDeltaInfo deltaInfo; protected int status; protected ResourceInfo oldInfo; @@ -45,16 +44,15 @@ * @see IResourceDelta#accept(IResourceDeltaVisitor, boolean) */ public void accept(IResourceDeltaVisitor visitor, boolean includePhantoms) throws CoreException { - int mask = REMOVED | ADDED | CHANGED; - if (includePhantoms) - mask |= REMOVED_PHANTOM | ADDED_PHANTOM; + int mask = includePhantoms ? ALL_WITH_PHANTOMS : REMOVED | ADDED | CHANGED; if ((getKind() | mask) == 0) return; if (!visitor.visit(this)) return; - IResourceDelta[] children = getAffectedChildren(mask); - for (int i = 0; i < children.length; i++) + //recurse over children + for (int i = 0; i < children.length; i++) { children[i].accept(visitor, includePhantoms); + } } /** * Check for marker deltas, and set the appropriate change flag if there are any. @@ -78,35 +76,46 @@ } } /** - * Do the analysis to recover MOVED operations from ADDED/REMOVED/CHANGED operations. + * Delta information on moves and on marker deltas can only be computed after + * the delta has been built. This method fixes up the delta to accurately + * reflect moves (setting MOVED_FROM and MOVED_TO), and marker changes on + * added and removed resources. */ -protected void checkForMove() { - if (path.isRoot()) - return; - int kind = getKind(); - switch (kind) { - case ADDED : - case CHANGED : - long nodeID = newInfo.getNodeId(); - IPath oldPath = (IPath) deltaInfo.getOldNodeIDMap().get(new Long(nodeID)); - if (oldPath != null && !oldPath.equals(path)) { - // Replace change flags by comparing old info with new info, - // Note that we want to retain the kind flag, but replace all other flags - // This is done only for MOVED_FROM, not MOVED_TO, since a resource may be both. - status = (status & KIND_MASK) | (deltaInfo.getComparator().compare(oldInfo, newInfo) & ~KIND_MASK); - status |= MOVED_FROM; - movedFromPath = oldPath; - } +protected void fixMovesAndMarkers() { + NodeIDMap nodeIDMap = deltaInfo.getNodeIDMap(); + if (!path.isRoot() && !nodeIDMap.isEmpty()) { + int kind = getKind(); + switch (kind) { + case ADDED : + case CHANGED : + long nodeID = newInfo.getNodeId(); + IPath oldPath = (IPath) nodeIDMap.getOldPath(nodeID); + if (oldPath != null && !oldPath.equals(path)) { + // Replace change flags by comparing old info with new info, + // Note that we want to retain the kind flag, but replace all other flags + // This is done only for MOVED_FROM, not MOVED_TO, since a resource may be both. + status = (status & KIND_MASK) | (deltaInfo.getComparator().compare(oldInfo, newInfo) & ~KIND_MASK); + status |= MOVED_FROM; + } + } + switch (kind) { + case CHANGED : + case REMOVED : + long nodeID = oldInfo.getNodeId(); + IPath newPath = (IPath) nodeIDMap.getNewPath(nodeID); + if (newPath != null && !newPath.equals(path)) { + status |= MOVED_TO; + } + } } - switch (kind) { - case CHANGED : - case REMOVED : - long nodeID = oldInfo.getNodeId(); - IPath newPath = (IPath) deltaInfo.getNewNodeIDMap().get(new Long(nodeID)); - if (newPath != null && !newPath.equals(path)) { - status |= MOVED_TO; - movedToPath = newPath; - } + + //check for marker deltas -- this is affected by move computation + //so must happen afterwards + checkForMarkerDeltas(); + + //recurse on children + for (int i = 0; i < children.length; i++) { + ((ResourceDelta)children[i]).fixMovesAndMarkers(); } } /** @@ -165,13 +174,19 @@ * @see IResourceDelta#getMovedFromPath */ public IPath getMovedFromPath() { - return movedFromPath; + if ((status & MOVED_FROM) != 0) { + return deltaInfo.getNodeIDMap().getOldPath(newInfo.getNodeId()); + } + return null; } /** * @see IResourceDelta#getMovedToPath */ public IPath getMovedToPath() { - return movedToPath; + if ((status & MOVED_TO) != 0) { + return deltaInfo.getNodeIDMap().getNewPath(oldInfo.getNodeId()); + } + return null; } /** * @see IResourceDelta#getProjectRelativePath @@ -207,6 +222,9 @@ cachedResource = deltaInfo.getWorkspace().newResource(path, info.getType()); return cachedResource; } +/** + * @see IResourceDelta#hasAffectedChildren + */ public boolean hasAffectedChildren() { return children.length > 0; } @@ -227,7 +245,7 @@ * immediate structure suitable for debug purposes. */ public String toDebugString() { - final StringBuffer buffer = new StringBuffer(""); + final StringBuffer buffer = new StringBuffer(); writeDebugString(buffer); return buffer.toString(); } @@ -250,7 +268,11 @@ public String toString() { return "ResourceDelta(" + path + ")"; } - +/** + * Provides a new set of markers for the delta. This is used + * when the delta is reused in cases where the only changes + * are marker changes. + */ public void updateMarkers(Map markers) { deltaInfo.setMarkerDeltas(markers); } @@ -373,4 +395,4 @@ } buffer.append("]"); } -} +} \ No newline at end of file
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/events/ResourceDeltaFactory.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/events/ResourceDeltaFactory.java index f690fd5..106fdde 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/events/ResourceDeltaFactory.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/events/ResourceDeltaFactory.java
@@ -16,9 +16,17 @@ import org.eclipse.core.internal.resources.Workspace; import org.eclipse.core.internal.watson.ElementTree; import java.util.*; - +/** + * This class is used for calculating and building resource delta trees for notification + * and build purposes. + */ public class ResourceDeltaFactory { +/** + * Returns the resource delta representing the changes made between the given old and new trees, + * starting from the given root element. + */ public static ResourceDelta computeDelta(Workspace workspace, ElementTree oldTree, ElementTree newTree, IPath root, boolean notification) { + //compute the underlying delta tree. ResourceComparator comparator = ResourceComparator.getComparator(notification); newTree.immutable(); DeltaDataTree delta = null; @@ -28,21 +36,22 @@ delta = newTree.getDataTree().compareWith(oldTree.getDataTree(), comparator, root); delta = delta.asReverseComparisonTree(comparator); - IPath pathInTree = root; + IPath pathInTree = root.isRoot() ? Path.ROOT : root; IPath pathInDelta = Path.ROOT; - Hashtable oldNodeIDMap = new Hashtable(11); - Hashtable newNodeIDMap = new Hashtable(11); - computeNodeIDMaps(delta, oldNodeIDMap, newNodeIDMap, pathInTree, pathInDelta); - // get the marker deltas for the delta info object....if needed Map allMarkerDeltas = null; if (notification) allMarkerDeltas = workspace.getMarkerManager().getMarkerDeltas(); + + //recursively walk the delta and create a tree of ResourceDelta objects. ResourceDeltaInfo deltaInfo = new ResourceDeltaInfo(workspace, allMarkerDeltas, comparator); - deltaInfo.setNodeMaps(oldNodeIDMap, newNodeIDMap); - ResourceDelta result = createDelta(workspace, delta, deltaInfo, pathInTree, pathInDelta); + + //compute node ID map and fix up moves + deltaInfo.setNodeIDMap(computeNodeIDMap(result, new NodeIDMap())); + result.fixMovesAndMarkers(); + // check all the projects and if they were added and opened then tweek the flags // so the delta reports both. int segmentCount = result.getFullPath().segmentCount(); @@ -68,36 +77,40 @@ checkForOpen((ResourceDelta) children[i], 1); } /** - * Creates the maps from node id to element id for the old and new states. - * Used for recognizing moves. + * Creates the map from node id to element id for the old and new states. + * Used for recognizing moves. Returns the map. */ -protected static void computeNodeIDMaps(DeltaDataTree delta, Hashtable oldNodeIDMap, Hashtable newNodeIDMap, IPath pathInTree, IPath pathInDelta) { - long id = 0; - NodeComparison nodeComparison = (NodeComparison) delta.getData(pathInDelta); - switch (nodeComparison.getUserComparison() & ResourceDelta.KIND_MASK) { - case IResourceDelta.ADDED : - id = ((ResourceInfo) nodeComparison.getNewData()).getNodeId(); - newNodeIDMap.put(new Long(id), pathInTree); - break; - case IResourceDelta.REMOVED : - id = ((ResourceInfo) nodeComparison.getOldData()).getNodeId(); - oldNodeIDMap.put(new Long(id), pathInTree); - break; - case IResourceDelta.CHANGED : - id = ((ResourceInfo) nodeComparison.getOldData()).getNodeId(); - oldNodeIDMap.put(new Long(id), pathInTree); - id = ((ResourceInfo) nodeComparison.getNewData()).getNodeId(); - newNodeIDMap.put(new Long(id), pathInTree); - break; +protected static NodeIDMap computeNodeIDMap(ResourceDelta delta, NodeIDMap nodeIDMap) { + IResourceDelta[] children = delta.children; + for (int i = 0; i < children.length; i++) { + ResourceDelta child = (ResourceDelta)children[i]; + IPath path = child.getFullPath(); + switch (child.getKind()) { + case IResourceDelta.ADDED : + nodeIDMap.putNewPath(child.newInfo.getNodeId(), path); + break; + case IResourceDelta.REMOVED : + nodeIDMap.putOldPath(child.oldInfo.getNodeId(), path); + break; + case IResourceDelta.CHANGED : + long oldID = child.oldInfo.getNodeId(); + long newID = child.newInfo.getNodeId(); + //don't add entries to the map if nothing has changed. + if (oldID != newID) { + nodeIDMap.putOldPath(oldID, path); + nodeIDMap.putNewPath(newID, path); + } + break; + } + //recurse + computeNodeIDMap(child, nodeIDMap); } - - // XXX: look at using one of the visitors to improve performance - // recurse - IPath[] children = delta.getChildren(pathInDelta); - for (int i = 0; i < children.length; ++i) { - computeNodeIDMaps(delta, oldNodeIDMap, newNodeIDMap, pathInTree.append(children[i].lastSegment()), children[i]); - } + return nodeIDMap; } +/** + * Recursively creates the tree of ResourceDelta objects rooted at + * the given path. + */ protected static ResourceDelta createDelta(Workspace workspace, DeltaDataTree delta, ResourceDeltaInfo deltaInfo, IPath pathInTree, IPath pathInDelta) { // create the delta and fill it with information ResourceDelta result = new ResourceDelta(pathInTree, deltaInfo); @@ -114,15 +127,14 @@ result.setOldInfo((ResourceInfo) compare.getOldData()); result.setNewInfo((ResourceInfo) compare.getNewData()); } - - result.checkForMove(); - result.checkForMarkerDeltas(); - // recurse over the children IPath[] childKeys = delta.getChildren(pathInDelta); IResourceDelta[] children = new IResourceDelta[childKeys.length]; - for (int i = 0; i < childKeys.length; i++) - children[i] = createDelta(workspace, delta, deltaInfo, pathInTree.append(childKeys[i].lastSegment()), childKeys[i]); + for (int i = 0; i < childKeys.length; i++) { + //reuse the delta path if tree-relative and delta-relative are the same + IPath newTreePath = pathInTree == pathInDelta ? childKeys[i] : pathInTree.append(childKeys[i].lastSegment()); + children[i] = createDelta(workspace, delta, deltaInfo, newTreePath, childKeys[i]); + } result.setChildren(children); // if this delta has children but no other changes, mark it as changed @@ -133,4 +145,4 @@ // return the delta return result; } -} +} \ No newline at end of file
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/events/ResourceDeltaInfo.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/events/ResourceDeltaInfo.java index dd9a879..3fef0d8 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/events/ResourceDeltaInfo.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/events/ResourceDeltaInfo.java
@@ -5,16 +5,14 @@ * All Rights Reserved. */ -import org.eclipse.core.runtime.*; -import org.eclipse.core.internal.resources.Workspace; -import org.eclipse.core.internal.watson.*; import java.util.Map; +import org.eclipse.core.internal.resources.Workspace; + public class ResourceDeltaInfo { protected Workspace workspace; - protected Map oldNodeIDMap; - protected Map newNodeIDMap; protected Map allMarkerDeltas; + protected NodeIDMap nodeIDMap; protected ResourceComparator comparator; public ResourceDeltaInfo(Workspace workspace, Map markerDeltas, ResourceComparator comparator) { @@ -25,8 +23,6 @@ } public void destroy() { workspace = null; - oldNodeIDMap = null; - newNodeIDMap = null; allMarkerDeltas = null; comparator = null; } @@ -36,11 +32,8 @@ public Map getMarkerDeltas() { return allMarkerDeltas; } -public Map getNewNodeIDMap() { - return newNodeIDMap; -} -public Map getOldNodeIDMap() { - return oldNodeIDMap; +public NodeIDMap getNodeIDMap() { + return nodeIDMap; } public Workspace getWorkspace() { return workspace; @@ -48,8 +41,7 @@ public void setMarkerDeltas(Map value) { allMarkerDeltas = value; } -public void setNodeMaps(Map oldMap, Map newMap) { - oldNodeIDMap = oldMap; - newNodeIDMap = newMap; +public void setNodeIDMap(NodeIDMap map) { + nodeIDMap = map; } }
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/CoreFileSystemLibrary.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/CoreFileSystemLibrary.java index 01ab35d..040266c 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/CoreFileSystemLibrary.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/CoreFileSystemLibrary.java
@@ -15,6 +15,9 @@ /** Indicates whether or not this FS is case sensitive */ private static final boolean caseSensitive = new File("a").compareTo(new File("A")) != 0; + + /** Indicates the default string encoding on this platform */ + private static String defaultEncoding = new java.io.InputStreamReader(new java.io.ByteArrayInputStream(new byte[0])).getEncoding(); /** * The following masks are used to represent the bits @@ -62,8 +65,23 @@ return new File(fileName).lastModified(); } public static long getStat(String fileName) { - if (hasNatives) - return internalGetStat(fileName.getBytes()); + /* Calling String.getBytes() creates a new encoding object and other garbage. + * This can be avoided by calling String.getBytes(String encoding) instead. + */ + if (hasNatives) { + //default encoding is unknown or previously failed, use no-arg getBytes(). + if (defaultEncoding == null) { + return internalGetStat(fileName.getBytes()); + } + //try to use the default encoding + try { + return internalGetStat(fileName.getBytes(defaultEncoding)); + } catch (java.io.UnsupportedEncodingException e) { + //null the default encoding so we don't try it again + defaultEncoding = null; + return internalGetStat(fileName.getBytes()); + } + } // inlined (no native) implementation File target = new File(fileName);
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/FileSystemResourceManager.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/FileSystemResourceManager.java index 1d1dbaa..9996ec5 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/FileSystemResourceManager.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/FileSystemResourceManager.java
@@ -18,6 +18,7 @@ protected Workspace workspace; protected HistoryStore historyStore; protected FileSystemStore localStore; + public FileSystemResourceManager(Workspace workspace) { this.workspace = workspace; localStore = new FileSystemStore(); @@ -115,13 +116,19 @@ case IResource.PROJECT : Project project = (Project) target.getProject(); IProjectDescription description = project.internalGetDescription(); - if (description != null && description.getLocation() != null) + if (description != null && description.getLocation() != null) { return description.getLocation(); + } return getProjectDefaultLocation(project); - default : - IPath location = locationFor(target.getProject()); - location = location.append(target.getFullPath().removeFirstSegments(1)); - return location; + default: + //first get the location of the project (without the project name) + IPath projectLocation = null; + description = ((Project)target.getProject()).internalGetDescription(); + if (description != null && description.getLocation() != null) { + return description.getLocation().append(target.getProjectRelativePath()); + } else { + return Platform.getLocation().append(target.getFullPath()); + } } } public void move(IResource target, IPath destination, boolean keepHistory, IProgressMonitor monitor) throws CoreException { @@ -327,10 +334,9 @@ * has NOT changed since last synchronization, otherwise a CoreException * is thrown. */ -public void write(IFile target, InputStream content, boolean force, boolean keepHistory, boolean append, IProgressMonitor monitor) throws CoreException { +public void write(IFile target, IPath location, InputStream content, boolean force, boolean keepHistory, boolean append, IProgressMonitor monitor) throws CoreException { monitor = Policy.monitorFor(null); try { - IPath location = locationFor(target); java.io.File localFile = location.toFile(); long stat = CoreFileSystemLibrary.getStat(localFile.getAbsolutePath()); if (CoreFileSystemLibrary.isReadOnly(stat)) {
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/FileSystemStore.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/FileSystemStore.java index 0659524..5fbddb6 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/FileSystemStore.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/FileSystemStore.java
@@ -13,6 +13,14 @@ import java.io.*; public class FileSystemStore implements ILocalStoreConstants { + /** + * Singleton buffer created to prevent buffer creations in the + * transferStreams method. Used an optimization, based on the + * assumption that multiple writes won't happen in a given + * instance of FileSystemStore. + */ + private final byte[] buffer = new byte[8192]; + public FileSystemStore() { } public void copy(File source, File destination, int depth, IProgressMonitor monitor) throws CoreException { @@ -194,16 +202,22 @@ /** * This method also closes both streams. */ -public static void transferStreams(InputStream source, OutputStream destination, IProgressMonitor monitor) throws IOException { +public void transferStreams(InputStream source, OutputStream destination, IProgressMonitor monitor) throws IOException { monitor = Policy.monitorFor(monitor); try { - byte[] buffer = new byte[8192]; - while (true) { - int bytesRead = source.read(buffer); - if (bytesRead == -1) - break; - destination.write(buffer, 0, bytesRead); - monitor.worked(1); + /* + * Note: although synchronizing on the buffer is thread-safe, + * it may result in slower performance in the future if we want + * to allow concurrent writes. + */ + synchronized (buffer) { + while (true) { + int bytesRead = source.read(buffer); + if (bytesRead == -1) + break; + destination.write(buffer, 0, bytesRead); + monitor.worked(1); + } } } finally { try {
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/RefreshLocalVisitor.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/RefreshLocalVisitor.java index 2d41faf..0a152a4 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/RefreshLocalVisitor.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/RefreshLocalVisitor.java
@@ -129,8 +129,7 @@ return RL_NOT_IN_SYNC; } IPath location = node.getLocalLocation(); - String name = target.getLocalManager().getLocalName(location.toFile()); - if (!target.getName().equals(name)) + if (!target.getName().equals(node.getLocalName())) return RL_IN_SYNC; } createResource(node, target);
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/UnifiedTree.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/UnifiedTree.java index 61268b4..e3ca95c 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/UnifiedTree.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/UnifiedTree.java
@@ -30,10 +30,10 @@ protected Sorter sorter; /** special node to mark the beginning of a level in the tree */ - protected static final UnifiedTreeNode levelMarker = new UnifiedTreeNode(null, null, 0, false); + protected static final UnifiedTreeNode levelMarker = new UnifiedTreeNode(null, null, 0, null, false); /** special node to mark the separation of a node's children */ - protected static final UnifiedTreeNode childrenMarker = new UnifiedTreeNode(null, null, 0, false); + protected static final UnifiedTreeNode childrenMarker = new UnifiedTreeNode(null, null, 0, null, false); public UnifiedTree() { } /** @@ -94,7 +94,7 @@ if (comp == 0) { // resource exists in workspace and file system long stat = CoreFileSystemLibrary.getStat(getLocalLocation(target).toOSString()); - child = new UnifiedTreeNode(this, target, stat, true); + child = new UnifiedTreeNode(this, target, stat, localName, true); index++; next = true; } else @@ -105,7 +105,7 @@ next = false; } else { // resource exists only in the workspace - child = new UnifiedTreeNode(this, target, 0, true); + child = new UnifiedTreeNode(this, target, 0, null, true); next = true; } addChildToTree(node, child); @@ -152,7 +152,7 @@ } protected void addRootToQueue() throws CoreException { long stat = CoreFileSystemLibrary.getStat(rootLocalLocation.toOSString()); - UnifiedTreeNode node = new UnifiedTreeNode(this, root, stat, root.exists()); + UnifiedTreeNode node = new UnifiedTreeNode(this, root, stat, rootLocalLocation.lastSegment(), root.exists()); if (!node.existsInFileSystem() && !node.existsInWorkspace()) return; addElementToQueue(node); @@ -160,11 +160,11 @@ protected UnifiedTreeNode createChildNodeFromFileSystem(UnifiedTreeNode node, IPath parentLocalLocation, String childName) throws CoreException { IPath childPath = node.getResource().getFullPath().append(childName); IPath childLocalLocation = parentLocalLocation.append(childName); - return createNode(childPath, childLocalLocation); + return createNode(childPath, childLocalLocation, childName); } -protected UnifiedTreeNode createNode(IPath path, IPath location) throws CoreException { +protected UnifiedTreeNode createNode(IPath path, IPath location, String localName) throws CoreException { long stat = CoreFileSystemLibrary.getStat(location.toOSString()); - UnifiedTreeNode node = new UnifiedTreeNode(this, null, stat, false); + UnifiedTreeNode node = new UnifiedTreeNode(this, null, stat, localName, false); int type = node.isFile() ? IResource.FILE : IResource.FOLDER; IResource target = getWorkspace().newResource(path, type); node.setResource(target);
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/UnifiedTreeNode.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/UnifiedTreeNode.java index 4751c37..bf365fb 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/UnifiedTreeNode.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/UnifiedTreeNode.java
@@ -16,11 +16,14 @@ protected UnifiedTree tree; protected long stat; protected boolean existsWorkspace; -public UnifiedTreeNode(UnifiedTree tree, IResource resource, long stat, boolean existsWorkspace) { + //the name of the resource in the local file system, if any + protected String localName; +public UnifiedTreeNode(UnifiedTree tree, IResource resource, long stat, String localName, boolean existsWorkspace) { this.tree = tree; setResource(resource); this.stat = stat; this.existsWorkspace = existsWorkspace; + this.localName = localName; } public boolean existsInFileSystem() { return isFile() || isFolder(); @@ -46,6 +49,13 @@ public IPath getLocalLocation() { return tree.getLocalLocation(resource); } +/** + * Gets the name of this node in the local filesystem. + * @return Returns a String + */ +public String getLocalName() { + return localName; +} public IResource getResource() { return resource; }
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Container.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Container.java index b2fc761..47050c7 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Container.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Container.java
@@ -98,29 +98,25 @@ * @see IContainer#getFile */ public IFile getFile(String name) { - return (IFile) getResource(new Path (name), FILE); + return (IFile)workspace.newResource(getFullPath().append(name), FILE); } /** * @see IContainer#getFile */ public IFile getFile(IPath path) { - return (IFile) getResource(path, FILE); + return (IFile)workspace.newResource(getFullPath().append(path), FILE); } /** * @see IContainer#getFolder */ public IFolder getFolder(String name) { - return (IFolder) getResource(new Path(name), FOLDER); + return (IFolder)workspace.newResource(getFullPath().append(name), FOLDER); } /** * @see IContainer#getFolder */ public IFolder getFolder(IPath path) { - return (IFolder) getResource(path, FOLDER); -} -private IResource getResource(IPath path, int type) { - IPath target = getFullPath().append(path); - return workspace.newResource(target, type); + return (IFolder)workspace.newResource(getFullPath().append(path), FOLDER); } public boolean isLocal(int flags, int depth) { if (!super.isLocal(flags, depth))
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/File.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/File.java index f0b48f1..18bd355 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/File.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/File.java
@@ -32,7 +32,7 @@ checkAccessible(getFlags(info)); workspace.beginOperation(true); - internalSetContents(content, force, keepHistory, true, Policy.subMonitorFor(monitor, Policy.opWork)); + internalSetContents(content, getLocalManager().locationFor(this), force, keepHistory, true, Policy.subMonitorFor(monitor, Policy.opWork)); } catch (OperationCanceledException e) { workspace.getWorkManager().operationCanceled(); throw e; @@ -106,7 +106,7 @@ boolean local = content != null; if (local) { try { - internalSetContents(content, force, false, false, Policy.subMonitorFor(monitor, Policy.opWork * 40 / 100)); + internalSetContents(content, location, force, false, false, Policy.subMonitorFor(monitor, Policy.opWork * 40 / 100)); } catch (CoreException e) { // a problem happened creating the file on disk, so delete from the workspace workspace.deleteResource(this); @@ -152,10 +152,10 @@ public int getType() { return FILE; } -protected void internalSetContents(InputStream content, boolean force, boolean keepHistory, boolean append, IProgressMonitor monitor) throws CoreException { +protected void internalSetContents(InputStream content, IPath location, boolean force, boolean keepHistory, boolean append, IProgressMonitor monitor) throws CoreException { if (content == null) content = new ByteArrayInputStream(new byte[0]); - getLocalManager().write(this, content, force, keepHistory, append, monitor); + getLocalManager().write(this, location, content, force, keepHistory, append, monitor); ResourceInfo info = getResourceInfo(false, true); info.incrementContentId(); workspace.updateModificationStamp(info); @@ -174,7 +174,7 @@ checkAccessible(getFlags(info)); workspace.beginOperation(true); - internalSetContents(content, force, keepHistory, false, Policy.subMonitorFor(monitor, Policy.opWork)); + internalSetContents(content, getLocalManager().locationFor(this), force, keepHistory, false, Policy.subMonitorFor(monitor, Policy.opWork)); } catch (OperationCanceledException e) { workspace.getWorkManager().operationCanceled(); throw e;
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerAttributeMap.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerAttributeMap.java new file mode 100644 index 0000000..cf9ff6e --- /dev/null +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerAttributeMap.java Binary files differ
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerInfo.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerInfo.java index dab5ceb..f701bad 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerInfo.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerInfo.java
@@ -5,10 +5,9 @@ * All Rights Reserved. */ -import org.eclipse.core.resources.IMarker; -import org.eclipse.core.resources.IResource; +import java.util.Map; + import org.eclipse.core.internal.utils.Assert; -import java.util.*; public class MarkerInfo implements IMarkerSetElement { @@ -22,7 +21,7 @@ protected String type = null; /** The store of attributes for this marker. */ - protected HashMap attributes = null; + protected Map attributes = null; public Object getAttribute(String attributeName) { return attributes == null ? null : attributes.get(attributeName); } @@ -38,7 +37,7 @@ public Map getAttributes(boolean makeCopy) { if (attributes == null) return null; - return makeCopy ? (Map) attributes.clone() : attributes; + return makeCopy ? new MarkerAttributeMap(attributes) : attributes; } public long getId() { return id; @@ -47,7 +46,9 @@ return type; } public void internalSetAttributes(Map map) { - attributes = (HashMap) map; + //the cast effectively acts as an assertion to make sure + //the right kind of map is being used + attributes = (MarkerAttributeMap)map; } /** * Returns whether the given object is a valid attribute value. @@ -61,7 +62,7 @@ if (value == null) return; else { - attributes = new HashMap(1); + attributes = new MarkerAttributeMap(); attributes.put(attributeName, value); } } else { @@ -83,7 +84,7 @@ if (map == null) attributes = null; else - attributes = new HashMap(map); + attributes = new MarkerAttributeMap(map); } public void setId(long value) { id = value;
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerManager.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerManager.java index c577378..872279b 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerManager.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerManager.java
@@ -98,13 +98,16 @@ * Removes markers of the specified type from the given resource. */ private void basicRemoveMarkers(IResource resource, String type, boolean includeSubtypes) { - ResourceInfo info = workspace.getResourceInfo(resource.getFullPath(), false, true); + //don't get a modifiable info until we know we need to modify it. + ResourceInfo info = workspace.getResourceInfo(resource.getFullPath(), false, false); MarkerSet markers = info.getMarkers(); if (markers == null) return; IMarkerSetElement[] matching; if (type == null) { // if the type is null, all markers are to be removed. + //now we need to crack open the tree + info = workspace.getResourceInfo(resource.getFullPath(), false, true); info.setMarkers(null); matching = markers.elements(); } else { @@ -112,6 +115,9 @@ // if none match, there is nothing to remove if (matching.length == 0) return; + //now we need to crack open the tree + info = workspace.getResourceInfo(resource.getFullPath(), false, true); + // remove all the matching markers and also the whole // set if there are no remaining markers markers.removeAll(matching);
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerReader_1.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerReader_1.java index 475f645..a8be6ab 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerReader_1.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerReader_1.java
@@ -5,15 +5,18 @@ * All Rights Reserved. */ +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.internal.utils.Assert; import org.eclipse.core.resources.IMarkerDelta; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; -import org.eclipse.core.internal.utils.Assert; -import java.io.DataInputStream; -import java.io.EOFException; -import java.io.IOException; -import java.util.*; /** * This class is used to read markers from disk. This is for version 1. @@ -89,7 +92,7 @@ int attributesSize = input.readInt(); if (attributesSize == 0) return null; - Map result = new HashMap(attributesSize); + Map result = new MarkerAttributeMap(attributesSize); for (int j = 0; j < attributesSize; j++) { String key = input.readUTF(); int type = input.readInt();
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerReader_2.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerReader_2.java index 17ea0b7..d8c7fc2 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerReader_2.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerReader_2.java
@@ -90,7 +90,7 @@ int attributesSize = input.readShort(); if (attributesSize == 0) return null; - Map result = new HashMap(attributesSize); + Map result = new MarkerAttributeMap(attributesSize); for (int j = 0; j < attributesSize; j++) { String key = input.readUTF(); byte type = input.readByte();
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerSnapshotReader_1.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerSnapshotReader_1.java index 86502cc..b278205 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerSnapshotReader_1.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerSnapshotReader_1.java
@@ -70,7 +70,7 @@ short attributesSize = input.readShort(); if (attributesSize == 0) return null; - Map result = new HashMap(attributesSize); + Map result = new MarkerAttributeMap(attributesSize); for (int j = 0; j < attributesSize; j++) { String key = input.readUTF(); byte type = input.readByte();
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Resource.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Resource.java index e001317..1d71f22 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Resource.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Resource.java
@@ -338,20 +338,7 @@ * the phantom boolean is true. */ public int countResources(int depth, boolean phantom) throws CoreException { - ResourceInfo info = getResourceInfo(phantom, false); - if (!exists(getFlags(info), false)) - return 0; - int total = 1; - if (getType() == FILE || depth == DEPTH_ZERO) - return total; - if (depth == DEPTH_ONE) - depth = DEPTH_ZERO; - IResource[] children = ((Container) this).members(phantom); - for (int i = 0; i < children.length; i++) { - Resource child = (Resource) children[i]; - total += child.countResources(depth, phantom); - } - return total; + return workspace.countResources(path, depth, phantom); } /** * @see IResource @@ -960,9 +947,7 @@ } } /** - * Helper method that considers case insensitive file systems. The parameter - * renaming is used for the last segment. It was added to handle cases when we are only - * renaming a resource. + * Helper method that considers case insensitive file systems. */ protected void checkDoesNotExist() throws CoreException { // should consider getting the ResourceInfo as a paramenter to reduce tree lookups @@ -977,25 +962,31 @@ throw new ResourceException(IResourceStatus.RESOURCE_EXISTS, variant.getFullPath(), message, null); } /** - * Helper method for case insensitive file systems. + * Helper method for case insensitive file systems. Returns + * an existing resource whose path differs only in case from + * the given path, or null if no such resource exists. */ public IResource findExistingResourceVariant(IPath target) { IPath result = Path.ROOT; - String[] segments = target.segments(); - for (int i = 0; i < segments.length; i++) { - IPath[] children = workspace.tree.getChildren(result); - String name = findVariant(segments[i], children); + int segmentCount = target.segmentCount(); + for (int i = 0; i < segmentCount; i++) { + String[] childNames = workspace.tree.getNamesOfChildren(result); + String name = findVariant(target.segment(i), childNames); if (name == null) return null; result = result.append(name); } return workspace.getRoot().findMember(result); } -private String findVariant(String target, IPath[] list) { +/** + * Searches for a variant of the given target in the list, + * that differs only in case. Returns the variant from + * the list if one is found, otherwise returns null. + */ +private String findVariant(String target, String[] list) { for (int i = 0; i < list.length; i++) { - String variant = list[i].lastSegment(); - if (target.equalsIgnoreCase(variant)) - return variant; + if (target.equalsIgnoreCase(list[i])) + return list[i]; } return null; }
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ResourceInfo.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ResourceInfo.java index ffc6427..815fd28 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ResourceInfo.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ResourceInfo.java
@@ -282,22 +282,17 @@ public synchronized void setSyncInfo(QualifiedName id, byte[] value) { // thread safety: (Concurrency001) if (value == null) { + //delete sync info if (syncInfo == null) return; - HashMap temp = (HashMap) syncInfo.clone(); - temp.remove(id); - if (temp.isEmpty()) + syncInfo.remove(id); + if (syncInfo.isEmpty()) syncInfo = null; - else - syncInfo = temp; } else { - HashMap temp = syncInfo; - if (temp == null) - temp = new HashMap(5); - else - temp = (HashMap) syncInfo.clone(); - temp.put(id, value.clone()); - syncInfo = temp; + //add sync info + if (syncInfo == null) + syncInfo = new HashMap(5); + syncInfo.put(id, value.clone()); } } /**
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ResourceStatus.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ResourceStatus.java index 1077417..50a3df6 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ResourceStatus.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ResourceStatus.java
@@ -5,13 +5,21 @@ * All Rights Reserved. */ -import org.eclipse.core.runtime.*; +import org.eclipse.core.internal.utils.Policy; +import org.eclipse.core.resources.IResourceStatus; import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; /** * */ public class ResourceStatus extends Status { IPath path; + + /** Singleton status indicating success */ + public static final ResourceStatus OK_STATUS = new ResourceStatus(IResourceStatus.OK, Policy.bind("ok")); + public ResourceStatus(int type, int code, IPath path, String message, Throwable exception) { super(type, ResourcesPlugin.PI_RESOURCES, code, message, exception); this.path = path;
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Workspace.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Workspace.java index c01e432..77cd1af 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Workspace.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Workspace.java
@@ -390,7 +390,7 @@ monitor.beginTask(message, totalWork); Assert.isLegal(resources != null); if (resources.length == 0) - return new ResourceStatus(IResourceStatus.OK, Policy.bind("ok")); + return ResourceStatus.OK_STATUS; // to avoid concurrent changes to this array resources = (IResource[]) resources.clone(); IPath parentPath = null; @@ -441,7 +441,7 @@ } if (status.matches(IStatus.ERROR)) throw new ResourceException(status); - return status.isOK() ? new ResourceStatus(IResourceStatus.OK, Policy.bind("ok")) : (IStatus) status; + return status.isOK() ? ResourceStatus.OK_STATUS : (IStatus) status; } finally { monitor.done(); } @@ -494,6 +494,27 @@ copyTree(child, childPath, depth, phantom); } } +/** + * Returns the number of resources in a subtree of the resource tree. + * @param path The subtree to count resources for + * @param depth The depth of the subtree to count + * @param phantom If true, phantoms are included, otherwise they are ignored. + */ +public int countResources(IPath root, int depth, boolean phantom) { + ResourceInfo info = getResourceInfo(root, phantom, false); + if (info == null) + return 0; + int total = 1; + if (info.getType() == IResource.FILE || depth == IResource.DEPTH_ZERO) + return total; + if (depth == IResource.DEPTH_ONE) + depth = IResource.DEPTH_ZERO; + IPath[] children = tree.getChildren(root); + for (int i = 0; i < children.length; i++) { + total += countResources(children[i], depth, phantom); + } + return total; +} /* * Creates the given resource in the tree and returns the new resource info object. * If phantom is true, the created element is marked as a phantom. @@ -842,6 +863,8 @@ if (path.equals(Path.ROOT)) return rootInfo; ResourceInfo result = null; + if (!tree.includes(path)) + return null; if (mutable) result = (ResourceInfo) tree.openElementData(path); else @@ -930,7 +953,7 @@ monitor.beginTask(message, totalWork); Assert.isLegal(resources != null); if (resources.length == 0) - return new ResourceStatus(IResourceStatus.OK, Policy.bind("ok")); + return ResourceStatus.OK_STATUS; resources = (IResource[]) resources.clone(); // to avoid concurrent changes to this array IPath parentPath = null; message = Policy.bind("resources.moveProblem"); @@ -986,7 +1009,7 @@ } if (status.matches(IStatus.ERROR)) throw new ResourceException(status); - return status.isOK() ? (IStatus) new ResourceStatus(IResourceStatus.OK, Policy.bind("ok")) : (IStatus) status; + return status.isOK() ? (IStatus) ResourceStatus.OK_STATUS : (IStatus) status; } finally { monitor.done(); } @@ -1369,7 +1392,7 @@ return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message); } - return new ResourceStatus(IResourceStatus.OK, Policy.bind("ok")); + return ResourceStatus.OK_STATUS; } /** * @see IWorkspace#validatePath @@ -1421,15 +1444,14 @@ if (!status.isOK()) return status; int fileFolderType = type &= ~IResource.PROJECT; - String[] segments = testPath.segments(); + int segmentCount = testPath.segmentCount(); // ignore first segment (the project) - for (int i = 1; i < segments.length; i++) { - String segment = segments[i]; - status = validateName(segment, fileFolderType); + for (int i = 1; i < segmentCount; i++) { + status = validateName(testPath.segment(i), fileFolderType); if (!status.isOK()) return status; } - return new ResourceStatus(IResourceStatus.OK, Policy.bind("ok")); + return ResourceStatus.OK_STATUS; } else { message = Policy.bind("resources.resourcePath"); return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message);
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/WorkspaceRoot.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/WorkspaceRoot.java index ba0bd08..587164e 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/WorkspaceRoot.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/WorkspaceRoot.java
@@ -5,12 +5,21 @@ * All Rights Reserved. */ -import org.eclipse.core.resources.*; -import org.eclipse.core.runtime.*; +import java.util.HashMap; + import org.eclipse.core.internal.utils.Assert; import org.eclipse.core.internal.utils.Policy; +import org.eclipse.core.resources.*; +import org.eclipse.core.runtime.*; public class WorkspaceRoot extends Container implements IWorkspaceRoot { + /** + * As an optimization, we store a table of project handles + * that have been requested from this root. This maps project + * name strings to project handles. + */ + private HashMap projectTable = new HashMap(10); + protected WorkspaceRoot(IPath path, Workspace container) { super(path, container); Assert.isTrue(path.equals(Path.ROOT)); @@ -105,10 +114,16 @@ * @see IResource#getProject */ public IProject getProject(String name) { - Path path = new Path(name); - String message = "resources.projectPath"; - Assert.isLegal(path.segmentCount() == ICoreConstants.PROJECT_SEGMENT_LENGTH, message); - return new Project(Path.ROOT.append(name), workspace); + //first check our project cache + Project result = (Project)projectTable.get(name); + if (result == null) { + IPath path = Path.ROOT.append(name); + String message = "resources.projectPath"; + Assert.isLegal(path.segmentCount() == ICoreConstants.PROJECT_SEGMENT_LENGTH, message); + result = new Project(path, workspace); + projectTable.put(name, result); + } + return result; } /** * @see IResource#getProjectRelativePath
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/watson/ElementTree.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/watson/ElementTree.java index b231d4c..8be82d2 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/watson/ElementTree.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/watson/ElementTree.java
@@ -547,6 +547,23 @@ return new ElementSubtree(elementNode); } /** + * Returns the names of the children of the specified element. + * The specified element must exist in the tree. + * If the specified element is null, returns the root element path. + */ +public String[] getNamesOfChildren(IPath key) { + try { + if (key == null) { + return new String[] {""}; + } else { + return tree.getNamesOfChildren(key); + } + } catch (ObjectNotFoundException e) { + elementNotFound(key); + return null; // can't get here + } +} +/** * Returns the parent tree, or <code>null</code> if there is no parent. */ public ElementTree getParent() {
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IProject.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IProject.java index 126fd5b..021e443 100644 --- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IProject.java +++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IProject.java
@@ -22,7 +22,7 @@ * <li>A project can be open or closed; a closed project is * passive and has a minimal in-memory footprint.</li> * <li>A project can carry references to other projects.</li> - * <li>A project can one or more project natures.</li> + * <li>A project can have one or more project natures.</li> * </ul> * </p> * <p>