diff options
Diffstat (limited to 'src/main/java/com/capitati/omtc/core/negotiation/SemanticVersionComparator.java')
-rw-r--r-- | src/main/java/com/capitati/omtc/core/negotiation/SemanticVersionComparator.java | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/src/main/java/com/capitati/omtc/core/negotiation/SemanticVersionComparator.java b/src/main/java/com/capitati/omtc/core/negotiation/SemanticVersionComparator.java new file mode 100644 index 0000000..017d675 --- /dev/null +++ b/src/main/java/com/capitati/omtc/core/negotiation/SemanticVersionComparator.java @@ -0,0 +1,223 @@ +/* + * Copyright Capita Translation and Interpreting 2013 + * + * This file is part of OMTC. + * + * OMTC is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OMTC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OMTC. If not, see <http://www.gnu.org/licenses/>. + */ +package com.capitati.omtc.core.negotiation; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +import org.apache.commons.collections.iterators.ArrayIterator; +import org.apache.commons.lang3.tuple.ImmutablePair; + +/** + * A comparator for two semantic versions. + * + * @author ian + * @see <a href="http://semver.org/">Semantic Versioning 2.0.0-rc.1</a> for + * the specification on how this comparator operates. + */ +public class SemanticVersionComparator { + public static final ImmutablePair<Integer, SemanticVersionComponent> compare( + final ISemanticVersion verOne, + final ISemanticVersion verTwo) { + if(verOne.equals(verTwo) == true) { + return new ImmutablePair<Integer, SemanticVersionComponent>( + 0, SemanticVersionComponent.INVALID); + } + + if(verOne.getMajor() == verTwo.getMajor()) { + if(verOne.getMinor() == verTwo.getMinor()) { + if(verOne.getPatch() == verTwo.getPatch()) { + /* + * Compare the pre-release components. + */ + final ImmutablePair<Integer, Boolean> preReleaseCompResult = + compareArray(verOne.getPreRelease(), verTwo.getPreRelease()); + /* + * Compare the build components. + */ + final ImmutablePair<Integer, Boolean> buildCompResult = + compareArray(verOne.getBuild(), verTwo.getBuild()); + + /* + * Unpack the comparison values. + */ + final int prc = preReleaseCompResult.getLeft(); + final int bc = buildCompResult.getLeft(); + + if((preReleaseCompResult.getRight() == true) && + (buildCompResult.getRight() == true)) { + /* + * There was at most one pre-release component, and at most one + * build component in both versions. + */ + if((Math.abs(prc) == 1) && (Math.abs(bc) == 1)) { + return new ImmutablePair<Integer, SemanticVersionComponent>( + (Math.signum((float )prc) == 1.0) ? -1 : 1, + SemanticVersionComponent.PRE_RELEASE_AND_BUILD_VERSION); + } else if((Math.abs(prc) == 1) && (bc == 0)) { + return new ImmutablePair<Integer, SemanticVersionComponent>( + (Math.signum((float )prc) == 1.0) ? -1 : 1, + SemanticVersionComponent.PRE_RELEASE_AND_BUILD_VERSION); + } + + return new ImmutablePair<Integer, SemanticVersionComponent>( + bc, SemanticVersionComponent.PRE_RELEASE_AND_BUILD_VERSION); + } else if((preReleaseCompResult.getRight() == true) && + (buildCompResult.getRight() == false)) { + /* + * One of the versions had a null pre-release. + */ + return new ImmutablePair<Integer, SemanticVersionComponent>( + bc, SemanticVersionComponent.PRE_RELEASE_AND_BUILD_VERSION); + } + + /* + * Either one of the version has a null build or both versions where + * fully specified. + */ + return new ImmutablePair<Integer, SemanticVersionComponent>( + (Math.abs(prc) == 1) ? prc : bc, + SemanticVersionComponent.PRE_RELEASE_AND_BUILD_VERSION); + } else { + return new ImmutablePair<Integer, SemanticVersionComponent>( + verOne.getPatch() - verTwo.getPatch(), + SemanticVersionComponent.PATCH_VERSION); + } + } else { + return new ImmutablePair<Integer, SemanticVersionComponent>( + verOne.getMinor() - verTwo.getMinor(), + SemanticVersionComponent.MINOR_VERSION); + } + } + + return new ImmutablePair<Integer, SemanticVersionComponent>( + verOne.getMajor() - verTwo.getMajor(), + SemanticVersionComponent.MAJOR_VERSION); + } + + /** + * Lexically compare the components of two string arrays. + * + * @param one - First string array. + * @param two - The array that is compared to one. + * @return If one array is determined to be lexically less than two then + * -1 is returned. If one is determined to be lexically greater then two then + * 1 is returned. Otherwise, 0 is returned. + */ + private static ImmutablePair<Integer, Boolean> compareArray( + final String[] one, + final String[] two) { + if((one == null) && (two == null)) { + return new ImmutablePair<Integer, Boolean>(0, true); + } + + if((one == null) && (two != null)) { + return new ImmutablePair<Integer, Boolean>(-1, true); + } + + if((one != null) && (two == null)) { + return new ImmutablePair<Integer, Boolean>(1, true); + } + + @SuppressWarnings("unchecked") + final Iterator<String> oneIterator = new ArrayIterator(one); + @SuppressWarnings("unchecked") + final Iterator<String> twoIterator = new ArrayIterator(two); + String oneComp = null; + String twoComp = null; + int comp = 0; + + while(true) { + try { + oneComp = oneIterator.next(); + } catch(final NoSuchElementException ex) { + oneComp = null; + } + + try { + twoComp = twoIterator.next(); + } catch(final NoSuchElementException ex) { + twoComp = null; + } + + if((oneComp == null) && (twoComp == null)) { + break; + } + + comp = componentCompare(oneComp, twoComp); + if(comp != 0) { + break; + } + } + + return new ImmutablePair<Integer, Boolean>(comp, false); + } + + /** + * Compare two components from the pre-release and build string arrays as + * detailed in the Semantic Versioning 2.0.0-rc.1 specification. + * + * @param oneComp - first value. + * @param twoComp - second value. + * @return If first value is determined to be less than the second value then + * return -1. If one value is determined to be greater than the second value + * then return 1. Otherwise, 0 is returned. + * @see <a href="http://semver.org/">Semantic Versioning 2.0.0-rc.1</a> + */ + private static int componentCompare( + final String oneComp, final String twoComp) { + if((oneComp == null) && (twoComp == null)) { + return 0; + } + + if((oneComp == null) && (twoComp != null)) { + return -1; + } + + if((oneComp != null) && (twoComp == null)) { + return 1; + } + + boolean isOneInt = false; + boolean isTwoInt = false; + int oneInt = 0; + int twoInt = 0; + + try { + oneInt = Integer.parseInt(oneComp); + isOneInt = true; + } catch(final NumberFormatException ex) { + isOneInt = false; + } + + try { + twoInt = Integer.parseInt(twoComp); + isTwoInt = true; + } catch(final NumberFormatException ex) { + isTwoInt = false; + } + + final int comp = ((isOneInt == true) && (isTwoInt == true)) ? + oneInt - twoInt : + oneComp.compareTo(twoComp); + final int compValue = (comp > 0) ? 1 : (comp < 0) ? -1 : 0; + + return compValue; + } +} |