sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1416118 - in /sis/branches/JDK7/sis-utility/src: main/java/org/apache/sis/util/Version.java test/java/org/apache/sis/util/VersionTest.java
Date Sun, 02 Dec 2012 07:08:26 GMT
Author: desruisseaux
Date: Sun Dec  2 07:08:26 2012
New Revision: 1416118

URL: http://svn.apache.org/viewvc?rev=1416118&view=rev
Log:
Ported the Version class.

Added:
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/Version.java   (with props)
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/VersionTest.java   (with
props)

Added: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/Version.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/Version.java?rev=1416118&view=auto
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/Version.java (added)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/Version.java Sun Dec 
2 07:08:26 2012
@@ -0,0 +1,304 @@
+/*
+ * 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.sis.util;
+
+import java.io.Serializable;
+import java.util.StringTokenizer;
+import net.jcip.annotations.Immutable;
+
+
+/**
+ * Holds a version number as a sequence of strings separated by either a dot or a dash.
+ * The first three strings, usually numbers, are called respectively {@linkplain #getMajor()
major},
+ * {@linkplain #getMinor() minor} and {@linkplain #getRevision() revision}.
+ * For example a version code such as {@code "6.11.2"} will have major number 6, minor
+ * number 11 and revision number 2. Alternatively a version code such as {@code "3.18-SNAPSHOT"}
+ * will have major version number 3, minor version number 18 and revision string "SNAPSHOT".
+ *
+ * <p>This class provides methods for performing comparisons of {@code Version} objects
where major,
+ * minor and revision parts are compared as numbers when possible, or as strings otherwise.</p>
+ *
+ * @author  Martin Desruisseaux (IRD)
+ * @since   0.3 (derived from geotk-2.4)
+ * @version 0.3
+ * @module
+ */
+@Immutable
+public class Version implements CharSequence, Comparable<Version>, Serializable {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -6793384507333713770L;
+
+    /**
+     * The version of this Apache SIS distribution.
+     */
+    public static final Version SIS = new Version("0.3-SNAPSHOT");
+
+    /**
+     * The separator characters between {@linkplain #getMajor() major}, {@linkplain #getMinor()
minor}
+     * and {@linkplain #getRevision() revision} components. Any character in this string
fits.
+     */
+    private static final String SEPARATORS = ".-";
+
+    /**
+     * The version in string form, with leading and trailing spaces removed.
+     */
+    private final String version;
+
+    /**
+     * The components of the version string. Will be created when first needed.
+     */
+    private transient String[] components;
+
+    /**
+     * The parsed components of the version string. Will be created when first needed.
+     */
+    private transient Comparable<?>[] parsed;
+
+    /**
+     * The hash code value. Will be computed when first needed.
+     */
+    private transient int hashCode;
+
+    /**
+     * Creates a new version object from the supplied string.
+     *
+     * @param version The version as a string.
+     */
+    public Version(final String version) {
+        this.version = CharSequences.trimWhitespaces(version);
+    }
+
+    /**
+     * Returns the major version number. This method returns an {@link Integer} if possible,
+     * or a {@link String} otherwise.
+     *
+     * @return The major version number.
+     */
+    public Comparable<?> getMajor() {
+        return getComponent(0);
+    }
+
+    /**
+     * Returns the minor version number. This method returns an {@link Integer} if possible,
+     * or a {@link String} otherwise. If there is no minor version number, then this method
+     * returns {@code null}.
+     *
+     * @return The minor version number, or {@code null} if none.
+     */
+    public Comparable<?> getMinor() {
+        return getComponent(1);
+    }
+
+    /**
+     * Returns the revision number. This method returns an {@link Integer} if possible,
+     * or a {@link String} otherwise. If there is no revision number, then this method
+     * returns {@code null}.
+     *
+     * @return The revision number, or {@code null} if none.
+     */
+    public Comparable<?> getRevision() {
+        return getComponent(2);
+    }
+
+    /**
+     * Returns the specified components of this version string. For a version of the
+     * {@code major.minor.revision} form, index 0 stands for the major version number,
+     * 1 stands for the minor version number and 2 stands for the revision number.
+     *
+     * <p>The return value is an {@link Integer} if the component is parsable as an
integer,
+     * or a {@link String} otherwise. If there is no component at the specified index,
+     * then this method returns {@code null}.</p>
+     *
+     * @param  index The index of the component to fetch.
+     * @return The value at the specified index, or {@code null} if none.
+     * @throws IndexOutOfBoundsException if {@code index} is negative.
+     */
+    final synchronized Comparable<?> getComponent(final int index) {
+        if (parsed == null) {
+            if (components == null) {
+                final StringTokenizer tokens = new StringTokenizer(version, SEPARATORS);
+                components = new String[tokens.countTokens()];
+                for (int i=0; tokens.hasMoreTokens(); i++) {
+                    components[i] = tokens.nextToken();
+                }
+            }
+            parsed = new Comparable<?>[components.length];
+        }
+        if (index >= parsed.length) {
+            return null;
+        }
+        Comparable<?> candidate = parsed[index];
+        if (candidate == null) {
+            final String value = components[index].trim();
+            try {
+                candidate = Integer.valueOf(value);
+            } catch (NumberFormatException e) {
+                candidate = value;
+            }
+            parsed[index] = candidate;
+        }
+        return candidate;
+    }
+
+    /**
+     * Get the rank of the specified object according this type.
+     * This is for {@link #compareTo(Version, int)} internal only.
+     */
+    private static int getTypeRank(final Object value) {
+        if (value instanceof CharSequence) {
+            return 0;
+        }
+        if (value instanceof Number) {
+            return 1;
+        }
+        throw new IllegalArgumentException(String.valueOf(value));
+    }
+
+    /**
+     * Compares this version with an other version object, up to the specified limit. A limit
+     * of 1 compares only the {@linkplain #getMajor() major} version number. A limit of 2
compares
+     * the major and {@linkplain #getMinor() minor} version numbers, <i>etc</i>.
+     * The comparisons are performed as {@link Integer} object if possible, or as {@link
String}
+     * otherwise.
+     *
+     * @param  other The other version object to compare with.
+     * @param  limit The maximum number of components to compare.
+     * @return A negative value if this version is lower than the supplied version, a positive
+     *         value if it is higher, or 0 if they are equal.
+     */
+    public int compareTo(final Version other, final int limit) {
+        ArgumentChecks.ensurePositive("limit", limit);
+        for (int i=0; i<limit; i++) {
+            final Comparable<?> v1 =  this.getComponent(i);
+            final Comparable<?> v2 = other.getComponent(i);
+            if (v1 == null) {
+                return (v2 == null) ? 0 : -1;
+            } else if (v2 == null) {
+                return +1;
+            }
+            final int dr = getTypeRank(v1) - getTypeRank(v2);
+            if (dr != 0) {
+                /*
+                 * One value is a text while the other value is a number.  We could be tempted
to
+                 * force a comparison by converting the number to a String and then invoking
the
+                 * String.compareTo(String) method, but this strategy would violate the following
+                 * contract from Comparable.compareTo(Object):  "The implementor must also
ensure
+                 * that the relation is transitive". Use case:
+                 *
+                 *    A is the integer 10
+                 *    B is the string "8Z"
+                 *    C is the integer 5.
+                 *
+                 * If mismatched types are converted to String before being compared, then
we
+                 * would have A < B < C. Transitivity implies that A < C, but if
we compare A
+                 * and C directly we get A > C because they are compared as numbers. 
An easy
+                 * way to fix this inconsistency is to define all String as lexicographically
+                 * preceding Integer, no matter their content. This is what we do here.
+                 */
+                return dr;
+            }
+            @SuppressWarnings({"unchecked","rawtypes"})
+            final int c = ((Comparable) v1).compareTo(v2);
+            if (c != 0) {
+                return c;
+            }
+        }
+        return 0;
+    }
+
+    /**
+     * Compares this version with an other version object. This method performs the same
+     * comparison than {@link #compareTo(Version, int)} with no limit.
+     *
+     * @param  other The other version object to compare with.
+     * @return A negative value if this version is lower than the supplied version,
+     *         a positive value if it is higher, or 0 if they are equal.
+     */
+    @Override
+    public int compareTo(final Version other) {
+        return compareTo(other, Integer.MAX_VALUE);
+    }
+
+    /**
+     * Compare this version string with the specified object for equality. Two version are
+     * considered equal if <code>{@linkplain #compareTo(Object) compareTo}(other) ==
0</code>.
+     *
+     * @param other The object to compare with this version for equality.
+     */
+    @Override
+    public boolean equals(final Object other) {
+        if (other != null && getClass() == other.getClass()) {
+            return compareTo((Version) other) == 0;
+        }
+        return false;
+    }
+
+    /**
+     * Returns the length of the version string.
+     */
+    @Override
+    public int length() {
+        return version.length();
+    }
+
+    /**
+     * Returns the {@code char} value at the specified index.
+     */
+    @Override
+    public char charAt(final int index) {
+        return version.charAt(index);
+    }
+
+    /**
+     * Returns a new version string that is a subsequence of this sequence.
+     */
+    @Override
+    public CharSequence subSequence(final int start, final int end) {
+        return version.subSequence(start, end);
+    }
+
+    /**
+     * Returns the version string. This is the string specified at construction time.
+     */
+    @Override
+    public String toString() {
+        return version;
+    }
+
+    /**
+     * Returns a hash code value for this version.
+     */
+    @Override
+    public int hashCode() {
+        if (hashCode == 0) {
+            int code = (int) serialVersionUID;
+            int index = 0;
+            Comparable<?> component;
+            while ((component = getComponent(index)) != null) {
+                code = code * 31 + component.hashCode();
+                index++;
+            }
+            if (code == 0) {
+                code = -1;
+            }
+            hashCode = code;
+        }
+        return hashCode;
+    }
+}

Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/Version.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/Version.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/VersionTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/VersionTest.java?rev=1416118&view=auto
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/VersionTest.java (added)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/VersionTest.java Sun Dec
 2 07:08:26 2012
