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 }