View Javadoc
1   package org.codehaus.plexus.languages.java.version;
2   
3   import java.util.Objects;
4   import java.util.StringTokenizer;
5   import java.util.regex.Matcher;
6   import java.util.regex.Pattern;
7   
8   /*
9    * Licensed to the Apache Software Foundation (ASF) under one
10   * or more contributor license agreements.  See the NOTICE file
11   * distributed with this work for additional information
12   * regarding copyright ownership.  The ASF licenses this file
13   * to you under the Apache License, Version 2.0 (the
14   * "License"); you may not use this file except in compliance
15   * with the License.  You may obtain a copy of the License at
16   *
17   *   http://www.apache.org/licenses/LICENSE-2.0
18   *
19   * Unless required by applicable law or agreed to in writing,
20   * software distributed under the License is distributed on an
21   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
22   * KIND, either express or implied.  See the License for the
23   * specific language governing permissions and limitations
24   * under the License.
25   */
26  
27  /**
28   * @author Robert Scholte
29   * @since 1.0.0
30   *
31   * @see <a href="http://www.oracle.com/technetwork/java/javase/namechange-140185.html">Java SE Naming and Versions</a>
32   * @see <a href="http://openjdk.java.net/jeps/223">JEP 223: New Version-String Scheme</a>
33   * @see <a href="http://openjdk.java.net/jeps/322">JEP 322: Time-Based Release Versioning</a>
34   */
35  public class JavaVersion implements Comparable<JavaVersion> {
36      /**
37       * Represents the System property {@code java.specification.version}
38       */
39      public static final JavaVersion JAVA_SPECIFICATION_VERSION =
40              parse(System.getProperty("java.specification.version"));
41  
42      /**
43       * Represents the System property {@code java.version}
44       */
45      public static final JavaVersion JAVA_VERSION = parse(System.getProperty("java.version"));
46  
47      private static final Pattern startingDigits = Pattern.compile("(\\d+)(.*)");
48  
49      private final String rawVersion;
50  
51      private final boolean isMajor;
52  
53      private JavaVersion(String rawVersion, boolean isMajor) {
54          this.rawVersion = rawVersion;
55          this.isMajor = isMajor;
56      }
57  
58      /**
59       * Lazy parse the version-scheme.
60       * Actual parsing is done when calling {@link #compareTo(JavaVersion)}
61       *
62       * @param s the version string, never {@code null}
63       * @return the version wrapped in a JavaVersion
64       */
65      public static JavaVersion parse(String s) {
66          return new JavaVersion(s, !s.startsWith("1."));
67      }
68  
69      @Override
70      public int compareTo(JavaVersion other) {
71          String[] thisSegments = this.rawVersion.split("\\.");
72          String[] otherSegments = other.rawVersion.split("\\.");
73  
74          int minSegments = Math.min(thisSegments.length, otherSegments.length);
75  
76          for (int index = 0; index < minSegments; index++) {
77              Matcher thisMatcher = startingDigits.matcher(thisSegments[index]);
78  
79              int thisValue;
80  
81              if (thisMatcher.find()) {
82                  thisValue = Integer.parseInt(thisMatcher.group(1));
83              } else {
84                  thisValue = -1;
85              }
86  
87              Matcher otherMatcher = startingDigits.matcher(otherSegments[index]);
88  
89              int otherValue;
90  
91              if (otherMatcher.find()) {
92                  otherValue = Integer.parseInt(otherMatcher.group(1));
93              } else {
94                  otherValue = -1;
95              }
96  
97              int compareValue = Integer.compare(thisValue, otherValue);
98  
99              if (compareValue != 0) {
100                 return compareValue;
101             }
102 
103             compareValue = suffixRate(thisMatcher.group(2)) - suffixRate(otherMatcher.group(2));
104             if (compareValue != 0) {
105                 return compareValue;
106             }
107 
108             // works for now, but needs improvement
109             compareValue = thisMatcher.group(2).compareTo(otherMatcher.group(2));
110 
111             if (compareValue != 0) {
112                 return compareValue;
113             }
114         }
115 
116         return (thisSegments.length - otherSegments.length);
117     }
118 
119     private int suffixRate(String suffix) {
120         if ("-ea".equals(suffix)) {
121             return -100;
122         } else if ("".equals(suffix)) {
123             return 0;
124         } else {
125             return 10;
126         }
127     }
128 
129     /**
130      * Verify if this version is before some other version
131      *
132      * @param other the version to compare with
133      * @return {@code true} is this is less than {@code other}, otherwise {@code false}
134      */
135     public boolean isBefore(JavaVersion other) {
136         return this.compareTo(other) < 0;
137     }
138 
139     /**
140      * Verify if this version is before some other version
141      *
142      * @param other the version to compare with
143      * @return {@code true}  is this is less than {@code other}, otherwise {@code false}
144      */
145     public boolean isBefore(String other) {
146         return this.compareTo(parse(other)) < 0;
147     }
148 
149     /**
150      * Verify if this version is at least some other version
151      *
152      * @param other the version to compare with
153      * @return  {@code true}  is this is greater than or equal to {@code other}, otherwise {@code false}
154      */
155     public boolean isAtLeast(JavaVersion other) {
156         return this.compareTo(other) >= 0;
157     }
158 
159     /**
160      * Verify if this version is at least some other version
161      *
162      * @param other the version to compare with
163      * @return  {@code true} is this is greater than or equal to {@code other}, otherwise {@code false}
164      */
165     public boolean isAtLeast(String other) {
166         return this.compareTo(parse(other)) >= 0;
167     }
168 
169     /**
170      * If original version starts with {@code "1."}, then remove this part from the version
171      *
172      * @return a new JavaVersion if version has to be changed, otherwise return itself
173      */
174     public JavaVersion asMajor() {
175         if (!isMajor) {
176             return new JavaVersion(rawVersion.substring(2), true);
177         } else {
178             return this;
179         }
180     }
181 
182     /**
183      * Returns the original version
184      *
185      * @return the raw version
186      */
187     public String getValue() {
188         return rawVersion;
189     }
190 
191     /**
192      * Returns a value respecting the nuber of groups.<br>
193      * If the original has more groups, the end of that value will be removed.<br>
194      * If the original has less groups, the value will be extended this ".0".<br>
195      *
196      * <pre>
197      *   JavaVersion.parse( "1" ).getValue( 1 )   is "1"
198      *   JavaVersion.parse( "1" ).getValue( 2 )   is "1.0"
199      *   JavaVersion.parse( "2.1" ).getValue( 1 ) is "2"
200      *   JavaVersion.parse( "2.1" ).getValue( 2 ) is "2.1"
201      * </pre>
202      *
203      * @param groups number of groups to return
204      * @return the version respecting the number of groups
205      */
206     public String getValue(int groups) {
207         StringBuilder value = new StringBuilder();
208         StringTokenizer tokenizer = new StringTokenizer(rawVersion, ".");
209 
210         value.append(tokenizer.nextToken());
211         for (int group = 1; group < groups; group++) {
212             value.append('.');
213             if (tokenizer.hasMoreTokens()) {
214                 value.append(tokenizer.nextToken());
215             } else {
216                 value.append("0");
217             }
218         }
219         return value.toString();
220     }
221 
222     @Override
223     public String toString() {
224         return rawVersion;
225     }
226 
227     @Override
228     public int hashCode() {
229         return Objects.hashCode(rawVersion);
230     }
231 
232     @Override
233     public boolean equals(Object obj) {
234         if (this == obj) {
235             return true;
236         }
237         if (obj == null) {
238             return false;
239         }
240         if (getClass() != obj.getClass()) {
241             return false;
242         }
243 
244         JavaVersion other = (JavaVersion) obj;
245         if (isMajor != other.isMajor) {
246             final String thisOneDotVersion;
247             final String otherOneDotVersion;
248             if (isMajor) {
249                 thisOneDotVersion = "1." + rawVersion;
250                 otherOneDotVersion = other.rawVersion;
251             } else {
252                 thisOneDotVersion = rawVersion;
253                 otherOneDotVersion = "1." + other.rawVersion;
254             }
255 
256             if (!Objects.equals(thisOneDotVersion, otherOneDotVersion)) {
257                 return false;
258             }
259         } else if (!Objects.equals(rawVersion, other.rawVersion)) {
260             return false;
261         }
262         return true;
263     }
264 }