Index: core/test/unit/org/openstreetmap/josm/io/BoundingBoxDownloaderTest.java
===================================================================
--- core/test/unit/org/openstreetmap/josm/io/BoundingBoxDownloaderTest.java	(revision 485)
+++ core/test/unit/org/openstreetmap/josm/io/BoundingBoxDownloaderTest.java	(working copy)
@@ -89,7 +89,7 @@
 	@Test public void parseOsmEmpty() throws Exception {
 	    in = "<osm version='0.4'></osm>";
 	    parseOsm();
-	    assertEquals(0, ds.nodes.size());
+	    assertEquals(0, ds.nrNodes());
 	    assertEquals(0, ds.segments.size());
 	    assertEquals(0, ds.ways.size());
     }
@@ -97,7 +97,7 @@
 	@Test public void parseOsmSimpleNode() throws Exception {
 	    in = "<osm version='0.4'><node id='123' lat='12' lon='23'/></osm>";
 	    parseOsm();
-	    assertEquals(1, ds.nodes.size());
+	    assertEquals(1, ds.nrNodes());
 	    Node node = ds.nodes.iterator().next();
 		assertEquals(123, node.id);
 	    assertEquals(12.0, node.coor.lat());
@@ -116,7 +116,7 @@
 	    
 	    parseOsm();
 	    
-	    assertEquals(1, ds.nodes.size());
+	    assertEquals(1, ds.nrNodes());
 	    assertEquals(1, ds.segments.size());
 	    assertEquals(1, ds.ways.size());
 	    
Index: core/test/unit/org/openstreetmap/josm/data/osm/visitor/MergeVisitorTest.java
===================================================================
--- core/test/unit/org/openstreetmap/josm/data/osm/visitor/MergeVisitorTest.java	(revision 485)
+++ core/test/unit/org/openstreetmap/josm/data/osm/visitor/MergeVisitorTest.java	(working copy)
@@ -123,8 +123,8 @@
 		v.visit(n);
 		v.fixReferences();
 		assertEquals(0,v.conflicts.size());
-		assertTrue(ds.nodes.contains(n));
-		assertEquals(2, ds.nodes.size());
+		assertTrue(ds.containsNode(n));
+		assertEquals(2, ds.nrNodes());
 	}
 
 	/**
Index: core/test/unit/org/openstreetmap/josm/data/osm/visitor/AllNodesVisitorTest.java
===================================================================
--- core/test/unit/org/openstreetmap/josm/data/osm/visitor/AllNodesVisitorTest.java	(revision 485)
+++ core/test/unit/org/openstreetmap/josm/data/osm/visitor/AllNodesVisitorTest.java	(working copy)
@@ -22,26 +22,26 @@
 	public void testVisitNode() {
 		Node node = createNode();
 		node.visit(v);
-		assertEquals(1, v.nodes.size());
+		assertEquals(1, v.nrNodes());
 		assertSame(node, v.nodes.iterator().next());
 	}
 
 	public void testVisitSegment() {
 		Segment s = createSegment();
 		s.visit(v);
-		assertEquals(2, v.nodes.size());
-		assertTrue(v.nodes.contains(s.from));
-		assertTrue(v.nodes.contains(s.to));
+		assertEquals(2, v.nrNodes());
+		assertTrue(v.containsNode(s.from));
+		assertTrue(v.containsNode(s.to));
 	}
 
 	public void testVisitWay() {
 		Way w = createWay(createSegment());
 		w.visit(v);
 		int numberOfNodes = 2*w.segments.size();
-		assertEquals(numberOfNodes, v.nodes.size());
+		assertEquals(numberOfNodes, v.nrNodes());
 		for (Segment s : w.segments) {
-			assertTrue(v.nodes.contains(s.from));
-			assertTrue(v.nodes.contains(s.to));
+			assertTrue(v.containsNode(s.from));
+			assertTrue(v.containsNode(s.to));
 		}
 	}
 
@@ -51,7 +51,7 @@
 		all.add(createSegment());
 		Collection<Node> nodes = AllNodesVisitor.getAllNodes(all);
 		
-		assertEquals(3, nodes.size());
+		assertEquals(3, nrNodes());
 	}
 
 }
Index: core/test/unit/org/openstreetmap/josm/data/osm/visitor/AddVisitorTest.java
===================================================================
--- core/test/unit/org/openstreetmap/josm/data/osm/visitor/AddVisitorTest.java	(revision 485)
+++ core/test/unit/org/openstreetmap/josm/data/osm/visitor/AddVisitorTest.java	(working copy)
@@ -26,7 +26,7 @@
 	
 	public void testVisitNode() {
 		createNode(23).visit(v);
-		assertEquals(1, ds.nodes.size());
+		assertEquals(1, ds.nrNodes());
 		assertEquals(23, ds.nodes.iterator().next().id);
 	}
 
Index: core/test/functional/NodeTest.java
===================================================================
--- core/test/functional/NodeTest.java	(revision 485)
+++ core/test/functional/NodeTest.java	(working copy)
@@ -16,7 +16,7 @@
 
 		key("n");
 		click(100,500);
-		assertEquals(1, Main.ds.nodes.size());
+		assertEquals(1, Main.ds.nrNodes());
 		assertEquals(1, Main.ds.getSelected().size());
 		assertEquals(Main.ds.nodes.iterator().next(), Main.ds.getSelected().iterator().next());
 		
@@ -30,7 +30,7 @@
 		drag(200,500,100,500);
 		key("n","shift-n");
 		click(150,500);
-		assertEquals(3, Main.ds.nodes.size());
+		assertEquals(3, Main.ds.nrNodes());
 		assertEquals(2, Main.ds.segments.size());
 		assertEquals(1, Main.ds.getSelected().size());
 		
@@ -90,7 +90,7 @@
 
 	private void checkSegments(Node n) {
 		assertEquals(3, Main.ds.segments.size());
-		assertEquals(4, Main.ds.nodes.size());
+		assertEquals(4, Main.ds.nrNodes());
 
 		Iterator<Segment> segIt = Main.ds.segments.iterator();
 		Segment s1 = segIt.next();
Index: core/src/org/openstreetmap/josm/actions/OpenAction.java
===================================================================
--- core/src/org/openstreetmap/josm/actions/OpenAction.java	(revision 485)
+++ core/src/org/openstreetmap/josm/actions/OpenAction.java	(working copy)
@@ -97,7 +97,6 @@
             if (ml.data.size() > 0) {
             	Main.main.addLayer(ml);
             }
-
 		} else {
 			throw new IllegalStateException();
 		}
Index: core/src/org/openstreetmap/josm/actions/CopyAction.java
===================================================================
--- core/src/org/openstreetmap/josm/actions/CopyAction.java	(revision 485)
+++ core/src/org/openstreetmap/josm/actions/CopyAction.java	(working copy)
@@ -63,16 +63,16 @@
 				if (map.containsKey(w)) { return; }
 				Way wnew = new Way();
 				wnew.cloneFrom(w);
-				wnew.nodes.clear();
+				wnew.clearAllNodes();
 				List<Node> nodes = new ArrayList<Node>();
-				for (Node n : w.nodes) {
+				for (Node n : w.allNodes()) {
 					if (! map.containsKey(n)) {
 						n.visit(this);
 					}
 					nodes.add((Node)map.get(n));
 				}
-				wnew.nodes.clear();
-				wnew.nodes.addAll(nodes);
+				wnew.clearAllNodes();
+				wnew.addNodes(nodes);
 				pasteBuffer.addPrimitive(wnew);
 			}
 			public void visit(Relation e) {
Index: core/src/org/openstreetmap/josm/actions/SplitWayAction.java
===================================================================
--- core/src/org/openstreetmap/josm/actions/SplitWayAction.java	(revision 485)
+++ core/src/org/openstreetmap/josm/actions/SplitWayAction.java	(working copy)
@@ -21,10 +21,7 @@
 import javax.swing.JOptionPane;
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.command.AddCommand;
-import org.openstreetmap.josm.command.ChangeCommand;
-import org.openstreetmap.josm.command.Command;
-import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.command.*;
 import org.openstreetmap.josm.data.SelectionChangedListener;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.Relation;
@@ -96,7 +93,7 @@
 			for (Node n : selectedNodes) {
 				for (Way w : Main.ds.ways) {
 					if (w.deleted || w.incomplete) continue;
-					for (Node wn : w.nodes) {
+					for (Node wn : w.allNodes()) {
 						if (n.equals(wn)) {
 							Integer old = wayOccurenceCounter.get(w);
 							wayOccurenceCounter.put(w, (old == null) ? 1 : old+1);
@@ -131,7 +128,7 @@
 		} else if (selectedWay != null && selectedNodes != null) {
 
 			HashSet<Node> nds = new HashSet<Node>(selectedNodes);
-			for (Node n : selectedWay.nodes) {
+			for (Node n : selectedWay.allNodes()) {
 				nds.remove(n);
 			}
 			if (!nds.isEmpty()) {
@@ -185,7 +182,7 @@
 		List<Node> currentWayChunk = new ArrayList<Node>();
 		wayChunks.add(currentWayChunk);
 
-		Iterator<Node> it = selectedWay.nodes.iterator();
+		Iterator<Node> it = selectedWay.allNodes().iterator();
 		while (it.hasNext()) {
 			Node currentNode = it.next();
 			boolean atEndOfWay = currentWayChunk.isEmpty() || !it.hasNext();
@@ -216,6 +213,8 @@
 			JOptionPane.showMessageDialog(Main.parent, tr("The way cannot be split at the selected nodes. (Hint: Select nodes in the middle of the way.)"));
 			return;
 		}
+		System.out.println("wayChunks.size(): " + wayChunks.size());
+		System.out.println("way id: " + selectedWay.id);
 
 		// build a list of commands, and also a new selection list
 		Collection<Command> commandList = new ArrayList<Command>(wayChunks.size());
@@ -223,12 +222,16 @@
 		
 		Iterator<List<Node>> chunkIt = wayChunks.iterator();
 		
-		// First, change the original way
-		Way changedWay = new Way(selectedWay);
-		changedWay.nodes.clear();
-		changedWay.nodes.addAll(chunkIt.next());
-		commandList.add(new ChangeCommand(selectedWay, changedWay));
 		newSelection.add(selectedWay);
+		// First, pull the first chunk off, it stays in
+		// the "original" way.
+		List<Node> firstChunk = chunkIt.next();
+		for (Node n : selectedWay.allNodes()) {
+			if (firstChunk.contains(n))
+				continue;
+			System.out.println("removing node: " + n + " from way: " + selectedWay.id);
+			commandList.add(new ReplaceSubObjectCommand(selectedWay, n, null));
+		}
 
 		// Second, create new ways
 		while (chunkIt.hasNext()) {
@@ -237,8 +240,10 @@
 				wayToAdd.keys = new HashMap<String, String>(selectedWay.keys);
 				wayToAdd.checkTagged();
 			}
-			wayToAdd.nodes.addAll(chunkIt.next());
+			List<Node> chunk = chunkIt.next();
+			wayToAdd.addNodes(chunk);
 			commandList.add(new AddCommand(wayToAdd));
+			System.out.println("wayToAdd: " + wayToAdd);
 			newSelection.add(wayToAdd);
 		}
 
Index: core/src/org/openstreetmap/josm/actions/JoinNodeWayAction.java
===================================================================
--- core/src/org/openstreetmap/josm/actions/JoinNodeWayAction.java	(revision 485)
+++ core/src/org/openstreetmap/josm/actions/JoinNodeWayAction.java	(working copy)
@@ -54,8 +54,8 @@
 				insertPoints.put(ws.way, is);
 			}
 
-			if (ws.way.nodes.get(ws.lowerIndex) != node
-					&& ws.way.nodes.get(ws.lowerIndex+1) != node) {
+			if (ws.way.nodeNr(ws.lowerIndex) != node
+					&& ws.way.nodeNr(ws.lowerIndex+1) != node) {
 				is.add(ws.lowerIndex);
 			}
 		}
@@ -66,7 +66,7 @@
 			Way wnew = new Way(w);
 			List<Integer> is = insertPoint.getValue();
 			pruneSuccsAndReverse(is);
-			for (int i : is) wnew.nodes.add(i+1, node);
+			for (int i : is) wnew.addNodeNr(i+1, node);
 			cmds.add(new ChangeCommand(w, wnew));
 		}
 
Index: core/src/org/openstreetmap/josm/actions/search/SearchCompiler.java
===================================================================
--- core/src/org/openstreetmap/josm/actions/search/SearchCompiler.java	(revision 485)
+++ core/src/org/openstreetmap/josm/actions/search/SearchCompiler.java	(working copy)
@@ -1,4 +1,3 @@
-// License: GPL. Copyright 2007 by Immanuel Scholz and others
 package org.openstreetmap.josm.actions.search;
 
 import java.io.IOException;
Index: core/src/org/openstreetmap/josm/actions/ReverseWayAction.java
===================================================================
--- core/src/org/openstreetmap/josm/actions/ReverseWayAction.java	(revision 485)
+++ core/src/org/openstreetmap/josm/actions/ReverseWayAction.java	(working copy)
@@ -49,7 +49,7 @@
     	Collection<Command> c = new LinkedList<Command>();
     	for (Way w : sel) {
     		Way wnew = new Way(w);
-			Collections.reverse(wnew.nodes);
+			w.reverseNodes();
     		c.add(new ChangeCommand(w, wnew));
     	}
     	Main.main.undoRedo.add(new SequenceCommand(tr("Reverse ways"), c));
Index: core/src/org/openstreetmap/josm/actions/mapmode/DeleteAction.java
===================================================================
--- core/src/org/openstreetmap/josm/actions/mapmode/DeleteAction.java	(revision 485)
+++ core/src/org/openstreetmap/josm/actions/mapmode/DeleteAction.java	(working copy)
@@ -23,6 +23,8 @@
 import org.openstreetmap.josm.command.ChangeCommand;
 import org.openstreetmap.josm.command.Command;
 import org.openstreetmap.josm.command.DeleteCommand;
+import org.openstreetmap.josm.command.ConditionalDeleteCommand;
+import org.openstreetmap.josm.command.ReplaceSubObjectCommand;
 import org.openstreetmap.josm.command.SequenceCommand;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
@@ -182,7 +184,7 @@
 		if (alsoDeleteNodesInWay) {
 			for (OsmPrimitive osm : del) {
 				if (osm instanceof Way) {
-					for (Node n : ((Way)osm).nodes) {
+					for (Node n : ((Way)osm).allNodes()) {
 						if (!n.tagged) {
 							CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(Main.ds, false);
 							n.visit(v);
@@ -201,9 +203,7 @@
 			osm.visit(v);
 			for (OsmPrimitive ref : v.data) {
 				if (del.contains(ref)) continue;
-				if (ref instanceof Way) {
-					waysToBeChanged.add((Way) ref);
-				} else if (ref instanceof Relation) {
+				if (ref instanceof Relation) {
 					JOptionPane.showMessageDialog(Main.parent,
 						tr("Cannot delete: Selection is used by relation"));
 					return null;
@@ -214,10 +214,20 @@
 		}
 
 		Collection<Command> cmds = new LinkedList<Command>();
+		HashSet<Way> checkForDel = new HashSet<Way>();
+		for (OsmPrimitive osm : del) {
+			if (!(osm instanceof Node))
+				continue;
+			Node n = (Node)osm;
+			for (Way w : n.waysUsing()) {
+				cmds.add(new ReplaceSubObjectCommand(w, n, null));
+				checkForDel.add(w);
+			}
+		}
 		for (Way w : waysToBeChanged) {
 			Way wnew = new Way(w);
-			wnew.nodes.removeAll(del);
-			if (wnew.nodes.size() < 2) {
+			wnew.removeNodes(del);
+			if (wnew.nrNodes() < 2) {
 				del.add(w);
 
 				CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(Main.ds, false);
@@ -237,6 +247,8 @@
 		}
 
 		if (!del.isEmpty()) cmds.add(new DeleteCommand(del));
+		if (!checkForDel.isEmpty())
+			cmds.add(new ConditionalDeleteCommand(checkForDel));
 
 		return new SequenceCommand(tr("Delete"), cmds);
 	}
@@ -245,26 +257,27 @@
 		List<Node> n1 = new ArrayList<Node>(),
 			n2 = new ArrayList<Node>();
 
-		n1.addAll(ws.way.nodes.subList(0, ws.lowerIndex + 1));
-		n2.addAll(ws.way.nodes.subList(ws.lowerIndex + 1, ws.way.nodes.size()));
+		List<Node> ws_way_an = ws.way.allNodes();
+		n1.addAll(ws_way_an.subList(0, ws.lowerIndex + 1));
+		n2.addAll(ws_way_an.subList(ws.lowerIndex + 1, ws.way.nrNodes()));
 
 		if (n1.size() < 2 && n2.size() < 2) {
 			return new DeleteCommand(Collections.singleton(ws.way));
 		}
 		
 		Way wnew = new Way(ws.way);
-		wnew.nodes.clear();
+		wnew.clearAllNodes();
 
 		if (n1.size() < 2) {
-			wnew.nodes.addAll(n2);
+			wnew.addNodes(n2);
 			return new ChangeCommand(ws.way, wnew);
 		} else if (n2.size() < 2) {
-			wnew.nodes.addAll(n1);
+			wnew.addNodes(n1);
 			return new ChangeCommand(ws.way, wnew);
 		} else {
 			Collection<Command> cmds = new LinkedList<Command>();
 
-			wnew.nodes.addAll(n1);
+			wnew.addNodes(n1);
 			cmds.add(new ChangeCommand(ws.way, wnew));
 
 			Way wnew2 = new Way();
@@ -272,7 +285,7 @@
 				wnew2.keys = new HashMap<String, String>(wnew.keys);
 				wnew2.checkTagged();
 			}
-			wnew2.nodes.addAll(n2);
+			wnew2.addNodes(n2);
 			cmds.add(new AddCommand(wnew2));
 
 			return new SequenceCommand(tr("Split way segment"), cmds);
Index: core/src/org/openstreetmap/josm/actions/mapmode/DrawAction.java
===================================================================
--- core/src/org/openstreetmap/josm/actions/mapmode/DrawAction.java	(revision 485)
+++ core/src/org/openstreetmap/josm/actions/mapmode/DrawAction.java	(working copy)
@@ -145,8 +145,8 @@
 
 					pruneSuccsAndReverse(is);
 					for (int i : is) segSet.add(
-						Pair.sort(new Pair<Node,Node>(w.nodes.get(i), w.nodes.get(i+1))));
-					for (int i : is) wnew.nodes.add(i + 1, n);
+						Pair.sort(new Pair<Node,Node>(w.nodeNr(i), w.nodeNr(i+1))));
+					for (int i : is) wnew.addNodeNr(i + 1, n);
 
 					cmds.add(new ChangeCommand(insertPoint.getKey(), wnew));
 					replacedWays.add(insertPoint.getKey());
@@ -187,13 +187,13 @@
 			
 			if (selectedNode == null) {
 				if (selectedWay == null) return;
-				if (lastUsedNode == selectedWay.nodes.get(0) || lastUsedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1)) {
+				if (lastUsedNode == selectedWay.nodeNr(0) || lastUsedNode == selectedWay.nodeNr(selectedWay.nrNodes()-1)) {
 					n0 = lastUsedNode;
 				}
 			} else if (selectedWay == null) {
 				n0 = selectedNode;
 			} else {
-				if (selectedNode == selectedWay.nodes.get(0) || selectedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1)) {
+				if (selectedNode == selectedWay.nodeNr(0) || selectedNode == selectedWay.nodeNr(selectedWay.nrNodes()-1)) {
 					n0 = selectedNode;
 				}			
 			}
@@ -209,7 +209,7 @@
 			Way way = alt ? null : (selectedWay != null) ? selectedWay : getWayForNode(n0);
 			if (way == null) {
 				way = new Way();
-				way.nodes.add(n0);
+				way.addNode(n0);
 				cmds.add(new AddCommand(way));
 			} else {
 				int i;
@@ -222,10 +222,10 @@
 				}
 			}
 
-			if (way.nodes.get(way.nodes.size() - 1) == n0) {
-				way.nodes.add(n);
+			if (way.nodeNr(way.nrNodes() - 1) == n0) {
+				way.addNode(n);
 			} else {
-				way.nodes.add(0, n);
+				way.addNodeNr(0, n);
 			}
 
 			extendedWay = true;
@@ -265,9 +265,9 @@
 	public static Way getWayForNode(Node n) {
 		Way way = null;
 		for (Way w : Main.ds.ways) {
-			if (w.deleted || w.incomplete || w.nodes.size() < 1) continue;
-			Node firstNode = w.nodes.get(0);
-			Node lastNode = w.nodes.get(w.nodes.size() - 1);
+			if (w.deleted || w.incomplete || w.nrNodes() < 1) continue;
+			Node firstNode = w.nodeNr(0);
+			Node lastNode = w.nodeNr(w.nrNodes() - 1);
 			if ((firstNode == n || lastNode == n) && (firstNode != lastNode)) {
 				if (way != null)
 					return null;
Index: core/src/org/openstreetmap/josm/actions/PasteAction.java
===================================================================
--- core/src/org/openstreetmap/josm/actions/PasteAction.java	(revision 485)
+++ core/src/org/openstreetmap/josm/actions/PasteAction.java	(working copy)
@@ -68,11 +68,11 @@
 			wnew.id = 0;
 			/* make sure we reference the new nodes corresponding to the old ones */
 			List<Node> nodes = new ArrayList<Node>();
