You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 09:58:15 UTC

[sling-org-apache-sling-provisioning-model] annotated tag org.apache.sling.provisioning.model-1.4.0 created (now 4517eab)

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

rombert pushed a change to annotated tag org.apache.sling.provisioning.model-1.4.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-provisioning-model.git.


      at 4517eab  (tag)
 tagging e888295bc7ec20f67bbfca25770bf6e56030c5c5 (commit)
      by Carsten Ziegeler
      on Wed Oct 21 13:03:39 2015 +0000

- Log -----------------------------------------------------------------
org.apache.sling.provisioning.model-1.4.0
-----------------------------------------------------------------------

This annotated tag includes the following new commits:

     new 11a204a  Add slingstart model
     new 03f094c  Add slingstart model
     new a86fcd4  Update javadocs and rename subsystem to deliverable and run mode to feature
     new 58cc5a6  Add simple txt parser
     new 43a5c06  Add simple txt parser
     new fdc1ccf  Fully support Apach Felix config file format. Move from properties to getter and setter methods
     new 470e4bc  Fully support Apach Felix config file format. Move from properties to getter and setter methods
     new 9857e6c  Fully support Apach Felix config file format. Move from properties to getter and setter methods
     new 75a411c  Simplify model, move completely to getter/setter methods
     new 1a36bd0  Implement txt format for reading and writing
     new 7bee16a  Add merger utility and variable replacement
     new ca9289e  Finish configuration support
     new 245be2d  Add resolver to resolve variables from environment, system properties etc.
     new ffb35f8  Don't output jar extension if not necessary
     new 252bf2d  Remove support for xml
     new 456ebb1  Implement provisioning model
     new 380d68e  Rename some constants
     new c58f1ab  Cleanup methods and constants
     new d3b6bc4  Update to Sling Parent POM 22 with baselining enabled
     new 5154296  Correct method name
     new 55e51ed  Remove old slingstart model
     new 4b98c32  Refactor model and add basic read/write test
     new 98736d5  Consistent method naming and javadocs
     new 2341da7  Rename to provisoning model
     new 4d55db0  Rename to provisioning model
     new dc48bc1  Keep ${sling.home} in settings
     new fb3de37  Remove artifact on merge regardless of the start level
     new 43e7bcf  Fix writing out raw configs
     new 54361e3  Fix writing out raw configs
     new d71c810  Fix reading configs
     new bbe9bcd  Allow comments in configurations
     new bccda6c  Add some tests to verify the parsed and effective models
     new c47da2f  [maven-release-plugin] prepare release org.apache.sling.provisioning.model-1.0.0
     new e93aff8  [maven-release-plugin] prepare for next development iteration
     new 7aee9d5  Fix comments
     new ee05f2c  Update to latest launchpad base release
     new 523138f  SLING-4473 : Provide a way to remove settings, artifacts and features through a model SLING-4475 : Artifact is twice in feature after merge
     new 94c918f  [maven-release-plugin] prepare release org.apache.sling.provisioning.model-1.1.0
     new 15d9a12  [maven-release-plugin] prepare for next development iteration
     new 89e8cf9  Add test case for run modes
     new 97ac28f  Update test case
     new 52ee32c  Fix javadocs
     new 7a21cc8  Add missing licence headers
     new ceeb1f7  SLING-4698 - Set parent.relativePath to empty for all modules
     new 43429c2  SLING-4768 : Sort entries alphabetical in written model
     new fba2533  SLING-4768 : Sort entries alphabetical in written model
     new ee04adc  SLING-4768 : Sort entries alphabetical in written model
     new 9c0eaaa  SLING-4766 Provisioning Model does not support adding variables to Sling Settings - allow escaping with \${var}
     new 9dcb074  SLING-4768 : Sort entries alphabetical in written model
     new 1f2cf68  SLING-4782 : Update to latest format from Apache Felix config admin
     new f66c21e  SLING-4786 : Attributes and configuration format are wronlgy written out
     new 2192bda  SLING-4126 : Provide a mechanism to merge configurations
     new 5f9f27c  SLING-4126 : Provide a mechanism to merge configurations
     new bd96e54  SLING-4124 : Bootstrap command file can't be appended
     new c2b343f  SLING-4790 : Removal of artifacts from inherited model is not possible
     new 9c0fcd5  [maven-release-plugin] prepare release org.apache.sling.provisioning.model-1.2.0
     new 690eb71  [maven-release-plugin] prepare for next development iteration
     new 0972528  SLING-4807 : Variables in configurations are not replaced
     new 312ade9  Update to Sling Parent 23
     new 1d0ef5f  Remove superflous sling.java.version=6 as it's the default now
     new 58dbd4a  set parent version to 24 and add empty relativePath where missing
     new 3509c1f  SLING-4645 Update Tika to 1.9
     new bbf7873  SLING-4880 Allow to get artifact versions from POM
     new 6a392fa  SLING-4879 refactor model processing logic from ModelUtility#getEffectiveModel into separate class to make it reusable
     new 82e4d3f  SLING-4879 move resolver/replace logic to separate class ModelResolverUtility
     new 7d6e31f  SLING-4807 make sure variable replacement is applied whether custom resolver exists or not
     new 5eea7de  SLING-4879 add ModelUtility.applyVariables method that allows to inject further variables from a custom variable resolver
     new ce9607f  SLING-4880 apply resolved dependencies to raw model instead of attaching effective model add new optional parameter "allowUnresolvedPomDependencies" (default: false)
     new 84e3f39  fix some javadoc errors java 8 complains about
     new 9a36bf7  SLING-4807 introduce explicit boolean flag to make sure variable replacement does not take place when merging models
     new e3b7720  [maven-release-plugin] prepare release org.apache.sling.provisioning.model-1.3.0
     new b0181cd  [maven-release-plugin] prepare for next development iteration
     new 9a80232  Update the main reactor to parent 25
     new b36cc99  SLING-5148 : Support OSGi Subsystems in the Sling Provisioning model. Apply patch from David Bosschaert
     new d0779ce  SLING-5172 : Provide support for custom sections in the provisioning model
     new 7fa8736  SLING-5148 : Support OSGi Subsystems in the Sling Provisioning model.
     new 94bc251  SLING-5174 : Refactor merging of models into separate utility class and add merge options
     new 4a90126  git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/tooling/support/provisioning-model@1709754 13f79535-47bb-0310-9956-ffa450edef68
     new b0f38e7  SLING-5172 : Provide support for custom sections in the provisioning model
     new 42a911c  SLING-5174 : Refactor merging of models into separate utility class and add merge options
     new b05b9ce  [maven-release-plugin] prepare release org.apache.sling.provisioning.model-1.4.0
     new e888295  [maven-release-plugin] copy for tag org.apache.sling.provisioning.model-1.4.0

The 82 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


-- 
To stop receiving notification emails like this one, please contact
['"commits@sling.apache.org" <co...@sling.apache.org>'].

[sling-org-apache-sling-provisioning-model] 03/11: SLING-5148 : Support OSGi Subsystems in the Sling Provisioning model. Apply patch from David Bosschaert

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.provisioning.model-1.4.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-provisioning-model.git

commit b36cc995b238a5cde86a0c8f72b84286dd656ec1
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Tue Oct 20 10:00:50 2015 +0000

    SLING-5148 : Support OSGi Subsystems in the Sling Provisioning model. Apply patch from David Bosschaert
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/tooling/support/provisioning-model@1709560 13f79535-47bb-0310-9956-ffa450edef68
---
 .../apache/sling/provisioning/model/Feature.java   | 41 +++++++++++++++++-
 .../sling/provisioning/model/ModelProcessor.java   |  1 +
 .../sling/provisioning/model/ModelUtility.java     |  1 +
 .../sling/provisioning/model/io/ModelReader.java   |  3 +-
 .../sling/provisioning/model/io/ModelWriter.java   |  4 ++
 .../sling/provisioning/model/package-info.java     |  2 +-
 .../sling/provisioning/model/FeatureTest.java      | 50 ++++++++++++++++++++++
 .../provisioning/model/ModelProcessorTest.java     | 44 ++++++++++---------
 .../sling/provisioning/model/ModelUtilityTest.java | 10 +++--
 .../org/apache/sling/provisioning/model/U.java     | 17 ++++----
 src/test/resources/example.txt                     |  2 +-
 src/test/resources/merge/artifact-merge.txt        |  2 +-
 12 files changed, 139 insertions(+), 38 deletions(-)

diff --git a/src/main/java/org/apache/sling/provisioning/model/Feature.java b/src/main/java/org/apache/sling/provisioning/model/Feature.java
index 3e851d7..1d892ec 100644
--- a/src/main/java/org/apache/sling/provisioning/model/Feature.java
+++ b/src/main/java/org/apache/sling/provisioning/model/Feature.java
@@ -32,9 +32,40 @@ public class Feature
     extends Commentable
     implements Comparable<Feature> {
 
+    public enum Type {
+        PLAIN("plain"),
+        SUBSYSTEM_FEATURE("osgi.subsystem.feature"),
+        SUBSYSTEM_APPLICATION("osgi.subsystem.application"),
+        SUBSYSTEM_COMPOSITE("osgi.subsystem.composite");
+
+        private final String textRepresentation;
+
+        private Type(final String textRep) {
+            textRepresentation = textRep;
+        }
+
+        public String getTextRepresentation() {
+            return textRepresentation;
+        }
+
+        public static Type fromTextRepresentation(final String textRep) {
+            if (textRep == null)
+                return PLAIN;
+
+            for (final Type t : values()) {
+                if (t.getTextRepresentation().equals(textRep))
+                    return t;
+            }
+            return null;
+        }
+    }
+
     /** All run modes. */
     private final List<RunMode> runModes = new ArrayList<RunMode>();
 
+    /** The type of feature */
+    private volatile Type type = Type.PLAIN;
+
     /** Variables. */
     private final KeyValueMap<String> variables = new KeyValueMap<String>();
 
@@ -113,6 +144,14 @@ public class Feature
         return result;
     }
 
