Bug 514848 - Make ElementDifferencer more flexible for subclasses
diff --git a/org.eclipse.handly/src/org/eclipse/handly/model/impl/ElementDifferencer.java b/org.eclipse.handly/src/org/eclipse/handly/model/impl/ElementDifferencer.java
index bcb75fd..862f908 100644
--- a/org.eclipse.handly/src/org/eclipse/handly/model/impl/ElementDifferencer.java
+++ b/org.eclipse.handly/src/org/eclipse/handly/model/impl/ElementDifferencer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2016 IBM Corporation and others.
+ * Copyright (c) 2000, 2017 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -23,6 +23,7 @@
 import java.util.Set;
 
 import org.eclipse.core.runtime.CoreException;
+import org.eclipse.handly.model.Elements;
 import org.eclipse.handly.model.IElement;
 import org.eclipse.handly.model.IElementDelta;
 
@@ -95,7 +96,7 @@
      */
     public IElement getElement()
     {
-        return builder.getDelta().hElement();
+        return getDelta().hElement();
     }
 
     /**
@@ -107,13 +108,23 @@
     {
         if (built)
             throw new IllegalStateException("Delta has already been built"); //$NON-NLS-1$
+        built = true;
         IElement element = getElement();
         recordNewPositions(element, 0);
         findAdditions(element, 0);
         findDeletions();
         findChangesInPositioning(element, 0);
-        trimDelta(builder.getDelta());
-        built = true;
+        trimDelta(getDelta());
+    }
+
+    /**
+     * Returns the delta builder used by this differencer.
+     *
+     * @return the delta builder (never <code>null</code>)
+     */
+    public ElementDelta.Builder getDeltaBuilder()
+    {
+        return builder;
     }
 
     /**
@@ -121,9 +132,9 @@
      *
      * @return the root of the delta tree (never <code>null</code>)
      */
-    public IElementDelta getDelta()
+    public ElementDelta getDelta()
     {
-        return builder.getDelta();
+        return getDeltaBuilder().getDelta();
     }
 
     /**
@@ -135,7 +146,7 @@
      */
     public boolean isEmptyDelta()
     {
-        return builder.isEmptyDelta();
+        return getDeltaBuilder().isEmptyDelta();
     }
 
     @Override
@@ -145,6 +156,19 @@
     }
 
     /**
+     * Remembers the given body for the given element. This method is called
+     * by the framework and is not intended to be invoked by clients. Subclasses
+     * may extend this method.
+     *
+     * @param body never <code>null</code>
+     * @param element never <code>null</code>
+     */
+    protected void recordBody(Object body, IElement element)
+    {
+        oldBodies.put(element, body);
+    }
+
+    /**
      * Finds whether the given element has had a content change.
      * <p>
      * Implementations can compare the given bodies (excepting children)
@@ -158,10 +182,11 @@
      * @param newBody the new version of the element's body (never <code>null</code>)
      * @param element the element whose bodies are to be compared (never <code>null</code>)
      */
-    protected void findContentChange(Object newBody, Object oldBody,
+    protected void findContentChange(Object oldBody, Object newBody,
         IElement element)
     {
-        ((Body)newBody).findContentChange((Body)oldBody, element, builder);
+        ((Body)newBody).findContentChange((Body)oldBody, element,
+            getDeltaBuilder());
     }
 
     private void initialize()
@@ -193,7 +218,7 @@
             return;
         }
 
-        oldBodies.put(element, body);
+        recordBody(body, element);
 
         IElement[] children = ((Element)element).hChildren(body);
         insertPositions(children, false);
@@ -211,17 +236,16 @@
         if (depth >= maxDepth)
             return;
 
-        Object body;
+        IElement[] children;
         try
         {
-            body = ((Element)newElement).hBody();
+            children = Elements.getChildren(newElement);
         }
         catch (CoreException e)
         {
             return;
         }
 
-        IElement[] children = ((Element)newElement).hChildren(body);
         insertPositions(children, true);
         for (IElement child : children)
         {
@@ -258,7 +282,7 @@
         Object oldBody = getOldBody(newElement);
         if (oldBody == null && depth < maxDepth)
         {
-            builder.added(newElement);
+            getDeltaBuilder().added(newElement);
             added(newElement);
         }
         else
@@ -269,7 +293,7 @@
         if (depth >= maxDepth)
         {
             // mark element as changed
-            builder.changed(newElement, F_CONTENT);
+            getDeltaBuilder().changed(newElement, F_CONTENT);
             return;
         }
 
@@ -285,7 +309,7 @@
                 return;
             }
 
-            findContentChange(newBody, oldBody, newElement);
+            findContentChange(oldBody, newBody, newElement);
 
             for (IElement child : ((Element)newElement).hChildren(newBody))
             {
@@ -303,7 +327,7 @@
         while (iter.hasNext())
         {
             IElement element = iter.next();
-            builder.removed(element);
+            getDeltaBuilder().removed(element);
             removed(element);
         }
     }
@@ -319,20 +343,20 @@
 
         if (!isPositionedCorrectly(element))
         {
-            builder.changed(element, F_REORDER);
+            getDeltaBuilder().changed(element, F_REORDER);
         }
 
-        Object body;
+        IElement[] children;
         try
         {
-            body = ((Element)element).hBody();
+            children = Elements.getChildren(element);
         }
         catch (CoreException e)
         {
             return;
         }
 
-        for (IElement child : ((Element)element).hChildren(body))
+        for (IElement child : children)
         {
             findChangesInPositioning(child, depth + 1);
         }
diff --git a/org.eclipse.handly/src/org/eclipse/handly/model/impl/SourceFile.java b/org.eclipse.handly/src/org/eclipse/handly/model/impl/SourceFile.java
index b6b9bfe..2fa6f81 100644
--- a/org.eclipse.handly/src/org/eclipse/handly/model/impl/SourceFile.java
+++ b/org.eclipse.handly/src/org/eclipse/handly/model/impl/SourceFile.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014, 2016 1C-Soft LLC and others.
+ * Copyright (c) 2014, 2017 1C-Soft LLC and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -810,13 +810,7 @@
         protected void reconcile(IContext context, IProgressMonitor monitor)
             throws CoreException
         {
-            ElementDelta.Factory deltaFactory = hModel().getModelContext().get(
-                ElementDelta.Factory.class);
-            if (deltaFactory == null)
-                deltaFactory = element -> new ElementDelta(element);
-            ElementDelta rootDelta = deltaFactory.newDelta(SourceFile.this);
-            ElementDifferencer differ = new ElementDifferencer(
-                new ElementDelta.Builder(rootDelta));
+            ElementDifferencer differ = createDifferencer();
 
             doReconcile(context, monitor);
 
@@ -832,6 +826,21 @@
         }
 
         /**
+         * Creates an element differencer for this operation's source file.
+         *
+         * @return a new element differencer (never <code>null</code>)
+         */
+        protected ElementDifferencer createDifferencer()
+        {
+            ElementDelta.Factory deltaFactory = hModel().getModelContext().get(
+                ElementDelta.Factory.class);
+            if (deltaFactory == null)
+                deltaFactory = element -> new ElementDelta(element);
+            ElementDelta rootDelta = deltaFactory.newDelta(SourceFile.this);
+            return new ElementDifferencer(new ElementDelta.Builder(rootDelta));
+        }
+
+        /**
          * This implementation calls {@link ReconcileOperation#reconcile(
          * IContext, IProgressMonitor) super.reconcile(..)}.
          * <p>