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>