You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by si...@apache.org on 2019/06/14 11:57:20 UTC

[sling-whiteboard] branch master updated: [feature-diff] re-designing the whole Diff APIs in order to produce a new Feature, which prototypes from the input previous Feature, in order to obtain the current one

This is an automated email from the ASF dual-hosted git repository.

simonetripodi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git


The following commit(s) were added to refs/heads/master by this push:
     new afda1b1  [feature-diff] re-designing the whole Diff APIs in order to produce a new Feature, which prototypes from the input previous Feature, in order to obtain the current one
     new a3fe287  Merge branch 'master' of github.com:apache/sling-whiteboard
afda1b1 is described below

commit afda1b1c082c3dc8ba16761f85d2c25eeff686f0
Author: Simo Tripodi <st...@adobe.com>
AuthorDate: Fri Jun 14 13:56:48 2019 +0200

    [feature-diff] re-designing the whole Diff APIs in order to produce a
    new Feature, which prototypes from the input previous Feature, in order
    to obtain the current one
---
 feature-diff/pom.xml                               |  19 ---
 .../diff/AbstractFeatureElementComparator.java     |  61 --------
 .../sling/feature/diff/ArtifactsComparator.java    |  46 +++---
 .../feature/diff/ComplexElementComparator.java     |  24 ----
 .../feature/diff/ConfigurationsComparator.java     |  61 +++-----
 .../org/apache/sling/feature/diff/DiffSection.java | 106 --------------
 .../sling/feature/diff/ExtensionsComparator.java   | 159 ++++++++++-----------
 .../org/apache/sling/feature/diff/FeatureDiff.java |  72 ++++------
 .../diff/FrameworkPropertiesComparator.java        |  59 ++++++++
 .../sling/feature/diff/GenericMapComparator.java   |  62 --------
 .../sling/feature/diff/RequirementsComparator.java |  54 -------
 .../org/apache/sling/feature/diff/UpdatedItem.java |  74 ----------
 .../diff/io/json/FeatureDiffJSONSerializer.java    | 142 ------------------
 .../apache/sling/feature/diff/package-info.java    |   2 +-
 .../FeatureElementComparator.java}                 |   8 +-
 .../diff/{io/json => spi}/package-info.java        |   4 +-
 ...ling.feature.diff.spi.FeatureElementComparartor |   4 +
 .../sling/feature/diff/AbstractComparatorTest.java |  47 ++++++
 .../feature/diff/ApiRegionsComparatorTest.java     | 112 ---------------
 .../feature/diff/ArtifactsComparatorTest.java      |  55 +++----
 .../feature/diff/ConfigurationsComparatorTest.java |  52 +++----
 .../apache/sling/feature/diff/DiffSectionTest.java |  98 -------------
 .../apache/sling/feature/diff/FeatureDiffTest.java |  85 -----------
 .../feature/diff/GenericMapComparatorTest.java     |  92 ------------
 .../apache/sling/feature/diff/UpdatedItemTest.java |  42 ------
 .../io/json/FeatureDiffJSONSerializerTest.java     | 126 ----------------
 .../sling/feature/diff/io/json/expectedDiff.json   |  81 -----------
 27 files changed, 300 insertions(+), 1447 deletions(-)

diff --git a/feature-diff/pom.xml b/feature-diff/pom.xml
index f952362..d40363c 100644
--- a/feature-diff/pom.xml
+++ b/feature-diff/pom.xml
@@ -63,11 +63,6 @@
       <version>1.0.2</version>
       <scope>provided</scope>
     </dependency>
-    <dependency>
-      <groupId>org.apache.sling</groupId>
-      <artifactId>org.apache.sling.feature.apiregions.model</artifactId>
-      <version>0.0.1-SNAPSHOT</version>
-    </dependency>
 
     <!--
      | JSON patch
@@ -79,14 +74,6 @@
       <scope>provided</scope>
     </dependency>
 
-    <!-- Serialize the JSON FeatureDiff -->
-    <dependency>
-      <groupId>org.apache.geronimo.specs</groupId>
-      <artifactId>geronimo-json_1.0_spec</artifactId>
-      <version>1.0-alpha-1</version>
-      <scope>provided</scope>
-    </dependency>
-
     <!--
      | Test only dependencies
     -->
@@ -95,12 +82,6 @@
       <artifactId>junit</artifactId>
       <scope>test</scope>
     </dependency>
-    <dependency>
-      <groupId>org.apache.johnzon</groupId>
-      <artifactId>johnzon-core</artifactId>
-      <version>1.0.0</version>
-      <scope>test</scope>
-    </dependency>
   </dependencies>
 
 </project>
diff --git a/feature-diff/src/main/java/org/apache/sling/feature/diff/AbstractFeatureElementComparator.java b/feature-diff/src/main/java/org/apache/sling/feature/diff/AbstractFeatureElementComparator.java
deleted file mode 100644
index e05a06a..0000000
--- a/feature-diff/src/main/java/org/apache/sling/feature/diff/AbstractFeatureElementComparator.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package org.apache.sling.feature.diff;
-
-import static java.util.Objects.requireNonNull;
-
-abstract class AbstractFeatureElementComparator<T, I extends Iterable<T>> implements ComplexElementComparator<T, I> {
-
-    private final String id;
-
-    public AbstractFeatureElementComparator(String id) {
-        this.id = requireNonNull(id, "Null id can not be used to the create a new comparator");
-    }
-
-    protected abstract String getId(T item);
-
-    protected abstract T find(T item, I collection);
-
-    @Override
-    public final DiffSection apply(I previouses, I currents) {
-        final DiffSection diffDsection = new DiffSection(id);
-
-        for (T previous : previouses) {
-            T current = find(previous, currents);
-
-            if (current == null) {
-                diffDsection.markRemoved(getId(previous));
-            } else {
-                DiffSection updateSection = compare(previous, current);
-                if (updateSection != null && !updateSection.isEmpty()) {
-                    diffDsection.markUpdated(updateSection);
-                }
-            }
-        }
-
-        for (T current : currents) {
-            T previous = find(current, previouses);
-
-            if (previous == null) {
-                diffDsection.markAdded(getId(current));
-            }
-        }
-
-        return diffDsection;
-    }
-
-}
diff --git a/feature-diff/src/main/java/org/apache/sling/feature/diff/ArtifactsComparator.java b/feature-diff/src/main/java/org/apache/sling/feature/diff/ArtifactsComparator.java
index dfd6136..a2935db 100644
--- a/feature-diff/src/main/java/org/apache/sling/feature/diff/ArtifactsComparator.java
+++ b/feature-diff/src/main/java/org/apache/sling/feature/diff/ArtifactsComparator.java
@@ -16,42 +16,40 @@
  */
 package org.apache.sling.feature.diff;
 
-import java.util.Objects;
-
 import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.Artifacts;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.diff.spi.FeatureElementComparator;
 
