View Javadoc
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  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      public static final Version VERSION_0 = Version.parse("0");
53  
54      /** Version 2.0 */
55      public static final Version VERSION_2_0 = Version.parse("2.0");
56  
57      /** Version 2.2 */
58      public static final Version VERSION_2_2 = Version.parse("2.2");
59  
60      /** Version 2.4 */
61      public static final Version VERSION_2_4 = Version.parse("2.4");
62  
63      /** Version 2.6 */
64      public static final Version VERSION_2_6 = Version.parse("2.6");
65  
66      /** Version 2.5.2 - Wire Protocol version = 1. */
67      protected static final Version VERSION_2_5_2 = Version.parse("2.5.2");
68  
69      /** Version 2.5.4 - Wire Protocol version = 2. */
70      protected static final Version VERSION_2_5_4 = Version.parse("2.5.4");
71  
72      /** The logger for the {@link Version}. */
73      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          final Properties props = new Properties();
81          InputStream in = null;
82          try {
83              in = Version.class
84                      .getResourceAsStream("/META-INF/maven/com.allanbank/"
85                              + "mongodb-async-driver/pom.properties");
86              if (in != null) {
87                  props.load(in);
88              }
89          }
90          catch (final IOException error) {
91              LOG.info("Could not read the version information for the driver.");
92          }
93          finally {
94              IOUtils.close(in);
95          }
96  
97          VERSION = parse(props.getProperty("version", "0-DEVELOPMENT"));
98          UNKNOWN = new Version(new int[0], "UNKNOWN");
99      }
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         if ((lhs == null) || ((rhs != null) && (lhs.compareTo(rhs) > 0))) {
114             return rhs;
115         }
116 
117         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         Version result = null;
133         if (wireVersion >= 2) {
134             result = VERSION_2_5_4;
135         }
136         else if (wireVersion == 1) {
137             result = VERSION_2_5_2;
138         }
139         else if (wireVersion == 0) {
140             result = VERSION_2_4;
141         }
142 
143         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         if ((lhs == null) || ((rhs != null) && (lhs.compareTo(rhs) < 0))) {
159             return rhs;
160         }
161         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         final int[] version = new int[versionArray.size()];
175         for (int i = 0; i < version.length; ++i) {
176             version[i] = versionArray.get(i).getIntValue();
177         }
178 
179         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         final String[] tokens = version.split("\\.");
192         final int[] versions = new int[tokens.length];
193         String suffix = "";
194         for (int i = 0; i < tokens.length; ++i) {
195             String token = tokens[i];
196 
197             // Check for a suffix on the last token.
198             if (i == (tokens.length - 1)) {
199                 final int dashIndex = token.indexOf('-');
200                 if (dashIndex >= 0) {
201                     suffix = token.substring(dashIndex + 1);
202                     token = token.substring(0, dashIndex);
203                 }
204             }
205 
206             try {
207                 versions[i] = Integer.parseInt(token);
208             }
209             catch (final NumberFormatException nfe) {
210                 LOG.debug(
211                         "Could not parse version string token ('{}') from version '{}'.",
212                         token, version);
213             }
214         }
215 
216         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     private Version(final int[] version, final String suffix) {
235         myVersion = version.clone();
236         mySuffix = (suffix != null) ? suffix : "";
237     }
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         int compare = 0;
249 
250         // Check to make sure UNKNOWN is the highest version.
251         if (UNKNOWN.equals(this)) {
252             compare = UNKNOWN.equals(other) ? 0 : 1;
253         }
254         else if (UNKNOWN.equals(other)) {
255             compare = -1;
256         }
257 
258         final int fields = Math.min(myVersion.length, other.myVersion.length);
259         for (int i = 0; (compare == 0) && (i < fields); ++i) {
260             compare = compare(myVersion[i], other.myVersion[i]);
261         }
262 
263         if (compare == 0) {
264             compare = compare(myVersion.length, other.myVersion.length);
265             if (compare == 0) {
266                 compare = mySuffix.compareTo(other.mySuffix);
267             }
268         }
269 
270         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         boolean result = false;
285         if (this == object) {
286             result = true;
287         }
288         else if ((object != null) && (getClass() == object.getClass())) {
289             final Version other = (Version) object;
290 
291             result = mySuffix.equals(other.mySuffix)
292                     && Arrays.equals(myVersion, other.myVersion);
293         }
294         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         int result = 1;
305         result = (31 * result) + mySuffix.hashCode();
306         result = (31 * result) + Arrays.hashCode(myVersion);
307         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         final StringBuilder builder = new StringBuilder();
319         for (int i = 0; i < myVersion.length; ++i) {
320             if (i != 0) {
321                 builder.append('.');
322             }
323             builder.append(String.valueOf(myVersion[i]));
324         }
325         if (!mySuffix.isEmpty()) {
326             if (myVersion.length > 0) {
327                 builder.append('-');
328             }
329             builder.append(mySuffix);
330         }
331         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         return (x < y) ? -1 : ((x == y) ? 0 : 1);
353     }
354 
355 }