1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.lang3;
18
19 import org.apache.commons.lang3.math.NumberUtils;
20
21 /**
22 * Enumerates all known versions of the Java specification. This is intended to mirror available values from the <em>java.specification.version</em> System
23 * property.
24 *
25 * @since 3.0
26 */
27 public enum JavaVersion {
28
29 /**
30 * The Java version reported by Android. This is not an official Java version number.
31 */
32 JAVA_0_9(1.5f, "0.9"),
33
34 /**
35 * Java 1.1.
36 */
37 JAVA_1_1(1.1f, "1.1"),
38
39 /**
40 * Java 1.2.
41 */
42 JAVA_1_2(1.2f, "1.2"),
43
44 /**
45 * Java 1.3.
46 */
47 JAVA_1_3(1.3f, "1.3"),
48
49 /**
50 * Java 1.4.
51 */
52 JAVA_1_4(1.4f, "1.4"),
53
54 /**
55 * Java 1.5.
56 */
57 JAVA_1_5(1.5f, "1.5"),
58
59 /**
60 * Java 1.6.
61 */
62 JAVA_1_6(1.6f, "1.6"),
63
64 /**
65 * Java 1.7.
66 */
67 JAVA_1_7(1.7f, "1.7"),
68
69 /**
70 * Java 1.8.
71 */
72 JAVA_1_8(1.8f, "1.8"),
73
74 /**
75 * Java 1.9.
76 *
77 * @deprecated As of release 3.5, replaced by {@link #JAVA_9}.
78 */
79 @Deprecated
80 JAVA_1_9(9.0f, "9"),
81
82 /**
83 * Java 9.
84 *
85 * @since 3.5
86 */
87 JAVA_9(9.0f, "9"),
88
89 /**
90 * Java 10.
91 *
92 * @since 3.7
93 */
94 JAVA_10(10.0f, "10"),
95
96 /**
97 * Java 11.
98 *
99 * @since 3.8
100 */
101 JAVA_11(11.0f, "11"),
102
103 /**
104 * Java 12.
105 *
106 * @since 3.9
107 */
108 JAVA_12(12.0f, "12"),
109
110 /**
111 * Java 13.
112 *
113 * @since 3.9
114 */
115 JAVA_13(13.0f, "13"),
116
117 /**
118 * Java 14.
119 *
120 * @since 3.11
121 */
122 JAVA_14(14.0f, "14"),
123
124 /**
125 * Java 15.
126 *
127 * @since 3.11
128 */
129 JAVA_15(15.0f, "15"),
130
131 /**
132 * Java 16.
133 *
134 * @since 3.11
135 */
136 JAVA_16(16.0f, "16"),
137
138 /**
139 * Java 17.
140 *
141 * @since 3.12.0
142 */
143 JAVA_17(17.0f, "17"),
144
145 /**
146 * Java 18.
147 *
148 * @since 3.13.0
149 */
150 JAVA_18(18.0f, "18"),
151
152 /**
153 * Java 19.
154 *
155 * @since 3.13.0
156 */
157 JAVA_19(19.0f, "19"),
158
159 /**
160 * Java 20.
161 *
162 * @since 3.13.0
163 */
164 JAVA_20(20, "20"),
165
166 /**
167 * Java 21.
168 *
169 * @since 3.13.0
170 */
171 JAVA_21(21, "21"),
172
173 /**
174 * Java 22.
175 *
176 * @since 3.15.0
177 */
178 JAVA_22(22, "22"),
179
180 /**
181 * Java 23.
182 *
183 * @since 3.18.0
184 */
185 JAVA_23(23, "23"),
186
187 /**
188 * Java 24.
189 *
190 * @since 3.18.0
191 */
192 JAVA_24(24, "24"),
193
194 /**
195 * Java 25.
196 *
197 * @since 3.20.0
198 */
199 JAVA_25(25, "25"),
200
201 /**
202 * Java 26.
203 *
204 * @since 3.20.0
205 */
206 JAVA_26(26, "26"),
207
208 /**
209 * Java 27.
210 *
211 * @since 3.21.0
212 */
213 JAVA_27(27, "27"),
214
215 /**
216 * Java 28.
217 *
218 * @since 3.21.0
219 */
220 JAVA_28(28, "28"),
221
222 /**
223 * The most recent Java version. Mainly introduced to avoid to break when a new version of Java is used.
224 */
225 JAVA_RECENT(maxVersion(), Float.toString(maxVersion()));
226
227 /**
228 * Gets the enumeration constant corresponding to the given Java version string. This method is used internally.
229 *
230 * @param versionStr The Java version as string.
231 * @return The corresponding enumeration constant or {@code null} if the version is unknown.
232 */
233 static JavaVersion get(final String versionStr) {
234 if (versionStr == null) {
235 return null;
236 }
237 switch (versionStr) {
238 case "0.9":
239 return JAVA_0_9;
240 case "1.1":
241 return JAVA_1_1;
242 case "1.2":
243 return JAVA_1_2;
244 case "1.3":
245 return JAVA_1_3;
246 case "1.4":
247 return JAVA_1_4;
248 case "1.5":
249 return JAVA_1_5;
250 case "1.6":
251 return JAVA_1_6;
252 case "1.7":
253 return JAVA_1_7;
254 case "1.8":
255 return JAVA_1_8;
256 case "9":
257 return JAVA_9;
258 case "10":
259 return JAVA_10;
260 case "11":
261 return JAVA_11;
262 case "12":
263 return JAVA_12;
264 case "13":
265 return JAVA_13;
266 case "14":
267 return JAVA_14;
268 case "15":
269 return JAVA_15;
270 case "16":
271 return JAVA_16;
272 case "17":
273 return JAVA_17;
274 case "18":
275 return JAVA_18;
276 case "19":
277 return JAVA_19;
278 case "20":
279 return JAVA_20;
280 case "21":
281 return JAVA_21;
282 case "22":
283 return JAVA_22;
284 case "23":
285 return JAVA_23;
286 case "24":
287 return JAVA_24;
288 case "25":
289 return JAVA_25;
290 case "26":
291 return JAVA_26;
292 case "27":
293 return JAVA_27;
294 case "28":
295 return JAVA_28;
296 default:
297 final float v = toFloatVersion(versionStr);
298 if (v - 1. < 1.) { // then we need to check decimals > .9
299 final int firstComma = Math.max(versionStr.indexOf('.'), versionStr.indexOf(','));
300 // read up to the next separator if present, otherwise to the end of the string
301 // (this was previously an inverted Math.max that always selected the full string)
302 int end = versionStr.indexOf(',', firstComma + 1);
303 if (end == -1) {
304 end = versionStr.length();
305 }
306 try {
307 if (Float.parseFloat(versionStr.substring(firstComma + 1, end)) > .9f) {
308 return JAVA_RECENT;
309 }
310 } catch (final NumberFormatException e) {
311 // malformed version string ("1.", "bogus"): the documented contract is to return null
312 // for unknown versions rather than propagate an exception, which would otherwise poison
313 // SystemUtils' static initializer for the class loader lifetime.
314 return null;
315 }
316 } else if (v > 10) {
317 return JAVA_RECENT;
318 }
319 return null;
320 }
321 }
322
323 /**
324 * Gets the enumeration constant corresponding to the given Java version string. This method is used internally.
325 *
326 * @param versionStr The Java version as string.
327 * @return The corresponding enumeration constant or {@code null} if the version is unknown.
328 */
329 static JavaVersion getJavaVersion(final String versionStr) {
330 return get(versionStr);
331 }
332
333 /**
334 * Gets the Java Version from the system or 99.0 if the {@code java.specification.version} system property is not set.
335 *
336 * @return The value of {@code java.specification.version} system property or 99.0 if it is not set.
337 */
338 private static float maxVersion() {
339 final float v = toFloatVersion(SystemProperties.getJavaSpecificationVersion("99.0"));
340 return v > 0 ? v : 99f;
341 }
342
343 static String[] split(final String value) {
344 return RegExUtils.VERSION_SPLIT_PATTERN.split(value);
345 }
346
347 /**
348 * Parses a float value from a String.
349 *
350 * @param value The String to parse.
351 * @return The float value represented by the string or -1 if the given String cannot be parsed.
352 */
353 private static float toFloatVersion(final String value) {
354 final int defaultReturnValue = -1;
355 if (!value.contains(".")) {
356 return NumberUtils.toFloat(value, defaultReturnValue);
357 }
358 final String[] toParse = split(value);
359 if (toParse.length >= 2) {
360 return NumberUtils.toFloat(toParse[0] + '.' + toParse[1], defaultReturnValue);
361 }
362 return defaultReturnValue;
363 }
364
365 /**
366 * The float value.
367 */
368 private final float value;
369
370 /**
371 * The standard name.
372 */
373 private final String name;
374
375 /**
376 * Constructs a new instance.
377 *
378 * @param value The float value.
379 * @param name The standard name, not null.
380 */
381 JavaVersion(final float value, final String name) {
382 this.value = value;
383 this.name = name;
384 }
385
386 /**
387 * Tests whether this version of Java is at least the version of Java passed in.
388 *
389 * <p>
390 * For example:
391 * </p>
392 *
393 * <pre>
394 * {@code
395 * myVersion.atLeast(JavaVersion.JAVA_1_8)
396 * }</pre>
397 *
398 * @param requiredVersion The version to check against, not null.
399 * @return true if this version is equal to or greater than the specified version.
400 */
401 public boolean atLeast(final JavaVersion requiredVersion) {
402 return this.value >= requiredVersion.value;
403 }
404
405 /**
406 * Tests whether this version of Java is at most the version of Java passed in.
407 *
408 * <p>
409 * For example:
410 * </p>
411 *
412 * <pre>
413 * {@code
414 * myVersion.atMost(JavaVersion.JAVA_1_4)
415 * }</pre>
416 *
417 * @param requiredVersion The version to check against, not null.
418 * @return true if this version is equal to or less than the specified version.
419 * @since 3.9
420 */
421 public boolean atMost(final JavaVersion requiredVersion) {
422 return this.value <= requiredVersion.value;
423 }
424
425 /**
426 * The string value is overridden to return the standard name.
427 *
428 * <p>
429 * For example, {@code "1.5"}.
430 * </p>
431 *
432 * @return The name, not null.
433 */
434 @Override
435 public String toString() {
436 return name;
437 }
438 }