    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      https://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.lang3;
    018
    019import org.apache.commons.lang3.math.NumberUtils;
    020
    021/**
    022 * Enumerates all known versions of the Java specification. This is intended to mirror available values from the <em>java.specification.version</em> System
    023 * property.
    024 *
    025 * @since 3.0
    026 */
    027public enum JavaVersion {
    028
    029    /**
    030     * The Java version reported by Android. This is not an official Java version number.
    031     */
    032    JAVA_0_9(1.5f, "0.9"),
    033
    034    /**
    035     * Java 1.1.
    036     */
    037    JAVA_1_1(1.1f, "1.1"),
    038
    039    /**
    040     * Java 1.2.
    041     */
    042    JAVA_1_2(1.2f, "1.2"),
    043
    044    /**
    045     * Java 1.3.
    046     */
    047    JAVA_1_3(1.3f, "1.3"),
    048
    049    /**
    050     * Java 1.4.
    051     */
    052    JAVA_1_4(1.4f, "1.4"),
    053
    054    /**
    055     * Java 1.5.
    056     */
    057    JAVA_1_5(1.5f, "1.5"),
    058
    059    /**
    060     * Java 1.6.
    061     */
    062    JAVA_1_6(1.6f, "1.6"),
    063
    064    /**
    065     * Java 1.7.
    066     */
    067    JAVA_1_7(1.7f, "1.7"),
    068
    069    /**
    070     * Java 1.8.
    071     */
    072    JAVA_1_8(1.8f, "1.8"),
    073
    074    /**
    075     * Java 1.9.
    076     *
    077     * @deprecated As of release 3.5, replaced by {@link #JAVA_9}.
    078     */
    079    @Deprecated
    080    JAVA_1_9(9.0f, "9"),
    081
    082    /**
    083     * Java 9.
    084     *
    085     * @since 3.5
    086     */
    087    JAVA_9(9.0f, "9"),
    088
    089    /**
    090     * Java 10.
    091     *
    092     * @since 3.7
    093     */
    094    JAVA_10(10.0f, "10"),
    095
    096    /**
    097     * Java 11.
    098     *
    099     * @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     * The most recent Java version. Mainly introduced to avoid to break when a new version of Java is used.
    210     */
    211    JAVA_RECENT(maxVersion(), Float.toString(maxVersion()));
    212
    213    /**
    214     * Transforms the given string with a Java version number to the corresponding constant of this enumeration class. This method is used internally.
    215     *
    216     * @param versionStr the Java version as string
    217     * @return the corresponding enumeration constant or <strong>null</strong> if the version is unknown
    218     */
    219    static JavaVersion get(final String versionStr) {
    220        if (versionStr == null) {
    221            return null;
    222        }
    223        switch (versionStr) {
    224        case "0.9":
    225            return JAVA_0_9;
    226        case "1.1":
    227            return JAVA_1_1;
    228        case "1.2":
    229            return JAVA_1_2;
    230        case "1.3":
    231            return JAVA_1_3;
    232        case "1.4":
    233            return JAVA_1_4;
    234        case "1.5":
    235            return JAVA_1_5;
    236        case "1.6":
    237            return JAVA_1_6;
    238        case "1.7":
    239            return JAVA_1_7;
    240        case "1.8":
    241            return JAVA_1_8;
    242        case "9":
    243            return JAVA_9;
    244        case "10":
    245            return JAVA_10;
    246        case "11":
    247            return JAVA_11;
    248        case "12":
    249            return JAVA_12;
    250        case "13":
    251            return JAVA_13;
    252        case "14":
    253            return JAVA_14;
    254        case "15":
    255            return JAVA_15;
    256        case "16":
    257            return JAVA_16;
    258        case "17":
    259            return JAVA_17;
    260        case "18":
    261            return JAVA_18;
    262        case "19":
    263            return JAVA_19;
    264        case "20":
    265            return JAVA_20;
    266        case "21":
    267            return JAVA_21;
    268        case "22":
    269            return JAVA_22;
    270        case "23":
    271            return JAVA_23;
    272        case "24":
    273            return JAVA_24;
    274        case "25":
    275            return JAVA_25;
    276        default:
    277            final float v = toFloatVersion(versionStr);
    278            if (v - 1. < 1.) { // then we need to check decimals > .9
    279                final int firstComma = Math.max(versionStr.indexOf('.'), versionStr.indexOf(','));
    280                final int end = Math.max(versionStr.length(), versionStr.indexOf(',', firstComma));
    281                if (Float.parseFloat(versionStr.substring(firstComma + 1, end)) > .9f) {
    282                    return JAVA_RECENT;
    283                }
    284            } else if (v > 10) {
    285                return JAVA_RECENT;
    286            }
    287            return null;
    288        }
    289    }
    290
    291    /**
    292     * Transforms the given string with a Java version number to the corresponding constant of this enumeration class. This method is used internally.
    293     *
    294     * @param versionStr the Java version as string
    295     * @return the corresponding enumeration constant or <strong>null</strong> if the version is unknown
    296     */
    297    static JavaVersion getJavaVersion(final String versionStr) {
    298        return get(versionStr);
    299    }
    300
    301    /**
    302     * Gets the Java Version from the system or 99.0 if the {@code java.specification.version} system property is not set.
    303     *
    304     * @return the value of {@code java.specification.version} system property or 99.0 if it is not set.
    305     */
    306    private static float maxVersion() {
    307        final float v = toFloatVersion(SystemProperties.getJavaSpecificationVersion("99.0"));
    308        return v > 0 ? v : 99f;
    309    }
    310
    311    static String[] split(final String value) {
    312        return RegExUtils.VERSION_SPLIT_PATTERN.split(value);
    313    }
    314
    315    /**
    316     * Parses a float value from a String.
    317     *
    318     * @param value the String to parse.
    319     * @return the float value represented by the string or -1 if the given String cannot be parsed.
    320     */
    321    private static float toFloatVersion(final String value) {
    322        final int defaultReturnValue = -1;
    323        if (!value.contains(".")) {
    324            return NumberUtils.toFloat(value, defaultReturnValue);
    325        }
    326        final String[] toParse = split(value);
    327        if (toParse.length >= 2) {
    328            return NumberUtils.toFloat(toParse[0] + '.' + toParse[1], defaultReturnValue);
    329        }
    330        return defaultReturnValue;
    331    }
    332
    333    /**
    334     * The float value.
    335     */
    336    private final float value;
    337
    338    /**
    339     * The standard name.
    340     */
    341    private final String name;
    342
    343    /**
    344     * Constructs a new instance.
    345     *
    346     * @param value the float value
    347     * @param name  the standard name, not null
    348     */
    349    JavaVersion(final float value, final String name) {
    350        this.value = value;
    351        this.name = name;
    352    }
    353
    354    /**
    355     * Tests whether this version of Java is at least the version of Java passed in.
    356     *
    357     * <p>
    358     * For example:
    359     * </p>
    360     *
    361     * <pre>
    362     *  {@code
    363     * myVersion.atLeast(JavaVersion.JAVA_1_8)
    364     * }</pre>
    365     *
    366     * @param requiredVersion the version to check against, not null
    367     * @return true if this version is equal to or greater than the specified version
    368     */
    369    public boolean atLeast(final JavaVersion requiredVersion) {
    370        return this.value >= requiredVersion.value;
    371    }
    372
    373    /**
    374     * Tests whether this version of Java is at most the version of Java passed in.
    375     *
    376     * <p>
    377     * For example:
    378     * </p>
    379     *
    380     * <pre>
    381     *  {@code
    382     * myVersion.atMost(JavaVersion.JAVA_1_4)
    383     * }</pre>
    384     *
    385     * @param requiredVersion the version to check against, not null
    386     * @return true if this version is equal to or greater than the specified version
    387     * @since 3.9
    388     */
    389    public boolean atMost(final JavaVersion requiredVersion) {
    390        return this.value <= requiredVersion.value;
    391    }
    392
    393    /**
    394     * The string value is overridden to return the standard name.
    395     *
    396     * <p>
    397     * For example, {@code "1.5"}.
    398     * </p>
    399     *
    400     * @return the name, not null
    401     */
    402    @Override
    403    public String toString() {
    404        return name;
    405    }
    406}