View Javadoc
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 }