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

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


The following commit(s) were added to refs/heads/master by this push:
     new 7565173  Updates to Feature Service implementation
7565173 is described below

commit 7565173915fa5567f48223f61f3434cc67beb944
Author: David Bosschaert <[email protected]>
AuthorDate: Wed Jun 30 16:20:49 2021 +0100

    Updates to Feature Service implementation
---
 ...leBuilderImpl.java => ArtifactBuilderImpl.java} |  68 +++----
 .../sling/feature/osgi/impl/ArtifactImpl.java      |  50 ------
 .../feature/osgi/impl/BuilderFactoryImpl.java      |   8 +-
 .../sling/feature/osgi/impl/BundleBuilderImpl.java |  50 +++---
 .../feature/osgi/impl/ExtensionBuilderImpl.java    |  13 +-
 .../feature/osgi/impl/FeatureBuilderImpl.java      |  62 +++----
 .../feature/osgi/impl/FeatureServiceImpl.java      |  33 +++-
 .../org/apache/sling/feature/osgi/impl/IDImpl.java | 198 +++++++++++++++++++++
 .../feature/osgi/impl/FeatureServiceImplTest.java  |  20 +--
 9 files changed, 343 insertions(+), 159 deletions(-)

diff --git 
a/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/BundleBuilderImpl.java
 
b/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/ArtifactBuilderImpl.java
similarity index 57%
copy from 
osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/BundleBuilderImpl.java
copy to 
osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/ArtifactBuilderImpl.java
index b9cb298..a294e11 100644
--- 
a/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/BundleBuilderImpl.java
+++ 
b/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/ArtifactBuilderImpl.java
@@ -16,78 +16,80 @@
  */
 package org.apache.sling.feature.osgi.impl;
 
-import org.osgi.service.feature.FeatureBundle;
-import org.osgi.service.feature.FeatureBundleBuilder;
-import org.osgi.service.feature.ID;
-
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
 
