[123959] [hotbug] Bad performance of XML Editor with large xml files.
diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/XMLModelNotifierImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/XMLModelNotifierImpl.java
index cd59f14..7e9149a 100644
--- a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/XMLModelNotifierImpl.java
+++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/XMLModelNotifierImpl.java
@@ -12,7 +12,9 @@
  *******************************************************************************/
 package org.eclipse.wst.xml.core.internal.document;
 
+import java.util.HashMap;
 import java.util.Iterator;
+import java.util.Map;
 import java.util.Vector;
 
 import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier;
@@ -38,6 +40,7 @@
 		int pos;
 		String reason;
 		int type;
+		int index;
 
 		NotifyEvent(INodeNotifier notifier, int type, Object changedFeature, Object oldValue, Object newValue, int pos) {
 			this.notifier = notifier;
@@ -247,79 +250,93 @@
 			return;
 		this.flushing = true; // force notification
 		int count = this.events.size();
-		for (int i = 0; i < count; i++) {
-			NotifyEvent event = (NotifyEvent) this.events.elementAt(i);
-			if (event == null)
-				continue; // error
-			if (event.discarded)
-				continue;
-			if (!doingNewModel && fOptimizeDeferred) {
-				// check redundant events (no need to check if doing NewModel,
-				// since
-				// shouldn't be redunancies)
-				if (event.type == INodeNotifier.ADD) {
-					for (int n = i + 1; n < count; n++) {
-						NotifyEvent next = (NotifyEvent) this.events.elementAt(n);
-						if (next == null)
-							continue; // error
-						if (next.type == INodeNotifier.REMOVE && next.oldValue == event.newValue) {
-							// Added then removed later, discard both
-							event.discarded = true;
-							next.discarded = true;
-							if (Debug.debugNotifyDeferred) {
-								event.reason = event.reason + ADDED_THEN_REMOVED + "(see " + n + ")"; //$NON-NLS-1$ //$NON-NLS-2$
-								next.reason = next.reason + ADDED_THEN_REMOVED + "(see " + i + ")"; //$NON-NLS-1$ //$NON-NLS-2$
-							}
-							break;
+		if (!doingNewModel && fOptimizeDeferred) {
+			Map values = new HashMap();
+			for (int i = 0; i < count; i++) {
+				NotifyEvent event = (NotifyEvent) this.events.elementAt(i);
+				if (event == null)
+					continue; // error
+				event.index = i;
+				if(event.type == INodeNotifier.REMOVE) {
+					addToMap(event.oldValue, event, values);
+				}
+				if(event.type == INodeNotifier.ADD) {
+					addToMap(event.newValue, event, values);
+				}
+			}
+			Iterator it = values.keySet().iterator();
+			while(it.hasNext()) {
+				Object value = it.next();
+				NotifyEvent[] es = (NotifyEvent[])values.get(value);
+				for (int i = 0; i < es.length - 1; i++) {
+					NotifyEvent event = es[i];
+					if(es[i].discarded) continue;
+					NotifyEvent next = es[i + 1];
+					if(es[i].type == INodeNotifier.ADD && next.type == INodeNotifier.REMOVE) {
+						// Added then removed later, discard both
+						event.discarded = true;
+						next.discarded = true;
+						if (Debug.debugNotifyDeferred) {
+							event.reason = event.reason + ADDED_THEN_REMOVED + "(see " + next.index + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+							next.reason = next.reason + ADDED_THEN_REMOVED + "(see " + event.index + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 						}
 					}
-					if (event.discarded)
-						continue;
-					if (fOptimizeDeferredAccordingToParentAdded) {
-						for (int p = 0; p < i; p++) {
-							NotifyEvent prev = (NotifyEvent) this.events.elementAt(p);
-							if (prev == null)
-								continue; // error
+				}
+			}
+			for (int i = 0; i < count; i++) {
+				NotifyEvent event = (NotifyEvent) this.events.elementAt(i);
+				if (event == null)
+					continue; // error
+				if(event.discarded) continue;
+				if (event.notifier != null && fOptimizeDeferredAccordingToParentAdded) {
+					if (event.type == INodeNotifier.ADD) {
+						NotifyEvent[] es = (NotifyEvent[])values.get(event.notifier);
+						if(es != null) for (int p = 0; p < es.length && es[p].index < event.index; p++) {
+							NotifyEvent prev = es[p];
 							if (prev.type == INodeNotifier.REMOVE && prev.oldValue == event.notifier) {
 								// parent is reparented, do not discard
 								if (Debug.debugNotifyDeferred) {
-									event.reason = event.reason + PARENT_IS_REPARENTED + "(see " + p + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+									event.reason = event.reason + PARENT_IS_REPARENTED + "(see " + prev.index + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 								}
 								break;
 							} else if (prev.type == INodeNotifier.ADD && prev.newValue == event.notifier) {
 								// parent has been added, discard this
 								event.discarded = true;
 								if (Debug.debugNotifyDeferred) {
-									event.reason = event.reason + PARENT_IS_ADDED + "(see " + p + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+									event.reason = event.reason + PARENT_IS_ADDED + "(see " + prev.index + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 								}
 								break;
 							}
-						}
-						if (event.discarded)
-							continue;
-					}
-				} else if (event.type == INodeNotifier.REMOVE) {
-					if (fOptimizeDeferredAccordingToParentRemoved) {
-						for (int n = i + 1; n < count; n++) {
-							NotifyEvent next = (NotifyEvent) this.events.elementAt(n);
-							if (next == null)
-								continue; // error
-							if (next.type == INodeNotifier.REMOVE) {
+						}							
+					}						
+				}
+				if(event.discarded) continue;
+				if (event.notifier != null && fOptimizeDeferredAccordingToParentRemoved) {
+					if (event.type == INodeNotifier.REMOVE) {
+						NotifyEvent[] es = (NotifyEvent[])values.get(event.notifier);
+						if(es != null) for (int n = 0; n < es.length; n++) {
+							NotifyEvent next = es[n];
+							if(next.index > event.index && next.type == INodeNotifier.REMOVE) {
 								if (next.oldValue == event.notifier) {
 									// parent will be removed, discard this
 									event.discarded = true;
 									if (Debug.debugNotifyDeferred) {
-										event.reason = event.reason + PARENT_IS_REMOVED_TOO + "(see " + n + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+										event.reason = event.reason + PARENT_IS_REMOVED_TOO + "(see " + next.index + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 									}
 									break;
 								}
 							}
 						}
-						if (event.discarded)
-							continue;
-					}
+					}						
 				}
+				if(event.discarded) continue;
 			}
+		}
+		for (int i = 0; i < count; i++) {
+			NotifyEvent event = (NotifyEvent) this.events.elementAt(i);
+			if (event == null)
+				continue; // error
+			if(event.discarded) continue;
 			notify(event.notifier, event.type, event.changedFeature, event.oldValue, event.newValue, event.pos);
 		}
 		if (Debug.debugNotifyDeferred) {
@@ -346,6 +363,20 @@
 		this.events = null;
 	}
 
+	void addToMap(Object o, NotifyEvent event, Map map) {
+		if(o == null) return;
+		Object x = map.get(o);
+		if(x == null) {
+			map.put(o, new NotifyEvent[]{event});
+		} else {
+			NotifyEvent[] es = (NotifyEvent[])x;
+			NotifyEvent[] es2 = new NotifyEvent[es.length + 1];
+			System.arraycopy(es, 0, es2, 0, es.length);
+			es2[es.length] = event;
+			map.put(o, es2);
+		}
+	}
+
 	/**
 	 */
 	private void notifyStructureChanged(Node root) {