karaf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jbono...@apache.org
Subject [1/2] karaf git commit: [KARAF-4105] Improve assembly version comparison dealing with Maven and OSGi version matching
Date Sun, 29 Jan 2017 09:55:11 GMT
Repository: karaf
Updated Branches:
  refs/heads/karaf-4.0.x daa3503bf -> 775bea029


[KARAF-4105] Improve assembly version comparison dealing with Maven and OSGi version matching

Signed-off-by: Markus Rathgeb <maggu2810@gmail.com>


Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/8f0c3b37
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/8f0c3b37
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/8f0c3b37

Branch: refs/heads/karaf-4.0.x
Commit: 8f0c3b3753f5f5978d4bf4151bb576b358fe7f49
Parents: daa3503
Author: Markus Rathgeb <maggu2810@gmail.com>
Authored: Mon Feb 1 19:21:29 2016 +0100
Committer: Jean-Baptiste Onofré <jbonofre@apache.org>
Committed: Sun Jan 29 06:46:01 2017 +0100

----------------------------------------------------------------------
 profile/pom.xml                                 |   1 +
 .../apache/karaf/profile/assembly/Builder.java  |  37 ++
 .../profile/versioning/ComparableVersion.java   | 441 +++++++++++++++++++
 .../karaf/profile/versioning/VersionUtils.java  |  76 ++++
 .../profile/versioning/VersionUtilsTest.java    |  40 ++
 5 files changed, 595 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/8f0c3b37/profile/pom.xml
----------------------------------------------------------------------
diff --git a/profile/pom.xml b/profile/pom.xml
index e13ba16..736d652 100644
--- a/profile/pom.xml
+++ b/profile/pom.xml
@@ -188,6 +188,7 @@
                             org.apache.karaf.profile.assembly,
                             org.apache.karaf.profile.impl,
                             org.apache.karaf.profile.impl.osgi,
+                            org.apache.karaf.profile.versioning,
                             org.apache.karaf.util.config,
                             org.apache.karaf.util.maven,
                             org.apache.felix.utils.manifest,

http://git-wip-us.apache.org/repos/asf/karaf/blob/8f0c3b37/profile/src/main/java/org/apache/karaf/profile/assembly/Builder.java
----------------------------------------------------------------------
diff --git a/profile/src/main/java/org/apache/karaf/profile/assembly/Builder.java b/profile/src/main/java/org/apache/karaf/profile/assembly/Builder.java
index a259b26..9adce76 100644
--- a/profile/src/main/java/org/apache/karaf/profile/assembly/Builder.java
+++ b/profile/src/main/java/org/apache/karaf/profile/assembly/Builder.java
@@ -80,6 +80,7 @@ import org.apache.karaf.profile.impl.Profiles;
 import org.apache.karaf.tools.utils.KarafPropertiesEditor;
 import org.apache.karaf.tools.utils.model.KarafPropertyEdit;
 import org.apache.karaf.tools.utils.model.KarafPropertyEdits;
+import org.apache.karaf.profile.versioning.VersionUtils;
 import org.apache.karaf.util.config.PropertiesLoader;
 import org.apache.karaf.util.maven.Parser;
 import org.ops4j.pax.url.mvn.MavenResolver;
@@ -1103,6 +1104,42 @@ public class Builder {
         }
     }
 
