    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 * An enum representing all the versions of the Java specification.
    023 * This is intended to mirror available values from the
    024 * <em>java.specification.version</em> System property.
    025 *
    026 * @since 3.0
    027 */
    028public enum JavaVersion {
    029
    030    /**
    031     * The Java version reported by Android. This is not an official Java version number.
    032     */
    033    JAVA_0_9(1.5f, "0.9"),
    034
    035    /**
    036     * Java 1.1.
    037     */
    038    JAVA_1_1(1.1f, "1.1"),
    039
    040    /**
    041     * Java 1.2.
    042     */
    043    JAVA_1_2(1.2f, "1.2"),
    044
    045    /**
    046     * Java 1.3.
    047     */
    048    JAVA_1_3(1.3f, "1.3"),
    049
    050    /**
    051     * Java 1.4.
    052     */
    053    JAVA_1_4(1.4f, "1.4"),
    054
    055    /**
    056     * Java 1.5.
    057     */
    058    JAVA_1_5(1.5f, "1.5"),
    059
    060    /**
    061     * Java 1.6.
    062     */
    063    JAVA_1_6(1.6f, "1.6"),
    064
    065    /**
    066     * Java 1.7.
    067     */
    068    JAVA_1_7(1.7f, "1.7"),
    069
    070    /**
    071     * Java 1.8.
    072     */
    073    JAVA_1_8(1.8f, "1.8"),
    074
    075    /**
    076     * Java 1.9.
    077     *
    078     * @deprecated As of release 3.5, replaced by {@link #JAVA_9}
    079     */
    080    @Deprecated
    081    JAVA_1_9(9.0f, "9"),
    082
    083    /**
    084     * Java 9.
    085     *
    086     * @since 3.5
    087     */
    088    JAVA_9(9.0f, "9"),
    089
    090    /**
    091     * Java 10.
    092     *
    093     * @since 3.7
    094     */
    095    JAVA_10(10.0f, "10"),
    096
    097    /**
    098     * Java 11.
    099     *
    100     * @since 3.8
    101     */
    102    JAVA_11(11.0f, "11"),
    103
    104    /**
    105     * Java 12.
    106     *
    107     * @since 3.9
    108     */
    109    JAVA_12(12.0f, "12"),
    110
    111    /**
    112     * Java 13.
    113     *
    114     * @since 3.9
    115     */
    116    JAVA_13(13.0f, "13"),
    117
    118    /**
    119     * Java 14.
    120     *
    121     * @since 3.11
    122     */
    123    JAVA_14(14.0f, "14"),
    124
    125    /**
    126     * Java 15.
    127     *
    128     * @since 3.11
    129     */
    130    JAVA_15(15.0f, "15"),
    131
    132    /**
    133     * Java 16.
    134     *
    135     * @since 3.11
    136     */
    137    JAVA_16(16.0f, "16"),
    138
    139    /**
    140     * Java 17.
    141     *
    142     * @since 3.12.0
    143     */
    144    JAVA_17(17.0f, "17"),
    145
    146    /**
    147     * Java 18.
    148     *
    149     * @since 3.13.0
    150     */
    151    JAVA_18(18.0f, "18"),
    152
    153    /**
    154     * Java 19.
    155     *
    156     * @since 3.13.0
    157     */
    158    JAVA_19(19.0f, "19"),
    159
    160    /**
    161     * Java 20.
    162     *
    163     * @since 3.13.0
    164     */
    165    JAVA_20(20, "20"),
    166
    167    /**
    168     * Java 21.
    169     *
    170     * @since 3.13.0
    171     */
    172    JAVA_21(21, "21"),
    173
    174    /**
    175     * Java 22.
    176     *
    177     * @since 3.15.0
    178     */
    179    JAVA_22(22, "22"),
    180
    181    /**
    182     * Java 23.
    183     *
    184     * @since 3.18.0
    185     */
    186    JAVA_23(23, "23"),
    187
    188    /**
    189     * Java 24.
    190     *
    191     * @since 3.18.0
    192     */
    193    JAVA_24(24, "24"),
    194
    195    /**
    196     * The most recent Java version. Mainly introduced to avoid to break when a new version of Java is used.
    197     */
    198    JAVA_RECENT(maxVersion(), Float.toString(maxVersion()));
    199
    200    /**
    201     * The regex to split version strings.
    202     */
    203    private static final String VERSION_SPLIT_REGEX = "\\.";
    204
    205    /**
    206     * Transforms the given string with a Java version number to the
    207     * corresponding constant of this enumeration class. This method is used
    208     * internally.
    209     *
    210     * @param versionStr the Java version as string
    211     * @return the corresponding enumeration constant or <strong>null</strong> if the
    212     * version is unknown
    213     */
    214    static JavaVersion get(final String versionStr) {
    215        if (versionStr == null) {
    216            return null;
    217        }
    218        switch (versionStr) {
    219        case "0.9":
    220            return JAVA_0_9;
    221        case "1.1":
    222            return JAVA_1_1;
    223        case "1.2":
    224            return JAVA_1_2;
    225        case "1.3":
    226            return JAVA_1_3;
    227        case "1.4":
    228            return JAVA_1_4;
    229        case "1.5":
    230            return JAVA_1_5;
    231        case "1.6":
    232            return JAVA_1_6;
    233        case "1.7":
    234            return JAVA_1_7;
    235        case "1.8":
    236            return JAVA_1_8;
    237        case "9":
    238            return JAVA_9;
    239        case "10":
    240            return JAVA_10;
    241        case "11":
    242            return JAVA_11;
    243        case "12":
    244            return JAVA_12;
    245        case "13":
    246            return JAVA_13;
    247        case "14":
    248            return JAVA_14;
    249        case "15":
    250            return JAVA_15;
    251        case "16":
    252            return JAVA_16;
    253        case "17":
    254            return JAVA_17;
    255        case "18":
    256            return JAVA_18;
    257        case "19":
    258            return JAVA_19;
    259        case "20":
    260            return JAVA_20;
    261        case "21":
    262            return JAVA_21;
    263        case "22":
    264            return JAVA_22;
    265        case "23":
    266            return JAVA_23;
    267        case "24":
    268            return JAVA_24;
    269        default:
    270            final float v = toFloatVersion(versionStr);
    271            if (v - 1. < 1.) { // then we need to check decimals > .9
    272                final int firstComma = Math.max(versionStr.indexOf('.'), versionStr.indexOf(','));
    273                final int end = Math.max(versionStr.length(), versionStr.indexOf(',', firstComma));
    274                if (Float.parseFloat(versionStr.substring(firstComma + 1, end)) > .9f) {
    275                    return JAVA_RECENT;
    276                }
    277            } else if (v > 10) {
    278                return JAVA_RECENT;
    279            }
    280            return null;
    281        }
    282    }
    283
    284    /**
    285     * Transforms the given string with a Java version number to the
    286     * corresponding constant of this enumeration class. This method is used
    287     * internally.
    288     *
    289     * @param versionStr the Java version as string
    290     * @return the corresponding enumeration constant or <strong>null</strong> if the
    291     * version is unknown
    292     */
    293    static JavaVersion getJavaVersion(final String versionStr) {
    294        return get(versionStr);
    295    }
    296
    297    /**
    298     * Gets the Java Version from the system or 99.0 if the {@code java.specification.version} system property is not set.
    299     *
    300     * @return the value of {@code java.specification.version} system property or 99.0 if it is not set.
    301     */
    302    private static float maxVersion() {
    303        final float v = toFloatVersion(SystemProperties.getJavaSpecificationVersion("99.0"));
    304        return v > 0 ? v : 99f;
    305    }
    306
    307    static String[] split(final String value) {
    308        return value.split(VERSION_SPLIT_REGEX);
    309    }
    310
    311    /**
    312     * Parses a float value from a String.
    313     *
    314     * @param value the String to parse.
    315     * @return the float value represented by the string or -1 if the given String cannot be parsed.
    316     */
    317    private static float toFloatVersion(final String value) {
    318        final int defaultReturnValue = -1;
    319        if (!value.contains(".")) {
    320            return NumberUtils.toFloat(value, defaultReturnValue);
    321        }
    322        final String[] toParse = split(value);
    323        if (toParse.length >= 2) {
    324            return NumberUtils.toFloat(toParse[0] + '.' + toParse[1], defaultReturnValue);
    325        }
    326        return defaultReturnValue;
    327    }
    328
    329    /**
    330     * The float value.
    331     */
    332    private final float value;
    333
    334    /**
    335     * The standard name.
    336     */
    337    private final String name;
    338
    339    /**
    340     * Constructs a new instance.
    341     *
    342     * @param value  the float value
    343     * @param name  the standard name, not null
    344     */
    345    JavaVersion(final float value, final String name) {
    346        this.value = value;
    347        this.name = name;
    348    }
    349
    350    /**
    351     * Tests whether this version of Java is at least the version of Java passed in.
    352     *
    353     * <p>For example:<br>
    354     *  {@code myVersion.atLeast(JavaVersion.JAVA_1_4)}</p>
    355     *
    356     * @param requiredVersion  the version to check against, not null
    357     * @return true if this version is equal to or greater than the specified version
    358     */
    359    public boolean atLeast(final JavaVersion requiredVersion) {
    360        return this.value >= requiredVersion.value;
    361    }
    362
    363    /**
    364     * Tests whether this version of Java is at most the version of Java passed in.
    365     *
    366     * <p>For example:<br>
    367     *  {@code myVersion.atMost(JavaVersion.JAVA_1_4)}</p>
    368     *
    369     * @param requiredVersion  the version to check against, not null
    370     * @return true if this version is equal to or greater than the specified version
    371     * @since 3.9
    372     */
    373    public boolean atMost(final JavaVersion requiredVersion) {
    374        return this.value <= requiredVersion.value;
    375    }
    376
    377    /**
    378     * The string value is overridden to return the standard name.
    379     *
    380     * <p>For example, {@code "1.5"}.</p>
    381     *
    382     * @return the name, not null
    383     */
    384    @Override
    385    public String toString() {
    386        return name;
    387    }
    388}