+    public Type getType() {
+        return type;
+    }
+
+    public void setType(Type t) {
+        type = t;
+    }
+
     @Override
     public int compareTo(final Feature o) {
         if ( this.name == null ) {
@@ -131,8 +170,8 @@ public class Feature
     public String toString() {
         return "Feature [runModes=" + runModes + ", variables=" + variables
                 + ", name=" + name
+                + ( type != Type.PLAIN ? ", type=" + type : "" )
                 + ( this.getLocation() != null ? ", location=" + this.getLocation() : "")
                 + "]";
     }
-
 }
diff --git a/src/main/java/org/apache/sling/provisioning/model/ModelProcessor.java b/src/main/java/org/apache/sling/provisioning/model/ModelProcessor.java
index 1f18624..ad4fcfc 100644
--- a/src/main/java/org/apache/sling/provisioning/model/ModelProcessor.java
+++ b/src/main/java/org/apache/sling/provisioning/model/ModelProcessor.java
@@ -39,6 +39,7 @@ class ModelProcessor {
 
         for(final Feature feature : model.getFeatures()) {
             final Feature newFeature = result.getOrCreateFeature(feature.getName());
+            newFeature.setType(feature.getType());
             newFeature.setComment(feature.getComment());
             newFeature.setLocation(feature.getLocation());
 
diff --git a/src/main/java/org/apache/sling/provisioning/model/ModelUtility.java b/src/main/java/org/apache/sling/provisioning/model/ModelUtility.java
index 968e32e..806975d 100644
--- a/src/main/java/org/apache/sling/provisioning/model/ModelUtility.java
+++ b/src/main/java/org/apache/sling/provisioning/model/ModelUtility.java
@@ -51,6 +51,7 @@ public abstract class ModelUtility {
         // features
         for(final Feature feature : additional.getFeatures()) {
             final Feature baseFeature = base.getOrCreateFeature(feature.getName());
+            baseFeature.setType(feature.getType());
 
             // variables
             baseFeature.getVariables().putAll(feature.getVariables());
diff --git a/src/main/java/org/apache/sling/provisioning/model/io/ModelReader.java b/src/main/java/org/apache/sling/provisioning/model/io/ModelReader.java
index 4d1f9b2..ec37e48 100644
--- a/src/main/java/org/apache/sling/provisioning/model/io/ModelReader.java
+++ b/src/main/java/org/apache/sling/provisioning/model/io/ModelReader.java
@@ -39,7 +39,7 @@ public class ModelReader {
 
     private enum CATEGORY {
         NONE(null, null),
-        FEATURE("feature", new String[] {"name"}),
+        FEATURE("feature", new String[] {"name", "type"}),
         VARIABLES("variables", null),
         ARTIFACTS("artifacts", new String[] {"runModes", "startLevel"}),
         SETTINGS("settings", new String[] {"runModes"}),
@@ -174,6 +174,7 @@ public class ModelReader {
                                        throw new IOException(exceptionPrefix + "Duplicate feature in line " + this.lineNumberReader.getLineNumber() + ": " + line);
                                    }
                                    this.feature = model.getOrCreateFeature(name);
+                                   this.feature.setType(Feature.Type.fromTextRepresentation(parameters.get("type")));
                                    this.init(this.feature);
                                    this.runMode = null;
                                    this.artifactGroup = null;
diff --git a/src/main/java/org/apache/sling/provisioning/model/io/ModelWriter.java b/src/main/java/org/apache/sling/provisioning/model/io/ModelWriter.java
index 3824756..d1fe473 100644
--- a/src/main/java/org/apache/sling/provisioning/model/io/ModelWriter.java
+++ b/src/main/java/org/apache/sling/provisioning/model/io/ModelWriter.java
@@ -87,6 +87,10 @@ public class ModelWriter {
             writeComment(pw, feature);
             pw.print("[feature name=");
             pw.print(feature.getName());
+            if (feature.getType() != Feature.Type.PLAIN) {
+                pw.print(" type=");
+                pw.print(feature.getType().getTextRepresentation());
+            }
             pw.println("]");
             pw.println();
 
diff --git a/src/main/java/org/apache/sling/provisioning/model/package-info.java b/src/main/java/org/apache/sling/provisioning/model/package-info.java
index 46ea4e8..656dcb0 100644
--- a/src/main/java/org/apache/sling/provisioning/model/package-info.java
+++ b/src/main/java/org/apache/sling/provisioning/model/package-info.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-@Version("1.3.0")
+@Version("1.4.0")
 package org.apache.sling.provisioning.model;
 
 import aQute.bnd.annotation.Version;
diff --git a/src/test/java/org/apache/sling/provisioning/model/FeatureTest.java b/src/test/java/org/apache/sling/provisioning/model/FeatureTest.java
new file mode 100644
index 0000000..a1b9a54
--- /dev/null
+++ b/src/test/java/org/apache/sling/provisioning/model/FeatureTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.provisioning.model;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class FeatureTest {
+    @Test
+    public void testTypeEnum() {
+        assertEquals(Feature.Type.SUBSYSTEM_APPLICATION,
+                Feature.Type.fromTextRepresentation("osgi.subsystem.application"));
+        assertEquals(Feature.Type.SUBSYSTEM_COMPOSITE,
+                Feature.Type.fromTextRepresentation("osgi.subsystem.composite"));
+        assertEquals(Feature.Type.SUBSYSTEM_FEATURE,
+                Feature.Type.fromTextRepresentation("osgi.subsystem.feature"));
+        assertEquals(Feature.Type.PLAIN, Feature.Type.fromTextRepresentation(null));
+
+        assertEquals("osgi.subsystem.application",
+                Feature.Type.SUBSYSTEM_APPLICATION.getTextRepresentation());
+        assertEquals("osgi.subsystem.composite",
+                Feature.Type.SUBSYSTEM_COMPOSITE.getTextRepresentation());
+        assertEquals("osgi.subsystem.feature",
+                Feature.Type.SUBSYSTEM_FEATURE.getTextRepresentation());
+    }
+
+    @Test
+    public void testFeatureType() {
+        Feature f = new Feature("blah");
+        assertEquals(Feature.Type.PLAIN, f.getType());
+
+        f.setType(Feature.Type.SUBSYSTEM_APPLICATION);
+        assertEquals(Feature.Type.SUBSYSTEM_APPLICATION, f.getType());
+    }
+}
diff --git a/src/test/java/org/apache/sling/provisioning/model/ModelProcessorTest.java b/src/test/java/org/apache/sling/provisioning/model/ModelProcessorTest.java
index b8e4145..16efe69 100644
--- a/src/test/java/org/apache/sling/provisioning/model/ModelProcessorTest.java
+++ b/src/test/java/org/apache/sling/provisioning/model/ModelProcessorTest.java
@@ -18,33 +18,34 @@
  */
 package org.apache.sling.provisioning.model;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
 import java.util.Enumeration;
 import java.util.Map.Entry;
 
 import org.junit.Before;
 import org.junit.Test;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
 public class ModelProcessorTest {
-    
+
     private Model testModel;
     private ModelProcessor underTest;
-    
+
     @Before
     public void setUp() {
         testModel = new Model();
         testModel.setLocation("LocM1");
-        
+
         Feature feature1 = testModel.getOrCreateFeature("feature1");
         feature1.setLocation("LocF1");
         feature1.setComment("ComF1");
+        feature1.setType(Feature.Type.SUBSYSTEM_COMPOSITE);
         feature1.getVariables().setLocation("LocFV1");
         feature1.getVariables().setComment("ComFV1");
         feature1.getVariables().put("k1", "v1");
         feature1.getVariables().put("k2", "v2");
-        
+
         RunMode runMode11 = feature1.getOrCreateRunMode(new String[] { "rm1", "rm2"});
         runMode11.setLocation("LocRM11");
 
@@ -52,19 +53,19 @@ public class ModelProcessorTest {
         ArtifactGroup group12 = runMode12.getOrCreateArtifactGroup(10);
         group12.setLocation("LocRMG11");
         group12.setComment("ComRMG11");
-        
+
         group12.add(new Artifact("g1", "a1", "v1", "c1", "t1"));
         group12.add(new Artifact("g2", "a2", "v2", null, null));
-        
+
         runMode12.getConfigurations().setLocation("LocConf12");
         runMode12.getConfigurations().setComment("ComConf12");
-        
+
         Configuration conf121 = runMode12.getOrCreateConfiguration("pid1", null);
         conf121.setLocation("LocConf121");
         conf121.setComment("ComConf121");
         conf121.getProperties().put("conf1", "v1");
         conf121.getProperties().put("conf2", "v2");
-        
+
         Configuration conf122 = runMode12.getOrCreateConfiguration("pid2", "fac2");
         conf122.setLocation("LocConf122");
         conf122.setComment("ComConf122");
@@ -72,16 +73,16 @@ public class ModelProcessorTest {
 
         runMode12.getSettings().setLocation("LocSet12");
         runMode12.getSettings().setComment("ComSet12");
-        
+
         runMode12.getSettings().put("set1", "v1");
         runMode12.getSettings().put("set2", "v2");
-        
+
         Feature feature2 = testModel.getOrCreateFeature("feature1");
 
         RunMode runMode21 = feature2.getOrCreateRunMode(new String[0]);
         ArtifactGroup group21 = runMode21.getOrCreateArtifactGroup(20);
         group21.add(new Artifact("g3", "a3", null, null, null));
-        
+
         underTest = new TestModelProcessor();
     }
 
@@ -90,11 +91,12 @@ public class ModelProcessorTest {
         Model model = underTest.process(testModel);
 
         assertEquals("LocM1", model.getLocation());
-        
+
         Feature feature1 = model.getFeature("feature1");
         assertNotNull(feature1);
         assertEquals("LocF1", feature1.getLocation());
         assertEquals("ComF1", feature1.getComment());
+        assertEquals(Feature.Type.SUBSYSTEM_COMPOSITE, feature1.getType());
         assertEquals("LocFV1", feature1.getVariables().getLocation());
         assertEquals("ComFV1", feature1.getVariables().getComment());
         assertEquals("#v1", feature1.getVariables().get("k1"));
@@ -111,20 +113,20 @@ public class ModelProcessorTest {
         assertNotNull(group12);
         assertEquals("LocRMG11", group12.getLocation());
         assertEquals("ComRMG11", group12.getComment());
-        
+
         U.assertArtifactsInGroup(group12, 2);
         U.assertArtifact(group12, "mvn:#g1/#a1/#v1/#t1/#c1");
         U.assertArtifact(group12, "mvn:#g2/#a2/#v2/#jar");
 
         assertEquals("LocConf12", runMode12.getConfigurations().getLocation());
         assertEquals("ComConf12", runMode12.getConfigurations().getComment());
-        
+
         Configuration conf121 = runMode12.getConfiguration("pid1", null);
         assertEquals("LocConf121", conf121.getLocation());
         assertEquals("ComConf121", conf121.getComment());
         assertEquals("#v1", conf121.getProperties().get("conf1"));
         assertEquals("#v2", conf121.getProperties().get("conf2"));
-        
+
         Configuration conf122 = runMode12.getConfiguration("pid2", "fac2");
         assertEquals("LocConf122", conf122.getLocation());
         assertEquals("ComConf122", conf122.getComment());
@@ -143,12 +145,12 @@ public class ModelProcessorTest {
 
         ArtifactGroup group21 = runMode21.getArtifactGroup(20);
         assertNotNull(group21);
-        
+
         U.assertArtifactsInGroup(group21, 1);
         U.assertArtifact(group21, "mvn:#g3/#a3/#LATEST/#jar");
     }
-    
-    
+
+
     static final class TestModelProcessor extends ModelProcessor {
 
         @Override
diff --git a/src/test/java/org/apache/sling/provisioning/model/ModelUtilityTest.java b/src/test/java/org/apache/sling/provisioning/model/ModelUtilityTest.java
index 7440d0a..b902ac1 100644
--- a/src/test/java/org/apache/sling/provisioning/model/ModelUtilityTest.java
+++ b/src/test/java/org/apache/sling/provisioning/model/ModelUtilityTest.java
@@ -16,16 +16,16 @@
  */
 package org.apache.sling.provisioning.model;
 
+import java.util.List;
+
+import org.junit.Test;
+
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
-import java.util.List;
-
-import org.junit.Test;
-
 public class ModelUtilityTest {
 
     @Test public void mergeArtifactsTest() throws Exception {
@@ -41,6 +41,7 @@ public class ModelUtilityTest {
         final List<Artifact> list = U.assertArtifactsInGroup(model.getFeature("f").getRunMode().getArtifactGroup(3), 1);
 
         U.assertArtifact(list.get(0), "g", "a", "2.0.0", "jar", null);
+        assertEquals(Feature.Type.SUBSYSTEM_COMPOSITE, model.getFeature("f").getType());
     }
 
     @Test public void removeTest() throws Exception {
@@ -51,6 +52,7 @@ public class ModelUtilityTest {
         assertNotNull(model.getFeature("f").getRunMode());
         assertNotNull(model.getFeature("f").getRunMode().getArtifactGroup(5));
         assertNotNull(model.getFeature("f").getRunMode().getArtifactGroup(7));
+        assertEquals(Feature.Type.PLAIN, model.getFeature("f").getType());
 
         final List<Artifact> group5 = U.assertArtifactsInGroup(model.getFeature("f").getRunMode().getArtifactGroup(5), 1);
         U.assertArtifact(group5.get(0), "g", "a", "1.0.0", "jar", null);
diff --git a/src/test/java/org/apache/sling/provisioning/model/U.java b/src/test/java/org/apache/sling/provisioning/model/U.java
index 65576de..7c6b2ed 100644
--- a/src/test/java/org/apache/sling/provisioning/model/U.java
+++ b/src/test/java/org/apache/sling/provisioning/model/U.java
@@ -16,14 +16,6 @@
  */
 package org.apache.sling.provisioning.model;
 
-import static org.apache.sling.provisioning.model.ModelConstants.DEFAULT_RUN_MODE;
-import static org.apache.sling.provisioning.model.ModelConstants.DEFAULT_START_LEVEL;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.util.ArrayList;
@@ -32,6 +24,14 @@ import java.util.Map;
 
 import org.apache.sling.provisioning.model.io.ModelReader;
 
+import static org.apache.sling.provisioning.model.ModelConstants.DEFAULT_RUN_MODE;
+import static org.apache.sling.provisioning.model.ModelConstants.DEFAULT_START_LEVEL;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 /** Test utilities */
 public class U {
 
@@ -120,6 +120,7 @@ public class U {
         final Feature exampleFeature = m.getFeature("example");
         final RunMode defaultExampleRM = exampleFeature.getRunMode();
         final List<Configuration> configs = assertConfigurationsInRunMode(defaultExampleRM, 3);
+        assertEquals(Feature.Type.SUBSYSTEM_FEATURE, exampleFeature.getType());
         final Configuration cfg = assertConfiguration(configs, "org.apache.sling.another.config");
     }
 
diff --git a/src/test/resources/example.txt b/src/test/resources/example.txt
index a409266..8dc198b 100644
--- a/src/test/resources/example.txt
+++ b/src/test/resources/example.txt
@@ -20,7 +20,7 @@
 #
 # A feature consists of variables and run mode dependent artifacts.
 #
-[feature name=example]
+[feature name=example type=osgi.subsystem.feature]
 # The variables are global and can be used within artifact definitions, configurations,
 # and settings.
 #
diff --git a/src/test/resources/merge/artifact-merge.txt b/src/test/resources/merge/artifact-merge.txt
index 3ba489d..707b822 100644
--- a/src/test/resources/merge/artifact-merge.txt
+++ b/src/test/resources/merge/artifact-merge.txt
@@ -16,7 +16,7 @@
 #  specific language governing permissions and limitations
 #  under the License.
 #
-[feature name=f]
+[feature name=f type=osgi.subsystem.composite]
 
 [artifacts startLevel=3]
  g/a/2.0.0
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.

[sling-org-apache-sling-provisioning-model] 08/11: SLING-5172 : Provide support for custom sections in the provisioning model

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.provisioning.model-1.4.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-provisioning-model.git

commit b0f38e779a1874097e1fa707e002e5ed43872058
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Wed Oct 21 08:44:46 2015 +0000

    SLING-5172 : Provide support for custom sections in the provisioning model
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/tooling/support/provisioning-model@1709755 13f79535-47bb-0310-9956-ffa450edef68
---
 .../java/org/apache/sling/provisioning/model/io/ModelReader.java     | 5 ++++-
 .../java/org/apache/sling/provisioning/model/io/ModelWriter.java     | 2 +-
 .../java/org/apache/sling/provisioning/model/io/package-info.java    | 2 +-
 src/test/resources/additional.txt                                    | 2 +-
 4 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/src/main/java/org/apache/sling/provisioning/model/io/ModelReader.java b/src/main/java/org/apache/sling/provisioning/model/io/ModelReader.java
index 836ff77..98619a4 100644
--- a/src/main/java/org/apache/sling/provisioning/model/io/ModelReader.java
+++ b/src/main/java/org/apache/sling/provisioning/model/io/ModelReader.java
@@ -176,6 +176,9 @@ public class ModelReader {
                 }
                 if ( found == null ) {
                     // additional section
+                    if ( !category.startsWith(":") ) {
+                        throw new IOException(exceptionPrefix + "Unknown category in line " + this.lineNumberReader.getLineNumber() + ": " + category);
+                    }
                     found = CATEGORY.ADDITIONAL;
                 }
                 this.mode = found;
@@ -232,7 +235,7 @@ public class ModelReader {
                     case ADDITIONAL: checkFeature();
                                      this.runMode = null;
                                      this.artifactGroup = null;
-                                     this.additionalSection = new Section(category);
+                                     this.additionalSection = new Section(category.substring(1));
                                      this.init(this.additionalSection);
                                      this.feature.getAdditionalSections().add(this.additionalSection);
                                      this.additionalSection.getAttributes().putAll(parameters);
diff --git a/src/main/java/org/apache/sling/provisioning/model/io/ModelWriter.java b/src/main/java/org/apache/sling/provisioning/model/io/ModelWriter.java
index 13e4d00..de004d9 100644
--- a/src/main/java/org/apache/sling/provisioning/model/io/ModelWriter.java
+++ b/src/main/java/org/apache/sling/provisioning/model/io/ModelWriter.java
@@ -241,7 +241,7 @@ public class ModelWriter {
 
             // additional sections
             for(final Section section : feature.getAdditionalSections()) {
-                pw.print("  [");
+                pw.print("  [:");
                 pw.print(section.getName());
                 for(final Map.Entry<String, String> entry : section.getAttributes().entrySet()) {
                     pw.print(' ');
diff --git a/src/main/java/org/apache/sling/provisioning/model/io/package-info.java b/src/main/java/org/apache/sling/provisioning/model/io/package-info.java
index 7db48c0..175208e 100644
--- a/src/main/java/org/apache/sling/provisioning/model/io/package-info.java
+++ b/src/main/java/org/apache/sling/provisioning/model/io/package-info.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-@Version("1.1")
+@Version("1.2")
 package org.apache.sling.provisioning.model.io;
 
 import aQute.bnd.annotation.Version;
diff --git a/src/test/resources/additional.txt b/src/test/resources/additional.txt
index b9b1524..b94f5e5 100644
--- a/src/test/resources/additional.txt
+++ b/src/test/resources/additional.txt
@@ -24,7 +24,7 @@
 [artifacts]
     commons-io/commons-io/1.4/jar
 
-[additional stuff=free]
+[:additional stuff=free]
   # Hello
   world
   

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.

[sling-org-apache-sling-provisioning-model] 09/11: SLING-5174 : Refactor merging of models into separate utility class and add merge options

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.provisioning.model-1.4.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-provisioning-model.git

commit 42a911c390376aaf787c2a276f99f5bcee0d3741
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Wed Oct 21 12:51:31 2015 +0000

    SLING-5174 : Refactor merging of models into separate utility class and add merge options
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/tooling/support/provisioning-model@1709822 13f79535-47bb-0310-9956-ffa450edef68
---
 .../sling/provisioning/model/MergeUtilityTest.java | 275 +++++++++++++++++++++
 .../org/apache/sling/provisioning/model/U.java     |  15 ++
 src/test/resources/merge/artifact-merge2.txt       |  22 ++
 3 files changed, 312 insertions(+)

diff --git a/src/test/java/org/apache/sling/provisioning/model/MergeUtilityTest.java b/src/test/java/org/apache/sling/provisioning/model/MergeUtilityTest.java
new file mode 100644
index 0000000..6f35fb0
--- /dev/null
+++ b/src/test/java/org/apache/sling/provisioning/model/MergeUtilityTest.java
@@ -0,0 +1,275 @@
+/*
+ * 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.provisioning.model;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.apache.sling.provisioning.model.MergeUtility.MergeOptions;
+import org.junit.Test;
+
+public class MergeUtilityTest {
+
+    @Test public void mergeArtifactsTest() throws Exception {
+        final Model model = U.readTestModel("merge/artifact-base.txt");
+        final Model merge = U.readTestModel("merge/artifact-merge.txt");
+        MergeUtility.merge(model, merge);
+
+        // model should now have one artifact
+        assertNotNull(model.getFeature("f"));
+        assertNotNull(model.getFeature("f").getRunMode());
+        assertNotNull(model.getFeature("f").getRunMode().getArtifactGroup(3));
+        assertNotNull(model.getFeature("f").getRunMode().getArtifactGroup(5));
+        U.assertArtifactsInGroup(model.getFeature("f").getRunMode().getArtifactGroup(5), 0);
+        final List<Artifact> list = U.assertArtifactsInGroup(model.getFeature("f").getRunMode().getArtifactGroup(3), 1);
+
+        U.assertArtifact(list.get(0), "g", "a", "2.0.0", "jar", null);
+        assertEquals(FeatureTypes.SUBSYSTEM_COMPOSITE, model.getFeature("f").getType());
+    }
+
+    @Test public void mergeArtifactsTest2() throws Exception {
+        final Model model = U.readTestModel("merge/artifact-base.txt");
+        final Model merge = U.readTestModel("merge/artifact-merge2.txt");
+        MergeUtility.merge(model, merge);
+
+        // model should now have one artifact
+        assertNotNull(model.getFeature("f"));
+        assertNotNull(model.getFeature("f").getRunMode());
+        assertNotNull(model.getFeature("f").getRunMode().getArtifactGroup(3));
+        assertNotNull(model.getFeature("f").getRunMode().getArtifactGroup(5));
+        U.assertArtifactsInGroup(model.getFeature("f").getRunMode().getArtifactGroup(5), 0);
+        final List<Artifact> list = U.assertArtifactsInGroup(model.getFeature("f").getRunMode().getArtifactGroup(3), 1);
+
+        U.assertArtifact(list.get(0), "g", "a", "0.5.0", "jar", null);
+        assertEquals(FeatureTypes.SUBSYSTEM_COMPOSITE, model.getFeature("f").getType());
+    }
+
+    @Test public void mergeArtifactsTestOptions1() throws Exception {
+        final Model model = U.readTestModel("merge/artifact-base.txt");
+        final Model merge = U.readTestModel("merge/artifact-merge.txt");
+        final MergeOptions opts = new MergeOptions();
+        opts.setLatestArtifactWins(false);
+        MergeUtility.merge(model, merge, opts);
+
+        // model should now have one artifact
+        assertNotNull(model.getFeature("f"));
+        assertNotNull(model.getFeature("f").getRunMode());
+        assertNotNull(model.getFeature("f").getRunMode().getArtifactGroup(3));
+        assertNotNull(model.getFeature("f").getRunMode().getArtifactGroup(5));
+        U.assertArtifactsInGroup(model.getFeature("f").getRunMode().getArtifactGroup(5), 0);
+        final List<Artifact> list = U.assertArtifactsInGroup(model.getFeature("f").getRunMode().getArtifactGroup(3), 1);
+
+        U.assertArtifact(list.get(0), "g", "a", "2.0.0", "jar", null);
+    }
+
+    @Test public void mergeArtifactsTestOptions2() throws Exception {
+        final Model model = U.readTestModel("merge/artifact-base.txt");
+        final Model merge = U.readTestModel("merge/artifact-merge2.txt");
+        final MergeOptions opts = new MergeOptions();
+        opts.setLatestArtifactWins(false);
+        MergeUtility.merge(model, merge, opts);
+
+        // model should now have one artifact
+        assertNotNull(model.getFeature("f"));
+        assertNotNull(model.getFeature("f").getRunMode());
+        assertNotNull(model.getFeature("f").getRunMode().getArtifactGroup(3));
+        assertNotNull(model.getFeature("f").getRunMode().getArtifactGroup(5));
+        U.assertArtifactsInGroup(model.getFeature("f").getRunMode().getArtifactGroup(3), 0);
+        final List<Artifact> list = U.assertArtifactsInGroup(model.getFeature("f").getRunMode().getArtifactGroup(5), 1);
+
+        U.assertArtifact(list.get(0), "g", "a", "1.0.0", "jar", null);
+    }
+
+    @Test public void removeTest() throws Exception {
+        final Model model = U.readTestModel("merge/remove-base.txt");
+        final Model merge = U.readTestModel("merge/remove-merge.txt");
+        MergeUtility.merge(model, merge);
+
+        assertNotNull(model.getFeature("f"));
+        assertNotNull(model.getFeature("f").getRunMode());
+        assertNotNull(model.getFeature("f").getRunMode().getArtifactGroup(5));
+        assertNotNull(model.getFeature("f").getRunMode().getArtifactGroup(7));
+        assertEquals(FeatureTypes.PLAIN, model.getFeature("f").getType());
+
+        final List<Artifact> group5 = U.assertArtifactsInGroup(model.getFeature("f").getRunMode().getArtifactGroup(5), 1);
+        U.assertArtifact(group5.get(0), "g", "a", "1.0.0", "jar", null);
+
+        final List<Artifact> group7 = U.assertArtifactsInGroup(model.getFeature("f").getRunMode().getArtifactGroup(7), 1);
+        U.assertArtifact(group7.get(0), "g", "c", "1.0.0", "jar", null);
+
+        final List<Configuration> cfgs = U.assertConfigurationsInRunMode(model.getFeature("f").getRunMode(), 2);
+        assertEquals("org.sling.service.A", cfgs.get(0).getPid());
+        assertEquals("org.sling.service.C", cfgs.get(1).getPid());
+
+        assertEquals(2, model.getFeature("f").getRunMode().getSettings().size());
+        assertEquals("a", model.getFeature("f").getRunMode().getSettings().get("key.a"));
+        assertEquals("c", model.getFeature("f").getRunMode().getSettings().get("key.c"));
+
+        assertNotNull(model.getFeature("f").getRunMode("myrunmode"));
+        final List<Configuration> cfgs2 = U.assertConfigurationsInRunMode(model.getFeature("f").getRunMode("myrunmode"), 2);
+        assertEquals("org.sling.service.runmode.A", cfgs2.get(0).getPid());
+        assertEquals("org.sling.service.runmode.C", cfgs2.get(1).getPid());
+    }
+
+    @Test public void mergeRawTest() throws Exception {
+        final Model baseRaw = U.readTestModel("merge/config-base.txt");
+        final Model mergeRaw = U.readTestModel("merge/config-merge.txt");
+
+        MergeUtility.merge(baseRaw, mergeRaw);
+
+        final List<Configuration> cfgs = U.assertConfigurationsInRunMode(baseRaw.getFeature("configadmin").getRunMode(), 4);
+
+        final Configuration cfgBoot = cfgs.get(0);
+        assertEquals(1, cfgBoot.getProperties().size());
+        assertTrue(cfgBoot.getProperties().get(":bootstrap").toString().contains("uninstall bundle.a"));
+        assertTrue(cfgBoot.getProperties().get(":bootstrap").toString().contains("uninstall bundle.b"));
+        assertTrue(cfgBoot.getProperties().get(":bootstrap").toString().contains("uninstall bundle.c"));
+
+        final Configuration cfgA = cfgs.get(1);
+        assertEquals("org.apache.test.A", cfgA.getPid());
+        assertNull(cfgA.getFactoryPid());
+        assertEquals(1, cfgA.getProperties().size());
+        assertEquals("AA", cfgA.getProperties().get("name"));
+
+        final Configuration cfgB = cfgs.get(2);
+        assertEquals("org.apache.test.B", cfgB.getPid());
+        assertNull(cfgB.getFactoryPid());
+        assertEquals(3, cfgB.getProperties().size());
+        assertEquals("BB", cfgB.getProperties().get("name"));
+        assertEquals("bar", cfgB.getProperties().get("foo"));
+        assertArrayEquals(new String[] {"one", "two", "three"}, (String[])cfgB.getProperties().get("array"));
+
+        final Configuration cfgC = cfgs.get(3);
+        assertEquals("org.apache.test.C", cfgC.getPid());
+        assertNull(cfgC.getFactoryPid());
+        assertEquals(3, cfgC.getProperties().size());
+        assertEquals("C", cfgC.getProperties().get("name"));
+        assertEquals("bar", cfgB.getProperties().get("foo"));
+        assertArrayEquals(new Integer[] {1,2,3}, (Integer[])cfgC.getProperties().get("array"));
+    }
+
+    @Test public void mergeEffectiveTest() throws Exception {
+        final Model baseRaw = U.readTestModel("merge/config-base.txt");
+        final Model mergeRaw = U.readTestModel("merge/config-merge.txt");
+
+        final Model baseEffective = ModelUtility.getEffectiveModel(baseRaw);
+        final Model mergeEffective = ModelUtility.getEffectiveModel(mergeRaw);
+
+        MergeUtility.merge(baseEffective, mergeEffective);
+
+        final List<Configuration> cfgs = U.assertConfigurationsInRunMode(baseEffective.getFeature("configadmin").getRunMode(), 4);
+
+        final Configuration cfgBoot = cfgs.get(0);
+        assertEquals(1, cfgBoot.getProperties().size());
+        assertTrue(cfgBoot.getProperties().get(":bootstrap").toString().contains("uninstall bundle.c"));
+
+        final Configuration cfgA = cfgs.get(1);
+        assertEquals("org.apache.test.A", cfgA.getPid());
+        assertNull(cfgA.getFactoryPid());
+        assertEquals(1, cfgA.getProperties().size());
+        assertEquals("AA", cfgA.getProperties().get("name"));
+
+        final Configuration cfgB = cfgs.get(2);
+        assertEquals("org.apache.test.B", cfgB.getPid());
+        assertNull(cfgB.getFactoryPid());
+        assertEquals(2, cfgB.getProperties().size());
+        assertEquals("BB", cfgB.getProperties().get("name"));
+        assertEquals("bar", cfgB.getProperties().get("foo"));
+
+        final Configuration cfgC = cfgs.get(3);
+        assertEquals("org.apache.test.C", cfgC.getPid());
+        assertNull(cfgC.getFactoryPid());
+        assertEquals(1, cfgC.getProperties().size());
+        assertEquals("bar", cfgB.getProperties().get("foo"));
+    }
+
+    @Test public void mergeBaseRawTest() throws Exception {
+        final Model baseRaw = U.readTestModel("merge/config-base.txt");
+        final Model mergeRaw = U.readTestModel("merge/config-merge.txt");
+        final Model mergeEffective = ModelUtility.getEffectiveModel(mergeRaw);
+
+        MergeUtility.merge(baseRaw, mergeEffective);
+
+        final List<Configuration> cfgs = U.assertConfigurationsInRunMode(baseRaw.getFeature("configadmin").getRunMode(), 4);
+
+        final Configuration cfgBoot = cfgs.get(0);
+        assertEquals(1, cfgBoot.getProperties().size());
+        assertTrue(cfgBoot.getProperties().get(":bootstrap").toString().contains("uninstall bundle.c"));
+
+        final Configuration cfgA = cfgs.get(1);
+        assertEquals("org.apache.test.A", cfgA.getPid());
+        assertNull(cfgA.getFactoryPid());
+        assertEquals(1, cfgA.getProperties().size());
+        assertEquals("AA", cfgA.getProperties().get("name"));
+
+        final Configuration cfgB = cfgs.get(2);
+        assertEquals("org.apache.test.B", cfgB.getPid());
+        assertNull(cfgB.getFactoryPid());
+        assertEquals(2, cfgB.getProperties().size());
+        assertEquals("BB", cfgB.getProperties().get("name"));
+        assertEquals("bar", cfgB.getProperties().get("foo"));
+
+        final Configuration cfgC = cfgs.get(3);
+        assertEquals("org.apache.test.C", cfgC.getPid());
+        assertNull(cfgC.getFactoryPid());
+        assertEquals(1, cfgC.getProperties().size());
+        assertEquals("bar", cfgB.getProperties().get("foo"));
+    }
+
+    @Test public void mergeBaseEffectiveTest() throws Exception {
+        final Model baseRaw = U.readTestModel("merge/config-base.txt");
+        final Model mergeRaw = U.readTestModel("merge/config-merge.txt");
+        final Model baseEffective = ModelUtility.getEffectiveModel(baseRaw);
+
+        MergeUtility.merge(baseEffective, mergeRaw);
+
+        final List<Configuration> cfgs = U.assertConfigurationsInRunMode(baseEffective.getFeature("configadmin").getRunMode(), 4);
+
+        final Configuration cfgBoot = cfgs.get(0);
+        assertEquals(1, cfgBoot.getProperties().size());
+        assertTrue(cfgBoot.getProperties().get(":bootstrap").toString().contains("uninstall bundle.a"));
+        assertTrue(cfgBoot.getProperties().get(":bootstrap").toString().contains("uninstall bundle.b"));
+        assertTrue(cfgBoot.getProperties().get(":bootstrap").toString().contains("uninstall bundle.c"));
+
+        final Configuration cfgA = cfgs.get(1);
+        assertEquals("org.apache.test.A", cfgA.getPid());
+        assertNull(cfgA.getFactoryPid());
+        assertEquals(1, cfgA.getProperties().size());
+        assertEquals("AA", cfgA.getProperties().get("name"));
+
+        final Configuration cfgB = cfgs.get(2);
+        assertEquals("org.apache.test.B", cfgB.getPid());
+        assertNull(cfgB.getFactoryPid());
+        assertEquals(3, cfgB.getProperties().size());
+        assertEquals("BB", cfgB.getProperties().get("name"));
+        assertEquals("bar", cfgB.getProperties().get("foo"));
+        assertArrayEquals(new String[] {"one", "two", "three"}, (String[])cfgB.getProperties().get("array"));
+
+        final Configuration cfgC = cfgs.get(3);
+        assertEquals("org.apache.test.C", cfgC.getPid());
+        assertNull(cfgC.getFactoryPid());
+        assertEquals(3, cfgC.getProperties().size());
+        assertEquals("C", cfgC.getProperties().get("name"));
+        assertEquals("bar", cfgB.getProperties().get("foo"));
+        assertArrayEquals(new Integer[] {1,2,3}, (Integer[])cfgC.getProperties().get("array"));
+    }
+}
diff --git a/src/test/java/org/apache/sling/provisioning/model/U.java b/src/test/java/org/apache/sling/provisioning/model/U.java
index de1c00f..45ac79c 100644
--- a/src/test/java/org/apache/sling/provisioning/model/U.java
+++ b/src/test/java/org/apache/sling/provisioning/model/U.java
@@ -50,6 +50,21 @@ public class U {
         return readCompleteTestModel(TEST_MODEL_FILENAMES);
     }
 
+    /** Read the  model from that name */
+    public static Model readTestModel(final String name) throws Exception {
+        final Reader reader = new InputStreamReader(U.class.getResourceAsStream("/" + name), "UTF-8");
+        try {
+            final Model current = ModelReader.read(reader, name);
+            final Map<Traceable, String> errors = ModelUtility.validate(current);
+            if (errors != null ) {
+                throw new Exception("Invalid model at " + name + " : " + errors);
+            }
+            return current;
+        } finally {
+            reader.close();
+        }
+    }
+
     /** Read the complete model from that names */
     public static Model readCompleteTestModel(final String[] names) throws Exception {
         final Model result = new Model();
diff --git a/src/test/resources/merge/artifact-merge2.txt b/src/test/resources/merge/artifact-merge2.txt
new file mode 100644
index 0000000..f19f56b
--- /dev/null
+++ b/src/test/resources/merge/artifact-merge2.txt
@@ -0,0 +1,22 @@
+#
+#  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.
+#
+[feature name=f type=osgi.subsystem.composite]
+
+[artifacts startLevel=3]
+ g/a/0.5.0
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.

[sling-org-apache-sling-provisioning-model] 05/11: SLING-5148 : Support OSGi Subsystems in the Sling Provisioning model.

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.provisioning.model-1.4.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-provisioning-model.git

commit 7fa873664a1e1319da346236fc9ad31abc18440b
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Wed Oct 21 05:47:52 2015 +0000

    SLING-5148 : Support OSGi Subsystems in the Sling Provisioning model.
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/tooling/support/provisioning-model@1709720 13f79535-47bb-0310-9956-ffa450edef68
---
 .../apache/sling/provisioning/model/Feature.java   | 42 +++-------------------
 .../sling/provisioning/model/FeatureTypes.java     | 30 ++++++++++++++++
 .../sling/provisioning/model/io/ModelReader.java   |  2 +-
 .../sling/provisioning/model/io/ModelWriter.java   |  5 +--
 .../sling/provisioning/model/FeatureTest.java      | 27 +++-----------
 .../provisioning/model/ModelProcessorTest.java     | 10 +++---
 .../sling/provisioning/model/ModelUtilityTest.java | 12 +++----
 .../org/apache/sling/provisioning/model/U.java     | 18 +++++-----
 8 files changed, 64 insertions(+), 82 deletions(-)

diff --git a/src/main/java/org/apache/sling/provisioning/model/Feature.java b/src/main/java/org/apache/sling/provisioning/model/Feature.java
index c78cb3e..0f27e84 100644
--- a/src/main/java/org/apache/sling/provisioning/model/Feature.java
+++ b/src/main/java/org/apache/sling/provisioning/model/Feature.java
@@ -32,43 +32,11 @@ public class Feature
     extends Commentable
     implements Comparable<Feature> {
 
-    /**
-     * The feature type
-     * @since 1.4.0
-     */
-    public enum Type {
-        PLAIN("plain"),
-        SUBSYSTEM_FEATURE("osgi.subsystem.feature"),
-        SUBSYSTEM_APPLICATION("osgi.subsystem.application"),
-        SUBSYSTEM_COMPOSITE("osgi.subsystem.composite");
-
-        private final String textRepresentation;
-
-        private Type(final String textRep) {
-            textRepresentation = textRep;
-        }
-
-        public String getTextRepresentation() {
-            return textRepresentation;
-        }
-
-        public static Type fromTextRepresentation(final String textRep) {
-            if (textRep == null)
-                return PLAIN;
-
-            for (final Type t : values()) {
-                if (t.getTextRepresentation().equals(textRep))
-                    return t;
-            }
-            return null;
-        }
-    }
-
     /** All run modes. */
     private final List<RunMode> runModes = new ArrayList<RunMode>();
 
     /** The type of feature */
-    private volatile Type type = Type.PLAIN;
+    private volatile String type = FeatureTypes.PLAIN;
 
     /** Variables. */
     private final KeyValueMap<String> variables = new KeyValueMap<String>();
@@ -156,7 +124,7 @@ public class Feature
      * @return The feature type.
      * @since 1.4.0
      */
-    public Type getType() {
+    public String getType() {
         return type;
     }
 
@@ -165,8 +133,8 @@ public class Feature
      * @param t The new type
      * @since 1.4.0
      */
-    public void setType(final Type t) {
-        type = ( t == null ? Type.PLAIN : t);
+    public void setType(final String t) {
+        type = ( t == null ? FeatureTypes.PLAIN : t);
     }
 
     /**
@@ -213,7 +181,7 @@ public class Feature
     public String toString() {
         return "Feature [runModes=" + runModes + ", variables=" + variables
                 + ", name=" + name
-                + ( type != Type.PLAIN ? ", type=" + type : "" )
+                + ( FeatureTypes.PLAIN.equals(this.type) ? "" : ", type=" + type )
                 + ( additionalSections.isEmpty() ? "" : ", additionalSections=" + this.additionalSections)
                 + ( this.getLocation() != null ? ", location=" + this.getLocation() : "")
                 + "]";
diff --git a/src/main/java/org/apache/sling/provisioning/model/FeatureTypes.java b/src/main/java/org/apache/sling/provisioning/model/FeatureTypes.java
new file mode 100644
index 0000000..54e27ee
--- /dev/null
+++ b/src/main/java/org/apache/sling/provisioning/model/FeatureTypes.java
@@ -0,0 +1,30 @@
+/*
+ * 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.provisioning.model;
+
+/**
+ * Constants for common feature types.
+ * @since 1.4.0
+ */
+public abstract class FeatureTypes {
+
+    public static final String PLAIN = "plain";
+    public static final String SUBSYSTEM_FEATURE = "osgi.subsystem.feature";
+    public static final String SUBSYSTEM_APPLICATION = "osgi.subsystem.application";
+    public static final String SUBSYSTEM_COMPOSITE = "osgi.subsystem.composite";
+
+}
diff --git a/src/main/java/org/apache/sling/provisioning/model/io/ModelReader.java b/src/main/java/org/apache/sling/provisioning/model/io/ModelReader.java
index 21ef564..836ff77 100644
--- a/src/main/java/org/apache/sling/provisioning/model/io/ModelReader.java
+++ b/src/main/java/org/apache/sling/provisioning/model/io/ModelReader.java
@@ -196,7 +196,7 @@ public class ModelReader {
                                        throw new IOException(exceptionPrefix + "Duplicate feature in line " + this.lineNumberReader.getLineNumber() + ": " + line);
                                    }
                                    this.feature = model.getOrCreateFeature(name);
-                                   this.feature.setType(Feature.Type.fromTextRepresentation(parameters.get("type")));
+                                   this.feature.setType(parameters.get("type"));
                                    this.init(this.feature);
                                    this.runMode = null;
                                    this.artifactGroup = null;
diff --git a/src/main/java/org/apache/sling/provisioning/model/io/ModelWriter.java b/src/main/java/org/apache/sling/provisioning/model/io/ModelWriter.java
index e6160b3..13e4d00 100644
--- a/src/main/java/org/apache/sling/provisioning/model/io/ModelWriter.java
+++ b/src/main/java/org/apache/sling/provisioning/model/io/ModelWriter.java
@@ -30,6 +30,7 @@ import org.apache.sling.provisioning.model.ArtifactGroup;
 import org.apache.sling.provisioning.model.Commentable;
 import org.apache.sling.provisioning.model.Configuration;
 import org.apache.sling.provisioning.model.Feature;
+import org.apache.sling.provisioning.model.FeatureTypes;
 import org.apache.sling.provisioning.model.Model;
 import org.apache.sling.provisioning.model.ModelConstants;
 import org.apache.sling.provisioning.model.RunMode;
@@ -88,9 +89,9 @@ public class ModelWriter {
             writeComment(pw, feature);
             pw.print("[feature name=");
             pw.print(feature.getName());
-            if (feature.getType() != Feature.Type.PLAIN) {
+            if (! FeatureTypes.PLAIN.equals(feature.getType()) ) {
                 pw.print(" type=");
-                pw.print(feature.getType().getTextRepresentation());
+                pw.print(feature.getType());
             }
             pw.println("]");
             pw.println();
diff --git a/src/test/java/org/apache/sling/provisioning/model/FeatureTest.java b/src/test/java/org/apache/sling/provisioning/model/FeatureTest.java
index a1b9a54..3ac3c36 100644
--- a/src/test/java/org/apache/sling/provisioning/model/FeatureTest.java
+++ b/src/test/java/org/apache/sling/provisioning/model/FeatureTest.java
@@ -16,35 +16,18 @@
  */
 package org.apache.sling.provisioning.model;
 
-import org.junit.Test;
-
 import static org.junit.Assert.assertEquals;
 
-public class FeatureTest {
-    @Test
-    public void testTypeEnum() {
-        assertEquals(Feature.Type.SUBSYSTEM_APPLICATION,
-                Feature.Type.fromTextRepresentation("osgi.subsystem.application"));
-        assertEquals(Feature.Type.SUBSYSTEM_COMPOSITE,
-                Feature.Type.fromTextRepresentation("osgi.subsystem.composite"));
-        assertEquals(Feature.Type.SUBSYSTEM_FEATURE,
-                Feature.Type.fromTextRepresentation("osgi.subsystem.feature"));
-        assertEquals(Feature.Type.PLAIN, Feature.Type.fromTextRepresentation(null));
+import org.junit.Test;
 
-        assertEquals("osgi.subsystem.application",
-                Feature.Type.SUBSYSTEM_APPLICATION.getTextRepresentation());
-        assertEquals("osgi.subsystem.composite",
-                Feature.Type.SUBSYSTEM_COMPOSITE.getTextRepresentation());
-        assertEquals("osgi.subsystem.feature",
-                Feature.Type.SUBSYSTEM_FEATURE.getTextRepresentation());
-    }
+public class FeatureTest {
 
     @Test
     public void testFeatureType() {
         Feature f = new Feature("blah");
-        assertEquals(Feature.Type.PLAIN, f.getType());
+        assertEquals(FeatureTypes.PLAIN, f.getType());
 
-        f.setType(Feature.Type.SUBSYSTEM_APPLICATION);
-        assertEquals(Feature.Type.SUBSYSTEM_APPLICATION, f.getType());
+        f.setType(FeatureTypes.SUBSYSTEM_APPLICATION);
+        assertEquals(FeatureTypes.SUBSYSTEM_APPLICATION, f.getType());
     }
 }
diff --git a/src/test/java/org/apache/sling/provisioning/model/ModelProcessorTest.java b/src/test/java/org/apache/sling/provisioning/model/ModelProcessorTest.java
index 16efe69..7a9a6a2 100644
--- a/src/test/java/org/apache/sling/provisioning/model/ModelProcessorTest.java
+++ b/src/test/java/org/apache/sling/provisioning/model/ModelProcessorTest.java
@@ -18,15 +18,15 @@
  */
 package org.apache.sling.provisioning.model;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
 import java.util.Enumeration;
 import java.util.Map.Entry;
 
 import org.junit.Before;
 import org.junit.Test;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
 public class ModelProcessorTest {
 
     private Model testModel;
@@ -40,7 +40,7 @@ public class ModelProcessorTest {
         Feature feature1 = testModel.getOrCreateFeature("feature1");
         feature1.setLocation("LocF1");
         feature1.setComment("ComF1");
-        feature1.setType(Feature.Type.SUBSYSTEM_COMPOSITE);
+        feature1.setType(FeatureTypes.SUBSYSTEM_COMPOSITE);
         feature1.getVariables().setLocation("LocFV1");
         feature1.getVariables().setComment("ComFV1");
         feature1.getVariables().put("k1", "v1");
@@ -96,7 +96,7 @@ public class ModelProcessorTest {
         assertNotNull(feature1);
         assertEquals("LocF1", feature1.getLocation());
         assertEquals("ComF1", feature1.getComment());
-        assertEquals(Feature.Type.SUBSYSTEM_COMPOSITE, feature1.getType());
+        assertEquals(FeatureTypes.SUBSYSTEM_COMPOSITE, feature1.getType());
         assertEquals("LocFV1", feature1.getVariables().getLocation());
         assertEquals("ComFV1", feature1.getVariables().getComment());
         assertEquals("#v1", feature1.getVariables().get("k1"));
diff --git a/src/test/java/org/apache/sling/provisioning/model/ModelUtilityTest.java b/src/test/java/org/apache/sling/provisioning/model/ModelUtilityTest.java
index b902ac1..baf1ec7 100644
--- a/src/test/java/org/apache/sling/provisioning/model/ModelUtilityTest.java
+++ b/src/test/java/org/apache/sling/provisioning/model/ModelUtilityTest.java
@@ -16,16 +16,16 @@
  */
 package org.apache.sling.provisioning.model;
 
-import java.util.List;
-
-import org.junit.Test;
-
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import java.util.List;
+
+import org.junit.Test;
+
 public class ModelUtilityTest {
 
     @Test public void mergeArtifactsTest() throws Exception {
@@ -41,7 +41,7 @@ public class ModelUtilityTest {
         final List<Artifact> list = U.assertArtifactsInGroup(model.getFeature("f").getRunMode().getArtifactGroup(3), 1);
 
         U.assertArtifact(list.get(0), "g", "a", "2.0.0", "jar", null);
-        assertEquals(Feature.Type.SUBSYSTEM_COMPOSITE, model.getFeature("f").getType());
+        assertEquals(FeatureTypes.SUBSYSTEM_COMPOSITE, model.getFeature("f").getType());
     }
 
     @Test public void removeTest() throws Exception {
@@ -52,7 +52,7 @@ public class ModelUtilityTest {
         assertNotNull(model.getFeature("f").getRunMode());
         assertNotNull(model.getFeature("f").getRunMode().getArtifactGroup(5));
         assertNotNull(model.getFeature("f").getRunMode().getArtifactGroup(7));
-        assertEquals(Feature.Type.PLAIN, model.getFeature("f").getType());
+        assertEquals(FeatureTypes.PLAIN, model.getFeature("f").getType());
 
         final List<Artifact> group5 = U.assertArtifactsInGroup(model.getFeature("f").getRunMode().getArtifactGroup(5), 1);
         U.assertArtifact(group5.get(0), "g", "a", "1.0.0", "jar", null);
diff --git a/src/test/java/org/apache/sling/provisioning/model/U.java b/src/test/java/org/apache/sling/provisioning/model/U.java
index 7c6b2ed..de1c00f 100644
--- a/src/test/java/org/apache/sling/provisioning/model/U.java
+++ b/src/test/java/org/apache/sling/provisioning/model/U.java
@@ -16,14 +16,6 @@
  */
 package org.apache.sling.provisioning.model;
 
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.sling.provisioning.model.io.ModelReader;
-
 import static org.apache.sling.provisioning.model.ModelConstants.DEFAULT_RUN_MODE;
 import static org.apache.sling.provisioning.model.ModelConstants.DEFAULT_START_LEVEL;
 import static org.junit.Assert.assertEquals;
@@ -32,6 +24,14 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sling.provisioning.model.io.ModelReader;
+
 /** Test utilities */
 public class U {
 
@@ -120,7 +120,7 @@ public class U {
         final Feature exampleFeature = m.getFeature("example");
         final RunMode defaultExampleRM = exampleFeature.getRunMode();
         final List<Configuration> configs = assertConfigurationsInRunMode(defaultExampleRM, 3);
-        assertEquals(Feature.Type.SUBSYSTEM_FEATURE, exampleFeature.getType());
+        assertEquals(FeatureTypes.SUBSYSTEM_FEATURE, exampleFeature.getType());
         final Configuration cfg = assertConfiguration(configs, "org.apache.sling.another.config");
     }
 

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.

[sling-org-apache-sling-provisioning-model] 11/11: [maven-release-plugin] copy for tag org.apache.sling.provisioning.model-1.4.0

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.provisioning.model-1.4.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-provisioning-model.git

commit e888295bc7ec20f67bbfca25770bf6e56030c5c5
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Wed Oct 21 13:03:39 2015 +0000

    [maven-release-plugin] copy for tag org.apache.sling.provisioning.model-1.4.0
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/tags/org.apache.sling.provisioning.model-1.4.0@1709826 13f79535-47bb-0310-9956-ffa450edef68

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.

[sling-org-apache-sling-provisioning-model] 01/11: [maven-release-plugin] prepare for next development iteration

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.provisioning.model-1.4.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-provisioning-model.git

commit b0181cdd128b0c5f378138f841b40d3b5b86c045
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Tue Jul 28 12:33:52 2015 +0000

    [maven-release-plugin] prepare for next development iteration
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/tooling/support/provisioning-model@1693080 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/pom.xml b/pom.xml
index 76c0ae0..667e808 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
     </parent>
 
     <artifactId>org.apache.sling.provisioning.model</artifactId>
-    <version>1.3.0</version>
+    <version>1.3.1-SNAPSHOT</version>
     <packaging>bundle</packaging>
 
     <name>Apache Sling Provisioning Model</name>
@@ -31,9 +31,9 @@
     </description>
 
     <scm>
-        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/tags/org.apache.sling.provisioning.model-1.3.0</connection>
-        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/tags/org.apache.sling.provisioning.model-1.3.0</developerConnection>
-        <url>http://svn.apache.org/viewvc/sling/tags/org.apache.sling.provisioning.model-1.3.0</url>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/tooling/support/provisioning-model</connection>
+        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/tooling/support/provisioning-model</developerConnection>
+        <url>http://svn.apache.org/viewvc/sling/trunk/tooling/support/provisioning-model</url>
     </scm>
 
     <build>

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.

[sling-org-apache-sling-provisioning-model] 04/11: SLING-5172 : Provide support for custom sections in the provisioning model

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.provisioning.model-1.4.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-provisioning-model.git

commit d0779ce506dbf28cc2b56539f56243b185fa74b2
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Tue Oct 20 14:00:14 2015 +0000

    SLING-5172 : Provide support for custom sections in the provisioning model
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/tooling/support/provisioning-model@1709592 13f79535-47bb-0310-9956-ffa450edef68
---
 .../apache/sling/provisioning/model/Feature.java   | 48 +++++++++++-
 .../sling/provisioning/model/ModelProcessor.java   | 13 ++--
 .../sling/provisioning/model/ModelUtility.java     | 35 +++++----
 .../apache/sling/provisioning/model/Section.java   | 87 ++++++++++++++++++++++
 .../sling/provisioning/model/io/ModelReader.java   | 39 +++++++++-
 .../sling/provisioning/model/io/ModelWriter.java   | 18 +++++
 .../apache/sling/provisioning/model/io/IOTest.java | 10 +++
 src/test/resources/additional.txt                  | 35 +++++++++
 8 files changed, 259 insertions(+), 26 deletions(-)

diff --git a/src/main/java/org/apache/sling/provisioning/model/Feature.java b/src/main/java/org/apache/sling/provisioning/model/Feature.java
index 1d892ec..c78cb3e 100644
--- a/src/main/java/org/apache/sling/provisioning/model/Feature.java
+++ b/src/main/java/org/apache/sling/provisioning/model/Feature.java
@@ -32,6 +32,10 @@ public class Feature
     extends Commentable
     implements Comparable<Feature> {
 
+    /**
+     * The feature type
+     * @since 1.4.0
+     */
     public enum Type {
         PLAIN("plain"),
         SUBSYSTEM_FEATURE("osgi.subsystem.feature"),
@@ -72,6 +76,9 @@ public class Feature
     /** Feature name. */
     private final String name;
 
+    /** Additional sections. */
+    private final List<Section> additionalSections = new ArrayList<Section>();
+
     /**
      * Construct a new feature.
      * @param name The feature name
@@ -144,12 +151,48 @@ public class Feature
         return result;
     }
 
+    /**
+     * Get the feature type.
+     * @return The feature type.
+     * @since 1.4.0
+     */
     public Type getType() {
         return type;
     }
 
-    public void setType(Type t) {
-        type = t;
+    /**
+     * Set the feature type.
+     * @param t The new type
+     * @since 1.4.0
+     */
+    public void setType(final Type t) {
+        type = ( t == null ? Type.PLAIN : t);
+    }
+
+    /**
+     * Get all additional sections
+     * @return The list of additional sections. It might be empty.
+     * @since 1.4.0
+     */
+    public List<Section> getAdditionalSections() {
+        return this.additionalSections;
+    }
+
+    /**
+     * Get all sections with the given name.
+     * @param name The section name.
+     * @return The list of sections. The list might be empty.
+     * @since 1.4.0
+     */
+    public List<Section> getAdditionalSections(final String name) {
+        final List<Section> result = new ArrayList<Section>();
+
+        for(final Section s : this.additionalSections) {
+            if ( name.equals(s.getName()) ) {
+                result.add(s);
+            }
+        }
+        return result;
     }
 
     @Override
@@ -171,6 +214,7 @@ public class Feature
         return "Feature [runModes=" + runModes + ", variables=" + variables
                 + ", name=" + name
                 + ( type != Type.PLAIN ? ", type=" + type : "" )
+                + ( additionalSections.isEmpty() ? "" : ", additionalSections=" + this.additionalSections)
                 + ( this.getLocation() != null ? ", location=" + this.getLocation() : "")
                 + "]";
     }
diff --git a/src/main/java/org/apache/sling/provisioning/model/ModelProcessor.java b/src/main/java/org/apache/sling/provisioning/model/ModelProcessor.java
index ad4fcfc..58cad6d 100644
--- a/src/main/java/org/apache/sling/provisioning/model/ModelProcessor.java
+++ b/src/main/java/org/apache/sling/provisioning/model/ModelProcessor.java
@@ -23,10 +23,10 @@ import java.util.Enumeration;
 /**
  * Allows to process a value. A new value is created and for each part in the model a process
  * method is called. Subclasses can overwrite those methods to inject specific behavior.
- * The processor itself does not change anything in the model. 
+ * The processor itself does not change anything in the model.
  */
 class ModelProcessor {
-    
+
     /**
      * Creates a copy of the model and calls a process method for each part found in the model.
      * This allows to modify the parts content (e.g. replace variables), but not to add or remove parts.
@@ -42,6 +42,7 @@ class ModelProcessor {
             newFeature.setType(feature.getType());
             newFeature.setComment(feature.getComment());
             newFeature.setLocation(feature.getLocation());
+            newFeature.getAdditionalSections().addAll(feature.getAdditionalSections());
 
             newFeature.getVariables().setComment(feature.getVariables().getComment());
             newFeature.getVariables().setLocation(feature.getVariables().getLocation());
@@ -86,15 +87,15 @@ class ModelProcessor {
         }
         return result;
     }
-    
+
     protected KeyValueMap<String> processVariables(KeyValueMap<String> variables, Feature newFeature) {
         return variables;
     }
-    
+
     protected Artifact processArtifact(Artifact artifact, Feature newFeature, RunMode newRunMode) {
         return artifact;
     }
-    
+
     protected Configuration processConfiguration(Configuration config, Feature newFeature, RunMode newRunMode) {
         return config;
     }
@@ -102,5 +103,5 @@ class ModelProcessor {
     protected KeyValueMap<String> processSettings(KeyValueMap<String> settings, Feature newFeature, RunMode newRunMode) {
         return settings;
     }
-    
+
 }
diff --git a/src/main/java/org/apache/sling/provisioning/model/ModelUtility.java b/src/main/java/org/apache/sling/provisioning/model/ModelUtility.java
index 806975d..3a30ab9 100644
--- a/src/main/java/org/apache/sling/provisioning/model/ModelUtility.java
+++ b/src/main/java/org/apache/sling/provisioning/model/ModelUtility.java
@@ -53,6 +53,9 @@ public abstract class ModelUtility {
             final Feature baseFeature = base.getOrCreateFeature(feature.getName());
             baseFeature.setType(feature.getType());
 
+            // additional sections
+            baseFeature.getAdditionalSections().addAll(feature.getAdditionalSections());
+
             // variables
             baseFeature.getVariables().putAll(feature.getVariables());
 
@@ -277,33 +280,33 @@ public abstract class ModelUtility {
          */
         String resolve(final Artifact artifact);
     }
-    
+
     /**
      * Parameter builder class for {@link ModelUtility#getEffectiveModel(Model, ResolverOptions)} method.
      */
     public static final class ResolverOptions {
-        
+
         private VariableResolver variableResolver;
         private ArtifactVersionResolver artifactVersionResolver;
-        
+
         public VariableResolver getVariableResolver() {
             return variableResolver;
         }
-        
+
         public ResolverOptions variableResolver(VariableResolver variableResolver) {
             this.variableResolver = variableResolver;
             return this;
         }
-        
+
         public ArtifactVersionResolver getArtifactVersionResolver() {
             return artifactVersionResolver;
         }
-        
+
         public ResolverOptions artifactVersionResolver(ArtifactVersionResolver dependencyVersionResolver) {
             this.artifactVersionResolver = dependencyVersionResolver;
             return this;
         }
-        
+
     }
 
     /**
@@ -318,7 +321,7 @@ public abstract class ModelUtility {
     public static Model getEffectiveModel(final Model model, final VariableResolver resolver) {
         return getEffectiveModel(model, new ResolverOptions().variableResolver(resolver));
     }
-    
+
     /**
      * Replace all variables in the model and return a new model with the replaced values.
      * @param model The base model.
@@ -329,7 +332,7 @@ public abstract class ModelUtility {
     public static Model getEffectiveModel(final Model model) {
         return getEffectiveModel(model, new ResolverOptions());
     }
-    
+
     /**
      * Replace all variables in the model and return a new model with the replaced values.
      * @param model The base model.
@@ -417,7 +420,7 @@ public abstract class ModelUtility {
         }
         return errors;
     }
-    
+
     /**
      * Applies a set of variables to the given model.
      * All variables that are referenced anywhere within the model are detected and passed to the given variable resolver.
@@ -430,7 +433,7 @@ public abstract class ModelUtility {
      * @since 1.3
      */
     public static Model applyVariables(final Model model, final VariableResolver resolver) {
-        
+
         // define delegating resolver that collects all variable names and value per feature
         final Map<String,Map<String,String>> collectedVars = new HashMap<String, Map<String,String>>();
         VariableResolver variableCollector = new VariableResolver() {
@@ -448,10 +451,10 @@ public abstract class ModelUtility {
                 return value;
             }
         };
-        
+
         // use effective model processor to collect variables, but drop the resulting model
         new EffectiveModelProcessor(new ResolverOptions().variableResolver(variableCollector)).process(model);
-        
+
         // define a processor that updates the "variables" sections in the features
         ModelProcessor variablesUpdater = new ModelProcessor() {
             @Override
@@ -466,7 +469,7 @@ public abstract class ModelUtility {
                 return newVariables;
             }
         };
-        
+
         // return model with replaced "variables" sections
         return variablesUpdater.process(model);
     }
@@ -482,7 +485,7 @@ public abstract class ModelUtility {
      * @since 1.3
      */
     public static Model applyArtifactVersions(final Model model, final ArtifactVersionResolver resolver) {
-        
+
         // define a processor that updates the versions of artifacts
         ModelProcessor versionUpdater = new ModelProcessor() {
             @Override
@@ -501,7 +504,7 @@ public abstract class ModelUtility {
                         artifact.getType());
             }
         };
-        
+
         // return model with updated version artifacts
         return versionUpdater.process(model);
     }
diff --git a/src/main/java/org/apache/sling/provisioning/model/Section.java b/src/main/java/org/apache/sling/provisioning/model/Section.java
new file mode 100644
index 0000000..5c4ce38
--- /dev/null
+++ b/src/main/java/org/apache/sling/provisioning/model/Section.java
@@ -0,0 +1,87 @@
+/*
+ * 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.provisioning.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * An additional section in the provisioning model.
+ * @since 1.4
+ */
+public class Section
+    extends Commentable {
+
+    /** The section name. */
+    private final String name;
+
+    /** Attributes. */
+    private final Map<String, String> attributes = new HashMap<String, String>();
+
+    /** Contents. */
+    private volatile String contents;
+
+    /**
+     * Construct a new feature.
+     * @param name The feature name
+     */
+    public Section(final String name) {
+        this.name = name;
+    }
+
+    /**
+     * Get the name of the section.
+     * @return The name or {@code null} for an anonymous feature.
+     */
+    public String getName() {
+        return this.name;
+    }
+
+    /**
+     * Get all attributes
+     * @return The map of attributes.
+     */
+    public Map<String, String> getAttributes() {
+        return this.attributes;
+    }
+
+    /**
+     * Get the contents of the section.
+     * @return The contents or {@code null}.
+     */
+    public String getContents() {
+        return contents;
+    }
+
+    /**
+     * Set the contents of the section.
+     * @param contents The new contents.
+     */
+    public void setContents(final String contents) {
+        this.contents = contents;
+    }
+
+    @Override
+    public String toString() {
+        return "Section [name=" + name
+                + ( attributes.isEmpty() ? "": ", attributes=" + attributes )
+                + ( contents == null ? "" : ", contents=" + contents)
+                + ( this.getLocation() != null ? ", location=" + this.getLocation() : "")
+                + "]";
+    }
+}
diff --git a/src/main/java/org/apache/sling/provisioning/model/io/ModelReader.java b/src/main/java/org/apache/sling/provisioning/model/io/ModelReader.java
index ec37e48..21ef564 100644
--- a/src/main/java/org/apache/sling/provisioning/model/io/ModelReader.java
+++ b/src/main/java/org/apache/sling/provisioning/model/io/ModelReader.java
@@ -31,6 +31,7 @@ import org.apache.sling.provisioning.model.Feature;
 import org.apache.sling.provisioning.model.Model;
 import org.apache.sling.provisioning.model.ModelConstants;
 import org.apache.sling.provisioning.model.RunMode;
+import org.apache.sling.provisioning.model.Section;
 
 /**
  * This class offers a method to read a model using a {@code Reader} instance.
@@ -44,7 +45,8 @@ public class ModelReader {
         ARTIFACTS("artifacts", new String[] {"runModes", "startLevel"}),
         SETTINGS("settings", new String[] {"runModes"}),
         CONFIGURATIONS("configurations", new String[] {"runModes"}),
-        CONFIG(null, null);
+        CONFIG(null, null),
+        ADDITIONAL(null, null);
 
         public final String name;
 
@@ -79,6 +81,8 @@ public class ModelReader {
     private ArtifactGroup artifactGroup;
     private Configuration config;
 
+    private Section additionalSection;
+
     private String comment;
 
     private StringBuilder configBuilder;
@@ -109,6 +113,14 @@ public class ModelReader {
 
             // ignore empty line
             if ( line.isEmpty() ) {
+                if ( this.mode == CATEGORY.ADDITIONAL ) {
+                    if ( this.additionalSection.getContents() == null ) {
+                        this.additionalSection.setContents(line);
+                    } else {
+                        this.additionalSection.setContents(this.additionalSection.getContents() + '\n' + line);
+                    }
+                    continue;
+                }
                 checkConfig();
                 continue;
             }
@@ -121,6 +133,14 @@ public class ModelReader {
 
                     continue;
                 }
+                if ( this.mode == CATEGORY.ADDITIONAL ) {
+                    if ( this.additionalSection.getContents() == null ) {
+                        this.additionalSection.setContents(line);
+                    } else {
+                        this.additionalSection.setContents(this.additionalSection.getContents() + '\n' + line);
+                    }
+                    continue;
+                }
                 final String c = line.substring(1).trim();
                 if ( comment == null ) {
                     comment = c;
@@ -138,6 +158,7 @@ public class ModelReader {
             }
 
             if ( line.startsWith("[") ) {
+                additionalSection = null;
                 if ( !line.endsWith("]") ) {
                     throw new IOException(exceptionPrefix + "Illegal category definition in line " + this.lineNumberReader.getLineNumber() + ": " + line);
                 }
@@ -154,7 +175,8 @@ public class ModelReader {
                     }
                 }
                 if ( found == null ) {
-                    throw new IOException(exceptionPrefix + "Unknown category in line " + this.lineNumberReader.getLineNumber() + ": " + line);
+                    // additional section
+                    found = CATEGORY.ADDITIONAL;
                 }
                 this.mode = found;
                 Map<String, String> parameters = Collections.emptyMap();
@@ -207,6 +229,13 @@ public class ModelReader {
                                          checkRunMode(parameters);
                                          this.init(this.runMode.getConfigurations());
                                          break;
+                    case ADDITIONAL: checkFeature();
+                                     this.runMode = null;
+                                     this.artifactGroup = null;
+                                     this.additionalSection = new Section(category);
+                                     this.init(this.additionalSection);
+                                     this.feature.getAdditionalSections().add(this.additionalSection);
+                                     this.additionalSection.getAttributes().putAll(parameters);
                 }
             } else {
                 switch ( this.mode ) {
@@ -288,6 +317,12 @@ public class ModelReader {
                     case CONFIG : configBuilder.append(line);
                                   configBuilder.append('\n');
                                   break;
+                    case ADDITIONAL : if ( this.additionalSection.getContents() == null ) {
+                                          this.additionalSection.setContents(line);
+                                      } else {
+                                          this.additionalSection.setContents(this.additionalSection.getContents() + '\n' + line);
+                                      }
+                                      break;
                 }
             }
 
diff --git a/src/main/java/org/apache/sling/provisioning/model/io/ModelWriter.java b/src/main/java/org/apache/sling/provisioning/model/io/ModelWriter.java
index d1fe473..e6160b3 100644
--- a/src/main/java/org/apache/sling/provisioning/model/io/ModelWriter.java
+++ b/src/main/java/org/apache/sling/provisioning/model/io/ModelWriter.java
@@ -33,6 +33,7 @@ import org.apache.sling.provisioning.model.Feature;
 import org.apache.sling.provisioning.model.Model;
 import org.apache.sling.provisioning.model.ModelConstants;
 import org.apache.sling.provisioning.model.RunMode;
+import org.apache.sling.provisioning.model.Section;
 
 /**
  * Simple writer for the a model
@@ -236,6 +237,23 @@ public class ModelWriter {
                     }
                 }
             }
+
+            // additional sections
+            for(final Section section : feature.getAdditionalSections()) {
+                pw.print("  [");
+                pw.print(section.getName());
+                for(final Map.Entry<String, String> entry : section.getAttributes().entrySet()) {
+                    pw.print(' ');
+                    pw.print(entry.getKey());
+                    pw.print('=');
+                    pw.print(entry.getValue());
+                }
+                pw.println("]");
+                if ( section.getContents() != null ) {
+                    pw.println(section.getContents());
+                }
+                pw.println();
+            }
         }
     }
 }
diff --git a/src/test/java/org/apache/sling/provisioning/model/io/IOTest.java b/src/test/java/org/apache/sling/provisioning/model/io/IOTest.java
index 5fa6a1e..bcfe275 100644
--- a/src/test/java/org/apache/sling/provisioning/model/io/IOTest.java
+++ b/src/test/java/org/apache/sling/provisioning/model/io/IOTest.java
@@ -18,6 +18,7 @@ package org.apache.sling.provisioning.model.io;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 
 import java.io.StringReader;
@@ -27,6 +28,7 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.sling.provisioning.model.Configuration;
+import org.apache.sling.provisioning.model.Feature;
 import org.apache.sling.provisioning.model.Model;
 import org.apache.sling.provisioning.model.ModelUtility;
 import org.apache.sling.provisioning.model.Traceable;
@@ -113,4 +115,12 @@ public class IOTest {
         assertEquals("C", cfgC.getProperties().get("name"));
         assertArrayEquals(new Integer[] {1,2,3}, (Integer[])cfgC.getProperties().get("array"));
     }
+
+    @Test public void testAddition() throws Exception {
+        final Model model = U.readCompleteTestModel(new String[] {"additional.txt"});
+        final Feature f = model.getFeature("main");
+        assertNotNull(f);
+        assertEquals(1, f.getAdditionalSections().size());
+        assertEquals(1, f.getAdditionalSections("additional").size());
+    }
 }
diff --git a/src/test/resources/additional.txt b/src/test/resources/additional.txt
new file mode 100644
index 0000000..b9b1524
--- /dev/null
+++ b/src/test/resources/additional.txt
@@ -0,0 +1,35 @@
+#
+#  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.
+#
+[feature name=main]
+
+[variables]
+  ws.version=1.0.2-from-main
+
+[artifacts]
+    commons-io/commons-io/1.4/jar
+
+[additional stuff=free]
+  # Hello
+  world
+  
+  Hello
+
+[configurations]
+  org.apache.test.A
+    name="A"    

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.

[sling-org-apache-sling-provisioning-model] 07/11: git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/tooling/support/provisioning-model@1709754 13f79535-47bb-0310-9956-ffa450edef68

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.provisioning.model-1.4.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-provisioning-model.git

commit 4a901267b6807e68a6cb4c0ac524ffac82418c14
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Wed Oct 21 08:37:08 2015 +0000

    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/tooling/support/provisioning-model@1709754 13f79535-47bb-0310-9956-ffa450edef68
---
 src/main/java/org/apache/sling/provisioning/model/Version.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/java/org/apache/sling/provisioning/model/Version.java b/src/main/java/org/apache/sling/provisioning/model/Version.java
index 2e90ba2..8d5e990 100644
--- a/src/main/java/org/apache/sling/provisioning/model/Version.java
+++ b/src/main/java/org/apache/sling/provisioning/model/Version.java
@@ -18,7 +18,7 @@ package org.apache.sling.provisioning.model;
 
 /**
  * Version object supporting Maven versions.
- *
+ * @since 1.4
  */
 public class Version implements Comparable<Version> {
 

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.

[sling-org-apache-sling-provisioning-model] 02/11: Update the main reactor to parent 25

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.provisioning.model-1.4.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-provisioning-model.git

commit 9a80232178d3a200db02729b74036ac321ee004f
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Mon Oct 5 10:03:45 2015 +0000

    Update the main reactor to parent 25
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/tooling/support/provisioning-model@1706780 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 667e808..db1d7ce 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,7 +17,7 @@
     <parent>
         <groupId>org.apache.sling</groupId>
         <artifactId>sling</artifactId>
-        <version>24</version>
+        <version>25</version>
         <relativePath />
     </parent>
 

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.

[sling-org-apache-sling-provisioning-model] 10/11: [maven-release-plugin] prepare release org.apache.sling.provisioning.model-1.4.0

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.provisioning.model-1.4.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-provisioning-model.git

commit b05b9ce671a5722bdb4b0b37776dc55008d203b5
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Wed Oct 21 13:03:26 2015 +0000

    [maven-release-plugin] prepare release org.apache.sling.provisioning.model-1.4.0
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/tooling/support/provisioning-model@1709825 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/pom.xml b/pom.xml
index db1d7ce..8fe0978 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
     </parent>
 
     <artifactId>org.apache.sling.provisioning.model</artifactId>
-    <version>1.3.1-SNAPSHOT</version>
+    <version>1.4.0</version>
     <packaging>bundle</packaging>
 
     <name>Apache Sling Provisioning Model</name>
@@ -31,9 +31,9 @@
     </description>
 
     <scm>
-        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/tooling/support/provisioning-model</connection>
-        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/tooling/support/provisioning-model</developerConnection>
-        <url>http://svn.apache.org/viewvc/sling/trunk/tooling/support/provisioning-model</url>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/tags/org.apache.sling.provisioning.model-1.4.0</connection>
+        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/tags/org.apache.sling.provisioning.model-1.4.0</developerConnection>
+        <url>http://svn.apache.org/viewvc/sling/tags/org.apache.sling.provisioning.model-1.4.0</url>
     </scm>
 
     <build>

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.

[sling-org-apache-sling-provisioning-model] 06/11: SLING-5174 : Refactor merging of models into separate utility class and add merge options

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.provisioning.model-1.4.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-provisioning-model.git

commit 94bc251ea0f4747362d969455242e2557add6ec8
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Wed Oct 21 08:36:50 2015 +0000

    SLING-5174 : Refactor merging of models into separate utility class and add merge options
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/tooling/support/provisioning-model@1709753 13f79535-47bb-0310-9956-ffa450edef68
---
 .../sling/provisioning/model/MergeUtility.java     | 315 +++++++++++++++++++++
 .../sling/provisioning/model/ModelUtility.java     | 213 +-------------
 .../apache/sling/provisioning/model/Version.java   | 131 +++++++++
 .../sling/provisioning/model/VersionTest.java      |  72 +++++
 4 files changed, 529 insertions(+), 202 deletions(-)

diff --git a/src/main/java/org/apache/sling/provisioning/model/MergeUtility.java b/src/main/java/org/apache/sling/provisioning/model/MergeUtility.java
new file mode 100644
index 0000000..85e3ad5
--- /dev/null
+++ b/src/main/java/org/apache/sling/provisioning/model/MergeUtility.java
@@ -0,0 +1,315 @@
+/*
+ * 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.provisioning.model;
+
+import static org.apache.sling.provisioning.model.ModelResolveUtility.getProcessedConfiguration;
+
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Utility for merging two model.
+ *
+ * @since 1.4
+ */
+public abstract class MergeUtility {
+
+    /**
+     * Options for specifying some parts of the merge operation.
+     */
+    public static class MergeOptions {
+
+        private boolean handleRemoveRunMode = true;
+
+        private boolean latestArtifactWins = true;
+
+        /**
+         * Returns {@code true} if the remove run mode should be respected.
+         * @return {@code true} or {@code false}
+         */
+        public boolean isHandleRemoveRunMode() {
+            return handleRemoveRunMode;
+        }
+
+        /**
+         * Set to {@code true} if the remove run mode should be respected.
+         * @param handleRemoveRunMode
+         */
+        public void setHandleRemoveRunMode(boolean handleRemoveRunMode) {
+            this.handleRemoveRunMode = handleRemoveRunMode;
+        }
+
+        /**
+         * Returns {@code true} if the latest artifact should win on a merge.
+         * @return {@code true} or {@code false} if the artifact with the
+         *         highest version should win
+         */
+        public boolean isLatestArtifactWins() {
+            return latestArtifactWins;
+        }
+
+        /**
+         * Set to {@code true} if the latest artifact should win on a merge.
+         * Set to {@code false} if the artifact with the highest version should win
+         */
+        public void setLatestArtifactWins(boolean latestArtifactWins) {
+            this.latestArtifactWins = latestArtifactWins;
+        }
+    }
+
+    /**
+     * Merge the additional model into the base model.
+     * @param base The base model.
+     * @param additional The additional model.
+     */
+    public static void merge(final Model base, final Model additional) {
+        merge(base, additional, new MergeOptions());
+    }
+
+    /**
+     * Merge the additional model into the base model.
+     * @param base The base model.
+     * @param additional The additional model.
+     * @param options The merge options
+     */
+    public static void merge(final Model base, final Model additional, final MergeOptions options) {
+        // features
+        for(final Feature feature : additional.getFeatures()) {
+            final Feature baseFeature = base.getOrCreateFeature(feature.getName());
+            baseFeature.setType(feature.getType());
+
+            // additional sections
+            baseFeature.getAdditionalSections().addAll(feature.getAdditionalSections());
+
+            // variables
+            baseFeature.getVariables().putAll(feature.getVariables());
+
+            // run modes
+            for(final RunMode runMode : feature.getRunModes()) {
+                // check for special remove run mode
+                final String names[] = runMode.getNames();
+                if ( options.isHandleRemoveRunMode() && names != null ) {
+                    if ( handleRemoveRunMode(baseFeature, runMode) ) {
+                        continue;
+                    }
+                }
+                final RunMode baseRunMode = baseFeature.getOrCreateRunMode(names);
+
+                // artifact groups
+                for(final ArtifactGroup group : runMode.getArtifactGroups()) {
+                    final ArtifactGroup baseGroup = baseRunMode.getOrCreateArtifactGroup(group.getStartLevel());
+
+                    for(final Artifact artifact : group) {
+                        boolean addArtifact = true;
+                        for(final ArtifactGroup searchGroup : baseRunMode.getArtifactGroups()) {
+                            final Artifact found = searchGroup.search(artifact);
+                            if ( found != null ) {
+                                if ( options.isLatestArtifactWins() ) {
+                                    searchGroup.remove(found);
+                                } else {
+                                    final Version baseVersion = new Version(found.getVersion());
+                                    final Version mergeVersion = new Version(artifact.getVersion());
+                                    if ( baseVersion.compareTo(mergeVersion) <= 0 ) {
+                                        searchGroup.remove(found);
+                                    } else {
+                                        addArtifact = false;
+                                    }
+                                }
+                            }
+                        }
+                        if ( addArtifact ) {
+                            baseGroup.add(artifact);
+                        }
+                    }
+                }
+
+                // configurations
+                for(final Configuration config : runMode.getConfigurations()) {
+                    final Configuration found = baseRunMode.getOrCreateConfiguration(config.getPid(), config.getFactoryPid());
+
+                    mergeConfiguration(found, config);
+                }
+
+                // settings
+                for(final Map.Entry<String, String> entry : runMode.getSettings() ) {
+                    baseRunMode.getSettings().put(entry.getKey(), entry.getValue());
+                }
+            }
+
+        }
+    }
+
+    /**
+     * Handle the remove run mode
+     * @param baseFeature The base feature
+     * @param runMode The current run mode
+     * @return {@code true} if the current run mode is a remove run mode
+     */
+    private static boolean handleRemoveRunMode(final Feature baseFeature, final RunMode runMode) {
+        String names[] = runMode.getNames();
+        int removeIndex = -1;
+        int index = 0;
+        for(final String name : names) {
+            if ( name.equals(ModelConstants.RUN_MODE_REMOVE) ) {
+                removeIndex = index;
+                break;
+            }
+            index++;
+        }
+        if ( removeIndex != -1 ) {
+            String[] newNames = null;
+            if ( names.length > 1 ) {
+                newNames = new String[names.length - 1];
+                index = 0;
+                for(final String name : names) {
+                    if ( !name.equals(ModelConstants.RUN_MODE_REMOVE) ) {
+                        newNames[index++] = name;
+                    }
+                }
+            }
+            names = newNames;
+            final RunMode baseRunMode = baseFeature.getRunMode(names);
+            if ( baseRunMode != null ) {
+
+                // artifact groups
+                for(final ArtifactGroup group : runMode.getArtifactGroups()) {
+                    for(final Artifact artifact : group) {
+                        for(final ArtifactGroup searchGroup : baseRunMode.getArtifactGroups()) {
+                            final Artifact found = searchGroup.search(artifact);
+                            if ( found != null ) {
+                                searchGroup.remove(found);
+                            }
+                        }
+                    }
+                }
+
+                // configurations
+                for(final Configuration config : runMode.getConfigurations()) {
+                    final Configuration found = baseRunMode.getConfiguration(config.getPid(), config.getFactoryPid());
+                    if ( found != null ) {
+                        baseRunMode.getConfigurations().remove(found);
+                    }
+                }
+
+                // settings
+                for(final Map.Entry<String, String> entry : runMode.getSettings() ) {
+                    baseRunMode.getSettings().remove(entry.getKey());
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Merge two configurations
+     * @param baseConfig The base configuration.
+     * @param mergeConfig The merge configuration.
+     */
+    private static void mergeConfiguration(final Configuration baseConfig, final Configuration mergeConfig) {
+        // check for merge mode
+        final boolean isNew = baseConfig.getProperties().isEmpty();
+        if ( isNew ) {
+            copyConfigurationProperties(baseConfig, mergeConfig);
+            final Object mode = mergeConfig.getProperties().get(ModelConstants.CFG_UNPROCESSED_MODE);
+            if ( mode != null ) {
+                baseConfig.getProperties().put(ModelConstants.CFG_UNPROCESSED_MODE, mode);
+            }
+        } else {
+            final boolean baseIsRaw = baseConfig.getProperties().get(ModelConstants.CFG_UNPROCESSED) != null;
+            final boolean mergeIsRaw = mergeConfig.getProperties().get(ModelConstants.CFG_UNPROCESSED) != null;
+            // simplest case, both are raw
+            if ( baseIsRaw && mergeIsRaw ) {
+                final String cfgMode = (String)mergeConfig.getProperties().get(ModelConstants.CFG_UNPROCESSED_MODE);
+                if ( cfgMode == null || ModelConstants.CFG_MODE_OVERWRITE.equals(cfgMode) ) {
+                    copyConfigurationProperties(baseConfig, mergeConfig);
+                } else {
+                    final Configuration newConfig = new Configuration(baseConfig.getPid(), baseConfig.getFactoryPid());
+                    getProcessedConfiguration(null, newConfig, baseConfig, false, null);
+                    clearConfiguration(baseConfig);
+                    copyConfigurationProperties(baseConfig, newConfig);
+
+                    clearConfiguration(newConfig);
+                    getProcessedConfiguration(null, newConfig, mergeConfig, false, null);
+
+                    if ( baseConfig.isSpecial() ) {
+                        final String baseValue = baseConfig.getProperties().get(baseConfig.getPid()).toString();
+                        final String mergeValue = newConfig.getProperties().get(baseConfig.getPid()).toString();
+                        baseConfig.getProperties().put(baseConfig.getPid(), baseValue + "\n" + mergeValue);
+                    } else {
+                        copyConfigurationProperties(baseConfig, newConfig);
+                    }
+                }
+
+            // another simple case, both are not raw
+            } else if ( !baseIsRaw && !mergeIsRaw ) {
+                // merge mode is always overwrite
+                clearConfiguration(baseConfig);
+                copyConfigurationProperties(baseConfig, mergeConfig);
+
+            // base is not raw but merge is
+            } else if ( !baseIsRaw && mergeIsRaw ) {
+                final String cfgMode = (String)mergeConfig.getProperties().get(ModelConstants.CFG_UNPROCESSED_MODE);
+                if ( cfgMode == null || ModelConstants.CFG_MODE_OVERWRITE.equals(cfgMode) ) {
+                    clearConfiguration(baseConfig);
+                    copyConfigurationProperties(baseConfig, mergeConfig);
+                } else {
+                    final Configuration newMergeConfig = new Configuration(mergeConfig.getPid(), mergeConfig.getFactoryPid());
+                    getProcessedConfiguration(null, newMergeConfig, mergeConfig, false, null);
+
+                    if ( baseConfig.isSpecial() ) {
+                        final String baseValue = baseConfig.getProperties().get(baseConfig.getPid()).toString();
+                        final String mergeValue = newMergeConfig.getProperties().get(baseConfig.getPid()).toString();
+                        baseConfig.getProperties().put(baseConfig.getPid(), baseValue + "\n" + mergeValue);
+                    } else {
+                        copyConfigurationProperties(baseConfig, newMergeConfig);
+                    }
+                }
+
+                // base is raw, but merge is not raw
+            } else {
+                // merge mode is always overwrite
+                clearConfiguration(baseConfig);
+                copyConfigurationProperties(baseConfig, mergeConfig);
+            }
+        }
+    }
+
+    private static void clearConfiguration(final Configuration cfg) {
+        final Set<String> keys = new HashSet<String>();
+        final Enumeration<String> e = cfg.getProperties().keys();
+        while ( e.hasMoreElements() ) {
+            keys.add(e.nextElement());
+        }
+
+        for(final String key : keys) {
+            cfg.getProperties().remove(key);
+        }
+    }
+
+    private static void copyConfigurationProperties(final Configuration baseConfig, final Configuration mergeConfig) {
+        final Enumeration<String> e = mergeConfig.getProperties().keys();
+        while ( e.hasMoreElements() ) {
+            final String key = e.nextElement();
+            if ( !key.equals(ModelConstants.CFG_UNPROCESSED_MODE) ) {
+                baseConfig.getProperties().put(key, mergeConfig.getProperties().get(key));
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/provisioning/model/ModelUtility.java b/src/main/java/org/apache/sling/provisioning/model/ModelUtility.java
index 3a30ab9..f01445d 100644
--- a/src/main/java/org/apache/sling/provisioning/model/ModelUtility.java
+++ b/src/main/java/org/apache/sling/provisioning/model/ModelUtility.java
@@ -16,18 +16,16 @@
  */
 package org.apache.sling.provisioning.model;
 
-import static org.apache.sling.provisioning.model.ModelResolveUtility.getProcessedConfiguration;
 import static org.apache.sling.provisioning.model.ModelResolveUtility.resolveArtifactVersion;
 
 import java.util.Arrays;
-import java.util.Enumeration;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
-import java.util.Set;
+
+import org.apache.sling.provisioning.model.MergeUtility.MergeOptions;
 
 /**
- * Merge two models
+ * Model utility
  */
 public abstract class ModelUtility {
 
@@ -35,9 +33,11 @@ public abstract class ModelUtility {
      * Merge the additional model into the base model.
      * @param base The base model.
      * @param additional The additional model.
+     * @deprecated Use {link {@link MergeUtility#merge(Model, Model)}
      */
+    @Deprecated
     public static void merge(final Model base, final Model additional) {
-        merge(base, additional, true);
+        MergeUtility.merge(base, additional);
     }
 
     /**
@@ -46,205 +46,14 @@ public abstract class ModelUtility {
      * @param additional The additional model.
      * @param handleRemove Handle special remove run mode
      * @since 1.2
+     * @deprecated Use {link {@link MergeUtility#merge(Model, Model, org.apache.sling.provisioning.model.MergeUtility.MergeOptions)}
      */
+    @Deprecated
     public static void merge(final Model base, final Model additional, final boolean handleRemove) {
-        // features
-        for(final Feature feature : additional.getFeatures()) {
-            final Feature baseFeature = base.getOrCreateFeature(feature.getName());
-            baseFeature.setType(feature.getType());
-
-            // additional sections
-            baseFeature.getAdditionalSections().addAll(feature.getAdditionalSections());
-
-            // variables
-            baseFeature.getVariables().putAll(feature.getVariables());
-
-            // run modes
-            for(final RunMode runMode : feature.getRunModes()) {
-                // check for special remove run mode
-                String names[] = runMode.getNames();
-                if ( handleRemove ) {
-                    if ( names != null ) {
-                        int removeIndex = -1;
-                        int index = 0;
-                        for(final String name : names) {
-                            if ( name.equals(ModelConstants.RUN_MODE_REMOVE) ) {
-                                removeIndex = index;
-                                break;
-                            }
-                            index++;
-                        }
-                        if ( removeIndex != -1 ) {
-                            String[] newNames = null;
-                            if ( names.length > 1 ) {
-                                newNames = new String[names.length - 1];
-                                index = 0;
-                                for(final String name : names) {
-                                    if ( !name.equals(ModelConstants.RUN_MODE_REMOVE) ) {
-                                        newNames[index++] = name;
-                                    }
-                                }
-                            }
-                            names = newNames;
-                            final RunMode baseRunMode = baseFeature.getRunMode(names);
-                            if ( baseRunMode != null ) {
-
-                                // artifact groups
-                                for(final ArtifactGroup group : runMode.getArtifactGroups()) {
-                                    for(final Artifact artifact : group) {
-                                        for(final ArtifactGroup searchGroup : baseRunMode.getArtifactGroups()) {
-                                            final Artifact found = searchGroup.search(artifact);
-                                            if ( found != null ) {
-                                                searchGroup.remove(found);
-                                            }
-                                        }
-                                    }
-                                }
-
-                                // configurations
-                                for(final Configuration config : runMode.getConfigurations()) {
-                                    final Configuration found = baseRunMode.getConfiguration(config.getPid(), config.getFactoryPid());
-                                    if ( found != null ) {
-                                        baseRunMode.getConfigurations().remove(found);
-                                    }
-                                }
-
-                                // settings
-                                for(final Map.Entry<String, String> entry : runMode.getSettings() ) {
-                                    baseRunMode.getSettings().remove(entry.getKey());
-                                }
-                            }
-                            continue;
-                        }
-                    }
-                }
-                final RunMode baseRunMode = baseFeature.getOrCreateRunMode(names);
-
-                // artifact groups
-                for(final ArtifactGroup group : runMode.getArtifactGroups()) {
-                    final ArtifactGroup baseGroup = baseRunMode.getOrCreateArtifactGroup(group.getStartLevel());
-
-                    for(final Artifact artifact : group) {
-                        for(final ArtifactGroup searchGroup : baseRunMode.getArtifactGroups()) {
-                            final Artifact found = searchGroup.search(artifact);
-                            if ( found != null ) {
-                                searchGroup.remove(found);
-                            }
-                        }
-                        baseGroup.add(artifact);
-                    }
-                }
-
-                // configurations
-                for(final Configuration config : runMode.getConfigurations()) {
-                    final Configuration found = baseRunMode.getOrCreateConfiguration(config.getPid(), config.getFactoryPid());
-
-                    mergeConfiguration(found, config);
-                }
-
-                // settings
-                for(final Map.Entry<String, String> entry : runMode.getSettings() ) {
-                    baseRunMode.getSettings().put(entry.getKey(), entry.getValue());
-                }
-            }
+        final MergeOptions opts = new MergeOptions();
+        opts.setHandleRemoveRunMode(handleRemove);
+        MergeUtility.merge(base, additional, opts);
 
-        }
-    }
-
-    /**
-     * Merge two configurations
-     * @param baseConfig The base configuration.
-     * @param mergeConfig The merge configuration.
-     */
-    private static void mergeConfiguration(final Configuration baseConfig, final Configuration mergeConfig) {
-        // check for merge mode
-        final boolean isNew = baseConfig.getProperties().isEmpty();
-        if ( isNew ) {
-            copyConfigurationProperties(baseConfig, mergeConfig);
-            final Object mode = mergeConfig.getProperties().get(ModelConstants.CFG_UNPROCESSED_MODE);
-            if ( mode != null ) {
-                baseConfig.getProperties().put(ModelConstants.CFG_UNPROCESSED_MODE, mode);
-            }
-        } else {
-            final boolean baseIsRaw = baseConfig.getProperties().get(ModelConstants.CFG_UNPROCESSED) != null;
-            final boolean mergeIsRaw = mergeConfig.getProperties().get(ModelConstants.CFG_UNPROCESSED) != null;
-            // simplest case, both are raw
-            if ( baseIsRaw && mergeIsRaw ) {
-                final String cfgMode = (String)mergeConfig.getProperties().get(ModelConstants.CFG_UNPROCESSED_MODE);
-                if ( cfgMode == null || ModelConstants.CFG_MODE_OVERWRITE.equals(cfgMode) ) {
-                    copyConfigurationProperties(baseConfig, mergeConfig);
-                } else {
-                    final Configuration newConfig = new Configuration(baseConfig.getPid(), baseConfig.getFactoryPid());
-                    getProcessedConfiguration(null, newConfig, baseConfig, false, null);
-                    clearConfiguration(baseConfig);
-                    copyConfigurationProperties(baseConfig, newConfig);
-
-                    clearConfiguration(newConfig);
-                    getProcessedConfiguration(null, newConfig, mergeConfig, false, null);
-
-                    if ( baseConfig.isSpecial() ) {
-                        final String baseValue = baseConfig.getProperties().get(baseConfig.getPid()).toString();
-                        final String mergeValue = newConfig.getProperties().get(baseConfig.getPid()).toString();
-                        baseConfig.getProperties().put(baseConfig.getPid(), baseValue + "\n" + mergeValue);
-                    } else {
-                        copyConfigurationProperties(baseConfig, newConfig);
-                    }
-                }
-
-            // another simple case, both are not raw
-            } else if ( !baseIsRaw && !mergeIsRaw ) {
-                // merge mode is always overwrite
-                clearConfiguration(baseConfig);
-                copyConfigurationProperties(baseConfig, mergeConfig);
-
-            // base is not raw but merge is
-            } else if ( !baseIsRaw && mergeIsRaw ) {
-                final String cfgMode = (String)mergeConfig.getProperties().get(ModelConstants.CFG_UNPROCESSED_MODE);
-                if ( cfgMode == null || ModelConstants.CFG_MODE_OVERWRITE.equals(cfgMode) ) {
-                    clearConfiguration(baseConfig);
-                    copyConfigurationProperties(baseConfig, mergeConfig);
-                } else {
-                    final Configuration newMergeConfig = new Configuration(mergeConfig.getPid(), mergeConfig.getFactoryPid());
-                    getProcessedConfiguration(null, newMergeConfig, mergeConfig, false, null);
-
-                    if ( baseConfig.isSpecial() ) {
-                        final String baseValue = baseConfig.getProperties().get(baseConfig.getPid()).toString();
-                        final String mergeValue = newMergeConfig.getProperties().get(baseConfig.getPid()).toString();
-                        baseConfig.getProperties().put(baseConfig.getPid(), baseValue + "\n" + mergeValue);
-                    } else {
-                        copyConfigurationProperties(baseConfig, newMergeConfig);
-                    }
-                }
-
-                // base is raw, but merge is not raw
-            } else {
-                // merge mode is always overwrite
-                clearConfiguration(baseConfig);
-                copyConfigurationProperties(baseConfig, mergeConfig);
-            }
-        }
-    }
-
-    private static void clearConfiguration(final Configuration cfg) {
-        final Set<String> keys = new HashSet<String>();
-        final Enumeration<String> e = cfg.getProperties().keys();
-        while ( e.hasMoreElements() ) {
-            keys.add(e.nextElement());
-        }
-
-        for(final String key : keys) {
-            cfg.getProperties().remove(key);
-        }
-    }
-
-    private static void copyConfigurationProperties(final Configuration baseConfig, final Configuration mergeConfig) {
-        final Enumeration<String> e = mergeConfig.getProperties().keys();
-        while ( e.hasMoreElements() ) {
-            final String key = e.nextElement();
-            if ( !key.equals(ModelConstants.CFG_UNPROCESSED_MODE) ) {
-                baseConfig.getProperties().put(key, mergeConfig.getProperties().get(key));
-            }
-        }
     }
 
     /**
diff --git a/src/main/java/org/apache/sling/provisioning/model/Version.java b/src/main/java/org/apache/sling/provisioning/model/Version.java
new file mode 100644
index 0000000..2e90ba2
--- /dev/null
+++ b/src/main/java/org/apache/sling/provisioning/model/Version.java
@@ -0,0 +1,131 @@
+/*
+ * 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.provisioning.model;
+
+/**
+ * Version object supporting Maven versions.
+ *
+ */
+public class Version implements Comparable<Version> {
+
+    private final int majorVersion;
+	private final int minorVersion;
+	private final int microVersion;
+	private final String qualifier;
+
+	/**
+	 * Creates a version identifier from the specified string.
+	 * @throws IllegalArgumentException if the version string can't be parsed
+	 */
+	public Version(final String version) {
+	    String parts[] = version.split("\\.");
+	    if ( parts.length > 3 ) {
+	        throw new IllegalArgumentException("Invalid version " + version);
+	    }
+	    final int pos = parts[parts.length - 1].indexOf('-');
+	    if ( pos != -1 ) {
+	        final String[] newParts = new String[4];
+	        newParts[0] = parts.length > 1 ? parts[0] : parts[0].substring(0, pos);
+            newParts[1] = parts.length > 2 ? parts[1] : (parts.length > 1 ? parts[1].substring(0, pos) : "0");
+            newParts[2] = parts.length > 3 ? parts[2] : (parts.length > 2 ? parts[2].substring(0, pos) : "0");
+            newParts[3] = parts[parts.length - 1].substring(pos + 1);
+            parts = newParts;
+	    }
+	    this.majorVersion = parseInt(parts[0], version);
+	    if ( parts.length > 1 ) {
+	        this.minorVersion = parseInt(parts[1], version);
+	    } else {
+	        this.minorVersion = 0;
+	    }
+        if ( parts.length > 2 ) {
+            this.microVersion = parseInt(parts[2], version);
+        } else {
+            this.microVersion = 0;
+        }
+        this.qualifier = (parts.length > 3 ? parts[3] : "");
+
+	}
+
+	/**
+	 * Parse an integer.
+	 */
+	private static int parseInt(final String value, final String version) {
+		try {
+			return Integer.parseInt(value);
+		} catch (NumberFormatException e) {
+			throw new IllegalArgumentException("Invalid version " + version);
+		}
+	}
+
+	@Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + majorVersion;
+        result = prime * result + microVersion;
+        result = prime * result + minorVersion;
+        result = prime * result + ((qualifier == null) ? 0 : qualifier.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        Version other = (Version) obj;
+        if (majorVersion != other.majorVersion)
+            return false;
+        if (microVersion != other.microVersion)
+            return false;
+        if (minorVersion != other.minorVersion)
+            return false;
+        if (qualifier == null) {
+            if (other.qualifier != null)
+                return false;
+        } else if (!qualifier.equals(other.qualifier))
+            return false;
+        return true;
+    }
+
+    /**
+	 * Compares this {@code Version} object to another {@code Version}.
+	 */
+	@Override
+    public int compareTo(final Version other) {
+	    int result = 0;
+		if (other != this) {
+
+	        result = majorVersion - other.majorVersion;
+	        if (result == 0) {
+	            result = minorVersion - other.minorVersion;
+	            if (result == 0) {
+	                result = microVersion - other.microVersion;
+	                if (result == 0) {
+	                    result = other.qualifier.compareTo(qualifier);
+	                }
+	            }
+
+	        }
+
+		}
+		return result;
+	}
+}
diff --git a/src/test/java/org/apache/sling/provisioning/model/VersionTest.java b/src/test/java/org/apache/sling/provisioning/model/VersionTest.java
new file mode 100644
index 0000000..d242c8c
--- /dev/null
+++ b/src/test/java/org/apache/sling/provisioning/model/VersionTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.provisioning.model;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class VersionTest {
+
+    @Test
+    public void testSameVersion() {
+        final String v1 = "1";
+        final String v10 = "1.0";
+        final String v100 = "1.0.0";
+
+        final Version ve1 = new Version(v1);
+        final Version ve10 = new Version(v10);
+        final Version ve100 = new Version(v100);
+
+        assertEquals(0, ve1.compareTo(ve10));
+        assertEquals(0, ve10.compareTo(ve100));
+        assertEquals(0, ve1.compareTo(ve100));
+        assertEquals(0, ve10.compareTo(ve1));
+        assertEquals(0, ve100.compareTo(ve10));
+        assertEquals(0, ve100.compareTo(ve1));
+    }
+
+    @Test
+    public void testVersions() {
+        final String v1 = "1";
+        final String v20 = "2.0";
+        final String v150 = "1.5.0";
+
+        final Version ve1 = new Version(v1);
+        final Version ve20 = new Version(v20);
+        final Version ve150 = new Version(v150);
+
+        assertTrue(ve1.compareTo(ve20) < 0);
+        assertTrue(ve20.compareTo(ve150) > 0);
+        assertTrue(ve1.compareTo(ve150) < 0);
+        assertTrue(ve20.compareTo(ve1) > 0);
+        assertTrue(ve150.compareTo(ve20) < 0);
+        assertTrue(ve150.compareTo(ve1) > 0);
+    }
+
+    @Test
+    public void testQualifier() {
+        final String v1 = "1";
+        final String v1snapshot = "1-SNAPSHOT";
+
+        final Version ve1 = new Version(v1);
+        final Version ve1snapshot = new Version(v1snapshot);
+
+        assertTrue(ve1.compareTo(ve1snapshot) > 0);
+    }
+}

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.