Coverage Report - com.allanbank.mongodb.Version
 
Classes in this File Line Coverage Branch Coverage Complexity
Version
95%
89/93
98%
63/64
4.091
 
 1  
 /*
 2  
  * #%L
 3  
  * Version.java - mongodb-async-driver - Allanbank Consulting, Inc.
 4  
  * %%
 5  
  * Copyright (C) 2011 - 2014 Allanbank Consulting, Inc.
 6  
  * %%
 7  
  * Licensed under the Apache License, Version 2.0 (the "License");
 8  
  * you may not use this file except in compliance with the License.
 9  
  * You may obtain a copy of the License at
 10  
  * 
 11  
  *      http://www.apache.org/licenses/LICENSE-2.0
 12  
  * 
 13  
  * Unless required by applicable law or agreed to in writing, software
 14  
  * distributed under the License is distributed on an "AS IS" BASIS,
 15  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 16  
  * See the License for the specific language governing permissions and
 17  
  * limitations under the License.
 18  
  * #L%
 19  
  */
 20  
 package com.allanbank.mongodb;
 21  
 
 22  
 import java.io.IOException;
 23  
 import java.io.InputStream;
 24  
 import java.io.Serializable;
 25  
 import java.util.Arrays;
 26  
 import java.util.List;
 27  
 import java.util.Properties;
 28  
 
 29  
 import com.allanbank.mongodb.bson.NumericElement;
 30  
 import com.allanbank.mongodb.util.IOUtils;
 31  
 import com.allanbank.mongodb.util.log.Log;
 32  
 import com.allanbank.mongodb.util.log.LogFactory;
 33  
 
 34  
 /**
 35  
  * Version provides a class to handle version numbers and provide the version of
 36  
  * the driver in use.
 37  
  * 
 38  
  * @copyright 2013, Allanbank Consulting, Inc., All Rights Reserved
 39  
  */
 40  0
 public class Version implements Serializable, Comparable<Version> {
 41  
 
 42  
     /**
 43  
      * A version to use when we don't know the version. This version will always
 44  
      * compare greater than (higher, more recent) than all other versions.
 45  
      */
 46  
     public static final Version UNKNOWN;
 47  
 
 48  
     /** The driver's version. */
 49  
     public static final Version VERSION;
 50  
 
 51  
     /** The "zero" version which should be before all other versions. */
 52  1
     public static final Version VERSION_0 = Version.parse("0");
 53  
 
 54  
     /** Version 2.0 */
 55  1
     public static final Version VERSION_2_0 = Version.parse("2.0");
 56  
 
 57  
     /** Version 2.2 */
 58  1
     public static final Version VERSION_2_2 = Version.parse("2.2");
 59  
 
 60  
     /** Version 2.4 */
 61  1
     public static final Version VERSION_2_4 = Version.parse("2.4");
 62  
 
 63  
     /** Version 2.6 */
 64  1
     public static final Version VERSION_2_6 = Version.parse("2.6");
 65  
 
 66  
     /** Version 2.5.2 - Wire Protocol version = 1. */
 67  1
     protected static final Version VERSION_2_5_2 = Version.parse("2.5.2");
 68  
 
 69  
     /** Version 2.5.4 - Wire Protocol version = 2. */
 70  1
     protected static final Version VERSION_2_5_4 = Version.parse("2.5.4");
 71  
 
 72  
     /** The logger for the {@link Version}. */
 73  1
     private static final Log LOG = LogFactory.getLog(Version.class);
 74  
 
 75  
     /** The serialization version for the class. */
 76  
     private static final long serialVersionUID = 4726973040107711788L;
 77  
 
 78  
     static {
 79  
         // Load the version from the maven pom.properties file.
 80  1
         final Properties props = new Properties();
 81  1
         InputStream in = null;
 82  
         try {
 83  1
             in = Version.class
 84  
                     .getResourceAsStream("/META-INF/maven/com.allanbank/"
 85  
                             + "mongodb-async-driver/pom.properties");
 86  1
             if (in != null) {
 87  0
                 props.load(in);
 88  
             }
 89  
         }
 90  0
         catch (final IOException error) {
 91  0
             LOG.info("Could not read the version information for the driver.");
 92  
         }
 93  
         finally {
 94  1
             IOUtils.close(in);
 95  1
         }
 96  
 
 97  1
         VERSION = parse(props.getProperty("version", "0-DEVELOPMENT"));
 98  1
         UNKNOWN = new Version(new int[0], "UNKNOWN");
 99  1
     }
 100  
 
 101  
     /**
 102  
      * Returns the earlier of the two versions. If either version is
 103  
      * {@code null} then the other version is returned. Only if both version are
 104  
      * {@code null} will {@code null} be returned.
 105  
      * 
 106  
      * @param lhs
 107  
      *            The first version to compare.
 108  
      * @param rhs
 109  
      *            The second version to compare.
 110  
      * @return The earlier (lesser) version of the two.
 111  
      */
 112  
     public static Version earlier(final Version lhs, final Version rhs) {
 113  228
         if ((lhs == null) || ((rhs != null) && (lhs.compareTo(rhs) > 0))) {
 114  159
             return rhs;
 115  
         }
 116  
 
 117  69
         return lhs;
 118  
     }
 119  
 
 120  
     /**
 121  
      * Returns the best guess at the version of the server based on the wire
 122  
      * protocol version number. Returns the first version of the server to
 123  
      * support the wire protocol version.
 124  
      * 
 125  
      * @param wireVersion
 126  
      *            Wire protocol version.
 127  
      * @return The best guess at the version of the server based on the wire
 128  
      *         protocol version number. Returns <code>null</code> if the version
 129  
      *         cannot be determined.
 130  
      */
 131  
     public static Version forWireVersion(final int wireVersion) {
 132  13
         Version result = null;
 133  13
         if (wireVersion >= 2) {
 134  6
             result = VERSION_2_5_4;
 135  
         }
 136  7
         else if (wireVersion == 1) {
 137  4
             result = VERSION_2_5_2;
 138  
         }
 139  3
         else if (wireVersion == 0) {
 140  1
             result = VERSION_2_4;
 141  
         }
 142  
 
 143  13
         return result;
 144  
     }
 145  
 
 146  
     /**
 147  
      * Returns the later of the two versions. If either version is {@code null}
 148  
      * then the other version is returned. Only if both version are {@code null}
 149  
      * will {@code null} be returned.
 150  
      * 
 151  
      * @param lhs
 152  
      *            The first version to compare.
 153  
      * @param rhs
 154  
      *            The second version to compare.
 155  
      * @return The later (greater) version of the two.
 156  
      */
 157  
     public static Version later(final Version lhs, final Version rhs) {
 158  259
         if ((lhs == null) || ((rhs != null) && (lhs.compareTo(rhs) < 0))) {
 159  186
             return rhs;
 160  
         }
 161  73
         return lhs;
 162  
     }
 163  
 
 164  
     /**
 165  
      * Parses a version from a version array. The values in the array are
 166  
      * assumed to be {@link NumericElement}s.
 167  
      * 
 168  
      * @param versionArray
 169  
      *            The version array to parse.
 170  
      * @return The version.
 171  
      */
 172  
     public static Version parse(final List<NumericElement> versionArray) {
 173  
 
 174  103
         final int[] version = new int[versionArray.size()];
 175  412
         for (int i = 0; i < version.length; ++i) {
 176  309
             version[i] = versionArray.get(i).getIntValue();
 177  
         }
 178  
 
 179  103
         return new Version(version, null);
 180  
     }
 181  
 
 182  
     /**
 183  
      * Parses a version of the general format 'int.int.int-suffix'.
 184  
      * 
 185  
      * @param version
 186  
      *            The version string to parse.
 187  
      * @return The version.
 188  
      */
 189  
     public static Version parse(final String version) {
 190  
 
 191  1025
         final String[] tokens = version.split("\\.");
 192  1025
         final int[] versions = new int[tokens.length];
 193  1025
         String suffix = "";
 194  2572
         for (int i = 0; i < tokens.length; ++i) {
 195  1547
             String token = tokens[i];
 196  
 
 197  
             // Check for a suffix on the last token.
 198  1547
             if (i == (tokens.length - 1)) {
 199  1025
                 final int dashIndex = token.indexOf('-');
 200  1025
                 if (dashIndex >= 0) {
 201  197
                     suffix = token.substring(dashIndex + 1);
 202  197
                     token = token.substring(0, dashIndex);
 203  
                 }
 204  
             }
 205  
 
 206  
             try {
 207  1547
                 versions[i] = Integer.parseInt(token);
 208  
             }
 209  40
             catch (final NumberFormatException nfe) {
 210  40
                 LOG.debug(
 211  
                         "Could not parse version string token ('{}') from version '{}'.",
 212  
                         token, version);
 213  1507
             }
 214  
         }
 215  
 
 216  1025
         return new Version(versions, suffix);
 217  
     }
 218  
 
 219  
     /** The suffix for the version like 'SNAPSHOT'. May be the empty string. */
 220  
     private final String mySuffix;
 221  
 
 222  
     /** The "values" for the version number. */
 223  
     private final int[] myVersion;
 224  
 
 225  
     /**
 226  
      * Creates a new Version.
 227  
      * 
 228  
      * @param version
 229  
      *            The "values" for the version number.
 230  
      * @param suffix
 231  
      *            The suffix for the version like 'SNAPSHOT'. May be the empty
 232  
      *            string.
 233  
      */
 234  1129
     private Version(final int[] version, final String suffix) {
 235  1129
         myVersion = version.clone();
 236  1129
         mySuffix = (suffix != null) ? suffix : "";
 237  1129
     }
 238  
 
 239  
     /**
 240  
      * {@inheritDoc}
 241  
      * <p>
 242  
      * Overridden to compare the two versions.
 243  
      * </p>
 244  
      */
 245  
     @Override
 246  
     public int compareTo(final Version other) {
 247  
 
 248  381
         int compare = 0;
 249  
 
 250  
         // Check to make sure UNKNOWN is the highest version.
 251  381
         if (UNKNOWN.equals(this)) {
 252  63
             compare = UNKNOWN.equals(other) ? 0 : 1;
 253  
         }
 254  318
         else if (UNKNOWN.equals(other)) {
 255  63
             compare = -1;
 256  
         }
 257  
 
 258  381
         final int fields = Math.min(myVersion.length, other.myVersion.length);
 259  863
         for (int i = 0; (compare == 0) && (i < fields); ++i) {
 260  482
             compare = compare(myVersion[i], other.myVersion[i]);
 261  
         }
 262  
 
 263  381
         if (compare == 0) {
 264  78
             compare = compare(myVersion.length, other.myVersion.length);
 265  78
             if (compare == 0) {
 266  60
                 compare = mySuffix.compareTo(other.mySuffix);
 267  
             }
 268  
         }
 269  
 
 270  381
         return compare;
 271  
     }
 272  
 
 273  
     /**
 274  
      * Determines if the passed object is of this same type as this object and
 275  
      * if so that its fields are equal.
 276  
      * 
 277  
      * @param object
 278  
      *            The object to compare to.
 279  
      * 
 280  
      * @see java.lang.Object#equals(java.lang.Object)
 281  
      */
 282  
     @Override
 283  
     public boolean equals(final Object object) {
 284  5335
         boolean result = false;
 285  5335
         if (this == object) {
 286  723
             result = true;
 287  
         }
 288  4612
         else if ((object != null) && (getClass() == object.getClass())) {
 289  4594
             final Version other = (Version) object;
 290  
 
 291  4594
             result = mySuffix.equals(other.mySuffix)
 292  
                     && Arrays.equals(myVersion, other.myVersion);
 293  
         }
 294  5335
         return result;
 295  
     }
 296  
 
 297  
     /**
 298  
      * Computes a reasonable hash code.
 299  
      * 
 300  
      * @return The hash code value.
 301  
      */
 302  
     @Override
 303  
     public int hashCode() {
 304  13050
         int result = 1;
 305  13050
         result = (31 * result) + mySuffix.hashCode();
 306  13050
         result = (31 * result) + Arrays.hashCode(myVersion);
 307  13050
         return result;
 308  
     }
 309  
 
 310  
     /**
 311  
      * {@inheritDoc}
 312  
      * <p>
 313  
      * Overridden to produce a human readable string for the version.
 314  
      * </p>
 315  
      */
 316  
     @Override
 317  
     public String toString() {
 318  4787
         final StringBuilder builder = new StringBuilder();
 319  9990
         for (int i = 0; i < myVersion.length; ++i) {
 320  5203
             if (i != 0) {
 321  2674
                 builder.append('.');
 322  
             }
 323  5203
             builder.append(String.valueOf(myVersion[i]));
 324  
         }
 325  4787
         if (!mySuffix.isEmpty()) {
 326  2344
             if (myVersion.length > 0) {
 327  86
                 builder.append('-');
 328  
             }
 329  2344
             builder.append(mySuffix);
 330  
         }
 331  4787
         return builder.toString();
 332  
     }
 333  
 
 334  
     /**
 335  
      * Compares two {@code int} values numerically. The value returned is
 336  
      * identical to what would be returned by:
 337  
      * 
 338  
      * <pre>
 339  
      * Integer.valueOf(x).compareTo(Integer.valueOf(y))
 340  
      * </pre>
 341  
      * 
 342  
      * @param x
 343  
      *            the first {@code int} to compare
 344  
      * @param y
 345  
      *            the second {@code int} to compare
 346  
      * @return the value {@code 0} if {@code x == y}; a value less than
 347  
      *         {@code 0} if {@code x < y}; and a value greater than {@code 0} if
 348  
      *         {@code x > y}
 349  
      * @since 1.7
 350  
      */
 351  
     protected int compare(final int x, final int y) {
 352  560
         return (x < y) ? -1 : ((x == y) ? 0 : 1);
 353  
     }
 354  
 
 355  
 }