1   /**
2    * Distribution License:
3    * JSword is free software; you can redistribute it and/or modify it under
4    * the terms of the GNU Lesser General Public License, version 2.1 as published by
5    * the Free Software Foundation. This program is distributed in the hope
6    * that it will be useful, but WITHOUT ANY WARRANTY; without even the
7    * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8    * See the GNU Lesser General Public License for more details.
9    *
10   * The License is available on the internet at:
11   *       http://www.gnu.org/copyleft/lgpl.html
12   * or by writing to:
13   *      Free Software Foundation, Inc.
14   *      59 Temple Place - Suite 330
15   *      Boston, MA 02111-1307, USA
16   *
17   * Copyright: 2011
18   *     The copyright to this program is held by it's authors.
19   *
20   * ID: $Id: Version.java 2238 2012-03-29 13:35:27Z dmsmith $
21   */
22  package org.crosswire.common.util;
23  
24  import java.util.regex.Matcher;
25  import java.util.regex.Pattern;
26  
27  /**
28   * Version is an immutable representation of dotted "number" consisting of 1 to 4 parts.
29   * 
30   * <p>
31   * Here is the grammar for version strings:
32   * 
33   * <pre>
34   * version ::= major('.'minor('.'micro('.'nano)?)?)?
35   * major ::= [0-9]+
36   * minor ::= [0-9]+
37   * micro ::= [0-9]+
38   * nano  ::= [0-9]+
39   * </pre>
40   * 
41   * @see gnu.lgpl.License for license details.<br>
42   *      The copyright to this program is held by it's authors.
43   * @author DM Smith [dmsmith555 at yahoo dot com]
44   */
45  public class Version implements Comparable<Version> {
46  
47      /**
48       * The default version "1.0".
49       */
50      public static final Version DEFAULT_VERSION = new Version("1.0");
51  
52      /**
53       * Created a version identifier from the specified string.
54       * 
55       * @param version String representation of the version identifier.
56       * @throws IllegalArgumentException If <code>version</code> is improperly
57       *         formatted.
58       */
59      public Version(String version) {
60          if (version == null) {
61              throw new IllegalArgumentException("Null version not allowed.");
62          }
63          this.original = version;
64          this.parts = new int[] { -1, -1, -1, -1 };
65          Pattern regexp = Pattern.compile("^(\\d+)(?:.(\\d+))?(?:.(\\d+))?(?:.(\\d+))?$");
66          Matcher matcher = regexp.matcher(this.original);
67          if (matcher.matches()) {
68              int count = matcher.groupCount();
69              for (int i = 1; i <= count; i++) {
70                  String part = matcher.group(i);
71                  if (part == null) {
72                      break;
73                  }
74                  parts[i - 1] = Integer.parseInt(part);
75              }
76          } else {
77              throw new IllegalArgumentException("invalid: " + version);
78          }
79      }
80  
81      /**
82       * Returns the original string representation of this version identifier.
83       *
84       * @return The original string representation of this version identifier.
85       */
86      @Override
87      public String toString() {
88          return original;
89      }
90  
91      @Override
92      public int hashCode() {
93          return original.hashCode();
94      }
95  
96      /**
97       * Compares this <code>Version object to another object.
98       * 
99       * <p>
100      * A version is considered to be equal to another version if all the
101      * parts are equal.
102      * 
103      * @param object The <code>Version</code> object to be compared.
104      * @return true if the two objects are equal.
105      */
106     @Override
107     public boolean equals(Object object) {
108         if (!(object instanceof Version)) {
109             return false;
110         }
111 
112         Version that = (Version) object;
113         if (that == this) {
114             return true;
115         }
116 
117         for (int i = 0; i < parts.length; i++) {
118             if (parts[i] != that.parts[i]) {
119                 return false;
120             }
121         }
122 
123         return true;
124     }
125 
126     /**
127      * Compares this <code>Version</code> object to another object.
128      * 
129      * <p>
130      * The comparison considers each of the parts (major, minor, micro, nano) in turn,
131      * comparing like with like. At any point the comparison is not equal, a result is
132      * known.
133      * </p>
134      * 
135      * @param object The <code>Version</code> object to be compared.
136      * @return A negative integer, zero, or a positive integer if this object is
137      *         less than, equal to, or greater than the specified
138      *         <code>Version</code> object.
139      * @throws ClassCastException If the specified object is not a <code>Version</code>.
140      */
141     public int compareTo(Version object) {
142         if (object == this) {
143             return 0;
144         }
145 
146         for (int i = 0; i < parts.length; i++) {
147             int result = parts[i] - object.parts[i];
148             if (result != 0) {
149                 return result;
150             }
151         }
152 
153         return 0;
154     }
155 
156     private final String         original;
157     private final int[]          parts;
158 
159 }
160