-class BundleBuilderImpl implements FeatureBundleBuilder {
+import org.osgi.service.feature.FeatureArtifact;
+import org.osgi.service.feature.FeatureArtifactBuilder;
+import org.osgi.service.feature.ID;
+
+class ArtifactBuilderImpl implements FeatureArtifactBuilder {
     private final ID id;
 
     private final Map<String,Object> metadata = new HashMap<>();
 
-    BundleBuilderImpl(ID id) {
+    ArtifactBuilderImpl(ID id) {
         this.id = id;
     }
 
     @Override
-    public FeatureBundleBuilder addMetadata(String key, Object value) {
+    public FeatureArtifactBuilder addMetadata(String key, Object value) {
         this.metadata.put(key, value);
         return this;
     }
 
     @Override
-    public FeatureBundleBuilder addMetadata(Map<String,Object> md) {
+    public FeatureArtifactBuilder addMetadata(Map<String,Object> md) {
         this.metadata.putAll(md);
         return this;
     }
 
     @Override
-    public FeatureBundle build() {
-        return new BundleImpl(id, metadata);
+    public FeatureArtifact build() {
+        return new ArtifactImpl(id, metadata);
     }
 
-    private static class BundleImpl extends ArtifactImpl implements 
FeatureBundle {
+    private static class ArtifactImpl implements FeatureArtifact {
+        private final ID id;
         private final Map<String, Object> metadata;
 
-        private BundleImpl(ID id, Map<String, Object> metadata) {
-            super(id);
-
+        private ArtifactImpl(ID id, Map<String, Object> metadata) {
+            this.id = id;
             this.metadata = Collections.unmodifiableMap(metadata);
         }
 
         @Override
+        public ID getID() {
+            return id;
+        }
+        
+        @Override
         public Map<String, Object> getMetadata() {
             return metadata;
         }
 
         @Override
-        public int hashCode() {
-            final int prime = 31;
-            int result = super.hashCode();
-            result = prime * result + Objects.hash(metadata);
-            return result;
-        }
+               public int hashCode() {
+                       return Objects.hash(id, metadata);
+               }
 
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj)
-                return true;
-            if (!super.equals(obj))
-                return false;
-            if (!(obj instanceof BundleImpl))
-                return false;
-            BundleImpl other = (BundleImpl) obj;
-            return Objects.equals(metadata, other.metadata);
-        }
+               @Override
+               public boolean equals(Object obj) {
+                       if (this == obj)
+                               return true;
+                       if (obj == null)
+                               return false;
+                       if (getClass() != obj.getClass())
+                               return false;
+                       ArtifactImpl other = (ArtifactImpl) obj;
+                       return Objects.equals(id, other.id) && 
Objects.equals(metadata, other.metadata);
+               }
 
-        @Override
+               @Override
         public String toString() {
-            return "BundleImpl [getID()=" + getID() + "]";
+            return "ArtifactImpl [getID()=" + getID() + "]";
         }
     }
 }
diff --git 
a/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/ArtifactImpl.java
 
b/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/ArtifactImpl.java
deleted file mode 100644
index 7b818cf..0000000
--- 
a/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/ArtifactImpl.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations 
under
- * the License.
- */
-package org.apache.sling.feature.osgi.impl;
-
-import org.osgi.service.feature.FeatureArtifact;
-import org.osgi.service.feature.ID;
-
-import java.util.Objects;
-
-class ArtifactImpl implements FeatureArtifact {
-    private final ID id;
-
-    ArtifactImpl(ID id) {
-        this.id = id;
-    }
-
-    @Override
-    public ID getID() {
-        return id;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(id);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj)
-            return true;
-        if (!(obj instanceof ArtifactImpl))
-            return false;
-        ArtifactImpl other = (ArtifactImpl) obj;
-        return Objects.equals(id, other.id);
-    }
-}
diff --git 
a/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/BuilderFactoryImpl.java
 
b/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/BuilderFactoryImpl.java
index bf8dfc7..97ae2b4 100644
--- 
a/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/BuilderFactoryImpl.java
+++ 
b/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/BuilderFactoryImpl.java
@@ -17,6 +17,7 @@
 package org.apache.sling.feature.osgi.impl;
 
 import org.osgi.service.feature.BuilderFactory;
+import org.osgi.service.feature.FeatureArtifactBuilder;
 import org.osgi.service.feature.FeatureBuilder;
 import org.osgi.service.feature.FeatureBundleBuilder;
 import org.osgi.service.feature.FeatureConfigurationBuilder;
@@ -26,7 +27,12 @@ import org.osgi.service.feature.FeatureExtensionBuilder;
 import org.osgi.service.feature.ID;
 
 class BuilderFactoryImpl implements BuilderFactory {
-    @Override
+       @Override
+       public FeatureArtifactBuilder newArtifactBuilder(ID id) {
+               return new ArtifactBuilderImpl(id);
+       }
+
+       @Override
     public FeatureBundleBuilder newBundleBuilder(ID id) {
         return new BundleBuilderImpl(id);
     }
diff --git 
a/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/BundleBuilderImpl.java
 
b/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/BundleBuilderImpl.java
index b9cb298..1d39e59 100644
--- 
a/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/BundleBuilderImpl.java
+++ 
b/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/BundleBuilderImpl.java
@@ -16,15 +16,15 @@
  */
 package org.apache.sling.feature.osgi.impl;
 
-import org.osgi.service.feature.FeatureBundle;
-import org.osgi.service.feature.FeatureBundleBuilder;
-import org.osgi.service.feature.ID;
-
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
 
+import org.osgi.service.feature.FeatureBundle;
+import org.osgi.service.feature.FeatureBundleBuilder;
+import org.osgi.service.feature.ID;
+
 class BundleBuilderImpl implements FeatureBundleBuilder {
     private final ID id;
 
@@ -51,39 +51,41 @@ class BundleBuilderImpl implements FeatureBundleBuilder {
         return new BundleImpl(id, metadata);
     }
 
-    private static class BundleImpl extends ArtifactImpl implements 
FeatureBundle {
+    private static class BundleImpl implements FeatureBundle {
+        private final ID id;
         private final Map<String, Object> metadata;
 
         private BundleImpl(ID id, Map<String, Object> metadata) {
-            super(id);
-
+            this.id = id;
             this.metadata = Collections.unmodifiableMap(metadata);
         }
 
         @Override
+        public ID getID() {
+            return id;
+        }
+        
+        @Override
         public Map<String, Object> getMetadata() {
             return metadata;
         }
 
         @Override
-        public int hashCode() {
-            final int prime = 31;
-            int result = super.hashCode();
-            result = prime * result + Objects.hash(metadata);
-            return result;
-        }
+               public int hashCode() {
+                       return Objects.hash(id, metadata);
+               }
 
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj)
-                return true;
-            if (!super.equals(obj))
-                return false;
-            if (!(obj instanceof BundleImpl))
-                return false;
-            BundleImpl other = (BundleImpl) obj;
-            return Objects.equals(metadata, other.metadata);
-        }
+               @Override
+               public boolean equals(Object obj) {
+                       if (this == obj)
+                               return true;
+                       if (obj == null)
+                               return false;
+                       if (getClass() != obj.getClass())
+                               return false;
+                       BundleImpl other = (BundleImpl) obj;
+                       return Objects.equals(id, other.id) && 
Objects.equals(metadata, other.metadata);
+               }
 
         @Override
         public String toString() {
diff --git 
a/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/ExtensionBuilderImpl.java
 
b/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/ExtensionBuilderImpl.java
index 6e2c9a0..6cd972b 100644
--- 
a/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/ExtensionBuilderImpl.java
+++ 
b/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/ExtensionBuilderImpl.java
@@ -21,13 +21,17 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
+import org.osgi.service.feature.FeatureArtifact;
 import org.osgi.service.feature.FeatureExtension;
 import org.osgi.service.feature.FeatureExtension.Kind;
 import org.osgi.service.feature.FeatureExtension.Type;
 import org.osgi.service.feature.FeatureExtensionBuilder;
+import org.osgi.service.feature.FeatureService;
 import org.osgi.service.feature.ID;
 
 class ExtensionBuilderImpl implements FeatureExtensionBuilder {
+       private static FeatureService FEATURE_SERVICE = new 
FeatureServiceImpl();
+       
     private final String name;
     private final Type type;
     private final Kind kind;
@@ -95,7 +99,7 @@ class ExtensionBuilderImpl implements FeatureExtensionBuilder 
{
 
     @Override
     public FeatureExtensionBuilder addArtifact(String groupId, String 
artifactId, String version, String at, String classifier) {
-       return addArtifact(new ID(groupId, artifactId, version, at, 
classifier)); 
+       return addArtifact(FEATURE_SERVICE.getID(groupId, artifactId, version, 
at, classifier)); 
     }
     
     @Override
@@ -145,11 +149,12 @@ class ExtensionBuilderImpl implements 
FeatureExtensionBuilder {
             return content;
         }
 
-        public List<ID> getArtifacts() {
-            List<ID> res = new ArrayList<>();
+        public List<FeatureArtifact> getArtifacts() {
+            List<FeatureArtifact> res = new ArrayList<>();
 
             for (String s : content) {
-                res.add(ID.fromMavenID(s));
+               res.add(FEATURE_SERVICE.getBuilderFactory().newArtifactBuilder(
+                               FEATURE_SERVICE.getIDfromMavenID(s)).build());
             }
 
             return res;
diff --git 
a/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/FeatureBuilderImpl.java
 
b/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/FeatureBuilderImpl.java
index 97d4df1..28f19e6 100644
--- 
a/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/FeatureBuilderImpl.java
+++ 
b/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/FeatureBuilderImpl.java
@@ -150,7 +150,8 @@ class FeatureBuilderImpl implements FeatureBuilder {
                 bundles, categories, configurations, extensions, variables);
     }
 
-    private static class FeatureImpl extends ArtifactImpl implements Feature {
+    private static class FeatureImpl implements Feature {
+        private final ID id;
         private final Optional<String> name;
         private final Optional<String> copyright;
         private final Optional<String> description;
@@ -169,8 +170,7 @@ class FeatureBuilderImpl implements FeatureBuilder {
         private FeatureImpl(ID id, String aName, String cr, String desc, 
String docs, String lic, String sc, String vnd,
                 boolean comp, List<FeatureBundle> bs, List<String> cats, 
Map<String,FeatureConfiguration> cs,
                 Map<String,FeatureExtension> es, Map<String,String> vars) {
-            super(id);
-
+            this.id = id;
             name = Optional.ofNullable(aName);
             copyright = Optional.ofNullable(cr);
             description = Optional.ofNullable(desc);
@@ -188,6 +188,11 @@ class FeatureBuilderImpl implements FeatureBuilder {
         }
 
         @Override
+        public ID getID() {
+            return id;
+        }
+        
+        @Override
         public Optional<String> getName() {
             return name;
         }
@@ -253,33 +258,30 @@ class FeatureBuilderImpl implements FeatureBuilder {
         }
 
         @Override
-        public int hashCode() {
-            final int prime = 31;
-            int result = super.hashCode();
-            result = prime * result + Objects.hash(bundles, categories, 
complete, configurations, copyright, description, docURL,
-                    extensions, license, name, scm, variables, vendor);
-            return result;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj)
-                return true;
-            if (!super.equals(obj))
-                return false;
-            if (getClass() != obj.getClass())
-                return false;
-            FeatureImpl other = (FeatureImpl) obj;
-            return Objects.equals(bundles, other.bundles) && 
Objects.equals(categories, other.categories)
-                    && complete == other.complete && 
Objects.equals(configurations, other.configurations)
-                    && Objects.equals(copyright, other.copyright) && 
Objects.equals(description, other.description)
-                    && Objects.equals(docURL, other.docURL) && 
Objects.equals(extensions, other.extensions)
-                    && Objects.equals(license, other.license) && 
Objects.equals(name, other.name)
-                    && Objects.equals(scm, other.scm) && 
Objects.equals(variables, other.variables)
-                    && Objects.equals(vendor, other.vendor);
-        }
-
-        @Override
+               public int hashCode() {
+                       return Objects.hash(bundles, categories, complete, 
configurations, copyright, description, docURL,
+                                       extensions, id, license, name, scm, 
variables, vendor);
+               }
+
+               @Override
+               public boolean equals(Object obj) {
+                       if (this == obj)
+                               return true;
+                       if (obj == null)
+                               return false;
+                       if (getClass() != obj.getClass())
+                               return false;
+                       FeatureImpl other = (FeatureImpl) obj;
+                       return Objects.equals(bundles, other.bundles) && 
Objects.equals(categories, other.categories)
+                                       && complete == other.complete && 
Objects.equals(configurations, other.configurations)
+                                       && Objects.equals(copyright, 
other.copyright) && Objects.equals(description, other.description)
+                                       && Objects.equals(docURL, other.docURL) 
&& Objects.equals(extensions, other.extensions)
+                                       && Objects.equals(id, other.id) && 
Objects.equals(license, other.license)
+                                       && Objects.equals(name, other.name) && 
Objects.equals(scm, other.scm)
+                                       && Objects.equals(variables, 
other.variables) && Objects.equals(vendor, other.vendor);
+               }
+
+               @Override
         public String toString() {
             return "FeatureImpl [getID()=" + getID() + "]";
         }
diff --git 
a/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/FeatureServiceImpl.java
 
b/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/FeatureServiceImpl.java
index ed1fd5e..5460dfd 100644
--- 
a/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/FeatureServiceImpl.java
+++ 
b/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/FeatureServiceImpl.java
@@ -39,20 +39,41 @@ import org.osgi.service.feature.FeatureConfiguration;
 import org.osgi.service.feature.FeatureConfigurationBuilder;
 import org.osgi.service.feature.FeatureExtension;
 import org.osgi.service.feature.FeatureExtensionBuilder;
+import org.osgi.service.feature.FeatureService;
 import org.osgi.service.feature.ID;
 
-public class FeatureServiceImpl {
+public class FeatureServiceImpl implements FeatureService {
     private final BuilderFactoryImpl builderFactory = new BuilderFactoryImpl();
 
     public BuilderFactory getBuilderFactory() {
         return builderFactory;
     }
-
-    public Feature readFeature(Reader jsonReader) throws IOException {
+    
+    @Override
+       public ID getIDfromMavenID(String mavenID) {
+       return IDImpl.fromMavenID(mavenID);
+       }
+
+       @Override
+       public ID getID(String groupId, String artifactId, String version) {
+               return new IDImpl(groupId, artifactId, version, null, null);
+       }
+
+       @Override
+       public ID getID(String groupId, String artifactId, String version, 
String type) {
+               return new IDImpl(groupId, artifactId, version, type, null);
+       }
+
+       @Override
+       public ID getID(String groupId, String artifactId, String version, 
String type, String classifier) {
+               return new IDImpl(groupId, artifactId, version, type, 
classifier);
+       }
+
+       public Feature readFeature(Reader jsonReader) throws IOException {
         JsonObject json = Json.createReader(jsonReader).readObject();
 
         String id = json.getString("id");
-        FeatureBuilder builder = 
builderFactory.newFeatureBuilder(ID.fromMavenID(id));
+        FeatureBuilder builder = 
builderFactory.newFeatureBuilder(getIDfromMavenID(id));
 
         builder.setName(json.getString("title", null));
         builder.setCopyright(json.getString("copyright", null));
@@ -83,7 +104,7 @@ public class FeatureServiceImpl {
             if (val.getValueType() == JsonValue.ValueType.OBJECT) {
                 JsonObject jo = val.asJsonObject();
                 String bid = jo.getString("id");
-                FeatureBundleBuilder builder = 
builderFactory.newBundleBuilder(ID.fromMavenID(bid));
+                FeatureBundleBuilder builder = 
builderFactory.newBundleBuilder(getIDfromMavenID(bid));
 
                 for (Map.Entry<String, JsonValue> entry : jo.entrySet()) {
                     if (entry.getKey().equals("id"))
@@ -208,7 +229,7 @@ public class FeatureServiceImpl {
                 for (JsonValue jv : ja2) {
                     if (jv.getValueType() == JsonValue.ValueType.STRING) {
                         String id = ((JsonString) jv).getString();
-                        builder.addArtifact(ID.fromMavenID(id));
+                        builder.addArtifact(getIDfromMavenID(id));
                     }
                 }
                 break;
diff --git 
a/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/IDImpl.java
 
b/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/IDImpl.java
new file mode 100644
index 0000000..92d8e7f
--- /dev/null
+++ 
b/osgi-featuremodel/src/main/java/org/apache/sling/feature/osgi/impl/IDImpl.java
@@ -0,0 +1,198 @@
+package org.apache.sling.feature.osgi.impl;
+
+import java.util.Objects;
+import java.util.Optional;
+
+import org.osgi.service.feature.ID;
+
+public class IDImpl implements ID {
+    private final String groupId;
+    private final String artifactId;
+    private final String version; // The Artifact Version may not follow OSGi 
version rules
+    private final String type;
+    private final String classifier;
+
+    /**
+        * Construct an ID from a Maven ID. Maven IDs have the following syntax:
+        * <p>
+        * {@code group-id ':' artifact-id [ ':' [type] [ ':' classifier ] ] 
':' version}
+        *
+        * @param mavenID
+        * @return The ID
+        * @throws IllegalArgumentException if the mavenID does not match the 
Syntax
+        */
+       public static IDImpl fromMavenID(String mavenID)
+                       throws IllegalArgumentException {
+        String[] parts = mavenID.split(":");
+
+        if (parts.length < 3 || parts.length > 5)
+            throw new IllegalArgumentException("Not a valid maven ID" + 
mavenID);
+
+        String gid = parts[0];
+        String aid = parts[1];
+               String ver = null;
+               String t = null;
+               String c = null;
+
+               if (parts.length == 3) {
+                       ver = parts[2];
+               } else if (parts.length == 4) {
+                       t = parts[2];
+                       ver = parts[3];
+               } else {
+                       t = parts[2];
+                       c = parts[3];
+                       ver = parts[4];
+               }
+        return new IDImpl(gid, aid, ver, t, c);
+    }
+
+    /**
+        * Construct an ID
+        * 
+        * @param groupId The group ID.
+        * @param artifactId The artifact ID.
+        * @param version The version.
+        * @param type The type identifier.
+        * @param classifier The classifier.
+        * @throws NullPointerException if one of the parameters (groupId,
+        *             artifactId, version) is null.
+        * @throws IllegalArgumentException if one of the parameters is empty or
+        *             contains an colon `:` or if a classifier is used without 
a
+        *             type.
+        */
+       public IDImpl(String groupId, String artifactId, String version, String 
type,
+                       String classifier)
+                       throws NullPointerException, IllegalArgumentException {
+
+               Objects.requireNonNull(groupId, "groupId");
+               Objects.requireNonNull(artifactId, "artifact");
+               Objects.requireNonNull(version, "version");
+
+               if (groupId.isEmpty()) {
+                       throw new IllegalArgumentException("groupId must not be 
empty");
+               }
+               if (artifactId.isEmpty()) {
+                       throw new IllegalArgumentException("artifactId must not 
be empty");
+               }
+               if (version.isEmpty()) {
+                       throw new IllegalArgumentException("version must not be 
empty");
+               }
+
+               if (type != null && type.isEmpty()) {
+                       throw new IllegalArgumentException("type must not be 
empty");
+               }
+
+               if (classifier != null && classifier.isEmpty()) {
+                       throw new IllegalArgumentException("classifier must not 
be empty");
+               }
+
+               if (groupId.contains(":")) {
+                       throw new IllegalArgumentException(
+                                       "groupId must not contain a colon `:`");
+               }
+               if (artifactId.contains(":")) {
+                       throw new IllegalArgumentException(
+                                       "artifactId must not contain a colon 
`:`");
+               }
+               if (version.contains(":")) {
+                       throw new IllegalArgumentException(
+                                       "version must not contain a colon `:`");
+               }
+               if (type != null && type.contains(":")) {
+                       throw new IllegalArgumentException(
+                                       "type must not contain a colon `:`");
+               }
+               if (classifier != null && classifier.contains(":")) {
+                       throw new IllegalArgumentException(
+                                       "classifier must not contain a colon 
`:`");
+               }
+               if (type == null && classifier != null) {
+                       throw new IllegalArgumentException(
+                                       "type must not be `null` if a 
classifier is set");
+               }
+               this.groupId = groupId;
+               this.artifactId = artifactId;
+               this.version = version;
+               this.type = type;
+               this.classifier = classifier;
+    }
+
+    /**
+     * Get the group ID.
+     * @return The group ID.
+     */
+    public String getGroupId() {
+        return groupId;
+    }
+
+    /**
+     * Get the artifact ID.
+     * @return The artifact ID.
+     */
+    public String getArtifactId() {
+        return artifactId;
+    }
+
+    /**
+     * Get the version.
+     * @return The version.
+     */
+    public String getVersion() {
+        return version;
+    }
+
+    /**
+     * Get the type identifier.
+     * @return The type identifier.
+     */
+    public Optional<String> getType() {
+        return Optional.ofNullable(type);
+    }
+
+    /**
+     * Get the classifier.
+     * @return The classifier.
+     */
+    public Optional<String> getClassifier() {
+        return Optional.ofNullable(classifier);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(artifactId, classifier, groupId, type, version);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (!(obj instanceof IDImpl))
+            return false;
+        IDImpl other = (IDImpl) obj;
+        return Objects.equals(artifactId, other.artifactId) && 
Objects.equals(classifier, other.classifier)
+                && Objects.equals(groupId, other.groupId) && 
Objects.equals(type, other.type)
+                && Objects.equals(version, other.version);
+    }
+
+       /**
+        * Returns the the mavenID. Maven IDs have the following syntax:
+        * <p>
+        * {@code group-id ':' artifact-id [ ':' [type] [ ':' classifier ] ] 
':' version}
+        * 
+        * @return the mavenID.
+        */
+    @Override
+    public String toString() {
+               StringBuilder sb = new StringBuilder(groupId).append(":")
+                               .append(artifactId);
+
+               if (type != null) {
+                       sb = sb.append(":").append(type);
+                       if (classifier != null) {
+                               sb = sb.append(":").append(classifier);
+                       }
+               }
+               return sb.append(":").append(version).toString();
+    }
+}
diff --git 
a/osgi-featuremodel/src/test/java/org/apache/sling/feature/osgi/impl/FeatureServiceImplTest.java
 
b/osgi-featuremodel/src/test/java/org/apache/sling/feature/osgi/impl/FeatureServiceImplTest.java
index 76765e3..5cced8c 100644
--- 
a/osgi-featuremodel/src/test/java/org/apache/sling/feature/osgi/impl/FeatureServiceImplTest.java
+++ 
b/osgi-featuremodel/src/test/java/org/apache/sling/feature/osgi/impl/FeatureServiceImplTest.java
@@ -17,7 +17,6 @@
 package org.apache.sling.feature.osgi.impl;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
@@ -32,7 +31,6 @@ import org.osgi.service.feature.BuilderFactory;
 import org.osgi.service.feature.Feature;
 import org.osgi.service.feature.FeatureBuilder;
 import org.osgi.service.feature.FeatureBundle;
-import org.osgi.service.feature.ID;
 
 public class FeatureServiceImplTest {
        FeatureServiceImpl features;
@@ -56,7 +54,7 @@ public class FeatureServiceImplTest {
             List<FeatureBundle> bundles = f.getBundles();
             assertEquals(3, bundles.size());
 
-            FeatureBundle bundle = bf.newBundleBuilder(new ID("org.osgi", 
"osgi.promise", "7.0.1"))
+            FeatureBundle bundle = 
bf.newBundleBuilder(features.getID("org.osgi", "osgi.promise", "7.0.1"))
                     .addMetadata("hash", "4632463464363646436")
                     .addMetadata("start-order", 1L)
                     .build();
@@ -65,8 +63,8 @@ public class FeatureServiceImplTest {
             ba.equals(bundle);
 
             assertTrue(bundles.contains(bundle));
-            assertTrue(bundles.contains(bf.newBundleBuilder(new 
ID("org.slf4j", "slf4j-api", "1.7.29")).build()));
-            assertTrue(bundles.contains(bf.newBundleBuilder(new 
ID("org.slf4j", "slf4j-simple", "1.7.29")).build()));
+            
assertTrue(bundles.contains(bf.newBundleBuilder(features.getID("org.slf4j", 
"slf4j-api", "1.7.29")).build()));
+            
assertTrue(bundles.contains(bf.newBundleBuilder(features.getID("org.slf4j", 
"slf4j-simple", "1.7.29")).build()));
         }
     }
 
@@ -74,7 +72,7 @@ public class FeatureServiceImplTest {
     public void testCreateFeature() {
         BuilderFactory factory = features.getBuilderFactory();
 
-        FeatureBuilder builder = factory.newFeatureBuilder(new ID("org.acme", 
"acmeapp", "1.0.0"));
+        FeatureBuilder builder = 
factory.newFeatureBuilder(features.getID("org.acme", "acmeapp", "1.0.0"));
         builder.setName("The ACME app");
         builder.setDescription("This is the main ACME app, "
                 + "from where all functionality can be reached.");
@@ -88,26 +86,26 @@ public class FeatureServiceImplTest {
         BuilderFactory factory = features.getBuilderFactory();
 
         FeatureBuilder builder = factory.newFeatureBuilder(
-            new ID("org.acme", "acmeapp", "1.0.1"));
+                       features.getID("org.acme", "acmeapp", "1.0.1"));
         builder.setName("The Acme Application");
         builder.setLicense("https://opensource.org/licenses/Apache-2.0";);
         builder.setComplete(true);
 
         FeatureBundle b1 = factory.newBundleBuilder(
-                ID.fromMavenID("org.osgi:org.osgi.util.function:1.1.0"))
+                
features.getIDfromMavenID("org.osgi:org.osgi.util.function:1.1.0"))
                 .build();
         FeatureBundle b2 = factory.newBundleBuilder(
-                ID.fromMavenID("org.osgi:org.osgi.util.promise:1.1.1"))
+                       
features.getIDfromMavenID("org.osgi:org.osgi.util.promise:1.1.1"))
                 .build();
 
         FeatureBundle b3 = factory.newBundleBuilder(
-                ID.fromMavenID("org.apache.commons:commons-email:1.1.5"))
+                       
features.getIDfromMavenID("org.apache.commons:commons-email:1.1.5"))
                 .addMetadata("org.acme.javadoc.link",
                         
"https://commons.apache.org/proper/commons-email/javadocs/api-1.5";)
                 .build();
 
         FeatureBundle b4 = factory.newBundleBuilder(
-                ID.fromMavenID("com.acme:acmelib:1.7.2"))
+                       features.getIDfromMavenID("com.acme:acmelib:1.7.2"))
                 .build();
 
         builder.addBundles(b1, b2, b3, b4);

Reply via email to