-final class ArtifactsComparator extends AbstractFeatureElementComparator<Artifact, Artifacts> {
-
-    public ArtifactsComparator(String id) {
-        super(id);
-    }
+final class ArtifactsComparator implements FeatureElementComparator {
 
     @Override
-    public String getId(Artifact artifact) {
-        return artifact.getId().toMvnId();
+    public void computeDiff(Feature previous, Feature current, Feature target) {
+        computeDiff(previous.getBundles(), current.getBundles(), target);
     }
 
-    @Override
-    public Artifact find(Artifact artifact, Artifacts artifacts) {
-        return artifacts.getSame(artifact.getId());
-    }
+    protected void computeDiff(Artifacts previouses, Artifacts currents, Feature target) {
+        for (Artifact previous : previouses) {
+            Artifact current = currents.getSame(previous.getId());
 
-    @Override
-    public DiffSection compare(Artifact previous, Artifact current) {
-        DiffSection diffSection = new DiffSection(getId(current));
+            boolean add = false;
 
-        String previousVersion = previous.getId().getVersion();
-        String currentVersion = current.getId().getVersion();
-        if (!Objects.equals(previousVersion, currentVersion)) {
-            diffSection.markItemUpdated("version", previousVersion, currentVersion);
-        }
+            if (current == null || (add = !previous.getId().equals(current.getId()))) {
+                target.getPrototype().getBundleRemovals().add(previous.getId());
+            }
 
-        if (previous.getStartOrder() != current.getStartOrder()) {
-            diffSection.markItemUpdated("start-order", previous.getStartOrder(), current.getStartOrder());
+            if (add) {
+                target.getBundles().add(current);
+            }
         }
 
-        return diffSection;
+        for (Artifact current : currents) {
+            Artifact previous = previouses.getSame(current.getId());
+
+            if (previous == null) {
+                target.getBundles().add(current);
+            }
+        }
     }
 
 }
diff --git a/feature-diff/src/main/java/org/apache/sling/feature/diff/ComplexElementComparator.java b/feature-diff/src/main/java/org/apache/sling/feature/diff/ComplexElementComparator.java
deleted file mode 100644
index 840e364..0000000
--- a/feature-diff/src/main/java/org/apache/sling/feature/diff/ComplexElementComparator.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package org.apache.sling.feature.diff;
-
-import java.util.function.BiFunction;
-
-public interface ComplexElementComparator<T, I extends Iterable<T>>
-        extends BiFunction<I, I, DiffSection>, ElementComparator<T> {
-
-}
diff --git a/feature-diff/src/main/java/org/apache/sling/feature/diff/ConfigurationsComparator.java b/feature-diff/src/main/java/org/apache/sling/feature/diff/ConfigurationsComparator.java
index 2f82e8d..718e5d4 100644
--- a/feature-diff/src/main/java/org/apache/sling/feature/diff/ConfigurationsComparator.java
+++ b/feature-diff/src/main/java/org/apache/sling/feature/diff/ConfigurationsComparator.java
@@ -16,62 +16,39 @@
  */
 package org.apache.sling.feature.diff;
 
-import java.util.Dictionary;
-import java.util.Enumeration;
-
-import org.apache.commons.lang3.builder.EqualsBuilder;
+import static org.apache.commons.lang3.builder.EqualsBuilder.reflectionEquals;
 import org.apache.sling.feature.Configuration;
 import org.apache.sling.feature.Configurations;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.diff.spi.FeatureElementComparator;
 
-final class ConfigurationsComparator extends AbstractFeatureElementComparator<Configuration, Configurations> {
-
-    public ConfigurationsComparator() {
-        super("configurations");
-    }
+final class ConfigurationsComparator implements FeatureElementComparator {
 
     @Override
-    public String getId(Configuration configuration) {
-        return configuration.getPid();
+    public void computeDiff(Feature previous, Feature current, Feature target) {
+        computeDiff(previous.getConfigurations(), current.getConfigurations(), target);
     }
 
-    @Override
-    public Configuration find(Configuration configuration, Configurations configurations) {
-        return configurations.getConfiguration(getId(configuration));
-    }
+    protected void computeDiff(Configurations previouses, Configurations currents, Feature target) {
+        for (Configuration previousConfiguration : previouses) {
+            Configuration currentConfiguration = currents.getConfiguration(previousConfiguration.getPid());
 
-    @Override
-    public DiffSection compare(Configuration previous, Configuration current) {
-        Dictionary<String, Object> previousProperties = previous.getConfigurationProperties();
-        Dictionary<String, Object> currentProperties = current.getConfigurationProperties();
-        final DiffSection dictionaryDiffs = new DiffSection(getId(current));
-
-        Enumeration<String> previousKeys = previousProperties.keys();
-        while (previousKeys.hasMoreElements()) {
-            String previousKey = previousKeys.nextElement();
-
-            Object previousValue = previousProperties.get(previousKey);
-            Object currentValue = currentProperties.get(previousKey);
-
-            if (currentValue == null && previousValue != null) {
-                dictionaryDiffs.markRemoved(previousKey);
-            } else if (!new EqualsBuilder().reflectionAppend(previousValue, currentValue).isEquals()) {
-                dictionaryDiffs.markItemUpdated(previousKey, previousValue, currentValue);
+            if (currentConfiguration == null) {
+                target.getPrototype().getConfigurationRemovals().add(previousConfiguration.getPid());
+            } else if (!reflectionEquals(previousConfiguration.getConfigurationProperties(),
+                                         currentConfiguration.getConfigurationProperties(),
+                                         true)) {
+                target.getConfigurations().add(currentConfiguration);
             }
         }
 
-        Enumeration<String> currentKeys = currentProperties.keys();
-        while (currentKeys.hasMoreElements()) {
-            String currentKey = currentKeys.nextElement();
+        for (Configuration currentConfiguration : currents) {
+            Configuration previousConfiguration = previouses.getConfiguration(currentConfiguration.getPid());
 
-            Object previousValue = previousProperties.get(currentKey);
-            Object currentValue = currentProperties.get(currentKey);
-
-            if (previousValue == null && currentValue != null) {
-                dictionaryDiffs.markAdded(currentKey);
+            if (previousConfiguration == null) {
+                target.getConfigurations().add(currentConfiguration);
             }
         }
-
-        return dictionaryDiffs;
     }
 
 }
diff --git a/feature-diff/src/main/java/org/apache/sling/feature/diff/DiffSection.java b/feature-diff/src/main/java/org/apache/sling/feature/diff/DiffSection.java
deleted file mode 100644
index 1d282ec..0000000
--- a/feature-diff/src/main/java/org/apache/sling/feature/diff/DiffSection.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package org.apache.sling.feature.diff;
-
-import static java.util.Objects.requireNonNull;
-
-import java.util.LinkedList;
-import java.util.List;
-
-public final class DiffSection {
-
-    private final List<String> added = new LinkedList<>();
-
-    private final List<String> removed = new LinkedList<>();
-
-    private final List<UpdatedItem<?>> updatedItems = new LinkedList<>();
-
-    private final List<DiffSection> updates = new LinkedList<>();
-
-    private final String id;
-
-    protected DiffSection(String id) {
-        this.id = requireNonNull(id, "A Diff section can not be declared with a null id.");
-    }
-
-    public String getId() {
-        return id;
-    }
-
-    protected void markAdded(String item) {
-        String checkedItem = requireNonNull(item, "Null item can not be added in the 'added' section");
-        added.add(checkedItem);
-    }
-
-    public Iterable<String> getAdded() {
-        return added;
-    }
-
-    protected void markRemoved(String item) {
-        String checkedItem = requireNonNull(item, "Null item can not be added in the 'removed' section");
-        removed.add(checkedItem);
-    }
-
-    public Iterable<String> getRemoved() {
-        return removed;
-    }
-
-    protected <T> void markItemUpdated(String id, T previous, T current) {
-        T checkedPrevious = previous;
-        T checkedCurrent = current;
-        updatedItems.add(new UpdatedItem<T>(id, checkedPrevious, checkedCurrent));
-    }
-
-    public Iterable<UpdatedItem<?>> getUpdatedItems() {
-        return updatedItems;
-    }
-
-    protected void markUpdated(DiffSection diffSection) {
-        DiffSection checkedSection = requireNonNull(diffSection);
-        if (!diffSection.isEmpty()) {
-            updates.add(checkedSection);
-        }
-    }
-
-    public Iterable<DiffSection> getUpdates() {
-        return updates;
-    }
-
-    public boolean hasAdded() {
-        return !added.isEmpty();
-    }
-
-    public boolean hasRemoved() {
-        return !removed.isEmpty();
-    }
-
-    public boolean hasUpdatedItems() {
-        return !updatedItems.isEmpty();
-    }
-
-    public boolean hasUpdates() {
-        return !updates.isEmpty();
-    }
-
-    public boolean isEmpty() {
-        return added.isEmpty()
-                && removed.isEmpty()
-                && updatedItems.isEmpty()
-                && updates.isEmpty();
-    }
-
-}
diff --git a/feature-diff/src/main/java/org/apache/sling/feature/diff/ExtensionsComparator.java b/feature-diff/src/main/java/org/apache/sling/feature/diff/ExtensionsComparator.java
index 8c9d5a8..c8a0555 100644
--- a/feature-diff/src/main/java/org/apache/sling/feature/diff/ExtensionsComparator.java
+++ b/feature-diff/src/main/java/org/apache/sling/feature/diff/ExtensionsComparator.java
@@ -16,118 +16,109 @@
  */
 package org.apache.sling.feature.diff;
 
-import static org.apache.sling.feature.apiregions.model.io.json.ApiRegionsJSONParser.parseApiRegions;
-import static java.lang.String.format;
-
 import java.io.IOException;
+import java.util.LinkedList;
 
+import org.apache.sling.feature.Artifact;
+import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Extension;
 import org.apache.sling.feature.Extensions;
-import org.apache.sling.feature.apiregions.model.ApiRegion;
-import org.apache.sling.feature.apiregions.model.ApiRegions;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.diff.spi.FeatureElementComparator;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.flipkart.zjsonpatch.JsonDiff;
 
-final class ExtensionsComparator extends AbstractFeatureElementComparator<Extension, Extensions> {
+final class ExtensionsComparator implements FeatureElementComparator {
 
-    private static final String API_REGIONS = "api-regions";
     private final ObjectMapper objectMapper = new ObjectMapper();
 
-    public ExtensionsComparator() {
-        super("extensions");
-    }
-
     @Override
-    public String getId(Extension extension) {
-        return extension.getName();
+    public void computeDiff(Feature previous, Feature current, Feature target) {
+        computeDiff(previous.getExtensions(), current.getExtensions(), target);
     }
 
-    @Override
-    public Extension find(Extension extension, Extensions extensions) {
-        return extensions.getByName(extension.getName());
-    }
+    private void computeDiff(Extensions previousExtensions, Extensions currentExtensions, Feature target) {
+        for (Extension previousExtension : previousExtensions) {
+            Extension currentExtension = currentExtensions.getByName(previousExtension.getName());
 
-    @Override
-    public DiffSection compare(Extension previous, Extension current) {
-        String diffName = format("%s:%s|%s", current.getName(), current.getType(), current.isRequired());
-        DiffSection diffSection = new DiffSection(diffName);
+            if (currentExtension == null) {
+                target.getPrototype().getExtensionRemovals().add(previousExtension.getName());
+            } else {
+                computeDiff(previousExtension, currentExtension, target);
+            }
+        }
 
-        if (previous.getType() != current.getType()) {
-            diffSection.markItemUpdated("type", previous.getType(), current.getType());
+        for (Extension currentExtension : currentExtensions) {
+            Extension previousConfiguration = previousExtensions.getByName(currentExtension.getName());
+
+            if (previousConfiguration == null) {
+                target.getExtensions().add(currentExtension);
+            }
         }
+    }
 
-        switch (previous.getType()) {
+    public void computeDiff(Extension previousExtension, Extension currentExtension, Feature target) {
+        switch (previousExtension.getType()) {
             case ARTIFACTS:
-                diffSection.markUpdated(new ArtifactsComparator("artifacts")
-                                        .apply(previous.getArtifacts(), current.getArtifacts()));
+                Extension targetExtension = new Extension(previousExtension.getType(), previousExtension.getName(), previousExtension.isRequired());
+
+                for (Artifact previous : previousExtension.getArtifacts()) {
+                    Artifact current = currentExtension.getArtifacts().getSame(previous.getId());
+
+                    boolean add = false;
+
+                    if (current == null || (add = !previous.getId().equals(current.getId()))) {
+                        target.getPrototype().getArtifactExtensionRemovals()
+                                             .computeIfAbsent(previousExtension.getName(), k -> new LinkedList<ArtifactId>())
+                                             .add(previous.getId());
+                    }
+
+                    if (add) {
+                        targetExtension.getArtifacts().add(current);
+                    }
+                }
+
+                for (Artifact current : currentExtension.getArtifacts()) {
+                    Artifact previous = previousExtension.getArtifacts().getSame(current.getId());
+
+                    if (previous == null) {
+                        targetExtension.getArtifacts().add(current);
+                    }
+                }
+
+                if (!targetExtension.getArtifacts().isEmpty()) {
+                    target.getExtensions().add(targetExtension);
+                }
+
                 break;
 
             case TEXT:
-                if (!previous.getText().equals(previous.getText())) {
-                    diffSection.markItemUpdated("text", previous.getType(), current.getType());
-                } 
+                if (!previousExtension.getText().equals(currentExtension.getText())) {
+                    target.getExtensions().add(currentExtension);
+                }
                 break;
 
             case JSON:
-                if (API_REGIONS.equals(current.getName())) {
-                    ApiRegions previousRegions = parseApiRegions(previous);
-                    ApiRegions currentRegions = parseApiRegions(current);
-
-                    for (ApiRegion previousRegion : previousRegions) {
-                        String regionName = previousRegion.getName();
-                        ApiRegion currentRegion = currentRegions.getByName(regionName);
-
-                        if (currentRegion == null) {
-                            diffSection.markRemoved(regionName);
-                        } else {
-                            DiffSection regionDiff = new DiffSection(regionName);
-
-                            for (String previousApi : previousRegion.getExports()) {
-                                if (!currentRegion.exports(previousApi)) {
-                                    regionDiff.markRemoved(previousApi);
-                                }
-                            }
-
-                            for (String currentApi : currentRegion.getExports()) {
-                                if (!previousRegion.exports(currentApi)) {
-                                    regionDiff.markAdded(currentApi);
-                                }
-                            }
-
-                            diffSection.markUpdated(regionDiff);
-                        }
-                    }
+                String previousJSON = previousExtension.getJSON();
+                String currentJSON = currentExtension.getJSON();
 
-                    for (ApiRegion currentRegion : currentRegions) {
-                        String regionName = currentRegion.getName();
-                        ApiRegion previousRegion = previousRegions.getByName(regionName);
+                try {
+                    JsonNode previousNode = objectMapper.readTree(previousJSON);
+                    JsonNode currentNode = objectMapper.readTree(currentJSON); 
+                    JsonNode patchNode = JsonDiff.asJson(previousNode, currentNode); 
 
-                        if (previousRegion == null) {
-                            diffSection.markAdded(regionName);
-                        }
-                    }
-                } else {
-                    String previousJSON = previous.getJSON();
-                    String currentJSON = current.getJSON();
-
-                    try {
-                        JsonNode previousNode = objectMapper.readTree(previousJSON);
-                        JsonNode currentNode = objectMapper.readTree(currentJSON); 
-                        JsonNode patchNode = JsonDiff.asJson(previousNode, currentNode); 
-
-                        if (patchNode.size() != 0) {
-                            diffSection.markItemUpdated("json", previousJSON, currentJSON);
-                        }
-                    } catch (IOException e) {
-                        // should not happen
-                        throw new RuntimeException("A JSON parse error occurred while parsing previous '"
-                                                   + previousJSON
-                                                   + "' and current '"
-                                                   + currentJSON
-                                                   + "', see nested errors:", e);
+                    if (patchNode.size() != 0) {
+                        target.getExtensions().add(currentExtension);
                     }
+                } catch (IOException e) {
+                    // should not happen
+                    throw new RuntimeException("A JSON parse error occurred while parsing previous '"
+                                               + previousJSON
+                                               + "' and current '"
+                                               + currentJSON
+                                               + "', see nested errors:", e);
                 }
                 break;
 
@@ -135,8 +126,6 @@ final class ExtensionsComparator extends AbstractFeatureElementComparator<Extens
             default:
                 break;
         }
-
-        return diffSection;
     }
 
 }
diff --git a/feature-diff/src/main/java/org/apache/sling/feature/diff/FeatureDiff.java b/feature-diff/src/main/java/org/apache/sling/feature/diff/FeatureDiff.java
index 76e0474..8e982d9 100644
--- a/feature-diff/src/main/java/org/apache/sling/feature/diff/FeatureDiff.java
+++ b/feature-diff/src/main/java/org/apache/sling/feature/diff/FeatureDiff.java
@@ -17,71 +17,51 @@
 package org.apache.sling.feature.diff;
 
 import static java.util.Objects.requireNonNull;
+import static java.util.ServiceLoader.load;
 
-import java.util.LinkedList;
-import java.util.List;
-
+import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.Prototype;
+import org.apache.sling.feature.diff.spi.FeatureElementComparator;
 
 public final class FeatureDiff {
 
-    public static FeatureDiff compareFeatures(Feature previous, Feature current) {
+    public static Feature compareFeatures(Feature previous, Feature current, String resultId) {
+        resultId = requireNonNull(resultId, "Impossible to create the Feature diff with a null id");
+
+        ArtifactId id = ArtifactId.parse(resultId);
+        return compareFeatures(previous, current, id);
+    }
+
+    public static Feature compareFeatures(Feature previous, Feature current, ArtifactId resultId) {
         previous = requireNonNull(previous, "Impossible to compare null previous feature.");
         current = requireNonNull(current, "Impossible to compare null current feature.");
 
-        if (!previous.getId().isSame(current.getId())) {
-            throw new IllegalArgumentException("Feature comparison has to be related to different versions of the same Feature.");
-        }
-
         if (previous.getId().equals(current.getId())) {
             throw new IllegalArgumentException("Input Features refer to the the same Feature version.");
         }
 
-        FeatureDiff featureDiff = new FeatureDiff(previous, current);
-
-        featureDiff.addSection(new GenericMapComparator("framework-properties").compare(previous.getFrameworkProperties(), current.getFrameworkProperties()));
-        featureDiff.addSection(new ArtifactsComparator("bundles").apply(previous.getBundles(), current.getBundles()));
-        featureDiff.addSection(new ConfigurationsComparator().apply(previous.getConfigurations(), current.getConfigurations()));
-        featureDiff.addSection(new RequirementsComparator().apply(previous.getRequirements(), current.getRequirements()));
-        featureDiff.addSection(new ExtensionsComparator().apply(previous.getExtensions(), current.getExtensions()));
-        featureDiff.addSection(new GenericMapComparator("variables").compare(previous.getVariables(), current.getVariables()));
-
-        return featureDiff;
-    }
+        resultId = requireNonNull(resultId, "Impossible to create the Feature diff with a null id");
 
-    private final List<DiffSection> diffSections = new LinkedList<>();
+        Feature target = new Feature(resultId);
+        target.setTitle(previous.getId() + " to " + current.getId());
+        target.setDescription(previous.getId() + " to " + current.getId() + " Feature upgrade");
 
-    private final Feature previous;
+        Prototype prototype = new Prototype(previous.getId());
+        target.setPrototype(prototype);
 
-    private final Feature current;
-
-    // this class can not be instantiated from outside
-    private FeatureDiff(Feature previous, Feature current) {
-        this.previous = previous;
-        this.current = current;
-    }
-
-    public Feature getPrevious() {
-        return previous;
-    }
-
-    public Feature getCurrent() {
-        return current;
-    }
-
-    protected void addSection(DiffSection diffSection) {
-        DiffSection checkedDiffSection = requireNonNull(diffSection, "Null diff section can not be added to the resulting diff");
-        if (!diffSection.isEmpty()) {
-            diffSections.add(checkedDiffSection);
+        for (FeatureElementComparator comparator : load(FeatureElementComparator.class)) {
+            comparator.computeDiff(previous, current, target);
         }
-    }
 
-    public boolean isEmpty() {
-        return diffSections.isEmpty();
+        return target;
     }
 
-    public Iterable<DiffSection> getSections() {
-        return diffSections;
+    /**
+     * this class must not be instantiated directly
+     */
+    private FeatureDiff() {
+        // do nothing
     }
 
 }
diff --git a/feature-diff/src/main/java/org/apache/sling/feature/diff/FrameworkPropertiesComparator.java b/feature-diff/src/main/java/org/apache/sling/feature/diff/FrameworkPropertiesComparator.java
new file mode 100644
index 0000000..1d4ce1a
--- /dev/null
+++ b/feature-diff/src/main/java/org/apache/sling/feature/diff/FrameworkPropertiesComparator.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.sling.feature.diff;
+
+import static java.util.Objects.deepEquals;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.diff.spi.FeatureElementComparator;
+
+final class FrameworkPropertiesComparator implements FeatureElementComparator {
+
+    @Override
+    public void computeDiff(Feature previous, Feature current, Feature target) {
+        computeDiff(previous.getFrameworkProperties(), current.getFrameworkProperties(), target);
+    }
+
+    public void computeDiff(Map<String, String> previous, Map<String, String> current, Feature target) {
+        for (Entry<String, String> previousEntry : previous.entrySet()) {
+            String previousKey = previousEntry.getKey();
+
+            if (!current.containsKey(previousKey)) {
+                target.getPrototype().getFrameworkPropertiesRemovals().add(previousKey);
+            } else {
+                String previousValue = previousEntry.getValue();
+                String currentValue = current.get(previousKey);
+
+                // override the previous set value
+                if (!deepEquals(previousValue, currentValue)) {
+                    target.getFrameworkProperties().put(previousKey, currentValue);
+                }
+            }
+        }
+
+        for (Entry<String, String> currentEntry : current.entrySet()) {
+            if (!previous.containsKey(currentEntry.getKey())) {
+                target.getFrameworkProperties().put(currentEntry.getKey(), currentEntry.getValue());
+            }
+        }
+    }
+
+
+}
diff --git a/feature-diff/src/main/java/org/apache/sling/feature/diff/GenericMapComparator.java b/feature-diff/src/main/java/org/apache/sling/feature/diff/GenericMapComparator.java
deleted file mode 100644
index 8c0ea24..0000000
--- a/feature-diff/src/main/java/org/apache/sling/feature/diff/GenericMapComparator.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package org.apache.sling.feature.diff;
-
-import static java.util.Objects.requireNonNull;
-
-import java.util.Map;
-import java.util.Map.Entry;
-
-import org.apache.commons.lang3.builder.EqualsBuilder;
-
-final class GenericMapComparator {
-
-    private final String id;
-
-    public GenericMapComparator(String id) {
-        this.id = requireNonNull(id, "Impossible to instantiate a generic Map comparator with null id");
-    }
-
-    public <V> DiffSection compare(Map<String, V> previous, Map<String, V> current) {
-        DiffSection diffSection = new DiffSection(id);
-
-        for (Entry<String, V> previousEntry : previous.entrySet()) {
-            String previousKey = previousEntry.getKey();
-
-            if (!current.containsKey(previousKey)) {
-                diffSection.markRemoved(previousKey);
-            } else {
-                V previousValue = previousEntry.getValue();
-                V currentValue = current.get(previousKey);
-
-                if (!new EqualsBuilder().reflectionAppend(previousValue, currentValue).isEquals()) {
-                    diffSection.markItemUpdated(previousKey, previousValue, currentValue);
-                }
-            }
-        }
-
-        for (String currentKey : current.keySet()) {
-            if (!previous.containsKey(currentKey)) {
-                diffSection.markAdded(currentKey);
-            }
-        }
-
-        return diffSection;
-    }
-
-
-}
diff --git a/feature-diff/src/main/java/org/apache/sling/feature/diff/RequirementsComparator.java b/feature-diff/src/main/java/org/apache/sling/feature/diff/RequirementsComparator.java
deleted file mode 100644
index 12b64da..0000000
--- a/feature-diff/src/main/java/org/apache/sling/feature/diff/RequirementsComparator.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package org.apache.sling.feature.diff;
-
-import java.util.List;
-
-import org.osgi.resource.Requirement;
-
-final class RequirementsComparator extends AbstractFeatureElementComparator<Requirement, List<Requirement>> {
-
-    public RequirementsComparator() {
-        super("requirements");
-    }
-
-    @Override
-    public DiffSection compare(Requirement previous, Requirement current) {
-        DiffSection diffSection = new DiffSection(getId(current));
-
-        diffSection.markUpdated(new GenericMapComparator("directives").compare(previous.getDirectives(), current.getDirectives()));
-        diffSection.markUpdated(new GenericMapComparator("attributes").compare(previous.getAttributes(), current.getAttributes()));
-
-        return diffSection;
-    }
-
-    @Override
-    protected String getId(Requirement item) {
-        return item.getNamespace();
-    }
-
-    @Override
-    protected Requirement find(Requirement item, List<Requirement> requirements) {
-        for (Requirement requirement : requirements) {
-            if (getId(item).equals(getId(requirement))) {
-                return requirement;
-            }
-        }
-        return null;
-    }
-
-}
diff --git a/feature-diff/src/main/java/org/apache/sling/feature/diff/UpdatedItem.java b/feature-diff/src/main/java/org/apache/sling/feature/diff/UpdatedItem.java
deleted file mode 100644
index a65a024..0000000
--- a/feature-diff/src/main/java/org/apache/sling/feature/diff/UpdatedItem.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package org.apache.sling.feature.diff;
-
-import static java.util.Objects.requireNonNull;
-
-import java.util.Objects;
-
-public final class UpdatedItem<T> {
-
-    private final String id;
-
-    private final T previous;
-
-    private final T current;
-
-    protected UpdatedItem(String id, T previous, T current) {
-        this.id = requireNonNull(id);
-        this.previous = previous;
-        this.current = current;
-    }
-
-    public String getId() {
-        return id;
-    }
-
-    public T getPrevious() {
-        return previous;
-    }
-
-    public T getCurrent() {
-        return current;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-
-        if (obj == null) {
-            return false;
-        }
-
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-
-        UpdatedItem<?> other = (UpdatedItem<?>) obj;
-        return Objects.equals(id, other.getId())
-                && Objects.equals(previous, other.getPrevious())
-                && Objects.equals(current, other.getCurrent());
-    }
-
-    @Override
-    public String toString() {
-        return "UpdatedItem [id=" + id + ", previous=" + previous + ", current=" + current + "]";
-    }
-
-}
diff --git a/feature-diff/src/main/java/org/apache/sling/feature/diff/io/json/FeatureDiffJSONSerializer.java b/feature-diff/src/main/java/org/apache/sling/feature/diff/io/json/FeatureDiffJSONSerializer.java
deleted file mode 100644
index d146dd3..0000000
--- a/feature-diff/src/main/java/org/apache/sling/feature/diff/io/json/FeatureDiffJSONSerializer.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package org.apache.sling.feature.diff.io.json;
-
-import static org.apache.commons.lang3.builder.ToStringStyle.NO_CLASS_NAME_STYLE;
-import static java.util.Objects.requireNonNull;
-
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.text.SimpleDateFormat;
-import java.util.Collections;
-import java.util.Date;
-
-import javax.json.Json;
-import javax.json.JsonValue;
-import javax.json.stream.JsonGenerator;
-import javax.json.stream.JsonGeneratorFactory;
-
-import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
-import org.apache.sling.feature.diff.DiffSection;
-import org.apache.sling.feature.diff.FeatureDiff;
-import org.apache.sling.feature.diff.UpdatedItem;
-
-public final class FeatureDiffJSONSerializer {
-
-    private static final JsonGeneratorFactory GENERATOR_FACTORY = Json.createGeneratorFactory(Collections.singletonMap(JsonGenerator.PRETTY_PRINTING, true));
-
-    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss Z");
-
-    public static void serializeFeatureDiff(FeatureDiff featureDiff, OutputStream output) {
-        requireNonNull(output, "Impossible to serialize Feature Diff to a null stream");
-        serializeFeatureDiff(featureDiff, new OutputStreamWriter(output));
-    }
-
-    public static void serializeFeatureDiff(FeatureDiff featureDiff, Writer writer) {
-        requireNonNull(featureDiff, "Impossible to serialize null Feature Diff");
-        requireNonNull(writer, "Impossible to serialize Feature Diff to a null stream");
-
-        JsonGenerator generator = GENERATOR_FACTORY.createGenerator(writer);
-        generator.writeStartObject();
-
-        generator.write("vendor", "The Apache Software Foundation");
-        generator.write("vendorURL", "http://www.apache.org/");
-        generator.write("generator", "Apache Sling Feature Diff tool");
-        generator.write("generatorURL", "https://github.com/apache/sling-org-apache-sling-feature-diff");
-        generator.write("generatedOn", DATE_FORMAT.format(new Date()));
-        generator.write("id", featureDiff.getCurrent().getId().toMvnId());
-        generator.write("previousVersion", featureDiff.getPrevious().getId().getVersion());
-
-        for (DiffSection diffSection : featureDiff.getSections()) {
-            serializeDiffSection(diffSection, generator);
-        }
-
-        generator.writeEnd().close();
-    }
-
-    private static void serializeDiffSection(DiffSection diffSection, JsonGenerator generator) {
-        generator.writeStartObject(diffSection.getId());
-
-        if (diffSection.hasRemoved()) {
-            writeArray("removed", diffSection.getRemoved(), generator);
-        }
-
-        if (diffSection.hasAdded()) {
-            writeArray("added", diffSection.getAdded(), generator);
-        }
-
-        if (diffSection.hasUpdatedItems() || diffSection.hasUpdates()) {
-            generator.writeStartObject("updated");
-
-            for (UpdatedItem<?> updatedItem : diffSection.getUpdatedItems()) {
-                generator.writeStartObject(updatedItem.getId());
-                writeValue("previous", updatedItem.getPrevious(), generator);
-                writeValue("current", updatedItem.getCurrent(), generator);
-                generator.writeEnd();
-            }
-
-            for (DiffSection updatesDiffSection : diffSection.getUpdates()) {
-                serializeDiffSection(updatesDiffSection, generator);
-            }
-
-            generator.writeEnd();
-        }
-
-        generator.writeEnd();
-    }
-
-    private static void writeArray(String name, Iterable<String> values, JsonGenerator generator) {
-        generator.writeStartArray(name);
-
-        for (String value : values) {
-            generator.write(value);
-        }
-
-        generator.writeEnd();
-    }
-
-    // TODO find a faster and more elegant implementation
-    private static <T> void writeValue(String key, T value, JsonGenerator generator) {
-        if (value == null) {
-            generator.write(key, JsonValue.NULL);
-        } else if (value instanceof Boolean) {
-            generator.write(key, ((Boolean) value).booleanValue());
-        } else if (value instanceof BigDecimal) {
-            generator.write(key, (BigDecimal) value);
-        } else if (value instanceof BigInteger) {
-            generator.write(key, (BigInteger) value);
-        } else if (value instanceof Integer) {
-            generator.write(key, (Integer) value);
-        } else if (value instanceof Long) {
-            generator.write(key, (Long) value);
-        } else if (value instanceof Double) {
-            generator.write(key, (Double) value);
-        } else if (value instanceof String) {
-            generator.write(key, (String) value);
-        } else {
-            generator.write(key, ReflectionToStringBuilder.toString(value, NO_CLASS_NAME_STYLE));
-        }
-    }
-
-    private FeatureDiffJSONSerializer() {
-        // this class can not be instantiated
-    }
-
-}
diff --git a/feature-diff/src/main/java/org/apache/sling/feature/diff/package-info.java b/feature-diff/src/main/java/org/apache/sling/feature/diff/package-info.java
index 01cc7aa..c8c9fa8 100644
--- a/feature-diff/src/main/java/org/apache/sling/feature/diff/package-info.java
+++ b/feature-diff/src/main/java/org/apache/sling/feature/diff/package-info.java
@@ -16,6 +16,6 @@
  */
 
 /**
- * Core APIs to compare different Apache Sling Feature Model versions.
+ * Core APIs to compare different Apache Sling Feature models.
  */
 package org.apache.sling.feature.diff;
diff --git a/feature-diff/src/main/java/org/apache/sling/feature/diff/ElementComparator.java b/feature-diff/src/main/java/org/apache/sling/feature/diff/spi/FeatureElementComparator.java
similarity index 79%
rename from feature-diff/src/main/java/org/apache/sling/feature/diff/ElementComparator.java
rename to feature-diff/src/main/java/org/apache/sling/feature/diff/spi/FeatureElementComparator.java
index dc36699..89cbe50 100644
--- a/feature-diff/src/main/java/org/apache/sling/feature/diff/ElementComparator.java
+++ b/feature-diff/src/main/java/org/apache/sling/feature/diff/spi/FeatureElementComparator.java
@@ -14,10 +14,12 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package org.apache.sling.feature.diff;
+package org.apache.sling.feature.diff.spi;
 
-public interface ElementComparator<T> {
+import org.apache.sling.feature.Feature;
 
-    DiffSection compare(T previous, T current);
+public interface FeatureElementComparator {
+
+    public void computeDiff(Feature previous, Feature current, Feature target);
 
 }
diff --git a/feature-diff/src/main/java/org/apache/sling/feature/diff/io/json/package-info.java b/feature-diff/src/main/java/org/apache/sling/feature/diff/spi/package-info.java
similarity index 87%
rename from feature-diff/src/main/java/org/apache/sling/feature/diff/io/json/package-info.java
rename to feature-diff/src/main/java/org/apache/sling/feature/diff/spi/package-info.java
index f74f569..42f68d8 100644
--- a/feature-diff/src/main/java/org/apache/sling/feature/diff/io/json/package-info.java
+++ b/feature-diff/src/main/java/org/apache/sling/feature/diff/spi/package-info.java
@@ -16,6 +16,6 @@
  */
 
 /**
- * JSON streaming serializer to represent the Feature diff result.
+ * APIs to compare different Apache Sling Feature Model versions.
  */
-package org.apache.sling.feature.diff.io.json;
+package org.apache.sling.feature.diff.spi;
diff --git a/feature-diff/src/main/resources/META-INF/services/org.apache.sling.feature.diff.spi.FeatureElementComparartor b/feature-diff/src/main/resources/META-INF/services/org.apache.sling.feature.diff.spi.FeatureElementComparartor
new file mode 100644
index 0000000..f3a8822
--- /dev/null
+++ b/feature-diff/src/main/resources/META-INF/services/org.apache.sling.feature.diff.spi.FeatureElementComparartor
@@ -0,0 +1,4 @@
+org.apache.sling.feature.diff.ArtifactsComparator
+org.apache.sling.feature.diff.ConfigurationsComparator
+org.apache.sling.feature.diff.FrameworkPropertiesComparator
+org.apache.sling.feature.diff.ExtensionsComparator
diff --git a/feature-diff/src/test/java/org/apache/sling/feature/diff/AbstractComparatorTest.java b/feature-diff/src/test/java/org/apache/sling/feature/diff/AbstractComparatorTest.java
new file mode 100644
index 0000000..e77708f
--- /dev/null
+++ b/feature-diff/src/test/java/org/apache/sling/feature/diff/AbstractComparatorTest.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.sling.feature.diff;
+
+import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.Prototype;
+import org.apache.sling.feature.diff.spi.FeatureElementComparator;
+import org.junit.After;
+import org.junit.Before;
+
+public abstract class AbstractComparatorTest<C extends FeatureElementComparator> {
+
+    protected Feature targetFeature;
+
+    protected C comparator;
+
+    @Before
+    public void setUp() {
+        targetFeature = new Feature(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.difftool:1.0"));
+        targetFeature.setPrototype(new Prototype(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.difftoolproto:1.0")));
+        comparator = newComparatorInstance();
+    }
+
+    @After
+    public void tearDown() {
+        targetFeature = null;
+        comparator = null;
+    }
+
+    protected abstract C newComparatorInstance();
+
+}
diff --git a/feature-diff/src/test/java/org/apache/sling/feature/diff/ApiRegionsComparatorTest.java b/feature-diff/src/test/java/org/apache/sling/feature/diff/ApiRegionsComparatorTest.java
deleted file mode 100644
index c624f87..0000000
--- a/feature-diff/src/test/java/org/apache/sling/feature/diff/ApiRegionsComparatorTest.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package org.apache.sling.feature.diff;
-
-import static org.junit.Assert.*;
-
-import org.apache.sling.feature.Extension;
-import org.apache.sling.feature.apiregions.model.ApiRegion;
-import org.apache.sling.feature.apiregions.model.ApiRegions;
-import org.apache.sling.feature.apiregions.model.io.json.ApiRegionsJSONSerializer;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class ApiRegionsComparatorTest {
-
-    private ExtensionsComparator extensionsComparator;
-
-    @Before
-    public void setUp() {
-        extensionsComparator = new ExtensionsComparator();
-    }
-
-    @After
-    public void tearDown() {
-        extensionsComparator = null;
-    }
-
-    @Test
-    public void checkRemoved() {
-        ApiRegions previousRegions = new ApiRegions();
-        ApiRegion base = previousRegions.addNew("base");
-        base.add("org.apache.felix.inventory");
-        base.add("org.apache.felix.metatype");
-
-        ApiRegion extended = previousRegions.addNew("extended");
-        extended.add("org.apache.felix.scr.component");
-        extended.add("org.apache.felix.scr.info");
-
-        ApiRegions currentRegions = new ApiRegions();
-        base = currentRegions.addNew("base");
-        base.add("org.apache.felix.inventory");
-        base.add("org.apache.felix.metatype");
-
-        DiffSection regionsDiff = serializeThenCompare(previousRegions, currentRegions);
-        assertEquals("extended", regionsDiff.getRemoved().iterator().next());
-    }
-
-    @Test
-    public void checkAdded() {
-        ApiRegions previousRegions = new ApiRegions();
-        ApiRegion base = previousRegions.addNew("base");
-        base.add("org.apache.felix.inventory");
-        base.add("org.apache.felix.metatype");
-
-        ApiRegions currentRegions = new ApiRegions();
-        base = currentRegions.addNew("base");
-        base.add("org.apache.felix.inventory");
-        base.add("org.apache.felix.metatype");
-
-        ApiRegion extended = currentRegions.addNew("extended");
-        extended.add("org.apache.felix.scr.component");
-        extended.add("org.apache.felix.scr.info");
-
-        DiffSection regionsDiff = serializeThenCompare(previousRegions, currentRegions);
-        assertEquals("extended", regionsDiff.getAdded().iterator().next());
-    }
-
-    @Test
-    public void checkUpdated() {
-        ApiRegions previousRegions = new ApiRegions();
-        ApiRegion base = previousRegions.addNew("base");
-        base.add("org.apache.felix.inventory");
-        base.add("org.apache.felix.metatype");
-
-        ApiRegions currentRegions = new ApiRegions();
-        base = currentRegions.addNew("base");
-        base.add("org.apache.felix.inventory");
-        base.add("org.apache.felix.scr.component");
-
-        DiffSection regionsDiff = serializeThenCompare(previousRegions, currentRegions);
-        DiffSection updatedDiff = regionsDiff.getUpdates().iterator().next();
-        assertFalse(updatedDiff.isEmpty());
-        assertEquals("base", updatedDiff.getId());
-        assertEquals("org.apache.felix.metatype", updatedDiff.getRemoved().iterator().next());
-        assertEquals("org.apache.felix.scr.component", updatedDiff.getAdded().iterator().next());
-    }
-
-    private DiffSection serializeThenCompare(ApiRegions previousRegions, ApiRegions currentRegions) {
-        Extension previousExtension = ApiRegionsJSONSerializer.serializeApiRegions(previousRegions);
-        Extension currentExtension = ApiRegionsJSONSerializer.serializeApiRegions(currentRegions);
-        DiffSection regionsDiff = extensionsComparator.compare(previousExtension, currentExtension);
-        assertNotNull(regionsDiff);
-        assertFalse(regionsDiff.isEmpty());
-        return regionsDiff;
-    }
-
-}
diff --git a/feature-diff/src/test/java/org/apache/sling/feature/diff/ArtifactsComparatorTest.java b/feature-diff/src/test/java/org/apache/sling/feature/diff/ArtifactsComparatorTest.java
index 673551d..cbcbef7 100644
--- a/feature-diff/src/test/java/org/apache/sling/feature/diff/ArtifactsComparatorTest.java
+++ b/feature-diff/src/test/java/org/apache/sling/feature/diff/ArtifactsComparatorTest.java
@@ -18,31 +18,18 @@ package org.apache.sling.feature.diff;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Artifacts;
-import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
 
-public class ArtifactsComparatorTest {
+public class ArtifactsComparatorTest extends AbstractComparatorTest<ArtifactsComparator> {
 
-    private ArtifactsComparator comparator;
-
-    @Before
-    public void setUp() {
-        comparator = new ArtifactsComparator("bundles");
-    }
-
-    @After
-    public void tearDown() {
-        comparator = null;
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void nullIdNotAcceptedByTheConstructor() {
-        new ArtifactsComparator(null);
+    @Override
+    protected ArtifactsComparator newComparatorInstance() {
+        return new ArtifactsComparator();
     }
 
     @Test
@@ -53,10 +40,10 @@ public class ArtifactsComparatorTest {
 
         Artifacts currentArtifacts = new Artifacts();
 
-        DiffSection artifactsDiff = comparator.apply(previousArtifacts, currentArtifacts);
+        comparator.computeDiff(previousArtifacts, currentArtifacts, targetFeature);
 
-        assertFalse(artifactsDiff.isEmpty());
-        assertEquals(previousArtifact.getId().toMvnId(), artifactsDiff.getRemoved().iterator().next());
+        assertFalse(targetFeature.getPrototype().getBundleRemovals().isEmpty());
+        assertEquals(previousArtifact.getId(), targetFeature.getPrototype().getBundleRemovals().iterator().next());
     }
 
     @Test
@@ -67,10 +54,10 @@ public class ArtifactsComparatorTest {
         Artifact currentArtifact = new Artifact(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:1.0.0"));
         currentArtifacts.add(currentArtifact);
 
-        DiffSection artifactsDiff = comparator.apply(previousArtifacts, currentArtifacts);
+        comparator.computeDiff(previousArtifacts, currentArtifacts, targetFeature);
 
-        assertFalse(artifactsDiff.isEmpty());
-        assertEquals(currentArtifact.getId().toMvnId(), artifactsDiff.getAdded().iterator().next());
+        assertTrue(targetFeature.getPrototype().getBundleRemovals().isEmpty());
+        assertEquals(currentArtifact.getId(), targetFeature.getBundles().iterator().next().getId());
     }
 
     @Test
@@ -83,19 +70,13 @@ public class ArtifactsComparatorTest {
         Artifact currentArtifact = new Artifact(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:1.0.0"));
         currentArtifacts.add(currentArtifact);
 
-        DiffSection artifactsDiff = comparator.apply(previousArtifacts, currentArtifacts);
-        assertFalse(artifactsDiff.isEmpty());
-
-        DiffSection artifactDiff = artifactsDiff.getUpdates().iterator().next();
-        for (UpdatedItem<?> updatedItem : artifactDiff.getUpdatedItems()) {
-            if ("version".equals(updatedItem.getId())) {
-                assertEquals(previousArtifact.getId().getVersion(), updatedItem.getPrevious());
-                assertEquals(currentArtifact.getId().getVersion(), updatedItem.getCurrent());
-            } else if ("start-order".equals(updatedItem.getId())) {
-                assertEquals(previousArtifact.getStartOrder(), updatedItem.getPrevious());
-                assertEquals(currentArtifact.getStartOrder(), updatedItem.getCurrent());
-            }
-        }
+        comparator.computeDiff(previousArtifacts, currentArtifacts, targetFeature);
+
+        assertFalse(targetFeature.getPrototype().getBundleRemovals().isEmpty());
+        assertEquals(previousArtifact.getId(), targetFeature.getPrototype().getBundleRemovals().iterator().next());
+
+        assertFalse(targetFeature.getBundles().isEmpty());
+        assertEquals(currentArtifact.getId(), targetFeature.getBundles().iterator().next().getId());
     }
 
 }
diff --git a/feature-diff/src/test/java/org/apache/sling/feature/diff/ConfigurationsComparatorTest.java b/feature-diff/src/test/java/org/apache/sling/feature/diff/ConfigurationsComparatorTest.java
index 77376c3..8e68926 100644
--- a/feature-diff/src/test/java/org/apache/sling/feature/diff/ConfigurationsComparatorTest.java
+++ b/feature-diff/src/test/java/org/apache/sling/feature/diff/ConfigurationsComparatorTest.java
@@ -19,25 +19,19 @@ package org.apache.sling.feature.diff;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Dictionary;
 
 import org.apache.sling.feature.Configuration;
 import org.apache.sling.feature.Configurations;
-import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
 
-public class ConfigurationsComparatorTest {
-
-    private ConfigurationsComparator comparator;
+public class ConfigurationsComparatorTest extends AbstractComparatorTest<ConfigurationsComparator> {
 
-    @Before
-    public void setUp() {
-        comparator = new ConfigurationsComparator();
-    }
-
-    @After
-    public void tearDown() {
-        comparator = null;
+    @Override
+    protected ConfigurationsComparator newComparatorInstance() {
+        return new ConfigurationsComparator();
     }
 
     @Test
@@ -48,10 +42,11 @@ public class ConfigurationsComparatorTest {
 
         Configurations currentConfigurations = new Configurations();
 
-        DiffSection configurationsDiff = comparator.apply(previousConfigurations, currentConfigurations);
-        assertFalse(configurationsDiff.isEmpty());
+        comparator.computeDiff(previousConfigurations, currentConfigurations, targetFeature);
 
-        assertEquals(previousConfiguration.getPid(), configurationsDiff.getRemoved().iterator().next());
+        assertFalse(targetFeature.getPrototype().getConfigurationRemovals().isEmpty());
+        assertEquals(previousConfiguration.getPid(), targetFeature.getPrototype().getConfigurationRemovals().iterator().next());
+        assertTrue(targetFeature.getConfigurations().isEmpty());
     }
 
     @Test
@@ -62,10 +57,12 @@ public class ConfigurationsComparatorTest {
         Configurations currentConfigurations = new Configurations();
         currentConfigurations.add(currentConfiguration);
 
-        DiffSection configurationsDiff = comparator.apply(previousConfigurations, currentConfigurations);
-        assertFalse(configurationsDiff.isEmpty());
+        comparator.computeDiff(previousConfigurations, currentConfigurations, targetFeature);
+
+        assertTrue(targetFeature.getPrototype().getConfigurationRemovals().isEmpty());
+        assertFalse(targetFeature.getConfigurations().isEmpty());
 
-        assertEquals(currentConfiguration.getPid(), configurationsDiff.getAdded().iterator().next());
+        assertEquals(currentConfiguration.getPid(), targetFeature.getConfigurations().iterator().next().getPid());
     }
 
     @Test
@@ -84,19 +81,16 @@ public class ConfigurationsComparatorTest {
         Configurations currentConfigurations = new Configurations();
         currentConfigurations.add(currentConfiguration);
 
-        DiffSection configurationsDiff = comparator.apply(previousConfigurations, currentConfigurations);
-        assertFalse(configurationsDiff.isEmpty());
+        comparator.computeDiff(previousConfigurations, currentConfigurations, targetFeature);
 
-        DiffSection configurationDiff = configurationsDiff.getUpdates().iterator().next();
+        assertTrue(targetFeature.getPrototype().getConfigurationRemovals().isEmpty());
+        assertFalse(targetFeature.getConfigurations().isEmpty());
 
-        assertEquals("removed", configurationDiff.getRemoved().iterator().next());
-        assertEquals("added", configurationDiff.getAdded().iterator().next());
+        assertEquals(currentConfiguration.getPid(), targetFeature.getConfigurations().iterator().next().getPid());
+        Dictionary<String, Object> properties = targetFeature.getConfigurations().iterator().next().getConfigurationProperties();
 
-        @SuppressWarnings("unchecked") // type known by design
-        UpdatedItem<String[]> updated = (UpdatedItem<String[]>) configurationDiff.getUpdatedItems().iterator().next();
-        assertEquals("updated", updated.getId());
-        assertArrayEquals(new String[] { "/log" }, updated.getPrevious());
-        assertArrayEquals(new String[] { "/log", "/etc" }, updated.getCurrent());
+        assertTrue((boolean) properties.get("added"));
+        assertArrayEquals(new String[] { "/log", "/etc" }, (String[]) properties.get("updated"));
     }
 
 }
diff --git a/feature-diff/src/test/java/org/apache/sling/feature/diff/DiffSectionTest.java b/feature-diff/src/test/java/org/apache/sling/feature/diff/DiffSectionTest.java
deleted file mode 100644
index b06fd39..0000000
--- a/feature-diff/src/test/java/org/apache/sling/feature/diff/DiffSectionTest.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package org.apache.sling.feature.diff;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Test;
-
-public class DiffSectionTest {
-
-    @Test(expected = NullPointerException.class)
-    public void requiresValidId() {
-        new DiffSection(null);
-    }
-
-    @Test
-    public void emptyCheck() {
-        DiffSection emptyDiff = new DiffSection("empty");
-        assertTrue(emptyDiff.isEmpty());
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void unacceptedNullAdded() {
-        new DiffSection("added").markAdded(null);
-    }
-
-    @Test
-    public void validAddedField() {
-        String expectedAddedValue = "sample";
-        DiffSection addedDiff = new DiffSection("added");
-        addedDiff.markAdded(expectedAddedValue);
-
-        assertFalse(addedDiff.isEmpty());
-        assertEquals(addedDiff.getAdded().iterator().next(), expectedAddedValue);
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void unacceptedNullRemoved() {
-        new DiffSection("removed").markRemoved(null);
-    }
-
-    @Test
-    public void validRemovedField() {
-        String expectedRemovedValue = "removed";
-        DiffSection removedDiff = new DiffSection("removed");
-        removedDiff.markRemoved(expectedRemovedValue);
-
-        assertFalse(removedDiff.isEmpty());
-        assertEquals(removedDiff.getRemoved().iterator().next(), expectedRemovedValue);
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void unacceptedNullUpdatedId() {
-        new DiffSection("updated").markItemUpdated(null, null, null);
-    }
-
-    @Test
-    public void validItemUpdated() {
-        UpdatedItem<String> expectedUpdatedItem = new UpdatedItem<String>("expected", "previous", "current");
-
-        DiffSection updatedDiff = new DiffSection("updated");
-        updatedDiff.markItemUpdated(expectedUpdatedItem.getId(), expectedUpdatedItem.getPrevious(), expectedUpdatedItem.getCurrent());
-
-        assertFalse(updatedDiff.isEmpty());
-        assertEquals(updatedDiff.getUpdatedItems().iterator().next(), expectedUpdatedItem);
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void unacceptedNullUpdated() {
-        new DiffSection("updated").markUpdated(null);
-    }
-
-    @Test
-    public void validUpdatedSubDiff() {
-        DiffSection mainDiff = new DiffSection("main");
-        DiffSection childDiff = new DiffSection("child");
-        mainDiff.markUpdated(childDiff);
-
-        assertTrue(mainDiff.isEmpty());
-    }
-
-}
diff --git a/feature-diff/src/test/java/org/apache/sling/feature/diff/FeatureDiffTest.java b/feature-diff/src/test/java/org/apache/sling/feature/diff/FeatureDiffTest.java
deleted file mode 100644
index 1230240..0000000
--- a/feature-diff/src/test/java/org/apache/sling/feature/diff/FeatureDiffTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package org.apache.sling.feature.diff;
-
-import static org.apache.sling.feature.diff.FeatureDiff.compareFeatures;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import org.apache.sling.feature.ArtifactId;
-import org.apache.sling.feature.Feature;
-import org.junit.Test;
-
-public class FeatureDiffTest {
-
-    @Test(expected = NullPointerException.class)
-    public void doesNotAcceptNullPreviousFeature() {
-        compareFeatures(null, null);
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void doesNotAcceptNullCurrentFeature() {
-        compareFeatures(new Feature(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:1.0.0")), null);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void doesNotAcceptDifferentFeatures() {
-        Feature previous = new Feature(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.apiregions:1.0.0"));
-        Feature current = new Feature(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:1.0.0"));
-        compareFeatures(previous, current);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void doesNotAcceptSameFeature() {
-        Feature previous = new Feature(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:1.0.0"));
-        Feature current = new Feature(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:1.0.0"));
-        compareFeatures(previous, current);
-    }
-
-    @Test
-    public void keepFeatureInputs() {
-        Feature previous = new Feature(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:0.9.0"));
-        Feature current = new Feature(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:1.0.0"));
-        FeatureDiff featureDiff = compareFeatures(previous, current);
-        assertTrue(featureDiff.isEmpty());
-        assertEquals(previous, featureDiff.getPrevious());
-        assertEquals(current, featureDiff.getCurrent());
-    }
-
-    @Test
-    public void frameworkPropertiesUpdated() {
-        Feature previous = new Feature(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:0.9.0"));
-        previous.getFrameworkProperties().put("env", "staging");
-
-        Feature current = new Feature(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:1.0.0"));
-        current.getFrameworkProperties().put("env", "prod");
-
-        FeatureDiff diff = compareFeatures(previous, current);
-        assertFalse(diff.isEmpty());
-
-        DiffSection fwPropertiesDiff = diff.getSections().iterator().next();
-        assertFalse(fwPropertiesDiff.isEmpty());
-
-        @SuppressWarnings("unchecked") // known type
-        UpdatedItem<String> updated = (UpdatedItem<String>) fwPropertiesDiff.getUpdatedItems().iterator().next();
-        assertEquals("env", updated.getId());
-        assertEquals("staging", updated.getPrevious());
-        assertEquals("prod", updated.getCurrent());
-    }
-
-}
diff --git a/feature-diff/src/test/java/org/apache/sling/feature/diff/GenericMapComparatorTest.java b/feature-diff/src/test/java/org/apache/sling/feature/diff/GenericMapComparatorTest.java
deleted file mode 100644
index a26b93a..0000000
--- a/feature-diff/src/test/java/org/apache/sling/feature/diff/GenericMapComparatorTest.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package org.apache.sling.feature.diff;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public final class GenericMapComparatorTest {
-
-    private GenericMapComparator comparator;
-    @Before
-    public void setUp() {
-        comparator = new GenericMapComparator("map");
-    }
-
-    @After
-    public void tearDown() {
-        comparator = null;
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void constructoreRequiresValidId() {
-        new GenericMapComparator(null);
-    }
-
-    @Test
-    public void checkRemoved() {
-        Map<String, String> previous = new HashMap<>();
-        previous.put("removed", "removed entry");
-
-        Map<String, String> current = new HashMap<>();
-
-        DiffSection mapsDiff = comparator.compare(previous, current);
-
-        assertFalse(mapsDiff.isEmpty());
-        assertEquals("removed", mapsDiff.getRemoved().iterator().next());
-    }
-
-    @Test
-    public void checkAdded() {
-        Map<String, String> previous = new HashMap<>();
-
-        Map<String, String> current = new HashMap<>();
-        current.put("added", "added entry");
-
-        DiffSection mapsDiff = comparator.compare(previous, current);
-
-        assertFalse(mapsDiff.isEmpty());
-        assertEquals("added", mapsDiff.getAdded().iterator().next());
-    }
-
-    @Test
-    public void checkUpdated() {
-        Map<String, String> previous = new HashMap<>();
-        previous.put("updated", "regular entry");
-
-        Map<String, String> current = new HashMap<>();
-        current.put("updated", "updated entry");
-
-        DiffSection mapsDiff = comparator.compare(previous, current);
-
-        assertFalse(mapsDiff.isEmpty());
-
-        @SuppressWarnings("unchecked") // type known by design
-        UpdatedItem<String> updated = (UpdatedItem<String>) mapsDiff.getUpdatedItems().iterator().next();
-        assertEquals("updated", updated.getId());
-        assertEquals("regular entry", updated.getPrevious());
-        assertEquals("updated entry", updated.getCurrent());
-    }
-
-}
diff --git a/feature-diff/src/test/java/org/apache/sling/feature/diff/UpdatedItemTest.java b/feature-diff/src/test/java/org/apache/sling/feature/diff/UpdatedItemTest.java
deleted file mode 100644
index 0e0fa6a..0000000
--- a/feature-diff/src/test/java/org/apache/sling/feature/diff/UpdatedItemTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package org.apache.sling.feature.diff;
-
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Test;
-
-public class UpdatedItemTest {
-
-    @Test(expected = NullPointerException.class)
-    public void requiresIdNotNull() {
-        new UpdatedItem<Object>(null, null, null);
-    }
-
-    @Test
-    public void expectedFields() {
-        String expectedId = "expectedId";
-        String expectedPrevious = "expectedPrevious";
-        String expectedCurrent = "expectedCurrent";
-        UpdatedItem<String> item = new UpdatedItem<String>(expectedId, expectedPrevious, expectedCurrent);
-
-        assertEquals(expectedId, item.getId());
-        assertEquals(expectedPrevious, item.getPrevious());
-        assertEquals(expectedCurrent, item.getCurrent());
-    }
-
-}
diff --git a/feature-diff/src/test/java/org/apache/sling/feature/diff/io/json/FeatureDiffJSONSerializerTest.java b/feature-diff/src/test/java/org/apache/sling/feature/diff/io/json/FeatureDiffJSONSerializerTest.java
deleted file mode 100644
index 63ad632..0000000
--- a/feature-diff/src/test/java/org/apache/sling/feature/diff/io/json/FeatureDiffJSONSerializerTest.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package org.apache.sling.feature.diff.io.json;
-
-import static org.apache.sling.feature.diff.FeatureDiff.compareFeatures;
-import static org.junit.Assert.assertEquals;
-
-import java.io.OutputStream;
-import java.io.StringWriter;
-import java.io.Writer;
-
-import org.apache.sling.feature.Artifact;
-import org.apache.sling.feature.ArtifactId;
-import org.apache.sling.feature.Configuration;
-import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.apiregions.model.ApiRegion;
-import org.apache.sling.feature.apiregions.model.ApiRegions;
-import org.apache.sling.feature.apiregions.model.io.json.ApiRegionsJSONSerializer;
-import org.apache.sling.feature.diff.FeatureDiff;
-import org.junit.Test;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.flipkart.zjsonpatch.JsonDiff;
-
-public class FeatureDiffJSONSerializerTest {
-
-    @Test(expected = NullPointerException.class)
-    public void nullOutputStreamNotAccepted() {
-        FeatureDiffJSONSerializer.serializeFeatureDiff(null, (OutputStream) null);
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void nullFeatureDiffNotAccepted() {
-        FeatureDiffJSONSerializer.serializeFeatureDiff(null, (Writer) null);
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void nullWriterNotAccepted() {
-        Feature previous = new Feature(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:0.9.0"));
-        Feature current = new Feature(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:1.0.0"));
-        FeatureDiff featureDiff = compareFeatures(previous, current);
-
-        FeatureDiffJSONSerializer.serializeFeatureDiff(featureDiff , (Writer) null);
-    }
-
-    @Test
-    public void serialization() throws Exception {
-        ObjectMapper objectMapper = new ObjectMapper();
-
-        // the expected result
-
-        JsonNode expectedNode = objectMapper.readTree(getClass().getResourceAsStream("expectedDiff.json"));
-
-        // define the previous Feature
-
-        Feature previous = new Feature(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:0.9.0"));
-        previous.getFrameworkProperties().put("env", "staging");
-        previous.getFrameworkProperties().put("sling.framework.install.incremental", "true");
-        previous.getBundles().add(new Artifact(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:removed:1.0.0")));
-        previous.getBundles().add(new Artifact(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:updated:1.0.0")));
-        previous.getConfigurations().add(new Configuration("org.apache.sling.feature.diff.config.removed"));
-
-        Configuration previousConfiguration = new Configuration("org.apache.sling.feature.diff.config.updated");
-        previousConfiguration.getProperties().put("it.will.appear.in.the.removed.section", 123);
-        previousConfiguration.getProperties().put("it.will.appear.in.the.updated.section", new String[] { "/log" });
-        previous.getConfigurations().add(previousConfiguration);
-
-        ApiRegions previousRegions = new ApiRegions();
-        ApiRegion base = previousRegions.addNew("base");
-        base.add("org.apache.felix.inventory");
-        base.add("org.apache.felix.metatype");
-
-        ApiRegionsJSONSerializer.serializeApiRegions(previousRegions, previous);
-
-        // define the current Feature
-
-        Feature current = new Feature(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:1.0.0"));
-        current.getFrameworkProperties().put("env", "prod");
-        current.getFrameworkProperties().put("sling.framework.install.startlevel", "1");
-        current.getBundles().add(new Artifact(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:added:1.0.0")));
-        current.getBundles().add(new Artifact(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:updated:2.0.0")));
-        current.getConfigurations().add(new Configuration("org.apache.sling.feature.diff.config.added"));
-
-        Configuration currentConfiguration = new Configuration("org.apache.sling.feature.diff.config.updated");
-        currentConfiguration.getProperties().put("it.will.appear.in.the.updated.section", new String[] { "/log", "/etc" });
-        currentConfiguration.getProperties().put("it.will.appear.in.the.added.section", true);
-        current.getConfigurations().add(currentConfiguration);
-
-        ApiRegions currentRegions = new ApiRegions();
-        base = currentRegions.addNew("base");
-        base.add("org.apache.felix.inventory");
-        base.add("org.apache.felix.scr.component");
-
-        ApiRegionsJSONSerializer.serializeApiRegions(currentRegions, current);
-
-        // now compare
-
-        FeatureDiff featureDiff = compareFeatures(previous, current);
-
-        StringWriter stringWriter = new StringWriter();
-        FeatureDiffJSONSerializer.serializeFeatureDiff(featureDiff , stringWriter);
-
-        JsonNode actualNode = objectMapper.readTree(stringWriter.toString());
-
-        // assert the differences
-        JsonNode patchNode = JsonDiff.asJson(expectedNode, actualNode);
-        // expected 1 node as diff, i.e. [{"op":"replace","path":"/generatedOn","value":"2019-04-04T12:38:46 +0200"}]
-        assertEquals(patchNode.toString(), 1, patchNode.size());
-    }
-
-}
diff --git a/feature-diff/src/test/resources/org/apache/sling/feature/diff/io/json/expectedDiff.json b/feature-diff/src/test/resources/org/apache/sling/feature/diff/io/json/expectedDiff.json
deleted file mode 100644
index 09e56b2..0000000
--- a/feature-diff/src/test/resources/org/apache/sling/feature/diff/io/json/expectedDiff.json
+++ /dev/null
@@ -1,81 +0,0 @@
-{
-  "vendor":"The Apache Software Foundation",
-  "vendorURL":"http://www.apache.org/",
-  "generator":"Apache Sling Feature Diff tool",
-  "generatorURL":"https://github.com/apache/sling-org-apache-sling-feature-diff",
-  "generatedOn":"2019-04-11T03:12:44 +0200",
-  "id":"org.apache.sling:org.apache.sling.feature.diff:1.0.0",
-  "previousVersion":"0.9.0",
-  "framework-properties":{
-    "removed":[
-      "sling.framework.install.incremental"
-    ],
-    "added":[
-      "sling.framework.install.startlevel"
-    ],
-    "updated":{
-      "env":{
-        "previous":"staging",
-        "current":"prod"
-      }
-    }
-  },
-  "bundles":{
-    "removed":[
-      "org.apache.sling:org.apache.sling.feature.diff:removed:1.0.0"
-    ],
-    "added":[
-      "org.apache.sling:org.apache.sling.feature.diff:added:1.0.0"
-    ],
-    "updated":{
-      "org.apache.sling:org.apache.sling.feature.diff:updated:2.0.0":{
-        "updated":{
-          "version":{
-            "previous":"1.0.0",
-            "current":"2.0.0"
-          }
-        }
-      }
-    }
-  },
-  "configurations":{
-    "removed":[
-      "org.apache.sling.feature.diff.config.removed"
-    ],
-    "added":[
-      "org.apache.sling.feature.diff.config.added"
-    ],
-    "updated":{
-      "org.apache.sling.feature.diff.config.updated":{
-        "removed":[
-          "it.will.appear.in.the.removed.section"
-        ],
-        "added":[
-          "it.will.appear.in.the.added.section"
-        ],
-        "updated":{
-          "it.will.appear.in.the.updated.section":{
-            "previous":"[{/log}]",
-            "current":"[{/log,/etc}]"
-          }
-        }
-      }
-    }
-  },
-  "extensions":{
-    "updated":{
-      "api-regions:JSON|false":{
-        "updated":{
-          "base":{
-            "removed":[
-              "org.apache.felix.metatype"
-            ],
-            "added":[
-              "org.apache.felix.scr.component"
-            ]
-          }
-        }
-      }
-    }
-  }
-}
\ No newline at end of file