+    private String getFeatureSt(Dependency dep) {
+        String version = dep.getVersion() == null || "0.0.0".equals(dep.getVersion()) ? ""
: "/" + dep.getVersion();
+        return dep.getName() + version;
+    }
+
+    /**
+     * Checks if a given feature f matches the featureRef.
+     * TODO Need to also check for version ranges. Currently ranges are ignored and all features
matching the name
+     * are copied in that case.
+     *  
+     * @param f
+     * @param featureRef
+     * @return
+     */
+    private boolean matches(Feature f, Dependency featureRef) {
+        if (!f.getName().equals(featureRef.getName())) {
+            return false;
+        }
+
+        final String featureRefVersion = featureRef.getVersion();
+
+        if (featureRefVersion == null) {
+            return true;
+        }
+
+        if (featureRefVersion.equals("0.0.0")) {
+            return true;
+        }
+
+        if (featureRefVersion.startsWith("[")) {
+            return true;
+        }
+
+        return VersionUtils.versionEquals(f.getVersion(), featureRefVersion);
+    }
+
     private List<String> getStaged(Stage stage, Map<String, Stage> data) {
         List<String> staged = new ArrayList<>();
         for (String s : data.keySet()) {

http://git-wip-us.apache.org/repos/asf/karaf/blob/8f0c3b37/profile/src/main/java/org/apache/karaf/profile/versioning/ComparableVersion.java
----------------------------------------------------------------------
diff --git a/profile/src/main/java/org/apache/karaf/profile/versioning/ComparableVersion.java
b/profile/src/main/java/org/apache/karaf/profile/versioning/ComparableVersion.java
new file mode 100644
index 0000000..0362183
--- /dev/null
+++ b/profile/src/main/java/org/apache/karaf/profile/versioning/ComparableVersion.java
@@ -0,0 +1,441 @@
+/*
+ * This file is taken from the "maven-artifact" project because the artifact lacks OSGi informations.
+ */
+package org.apache.karaf.profile.versioning;
+
+/*
+ * 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.
+ */
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Properties;
+import java.util.Stack;
+
+/**
+ * Generic implementation of version comparison.
+ *
+ * <p>
+ * Features:
+ * <ul>
+ * <li>mixing of '<code>-</code>' (hyphen) and '<code>.</code>'
(dot) separators,</li>
+ * <li>transition between characters and digits also constitutes a separator:
+ * <code>1.0alpha1 =&gt; [1, 0, alpha, 1]</code></li>
+ * <li>unlimited number of version components,</li>
+ * <li>version components in the text can be digits or strings,</li>
+ * <li>strings are checked for well-known qualifiers and the qualifier ordering is
used for version ordering. Well-known
+ * qualifiers (case insensitive) are:<ul>
+ * <li><code>alpha</code> or <code>a</code></li>
+ * <li><code>beta</code> or <code>b</code></li>
+ * <li><code>milestone</code> or <code>m</code></li>
+ * <li><code>rc</code> or <code>cr</code></li>
+ * <li><code>snapshot</code></li>
+ * <li><code>(the empty string)</code> or <code>ga</code> or
<code>final</code></li>
+ * <li><code>sp</code></li>
+ * </ul>
+ * Unknown qualifiers are considered after known qualifiers, with lexical order (always case
insensitive),
+ * </li>
+ * <li>a hyphen usually precedes a qualifier, and is always less important than something
preceded with a dot.</li>
+ * </ul></p>
+ *
+ * @see <a href="https://cwiki.apache.org/confluence/display/MAVENOLD/Versioning">"Versioning"
on Maven Wiki</a>
+ * @author <a href="mailto:kenney@apache.org">Kenney Westerhof</a>
+ * @author <a href="mailto:hboutemy@apache.org">Hervé Boutemy</a>
+ */
+public class ComparableVersion
+        implements Comparable<ComparableVersion> {
+
+    private String value;
+
+    private String canonical;
+
+    private ListItem items;
+
+    private interface Item {
+
+        int INTEGER_ITEM = 0;
+        int STRING_ITEM = 1;
+        int LIST_ITEM = 2;
+
+        int compareTo(Item item);
+
+        int getType();
+
+        boolean isNull();
+    }
+
+    /**
+     * Represents a numeric item in the version item list.
+     */
+    private static class IntegerItem
+            implements Item {
+
+        private static final BigInteger BIG_INTEGER_ZERO = new BigInteger("0");
+
+        private final BigInteger value;
+
+        public static final IntegerItem ZERO = new IntegerItem();
+
+        private IntegerItem() {
+            this.value = BIG_INTEGER_ZERO;
+        }
+
+        public IntegerItem(String str) {
+            this.value = new BigInteger(str);
+        }
+
+        public int getType() {
+            return INTEGER_ITEM;
+        }
+
+        public boolean isNull() {
+            return BIG_INTEGER_ZERO.equals(value);
+        }
+
+        public int compareTo(Item item) {
+            if (item == null) {
+                return BIG_INTEGER_ZERO.equals(value) ? 0 : 1; // 1.0 == 1, 1.1 > 1
+            }
+
+            switch (item.getType()) {
+                case INTEGER_ITEM:
+                    return value.compareTo(((IntegerItem) item).value);
+
+                case STRING_ITEM:
+                    return 1; // 1.1 > 1-sp
+
+                case LIST_ITEM:
+                    return 1; // 1.1 > 1-1
+
+                default:
+                    throw new RuntimeException("invalid item: " + item.getClass());
+            }
+        }
+
+        public String toString() {
+            return value.toString();
+        }
+    }
+
+    /**
+     * Represents a string in the version item list, usually a qualifier.
+     */
+    private static class StringItem
+            implements Item {
+
+        private static final String[] QUALIFIERS = {"alpha", "beta", "milestone", "rc", "snapshot",
"", "sp"};
+
+        @SuppressWarnings("checkstyle:constantname")
+        private static final List<String> _QUALIFIERS = Arrays.asList(QUALIFIERS);
+
+        private static final Properties ALIASES = new Properties();
+
+        static {
+            ALIASES.put("ga", "");
+            ALIASES.put("final", "");
+            ALIASES.put("cr", "rc");
+        }
+
+        /**
+         * A comparable value for the empty-string qualifier. This one is used to determine
if a given qualifier makes
+         * the version older than one without a qualifier, or more recent.
+         */
+        private static final String RELEASE_VERSION_INDEX = String.valueOf(_QUALIFIERS.indexOf(""));
+
+        private String value;
+
+        public StringItem(String value, boolean followedByDigit) {
+            if (followedByDigit && value.length() == 1) {
+                // a1 = alpha-1, b1 = beta-1, m1 = milestone-1
+                switch (value.charAt(0)) {
+                    case 'a':
+                        value = "alpha";
+                        break;
+                    case 'b':
+                        value = "beta";
+                        break;
+                    case 'm':
+                        value = "milestone";
+                        break;
+                    default:
+                }
+            }
+            this.value = ALIASES.getProperty(value, value);
+        }
+
+        public int getType() {
+            return STRING_ITEM;
+        }
+
+        public boolean isNull() {
+            return (comparableQualifier(value).compareTo(RELEASE_VERSION_INDEX) == 0);
+        }
+
+        /**
+         * Returns a comparable value for a qualifier.
+         *
+         * This method takes into account the ordering of known qualifiers then unknown qualifiers
with lexical
+         * ordering.
+         *
+         * just returning an Integer with the index here is faster, but requires a lot of
if/then/else to check for -1
+         * or QUALIFIERS.size and then resort to lexical ordering. Most comparisons are decided
by the first character,
+         * so this is still fast. If more characters are needed then it requires a lexical
sort anyway.
+         *
+         * @param qualifier
+         * @return an equivalent value that can be used with lexical comparison
+         */
+        public static String comparableQualifier(String qualifier) {
+            int i = _QUALIFIERS.indexOf(qualifier);
+
+            return i == -1 ? (_QUALIFIERS.size() + "-" + qualifier) : String.valueOf(i);
+        }
+
+        public int compareTo(Item item) {
+            if (item == null) {
+                // 1-rc < 1, 1-ga > 1
+                return comparableQualifier(value).compareTo(RELEASE_VERSION_INDEX);
+            }
+            switch (item.getType()) {
+                case INTEGER_ITEM:
+                    return -1; // 1.any < 1.1 ?
+
+                case STRING_ITEM:
+                    return comparableQualifier(value).compareTo(comparableQualifier(((StringItem)
item).value));
+
+                case LIST_ITEM:
+                    return -1; // 1.any < 1-1
+
+                default:
+                    throw new RuntimeException("invalid item: " + item.getClass());
+            }
+        }
+
+        public String toString() {
+            return value;
+        }
+    }
+
+    /**
+     * Represents a version list item. This class is used both for the global item list and
for sub-lists (which start
+     * with '-(number)' in the version specification).
+     */
+    private static class ListItem
+            extends ArrayList<Item>
+            implements Item {
+
+        public int getType() {
+            return LIST_ITEM;
+        }
+
+        public boolean isNull() {
+            return (size() == 0);
+        }
+
+        void normalize() {
+            for (int i = size() - 1; i >= 0; i--) {
+                Item lastItem = get(i);
+
+                if (lastItem.isNull()) {
+                    // remove null trailing items: 0, "", empty list
+                    remove(i);
+                } else if (!(lastItem instanceof ListItem)) {
+                    break;
+                }
+            }
+        }
+
+        public int compareTo(Item item) {
+            if (item == null) {
+                if (size() == 0) {
+                    return 0; // 1-0 = 1- (normalize) = 1
+                }
+                Item first = get(0);
+                return first.compareTo(null);
+            }
+            switch (item.getType()) {
+                case INTEGER_ITEM:
+                    return -1; // 1-1 < 1.0.x
+
+                case STRING_ITEM:
+                    return 1; // 1-1 > 1-sp
+
+                case LIST_ITEM:
+                    Iterator<Item> left = iterator();
+                    Iterator<Item> right = ((ListItem) item).iterator();
+
+                    while (left.hasNext() || right.hasNext()) {
+                        Item l = left.hasNext() ? left.next() : null;
+                        Item r = right.hasNext() ? right.next() : null;
+
+                        // if this is shorter, then invert the compare and mul with -1
+                        int result = l == null ? (r == null ? 0 : -1 * r.compareTo(l)) :
l.compareTo(r);
+
+                        if (result != 0) {
+                            return result;
+                        }
+                    }
+
+                    return 0;
+
+                default:
+                    throw new RuntimeException("invalid item: " + item.getClass());
+            }
+        }
+
+        public String toString() {
+            StringBuilder buffer = new StringBuilder();
+            for (Item item : this) {
+                if (buffer.length() > 0) {
+                    buffer.append((item instanceof ListItem) ? '-' : '.');
+                }
+                buffer.append(item);
+            }
+            return buffer.toString();
+        }
+    }
+
+    public ComparableVersion(String version) {
+        parseVersion(version);
+    }
+
+    public final void parseVersion(String version) {
+        this.value = version;
+
+        items = new ListItem();
+
+        version = version.toLowerCase(Locale.ENGLISH);
+
+        ListItem list = items;
+
+        Stack<Item> stack = new Stack<>();
+        stack.push(list);
+
+        boolean isDigit = false;
+
+        int startIndex = 0;
+
+        for (int i = 0; i < version.length(); i++) {
+            char c = version.charAt(i);
+
+            if (c == '.') {
+                if (i == startIndex) {
+                    list.add(IntegerItem.ZERO);
+                } else {
+                    list.add(parseItem(isDigit, version.substring(startIndex, i)));
+                }
+                startIndex = i + 1;
+            } else if (c == '-') {
+                if (i == startIndex) {
+                    list.add(IntegerItem.ZERO);
+                } else {
+                    list.add(parseItem(isDigit, version.substring(startIndex, i)));
+                }
+                startIndex = i + 1;
+
+                list.add(list = new ListItem());
+                stack.push(list);
+            } else if (Character.isDigit(c)) {
+                if (!isDigit && i > startIndex) {
+                    list.add(new StringItem(version.substring(startIndex, i), true));
+                    startIndex = i;
+
+                    list.add(list = new ListItem());
+                    stack.push(list);
+                }
+
+                isDigit = true;
+            } else {
+                if (isDigit && i > startIndex) {
+                    list.add(parseItem(true, version.substring(startIndex, i)));
+                    startIndex = i;
+
+                    list.add(list = new ListItem());
+                    stack.push(list);
+                }
+
+                isDigit = false;
+            }
+        }
+
+        if (version.length() > startIndex) {
+            list.add(parseItem(isDigit, version.substring(startIndex)));
+        }
+
+        while (!stack.isEmpty()) {
+            list = (ListItem) stack.pop();
+            list.normalize();
+        }
+
+        canonical = items.toString();
+    }
+
+    private static Item parseItem(boolean isDigit, String buf) {
+        return isDigit ? new IntegerItem(buf) : new StringItem(buf, false);
+    }
+
+    public int compareTo(ComparableVersion o) {
+        return items.compareTo(o.items);
+    }
+
+    public String toString() {
+        return value;
+    }
+
+    public String getCanonical() {
+        return canonical;
+    }
+
+    public boolean equals(Object o) {
+        return (o instanceof ComparableVersion) && canonical.equals(((ComparableVersion)
o).canonical);
+    }
+
+    public int hashCode() {
+        return canonical.hashCode();
+    }
+
+    /**
+     * Main to test version parsing and comparison.
+     *
+     * @param args the version strings to parse and compare
+     */
+    public static void main(String... args) {
+        System.out.println("Display parameters as parsed by Maven (in canonical form) and
comparison result:");
+        if (args.length == 0) {
+            return;
+        }
+
+        ComparableVersion prev = null;
+        int i = 1;
+        for (String version : args) {
+            ComparableVersion c = new ComparableVersion(version);
+
+            if (prev != null) {
+                int compare = prev.compareTo(c);
+                System.out.println("   " + prev.toString() + ' '
+                        + ((compare == 0) ? "==" : ((compare < 0) ? "<" : ">"))
+ ' ' + version);
+            }
+
+            System.out.println(String.valueOf(i++) + ". " + version + " == " + c.getCanonical());
+
+            prev = c;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/8f0c3b37/profile/src/main/java/org/apache/karaf/profile/versioning/VersionUtils.java
----------------------------------------------------------------------
diff --git a/profile/src/main/java/org/apache/karaf/profile/versioning/VersionUtils.java b/profile/src/main/java/org/apache/karaf/profile/versioning/VersionUtils.java
new file mode 100644
index 0000000..cd42256
--- /dev/null
+++ b/profile/src/main/java/org/apache/karaf/profile/versioning/VersionUtils.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2016 The Apache Software Foundation.
+ *
+ * Licensed 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.karaf.profile.versioning;
+
+public class VersionUtils {
+
+    private static final String SNAPSHOT = "SNAPSHOT";
+    private static final char DELIM_DASH = '-';
+    private static final char DELIM_DOT = '.';
+    private static final String EMPTY_VERSION = "0.0.0";
+
+    private VersionUtils() {
+    }
+
+    public static boolean versionEquals(String versionOne, String versionTwo) {
+        if (isSnapshotVersion(versionOne) && isSnapshotVersion(versionTwo)) {
+            // If both (and only if both) versions are snapshots, we compare without the
snapshot classifier.
+            // This is done to consider e.g. 1.2.0.SNAPSHOT and 1.2.SNAPSHOT equal.
+            versionOne = versionWithoutSnapshot(versionOne);
+            versionTwo = versionWithoutSnapshot(versionTwo);
+        }
+
+        // Create comparable version objects.
+        final ComparableVersion cvOne = new ComparableVersion(versionOne);
+        final ComparableVersion cvTwo = new ComparableVersion(versionTwo);
+
+        // Use equals of comparable version class.
+        return cvOne.equals(cvTwo);
+    }
+
+    /**
+     * Check if a version is a snapshot version.
+     *
+     * @param version the version to check
+     * @return true if {@code version} refers a snapshot version otherwise false
+     */
+    protected static boolean isSnapshotVersion(final String version) {
+        return version.endsWith(SNAPSHOT);
+    }
+
+    /**
+     * Remove the snapshot classifier at the end of a version.
+     *
+     * @param version the version
+     * @return the given {@code version} without the snapshot classifier
+     */
+    protected static String versionWithoutSnapshot(final String version) {
+        int idx;
+        idx = version.lastIndexOf(SNAPSHOT);
+        if (idx < 0) {
+            return version;
+        } else if (idx == 0) {
+            return EMPTY_VERSION;
+        } else {
+            final char delim = version.charAt(idx - 1);
+            if (delim == DELIM_DOT || delim == DELIM_DASH) {
+                --idx;
+            }
+            return version.substring(0, idx);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/8f0c3b37/profile/src/test/java/org/apache/karaf/profile/versioning/VersionUtilsTest.java
----------------------------------------------------------------------
diff --git a/profile/src/test/java/org/apache/karaf/profile/versioning/VersionUtilsTest.java
b/profile/src/test/java/org/apache/karaf/profile/versioning/VersionUtilsTest.java
new file mode 100644
index 0000000..a63d979
--- /dev/null
+++ b/profile/src/test/java/org/apache/karaf/profile/versioning/VersionUtilsTest.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 The Apache Software Foundation.
+ *
+ * Licensed 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.karaf.profile.versioning;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class VersionUtilsTest {
+
+    @Test
+    public void testVersionWithoutSnapshot() {
+        assertEquals(VersionUtils.versionWithoutSnapshot("1.2.0-SNAPSHOT"), "1.2.0");
+        assertEquals(VersionUtils.versionWithoutSnapshot("1.2.0.SNAPSHOT"), "1.2.0");
+        assertEquals(VersionUtils.versionWithoutSnapshot("1.2.0SNAPSHOT"), "1.2.0");
+    }
+
+    @Test
+    public void testVersions() {
+        assertTrue(VersionUtils.versionEquals("1.2", "1.2"));
+        assertTrue(VersionUtils.versionEquals("1.2", "1.2.0"));
+        assertTrue(VersionUtils.versionEquals("1.2.0", "1.2"));
+        assertFalse(VersionUtils.versionEquals("1.2.0", "1.2.1"));
+        assertFalse(VersionUtils.versionEquals("1.2", "1.2.1"));
+        assertTrue(VersionUtils.versionEquals("1.2.0-SNAPSHOT", "1.2.0.SNAPSHOT"));
+        assertTrue(VersionUtils.versionEquals("1.3-SNAPSHOT", "1.3.0.SNAPSHOT"));
+    }
+}


Mime
View raw message