-			for (Node n : w.nodes) {
+			for (Node n : w.allNodes()) {
 				nodes.add((Node)map.get(n));
 			}
-			wnew.nodes.clear();
-			wnew.nodes.addAll(nodes);
+			wnew.clearAllNodes();
+			wnew.addNodes(nodes);
 			map.put(w, wnew);
 		}
 		for (Relation r : pasteBuffer.relations) {
Index: core/src/org/openstreetmap/josm/actions/DiskAccessAction.java
===================================================================
--- core/src/org/openstreetmap/josm/actions/DiskAccessAction.java	(revision 485)
+++ core/src/org/openstreetmap/josm/actions/DiskAccessAction.java	(working copy)
@@ -30,8 +30,9 @@
 		fc.setAcceptAllFileFilterUsed(true);
 	
 		int answer = open ? fc.showOpenDialog(Main.parent) : fc.showSaveDialog(Main.parent);
-		if (answer != JFileChooser.APPROVE_OPTION)
-			return null;
+		if (answer != JFileChooser.APPROVE_OPTION) {
+			return null;
+		}
 		
 		if (!fc.getCurrentDirectory().getAbsolutePath().equals(curDir))
 			Main.pref.put("lastDirectory", fc.getCurrentDirectory().getAbsolutePath());
Index: core/src/org/openstreetmap/josm/actions/MergeNodesAction.java
===================================================================
--- core/src/org/openstreetmap/josm/actions/MergeNodesAction.java	(revision 485)
+++ core/src/org/openstreetmap/josm/actions/MergeNodesAction.java	(working copy)
@@ -169,7 +169,37 @@
 		Map<String, JComboBox> components = new HashMap<String, JComboBox>();
 		JPanel p = new JPanel(new GridBagLayout());
 		for (Entry<String, Set<String>> e : props.entrySet()) {
-			if (e.getValue().size() > 1) {
+			if (e.getKey().equals("tiger:tlid")) {
+				TreeMap tlids = new TreeMap();
+				for (String tlid_list: e.getValue()) {
+					for (String tlid_str: tlid_list.split(":")) {
+						Integer tlid = new Integer(tlid_str);
+						tlids.put(tlid, 1);
+					}
+				}
+				String combined = "";
+				for (Object _tlid : tlids.keySet()) {
+					Integer tlid = (Integer)_tlid;
+					if (combined.length() > 0)
+						combined += ":";
+					combined += tlid;
+				}
+				newNode.put(e.getKey(), combined);
+			} else if (e.getKey().indexOf("tiger:") != -1) {
+				TreeMap attrs = new TreeMap();
+				for (String attr_list: e.getValue()) {
+					for (String attr_str: attr_list.split(":")) {
+						attrs.put(attr_str, 1);
+					}
+				}
+				String combined = "";
+				for (Object attr : attrs.keySet()) {
+					if (combined.length() > 0)
+						combined += ":";
+					combined += attr;
+				}
+				newNode.put(e.getKey(), combined);
+			} else if (e.getValue().size() > 1) {
 				JComboBox c = new JComboBox(e.getValue().toArray());
 				c.setEditable(true);
 				p.add(new JLabel(e.getKey()), GBC.std());
@@ -194,11 +224,11 @@
 		Collection<OsmPrimitive> del = new HashSet<OsmPrimitive>();
 
 		for (Way w : Main.ds.ways) {
-			if (w.deleted || w.incomplete || w.nodes.size() < 1) continue;
+			if (w.deleted || w.incomplete || w.nrNodes() < 1) continue;
 			boolean modify = false;
 			for (Node sn : allNodes) {
 				if (sn == dest) continue;
-				if (w.nodes.contains(sn)) {
+				if (w.containsNode(sn)) {
 					modify = true;
 				}
 			}
@@ -206,8 +236,8 @@
 			// OK - this way contains one or more nodes to change
 			ArrayList<Node> nn = new ArrayList<Node>();
 			Node lastNode = null;
-			for (int i = 0; i < w.nodes.size(); i++) {
-				Node pushNode = w.nodes.get(i);
+			for (int i = 0; i < w.nrNodes(); i++) {
+				Node pushNode = w.nodeNr(i);
 				if (allNodes.contains(pushNode)) {
 					pushNode = dest;
 				}
@@ -229,8 +259,8 @@
 				del.add(w);
 			} else {
 				Way newWay = new Way(w);
-				newWay.nodes.clear();
-				newWay.nodes.addAll(nn);
+				newWay.clearAllNodes();
+				newWay.addNodes(nn);
 				cmds.add(new ChangeCommand(w, newWay));
 			}
 		}
Index: core/src/org/openstreetmap/josm/actions/SaveActionBase.java
===================================================================
--- core/src/org/openstreetmap/josm/actions/SaveActionBase.java	(revision 485)
+++ core/src/org/openstreetmap/josm/actions/SaveActionBase.java	(working copy)
@@ -135,7 +135,7 @@
 
 	public static void save(File file, OsmDataLayer layer) {
 		File tmpFile = null;
-		try {
+	    try {
 			if (ExtensionFileFilter.filters[ExtensionFileFilter.GPX].acceptName(file.getPath())) {
 				GpxExportAction.exportGpx(file, layer);
 			} else if (ExtensionFileFilter.filters[ExtensionFileFilter.OSM].acceptName(file.getPath())) {
Index: core/src/org/openstreetmap/josm/actions/CombineWayAction.java
===================================================================
--- core/src/org/openstreetmap/josm/actions/CombineWayAction.java	(revision 485)
+++ core/src/org/openstreetmap/josm/actions/CombineWayAction.java	(working copy)
@@ -150,8 +150,8 @@
 		}
 
 		Way newWay = new Way(selectedWays.get(0));
-		newWay.nodes.clear();
-		newWay.nodes.addAll(nodeList);
+		newWay.clearAllNodes();
+		newWay.addNodes(nodeList);
 
 		// display conflict dialog
 		Map<String, JComboBox> components = new HashMap<String, JComboBox>();
@@ -218,24 +218,9 @@
 		//  4. Profit!
 
 		HashSet<Pair<Node,Node>> chunkSet = new HashSet<Pair<Node,Node>>();
-		for (Way w : ways) {
-			if (w.nodes.size() == 0) continue;
-			Node lastN = null;
-			for (Node n : w.nodes) {
-				if (lastN == null) {
-					lastN = n;
-					continue;
-				}
+		for (Way w : ways)
+			chunkSet.addAll(w.getNodePairs(ignoreDirection));
 
-				Pair<Node,Node> np = new Pair<Node,Node>(lastN, n);
-				if (ignoreDirection) {
-					Pair.sort(np);
-				}
-				chunkSet.add(np);
-
-				lastN = n;
-			}
-		}
 		LinkedList<Pair<Node,Node>> chunks = new LinkedList<Pair<Node,Node>>(chunkSet);
 
 		if (chunks.isEmpty()) {
Index: core/src/org/openstreetmap/josm/tools/ImageProvider.java
===================================================================
--- core/src/org/openstreetmap/josm/tools/ImageProvider.java	(revision 485)
+++ core/src/org/openstreetmap/josm/tools/ImageProvider.java	(working copy)
@@ -38,7 +38,7 @@
 	/**
 	 * The icon cache
 	 */
-	private static Map<URL, Image> cache = new HashMap<URL, Image>();
+	private static Map<String, Image> cache = new HashMap<String, Image>();
 
 	/**
 	 * Add here all ClassLoader whose ressource should be searched.
@@ -74,16 +74,22 @@
 		else if (!subdir.equals(""))
 			subdir += "/";
 		String ext = name.indexOf('.') != -1 ? "" : ".png";
+		String full_name = subdir+name+ext;
 
-		URL path = getImageUrl(subdir+name+ext);
-		if (path == null)
-			return null;
-		
-		Image img = cache.get(path);
+		Image img = cache.get(full_name);
 		if (img == null) {
+			// getImageUrl() does a ton of "stat()" calls and gets expensive
+			// and redundant when you have a whole ton of objects.  So,
+			// index the cache by the name of the icon we're looking for
+			// and don't bother to create a URL unless we're actually
+			// creating the image.
+			URL path = getImageUrl(full_name);
+			if (path == null)
+				return null;
 			img = Toolkit.getDefaultToolkit().createImage(path);
-			cache.put(path, img);
+			cache.put(full_name, img);
 		}
+	
 		return new ImageIcon(img);
 	}
 
Index: core/src/org/openstreetmap/josm/gui/NavigatableComponent.java
===================================================================
--- core/src/org/openstreetmap/josm/gui/NavigatableComponent.java	(revision 485)
+++ core/src/org/openstreetmap/josm/gui/NavigatableComponent.java	(working copy)
@@ -160,7 +160,7 @@
 			if (w.deleted || w.incomplete) continue;
 			Node lastN = null;
 			int i = -2;
-			for (Node n : w.nodes) {
+			for (Node n : w.allNodes()) {
 				i++;
 				if (n.deleted || n.incomplete) continue;
 				if (lastN == null) {
@@ -274,7 +274,7 @@
 			for (Way w : Main.ds.ways) {
 			if (w.deleted || w.incomplete) continue;
 			Node lastN = null;
-			for (Node n : w.nodes) {
+			for (Node n : w.allNodes()) {
 				if (n.deleted || n.incomplete) continue;
 				if (lastN == null) {
 					lastN = n;
Index: core/src/org/openstreetmap/josm/gui/dialogs/ConflictDialog.java
===================================================================
--- core/src/org/openstreetmap/josm/gui/dialogs/ConflictDialog.java	(revision 485)
+++ core/src/org/openstreetmap/josm/gui/dialogs/ConflictDialog.java	(working copy)
@@ -159,7 +159,7 @@
 			}
 			public void visit(Way w) {
 				Node lastN = null;
-				for (Node n : w.nodes) {
+				for (Node n : w.allNodes()) {
 					if (lastN == null) {
 						lastN = n;
 						continue;
Index: core/src/org/openstreetmap/josm/gui/SelectionManager.java
===================================================================
--- core/src/org/openstreetmap/josm/gui/SelectionManager.java	(revision 485)
+++ core/src/org/openstreetmap/josm/gui/SelectionManager.java	(working copy)
@@ -302,10 +302,10 @@
 			
 			// ways
 			for (Way w : Main.ds.ways) {
-				if (w.deleted || w.nodes.isEmpty() || w.incomplete)
+				if (w.deleted || w.emptyNodes() || w.incomplete)
 						continue;
 				if (alt) {
-					for (Node n : w.nodes) {
+					for (Node n : w.allNodes()) {
 						if (!n.incomplete && r.contains(nc.getPoint(n.eastNorth))) {
 							selection.add(w);
 							break;
@@ -313,7 +313,7 @@
 					}
 				} else {
 					boolean allIn = true;
-					for (Node n : w.nodes) {
+					for (Node n : w.allNodes()) {
 						if (!n.incomplete && !r.contains(nc.getPoint(n.eastNorth))) {
 							allIn = false;
 							break;
Index: core/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java
===================================================================
--- core/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java	(revision 485)
+++ core/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java	(working copy)
@@ -86,7 +86,7 @@
 				for (GpsPoint p : c) {
 					Node n = new Node(p.latlon);
 					ds.nodes.add(n);
-					w.nodes.add(n);
+					w.addNode(n);
 				}
 				ds.ways.add(w);
 			}
Index: core/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
===================================================================
--- core/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 485)
+++ core/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(working copy)
@@ -339,7 +339,7 @@
 			GpxTrack trk = new GpxTrack();
 			gpxData.tracks.add(trk);
 			ArrayList<WayPoint> trkseg = null;
-			for (Node n : w.nodes) {
+			for (Node n : w.allNodes()) {
 				if (n.incomplete || n.deleted) {
 					trkseg = null;
 					continue;
Index: core/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
===================================================================
--- core/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 485)
+++ core/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(working copy)
@@ -400,7 +400,7 @@
 					for (WayPoint p : segment) {
 						Node n = new Node(p.latlon);
 						ds.nodes.add(n);
-						w.nodes.add(n);
+						w.addNode(n);
 					}
 					ds.ways.add(w);
 				}
Index: core/src/org/openstreetmap/josm/io/OsmReader.java
===================================================================
--- core/src/org/openstreetmap/josm/io/OsmReader.java	(revision 485)
+++ core/src/org/openstreetmap/josm/io/OsmReader.java	(working copy)
@@ -290,7 +290,7 @@
 					failed = true;
 					break;
 				}
-				w.nodes.add(n);
+				w.addNode(n);
 			}
 			if (failed) continue;
 			e.getKey().copyTo(w);
Index: core/src/org/openstreetmap/josm/io/OsmServerWriter.java
===================================================================
--- core/src/org/openstreetmap/josm/io/OsmServerWriter.java	(revision 485)
+++ core/src/org/openstreetmap/josm/io/OsmServerWriter.java	(working copy)
@@ -12,6 +12,8 @@
 import java.net.HttpURLConnection;
 import java.net.URL;
 import java.net.UnknownHostException;
+import java.net.SocketTimeoutException;
+import java.sql.Time;
 import java.util.Collection;
 import java.util.LinkedList;
 
@@ -56,6 +58,11 @@
 	 * Send the dataset to the server. Ask the user first and does nothing if he
 	 * does not want to send the data.
 	 */
+	static int MSECS_PER_SECOND = 1000;
+	static int SECONDS_PER_MINUTE = 60;
+	static int MINUTES_PER_HOUR = 60;
+	static int HOURS_PER_DAY = 24;
+	static int MSECS_PER_MINUTE = MSECS_PER_SECOND * SECONDS_PER_MINUTE;
 	public void uploadOsm(Collection<OsmPrimitive> list) throws SAXException {
 		processed = new LinkedList<OsmPrimitive>();
 		initAuthentication();
@@ -65,13 +72,31 @@
 
 		NameVisitor v = new NameVisitor();
 		try {
+			long start = System.currentTimeMillis();
+			Time startTime = new Time(start);
 			for (OsmPrimitive osm : list) {
 				if (cancel)
 					return;
+				long now = System.currentTimeMillis();
+				long elapsed = now - start;
+				if (elapsed == 0)
+					elapsed = 1;
+				int progress = Main.pleaseWaitDlg.progress.getValue();
+				float uploads_per_ms = (float)progress / elapsed;
+				float uploads_left = list.size() - progress;
+				int ms_left = (int)(uploads_left / uploads_per_ms);
+				int minutes_left = ms_left / MSECS_PER_MINUTE;
+				int seconds_left = (ms_left / MSECS_PER_SECOND) % SECONDS_PER_MINUTE ;
+				String time_left_str = Integer.toString(minutes_left) + ":";
+				if (seconds_left < 10)
+					time_left_str += "0";
+				time_left_str += Integer.toString(seconds_left);
 				osm.visit(v);
-				Main.pleaseWaitDlg.currentAction.setText(tr("Upload {0} {1} ({2})...", tr(v.className), v.name, osm.id));
+				Main.pleaseWaitDlg.currentAction.setText(tr("Upload {0} {1} (id: {2}) {3}% {4}/{5} ({6} left)...",
+					tr(v.className), v.name, osm.id, 100.0*progress/list.size(), progress, list.size(), time_left_str));
 				osm.visit(this);
 				Main.pleaseWaitDlg.progress.setValue(Main.pleaseWaitDlg.progress.getValue()+1);
+				Main.pleaseWaitDlg.progress.setValue(progress+1);
 			}
 		} catch (RuntimeException e) {
 			e.printStackTrace();
@@ -149,23 +174,26 @@
 	 * @param addBody <code>true</code>, if the whole primitive body should be added.
 	 * 		<code>false</code>, if only the id is encoded.
 	 */
-	private void sendRequest(String requestMethod, String urlSuffix,
-			OsmPrimitive osm, boolean addBody) {
+	private void __sendRequest(String requestMethod, String urlSuffix,
+			OsmPrimitive osm, boolean addBody, int retries) {
 		try {
+			if (cancel)
+				return; // assume cancel
 			String version = Main.pref.get("osm-server.version", "0.5");
 			URL url = new URL(
 					Main.pref.get("osm-server.url") +
 					"/" + version +
 					"/" + urlSuffix + 
 					"/" + (osm.id==0 ? "create" : osm.id));
-			System.out.println("upload to: "+url);
+			System.out.print("upload to: "+url+ "..." );
 			activeConnection = (HttpURLConnection)url.openConnection();
 			activeConnection.setConnectTimeout(15000);
 			activeConnection.setRequestMethod(requestMethod);
 			if (addBody)
 				activeConnection.setDoOutput(true);
+
 			activeConnection.connect();
-
+			System.out.println("connected");
 			if (addBody) {
 				OutputStream out = activeConnection.getOutputStream();
 				OsmWriter.output(out, new OsmWriter.Single(osm, true));
@@ -180,25 +208,45 @@
 			activeConnection.disconnect();
 			if (retCode == 410 && requestMethod.equals("DELETE"))
 				return; // everything fine.. was already deleted.
-			if (retCode != 200) {
-				// Look for a detailed error message from the server
-				if (activeConnection.getHeaderField("Error") != null)
-					retMsg += "\n" + activeConnection.getHeaderField("Error");
+			if (retCode != 200 && retCode != 412) {
+				if (retries >= 0) {
+					retries--;
+					System.out.print("backing off for 10 seconds...");
+					Thread.sleep(10000);
+					System.out.println("retrying ("+retries+" left)");
+					__sendRequest(requestMethod, urlSuffix, osm, addBody, retries);
+				} else { 
+					// Look for a detailed error message from the server
+					if (activeConnection.getHeaderField("Error") != null)
+						retMsg += "\n" + activeConnection.getHeaderField("Error");
 
-				// Report our error
-				ByteArrayOutputStream o = new ByteArrayOutputStream();
-				OsmWriter.output(o, new OsmWriter.Single(osm, true));
-				System.out.println(new String(o.toByteArray(), "UTF-8").toString());
-				throw new RuntimeException(retCode+" "+retMsg);
+					// Report our error
+					ByteArrayOutputStream o = new ByteArrayOutputStream();
+					OsmWriter.output(o, new OsmWriter.Single(osm, true));
+					System.out.println(new String(o.toByteArray(), "UTF-8").toString());
+					throw new RuntimeException(retCode+" "+retMsg);
+				}
 			}
 		} catch (UnknownHostException e) {
 			throw new RuntimeException(tr("Unknown host")+": "+e.getMessage(), e);
+		} catch(SocketTimeoutException e) {
+			System.out.println(" timed out, retries left: " + retries);
+			if (cancel)
+				return; // assume cancel
+			if (retries-- > 0)
+				__sendRequest(requestMethod, urlSuffix, osm, addBody, retries);
+			else
+				throw new RuntimeException(e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
 		} catch (Exception e) {
 			if (cancel)
 				return; // assume cancel
 			if (e instanceof RuntimeException)
 				throw (RuntimeException)e;
-			throw new RuntimeException(e.getMessage(), e);
+			throw new RuntimeException(e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
 		}
 	}
+	private void sendRequest(String requestMethod, String urlSuffix,
+			OsmPrimitive osm, boolean addBody) {
+		__sendRequest(requestMethod, urlSuffix, osm, addBody, 10);
+	}
 }
Index: core/src/org/openstreetmap/josm/io/OsmWriter.java
===================================================================
--- core/src/org/openstreetmap/josm/io/OsmWriter.java	(revision 485)
+++ core/src/org/openstreetmap/josm/io/OsmWriter.java	(working copy)
@@ -135,7 +135,7 @@
 		if (w.incomplete) return;
 		addCommon(w, "way");
 		out.println(">");
-		for (Node n : w.nodes)
+		for (Node n : w.allNodes())
 			out.println("    <nd ref='"+getUsedId(n)+"' />");
 		addTags(w, "way", false);
 	}
Index: core/src/org/openstreetmap/josm/data/osm/visitor/MergeVisitor.java
===================================================================
--- core/src/org/openstreetmap/josm/data/osm/visitor/MergeVisitor.java	(revision 485)
+++ core/src/org/openstreetmap/josm/data/osm/visitor/MergeVisitor.java	(working copy)
@@ -106,15 +106,15 @@
 	private void fixWay(Way w) {
 	    boolean replacedSomething = false;
 	    LinkedList<Node> newNodes = new LinkedList<Node>();
-	    for (Node n : w.nodes) {
+	    for (Node n : w.allNodes()) {
 	    	Node otherN = (Node) merged.get(n);
 	    	newNodes.add(otherN == null ? n : otherN);
 	    	if (otherN != null)
 	    		replacedSomething = true;
 	    }
 	    if (replacedSomething) {
-	    	w.nodes.clear();
-	    	w.nodes.addAll(newNodes);
+	    	w.clearAllNodes();
+	    	w.addNodes(newNodes);
 		}
     }
 
@@ -166,10 +166,11 @@
 	}
 
 	private static boolean realMatch(Way w1, Way w2) {
-		if (w1.nodes.size() != w2.nodes.size())
+		if (w1.nrNodes() != w2.nrNodes())
 			return false;
-		Iterator<Node> it = w1.nodes.iterator();
-		for (Node n : w2.nodes)
+		Collection<Node> an = w1.allNodes();
+		Iterator<Node> it = an.iterator();
+		for (Node n : an)
 			if (!match(n, it.next()))
 				return false;
 		return true;
Index: core/src/org/openstreetmap/josm/data/osm/visitor/NameVisitor.java
===================================================================
--- core/src/org/openstreetmap/josm/data/osm/visitor/NameVisitor.java	(revision 485)
+++ core/src/org/openstreetmap/josm/data/osm/visitor/NameVisitor.java	(working copy)
@@ -72,7 +72,7 @@
 		if (name == null) name = w.get("ref");
 		if (name == null) {
 			String what = (w.get("highway") != null) ? "highway " : (w.get("railway") != null) ? "railway " : (w.get("waterway") != null) ? "waterway " : "";
-			int nodesNo = new HashSet<Node>(w.nodes).size();
+			int nodesNo = new HashSet<Node>(w.allNodes()).size();
 			name = what + trn("{0} node", "{0} nodes", nodesNo, nodesNo);
 		}
 		addId(w);
Index: core/src/org/openstreetmap/josm/data/osm/visitor/AllNodesVisitor.java
===================================================================
--- core/src/org/openstreetmap/josm/data/osm/visitor/AllNodesVisitor.java	(revision 485)
+++ core/src/org/openstreetmap/josm/data/osm/visitor/AllNodesVisitor.java	(working copy)
@@ -33,8 +33,7 @@
 	 * Ways have their way nodes.
 	 */
 	public void visit(Way w) {
-		for (Node n : w.nodes)
-			visit(n);
+		w.visitNodes(this);
 	}
 
 	/**
Index: core/src/org/openstreetmap/josm/data/osm/visitor/SimplePaintVisitor.java
===================================================================
--- core/src/org/openstreetmap/josm/data/osm/visitor/SimplePaintVisitor.java	(revision 485)
+++ core/src/org/openstreetmap/josm/data/osm/visitor/SimplePaintVisitor.java	(working copy)
@@ -136,7 +136,7 @@
 
 		int orderNumber = 0;
 		Node lastN = null;
-		for (Node n : w.nodes) {
+		for (Node n : w.allNodes()) {
 			if (lastN == null) {
 				lastN = n;
 				continue;
Index: core/src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java
===================================================================
--- core/src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java	(revision 485)
+++ core/src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java	(working copy)
@@ -22,8 +22,7 @@
 	}
 
 	public void visit(Way w) {
-		for (Node n : w.nodes)
-			visit(n);
+		w.visitNodes(this);
 	}
 
 	public void visit(Relation e) {
Index: core/src/org/openstreetmap/josm/data/osm/visitor/CollectBackReferencesVisitor.java
===================================================================
--- core/src/org/openstreetmap/josm/data/osm/visitor/CollectBackReferencesVisitor.java	(revision 485)
+++ core/src/org/openstreetmap/josm/data/osm/visitor/CollectBackReferencesVisitor.java	(working copy)
@@ -46,7 +46,7 @@
 	public void visit(Node n) {
 		for (Way w : ds.ways) {
 			if (w.deleted || w.incomplete) continue;
-			for (Node n2 : w.nodes) {
+			for (Node n2 : w.allNodes()) {
 				if (n == n2) {
 					data.add(w);
 					if (indirectRefs) {
Index: core/src/org/openstreetmap/josm/data/osm/Way.java
===================================================================
--- core/src/org/openstreetmap/josm/data/osm/Way.java	(revision 485)
+++ core/src/org/openstreetmap/josm/data/osm/Way.java	(working copy)
@@ -1,11 +1,16 @@
 // License: GPL. Copyright 2007 by Immanuel Scholz and others
 package org.openstreetmap.josm.data.osm;
+//package org.openstreetmap.josm.data.osm.visitor;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.HashSet;
+import java.util.Collection;
+import java.util.Collections;
 
 import org.openstreetmap.josm.data.osm.visitor.Visitor;
+import org.openstreetmap.josm.tools.Pair;
 
 /**
  * One full way, consisting of a list of way nodes.
@@ -17,8 +22,96 @@
 	/**
 	 * All way nodes in this way
 	 */
-	public final List<Node> nodes = new ArrayList<Node>();
+	private final List<Node> nodes = new ArrayList<Node>();
 
+	public void addNodes(List<Node> _nodes) {
+			nodes.addAll(_nodes);
+			for (Node n : _nodes)
+					n.addTo(this);
+	}
+
+	public void addNodeNr(int nr, Node n) {
+			nodes.add(nr, n);
+			n.addTo(this);
+	}
+
+	public void addNode(Node n) {
+			nodes.add(n);
+			n.addTo(this);
+	}
+
+	public boolean removeNode(Node n) {
+			if (!nodes.remove(n))
+					return false;
+			n.removeFrom(this);
+			return true;
+	}
+
+	public void removeNodes(Collection<OsmPrimitive> del_nodes) {
+			nodes.removeAll(del_nodes);
+			for (OsmPrimitive _n : del_nodes) {
+					Node n = (Node)_n;
+					n.removeFrom(this);
+			}
+	}
+
+	public void reverseNodes() {
+			Collections.reverse(this.nodes);
+	}
+
+	public int nrNodes() {
+			return nodes.size();
+	}
+
+	public boolean containsNode(Node n) {
+			return nodes.contains(n);
+	}
+
+	public boolean emptyNodes() {
+			return nodes.isEmpty();
+	}
+
+	public void clearAllNodes() {
+			for (Node n : nodes)
+					n.removeFrom(this);
+			nodes.clear();
+	}
+
+	public Node nodeNr(int i) {
+			return nodes.get(i);
+	}
+
+	public int lookupNodeNr(Node n) {
+			return nodes.indexOf(n);
+	}
+
+	public List<Node> allNodes() {
+			return new ArrayList<Node>(nodes);
+	}
+
+	public void visitNodes(Visitor v) {
+		for (Node n : this.nodes)
+			v.visit(n);
+	}
+
+	public HashSet<Pair<Node,Node>> getNodePairs(boolean sort) {
+			HashSet<Pair<Node,Node>> chunkSet = new HashSet<Pair<Node,Node>>();
+			Node lastN = null;
+			for (Node n : this.nodes) {
+    	        if (lastN == null) {
+    	    	    lastN = n;
+            		continue;
+	    	    }
+				Pair<Node,Node> np = new Pair<Node,Node>(lastN, n);
+            	if (sort) {
+            		Pair.sort(np);
+            	}
+            	chunkSet.add(np);
+	            lastN = n;
+			}
+			return chunkSet;
+	}
+
 	@Override public void visit(Visitor visitor) {
 		visitor.visit(this);
 	}
@@ -67,4 +160,53 @@
 	public boolean isIncomplete() {
 		return incomplete;
 	}
+
+	@Override public Object replace(OsmPrimitive old, OsmPrimitive update, Object cookie) {
+			boolean result;
+			//stem.out.println("Way("+this.hashCode()+")::replace(" +old+", "+update+")");
+			// a replacement with null is a delete
+			if (update == null && old != null && (old instanceof Node)) {
+				int index = this.nodes.indexOf((Node)old);
+				if (index == -1)
+					return null;
+				this.removeNode((Node)old); 
+				//stem.out.println("Way()::replace() result1: " + result);
+				return new Integer(index);
+			}
+			// a replacement _of_ null is an addition
+			if (old == null && update != null && (update instanceof Node)) {
+				if (cookie == null) {
+					this.addNode((Node)update);
+				} else {
+					int index = ((Integer)cookie).intValue();
+					// -1 means we didn't do anything
+					if (index != -1)
+						this.addNodeNr(index, (Node)update);
+				}
+				return true;
+			}
+			if (!nodes.contains(old)) {
+				if (nodes.contains(update)) {
+					// Somebody already did this for us, somehow
+					// probably some other way that saw the same
+					// set of duplicate nodes.
+					return new Integer(-1);
+				}
+				//stem.out.println("Way()::replace() result3: " + false);
+				return null;
+			}
+			Node oldn = (Node)old;
+			Node updaten = (Node)update;
+			int old_index = nodes.indexOf(old);
+			nodes.set(old_index, updaten);
+			updaten.addTo(this);
+			oldn.removeFrom(this);
+				//stem.out.println("Way()::replace() result4: " + true);
+			return new Integer(old_index);
+	}
+	@Override public boolean mayDelete() {
+			if (nodes.size() == 0)
+					return true;
+			return false;
+	}
 }
Index: core/src/org/openstreetmap/josm/data/osm/Node.java
===================================================================
--- core/src/org/openstreetmap/josm/data/osm/Node.java	(revision 485)
+++ core/src/org/openstreetmap/josm/data/osm/Node.java	(working copy)
@@ -6,6 +6,8 @@
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.osm.visitor.Visitor;
 
+import java.util.ArrayList;
+import java.util.Collection;
 
 /**
  * One node data, consisting of one world coordinate waypoint.
@@ -17,12 +19,14 @@
 	public LatLon coor;
 	public volatile EastNorth eastNorth;
 
+	private ArrayList<Way> wayList;
 	/**
 	 * Create an incomplete Node object
 	 */
 	public Node(long id) {
 		this.id = id;
 		incomplete = true;
+		wayList = new ArrayList<Way>();
 	}
 	
 	/**
@@ -35,6 +39,7 @@
 	public Node(LatLon latlon) {
 		this.coor = latlon;
 		eastNorth = Main.proj.latlon2eastNorth(latlon);
+		wayList = new ArrayList<Way>();
 	}
 
 	@Override public void visit(Visitor visitor) {
@@ -42,9 +47,11 @@
 	}
 	
 	@Override public void cloneFrom(OsmPrimitive osm) {
+		Node src = (Node)osm;
 		super.cloneFrom(osm);
-		coor = ((Node)osm).coor;
-		eastNorth = ((Node)osm).eastNorth;
+		coor = (src).coor;
+		eastNorth = src.eastNorth;
+		wayList = new ArrayList<Way>(src.wayList);
 	}
 
 	@Override public String toString() {
@@ -59,4 +66,14 @@
 	public int compareTo(OsmPrimitive o) {
 	    return o instanceof Node ? Long.valueOf(id).compareTo(o.id) : 1;
     }
+
+	public void addTo(Way w) {
+		wayList.add(w);
+	}
+	public void removeFrom(Way w) {
+		wayList.remove(w);
+	}
+	public Collection<Way> waysUsing() {
+		return wayList;
+	}
 }
Index: core/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java
===================================================================
--- core/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 485)
+++ core/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(working copy)
@@ -114,6 +114,12 @@
 	public boolean incomplete = false; 
 
 	/**
+	 * This is to help determine when individual objects are modified.
+	 * When making changes, a user may increment this count.
+	 */
+	public int version = 0; 
+
+	/**
 	 * Contains a list of "uninteresting" keys that do not make an object
 	 * "tagged".
 	 */
@@ -161,6 +167,13 @@
 		return id == ((OsmPrimitive)obj).id;
 	}
 
+	public Object replace(OsmPrimitive x, OsmPrimitive y) {
+		return this.replace(x, y, null);
+	}
+	public Object replace(OsmPrimitive x, OsmPrimitive y, Object cookie) {
+		System.out.println("Called OsmPrimitive::replace() on: " + this.getClass().getName());
+		return null;
+	}
 	/**
 	 * Return the id plus the class type encoded as hashcode or supers hashcode if id is 0.
 	 * 
@@ -185,6 +198,7 @@
 	 * @param value The value for the key.
 	 */
 	public final void put(String key, String value) {
+		this.version++;
 		if (value == null)
 			remove(key);
 		else {
@@ -198,6 +212,7 @@
 	 * Remove the given key from the list.
 	 */
 	public final void remove(String key) {
+		this.version++;
 		if (keys != null) {
 			keys.remove(key);
 			if (keys.isEmpty())
@@ -227,6 +242,7 @@
 	 * use this only in the data initializing phase
 	 */
 	public void cloneFrom(OsmPrimitive osm) {
+		this.version++;
 		keys = osm.keys == null ? null : new HashMap<String, String>(osm.keys);
 		id = osm.id;
 		modified = osm.modified;
@@ -254,6 +270,9 @@
 			(keys == null ? osm.keys==null : keys.equals(osm.keys));
 	}
 	
+	public boolean mayDelete() {
+			return false;
+	}
 	public String getTimeStr() {
 		return timestamp == null ? null : new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(timestamp);
 	}
Index: core/src/org/openstreetmap/josm/command/ConflictResolveCommand.java
===================================================================
--- core/src/org/openstreetmap/josm/command/ConflictResolveCommand.java	(revision 485)
+++ core/src/org/openstreetmap/josm/command/ConflictResolveCommand.java	(working copy)
@@ -35,7 +35,7 @@
 		conflictDialog = Main.map.conflictDialog;
 	}
 
-	@Override public void executeCommand() {
+	@Override public boolean executeCommand() {
 		super.executeCommand();
 
 		origAllConflicts = new HashMap<OsmPrimitive, OsmPrimitive>(conflictDialog.conflicts);
@@ -56,6 +56,7 @@
 				conflictDialog.conflicts.remove(k);
 			conflictDialog.rebuildList();
  		}
+		return true;
 	}
 
 	@Override public void undoCommand() {
Index: core/src/org/openstreetmap/josm/command/DeleteCommand.java
===================================================================
--- core/src/org/openstreetmap/josm/command/DeleteCommand.java	(revision 485)
+++ core/src/org/openstreetmap/josm/command/DeleteCommand.java	(working copy)
@@ -21,20 +21,18 @@
  */
 public class DeleteCommand extends Command {
 
-	/**
-	 * The primitive that get deleted.
-	 */
 	private final Collection<? extends OsmPrimitive> data;
 
 	public DeleteCommand(Collection<? extends OsmPrimitive> data) {
 		this.data = data;
 	}
 
-	@Override public void executeCommand() {
+	@Override public boolean executeCommand() {
 		super.executeCommand();
 		for (OsmPrimitive osm : data) {
 			osm.delete(true);
 		}
+		return true;
 	}
 	
 	@Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
Index: core/src/org/openstreetmap/josm/command/ChangeCommand.java
===================================================================
--- core/src/org/openstreetmap/josm/command/ChangeCommand.java	(revision 485)
+++ core/src/org/openstreetmap/josm/command/ChangeCommand.java	(working copy)
@@ -21,17 +21,22 @@
 public class ChangeCommand extends Command {
 
 	private final OsmPrimitive osm;
+	private final int osm_orig_version;
 	private final OsmPrimitive newOsm;
 
 	public ChangeCommand(OsmPrimitive osm, OsmPrimitive newOsm) {
 		this.osm = osm;
+		this.osm_orig_version = osm.version;
 		this.newOsm = newOsm;
     }
 
-	@Override public void executeCommand() {
+	@Override public boolean executeCommand() {
 	    super.executeCommand();
+		if (osm.version != this.osm_orig_version)
+			return false;
 	    osm.cloneFrom(newOsm);
 	    osm.modified = true;
+		return true;
     }
 
 	@Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
Index: core/src/org/openstreetmap/josm/command/MoveCommand.java
===================================================================
--- core/src/org/openstreetmap/josm/command/MoveCommand.java	(revision 485)
+++ core/src/org/openstreetmap/josm/command/MoveCommand.java	(working copy)
@@ -94,12 +94,13 @@
 		this.y += y;
 	}
 	
-	@Override public void executeCommand() {
+	@Override public boolean executeCommand() {
 		for (Node n : objects) {
 			n.eastNorth = new EastNorth(n.eastNorth.east()+x, n.eastNorth.north()+y);
 			n.coor = Main.proj.eastNorth2latlon(n.eastNorth);
 			n.modified = true;
 		}
+		return true;
 	}
 
 	@Override public void undoCommand() {
Index: core/src/org/openstreetmap/josm/command/AddCommand.java
===================================================================
--- core/src/org/openstreetmap/josm/command/AddCommand.java	(revision 485)
+++ core/src/org/openstreetmap/josm/command/AddCommand.java	(working copy)
@@ -44,8 +44,9 @@
 		this.ds = Main.main.editLayer().data;
 	}
 
-	@Override public void executeCommand() {
+	@Override public boolean executeCommand() {
 		osm.visit(new AddVisitor(ds));
+		return true;
 	}
 
 	@Override public void undoCommand() {
Index: core/src/org/openstreetmap/josm/command/SequenceCommand.java
===================================================================
--- core/src/org/openstreetmap/josm/command/SequenceCommand.java	(revision 485)
+++ core/src/org/openstreetmap/josm/command/SequenceCommand.java	(working copy)
@@ -22,7 +22,9 @@
 	 * The command sequenz to be executed.
 	 */
 	private Command[] sequence;
+	private boolean sequence_complete;
 	private final String name;
+	public boolean continueOnError = false;
 
 	/**
 	 * Create the command by specifying the list of commands to execute.
@@ -41,14 +43,32 @@
 		this(name, Arrays.asList(sequenz));
 	}
 	
-	@Override public void executeCommand() {
-		for (Command c : sequence)
-			c.executeCommand();
+	public int executed_commands = 0;
+	@Override public boolean executeCommand() {
+		for (int i=0; i < sequence.length; i++) {
+			Command c = sequence[i];
+			boolean result = c.executeCommand();
+			if (!result && !continueOnError) {
+				this.undoCommands(i-1);
+				return false;
+			}
+		}
+		sequence_complete = true;
+		return true;
 	}
 
+	private void undoCommands(int start) {
+		// We probably aborted this halfway though the
+		// execution sequence because of a sub-command
+		// error.  We already undid the sub-commands.
+		if (!sequence_complete)
+			return;
+		for (int i = start; i >= 0; --i)
+			sequence[i].undoCommand();
+	}
+
 	@Override public void undoCommand() {
-		for (int i = sequence.length-1; i >= 0; --i)
-			sequence[i].undoCommand();
+		this.undoCommands(sequence.length-1);
 	}
 
 	@Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
Index: core/src/org/openstreetmap/josm/command/ChangePropertyCommand.java
===================================================================
--- core/src/org/openstreetmap/josm/command/ChangePropertyCommand.java	(revision 485)
+++ core/src/org/openstreetmap/josm/command/ChangePropertyCommand.java	(working copy)
@@ -44,7 +44,7 @@
 		this.value = value;
 	}
 	
-	@Override public void executeCommand() {
+	@Override public boolean executeCommand() {
 		super.executeCommand(); // save old
 		if (value == null) {
 			for (OsmPrimitive osm : objects) {
@@ -57,6 +57,7 @@
 				osm.put(key, value);
 			}
 		}
+		return true;
 	}
 
 	@Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
Index: core/src/org/openstreetmap/josm/command/RotateCommand.java
===================================================================
--- core/src/org/openstreetmap/josm/command/RotateCommand.java	(revision 485)
+++ core/src/org/openstreetmap/josm/command/RotateCommand.java	(working copy)
@@ -112,8 +112,9 @@
 		}
 	}
 	
-	@Override public void executeCommand() {
+	@Override public boolean executeCommand() {
 		rotateNodes(true);
+		return true;
 	}
 
 	@Override public void undoCommand() {
Index: core/src/org/openstreetmap/josm/command/Command.java
===================================================================
--- core/src/org/openstreetmap/josm/command/Command.java	(revision 485)
+++ core/src/org/openstreetmap/josm/command/Command.java	(working copy)
@@ -50,12 +50,13 @@
 	 * Executes the command on the dataset. This implementation will remember all
 	 * primitives returned by fillModifiedData for restoring them on undo.
 	 */
-	public void executeCommand() {
+	public boolean executeCommand() {
 		orig = new CloneVisitor();
 		Collection<OsmPrimitive> all = new HashSet<OsmPrimitive>();
 		fillModifiedData(all, all, all);
 		for (OsmPrimitive osm : all)
 			osm.visit(orig);
+		return true;
 	}
 
 	/**