@@ -0,0 +1,79 @@
+/*
+ * 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.sis.util;
+
+import org.apache.sis.test.TestCase;
+import org.junit.Test;
+
+import static org.apache.sis.test.Assert.*;
+
+
+/**
+ * Tests the {@link Version} class, especially the {@code compareTo} method.
+ *
+ * @author  Martin Desruisseaux (IRD)
+ * @since   0.3 (derived from geotk-2.4)
+ * @version 0.3
+ * @module
+ */
+public final strictfp class VersionTest extends TestCase {
+    /**
+     * Tests a numeric-only version.
+     */
+    @Test
+    public void testNumeric() {
+        final Version version = new Version("6.11.2");
+        assertEquals("6.11.2", version.toString());
+        assertEquals( 6, version.getMajor());
+        assertEquals(11, version.getMinor());
+        assertEquals( 2, version.getRevision());
+        assertSame(version.getRevision(), version.getComponent(2));
+        assertNull(version.getComponent(3));
+
+        assertTrue(version.compareTo(new Version("6.11.2")) == 0);
+        assertTrue(version.compareTo(new Version("6.8"   )) >  0);
+        assertTrue(version.compareTo(new Version("6.12.0")) <  0);
+        assertTrue(version.compareTo(new Version("6.11"  )) >  0);
+    }
+
+    /**
+     * Tests a alpha-numeric version.
+     */
+    @Test
+    public void testAlphaNumeric() {
+        final Version version = new Version("1.6.b2");
+        assertEquals("1.6.b2", version.toString());
+        assertEquals( 1, version.getMajor());
+        assertEquals( 6, version.getMinor());
+        assertEquals("b2", version.getRevision());
+        assertSame(version.getRevision(), version.getComponent(2));
+        assertNull(version.getComponent(3));
+
+        assertTrue(version.compareTo(new Version("1.6.b2")) == 0);
+        assertTrue(version.compareTo(new Version("1.6.b1"))  > 0);
+        assertTrue(version.compareTo(new Version("1.07.b1")) < 0);
+    }
+
+    /**
+     * Tests serialization.
+     */
+    @Test
+    public void testSerialization() {
+        final Version version = new Version("1.6.b2");
+        assertNotSame(version, assertSerializedEquals(version));
+    }
+}

Propchange: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/VersionTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/VersionTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



Mime
View raw message