jdk/src/java.base/share/classes/java/lang/Runtime.java
changeset 38432 892603099bb0
parent 37606 e74fdd1e637f
child 39302 aa8d0bc2a6d2
--- a/jdk/src/java.base/share/classes/java/lang/Runtime.java	Thu May 19 11:17:35 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/lang/Runtime.java	Thu May 19 12:04:54 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1995, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,9 +26,21 @@
 package java.lang;
 
 import java.io.*;
+import java.math.BigInteger;
+import java.util.AbstractList;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.RandomAccess;
 import java.util.StringTokenizer;
 import jdk.internal.reflect.CallerSensitive;
 import jdk.internal.reflect.Reflection;
+import sun.security.action.GetPropertyAction;
 
 /**
  * Every Java application has a single instance of class
@@ -46,6 +58,8 @@
 public class Runtime {
     private static final Runtime currentRuntime = new Runtime();
 
+    private static Version version;
+
     /**
      * Returns the runtime object associated with the current Java application.
      * Most of the methods of class {@code Runtime} are instance
@@ -917,4 +931,591 @@
         return out;
     }
 
+    /**
+     * Returns the version of the Java Runtime Environment as a {@link
+     * Runtime.Version}.
+     *
+     * @return  the {@link Runtime.Version} of the Java Runtime Environment
+     *
+     * @since  9
+     */
+    public static Version version() {
+        if (version == null) {
+            version = Version.parse(
+                GetPropertyAction.privilegedGetProperty("java.runtime.version"));
+        }
+        return version;
+    }
+
+    /**
+     * A representation of a version string for an implemenation of the
+     * Java SE Platform.  A version string contains a version number
+     * optionally followed by pre-release and build information.
+     *
+     * <h2><a name="verNum">Version numbers</a></h2>
+     *
+     * <p> A <em>version number</em>, {@code $VNUM}, is a non-empty sequence
+     * of elements separated by period characters (U+002E).  An element is
+     * either zero, or a unsigned integer numeral without leading zeros.  The
+     * final element in a version number must not be zero.  The format is:
+     * </p>
+     *
+     * <blockquote><pre>
+     *     ^[1-9][0-9]*(((\.0)*\.[1-9][0-9]*)*)*$
+     * </pre></blockquote>
+     *
+     * <p> The sequence may be of arbitrary length but the first three
+     * elements are assigned specific meanings, as follows:</p>
+     *
+     * <blockquote><pre>
+     *     $MAJOR.$MINOR.$SECURITY
+     * </pre></blockquote>
+     *
+     * <ul>
+     *
+     * <li><p> <a name="major">{@code $MAJOR}</a> --- The major version
+     * number, incremented for a major release that contains significant new
+     * features as specified in a new edition of the Java&#160;SE Platform
+     * Specification, <em>e.g.</em>, <a
+     * href="https://jcp.org/en/jsr/detail?id=337">JSR 337</a> for
+     * Java&#160;SE&#160;8.  Features may be removed in a major release, given
+     * advance notice at least one major release ahead of time, and
+     * incompatible changes may be made when justified. The {@code $MAJOR}
+     * version number of JDK&#160;8 is {@code 8}; the {@code $MAJOR} version
+     * number of JDK&#160;9 is {@code 9}.  When {@code $MAJOR} is incremented,
+     * all subsequent elements are removed. </p></li>
+     *
+     * <li><p> <a name="minor">{@code $MINOR}</a> --- The minor version
+     * number, incremented for a minor update release that may contain
+     * compatible bug fixes, revisions to standard APIs mandated by a
+     * <a href="https://jcp.org/en/procedures/jcp2#5.3">Maintenance Release</a>
+     * of the relevant Platform Specification, and implementation features
+     * outside the scope of that Specification such as new JDK-specific APIs,
+     * additional service providers, new garbage collectors, and ports to new
+     * hardware architectures. </p></li>
+     *
+     * <li><p> <a name="security">{@code $SECURITY}</a> --- The security
+     * level, incremented for a security update release that contains critical
+     * fixes including those necessary to improve security.  {@code $SECURITY}
+     * is <strong>not</strong> reset when {@code $MINOR} is incremented.  A
+     * higher value of {@code $SECURITY} for a given {@code $MAJOR} value,
+     * therefore, always indicates a more secure release, regardless of the
+     * value of {@code $MINOR}. </p></li>
+     *
+     * </ul>
+     *
+     * <p> The fourth and later elements of a version number are free for use
+     * by downstream consumers of this code base.  Such a consumer may,
+     * <em>e.g.</em>, use the fourth element to identify patch releases which
+     * contain a small number of critical non-security fixes in addition to
+     * the security fixes in the corresponding security release. </p>
+     *
+     * <p> The version number does not include trailing zero elements;
+     * <em>i.e.</em>, {@code $SECURITY} is omitted if it has the value zero,
+     * and {@code $MINOR} is omitted if both {@code $MINOR} and {@code
+     * $SECURITY} have the value zero. </p>
+     *
+     * <p> The sequence of numerals in a version number is compared to another
+     * such sequence in numerical, pointwise fashion; <em>e.g.</em>, {@code
+     * 9.9.1} is less than {@code 9.10.3}. If one sequence is shorter than
+     * another then the missing elements of the shorter sequence are
+     * considered to be less than the corresponding elements of the longer
+     * sequence; <em>e.g.</em>, {@code 9.1.2} is less than {@code 9.1.2.1}.
+     * </p>
+     *
+     * <h2><a name="verStr">Version strings</a></h2>
+     *
+     * <p> A <em>version string</em>, {@code $VSTR}, consists of a version
+     * number {@code $VNUM}, as described above, optionally followed by
+     * pre-release and build information, in the format </p>
+     *
+     * <blockquote><pre>
+     *     $VNUM(-$PRE)?(\+($BUILD)?(-$OPT)?)?
+     * </pre></blockquote>
+     *
+     * <p> where: </p>
+     *
+     * <ul>
+     *
+     * <li><p> <a name="pre">{@code $PRE}</a>, matching {@code ([a-zA-Z0-9]+)}
+     * --- A pre-release identifier.  Typically {@code ea}, for a
+     * potentially unstable early-access release under active development,
+     * or {@code internal}, for an internal developer build.
+     *
+     * <li><p> <a name="build">{@code $BUILD}</a>, matching {@code
+     * (0|[1-9][0-9]*)} --- The build number, incremented for each promoted
+     * build.  {@code $BUILD} is reset to {@code 1} when any portion of {@code
+     * $VNUM} is incremented. </p>
+     *
+     * <li><p> <a name="opt">{@code $OPT}</a>, matching {@code
+     * ([-a-zA-Z0-9\.]+)} --- Additional build information, if desired.  In
+     * the case of an {@code internal} build this will often contain the date
+     * and time of the build. </p>
+     *
+     * </ul>
+     *
+     * <p> A version number {@code 10-ea} matches {@code $VNUM = "10"} and
+     * {@code $PRE = "ea"}.  The version number {@code 10+-ea} matches
+     * {@code $VNUM = "10"} and {@code $OPT = "ea"}. </p>
+     *
+     * <p> When comparing two version strings, the value of {@code $OPT}, if
+     * present, may or may not be significant depending on the chosen
+     * comparison method.  The comparison methods {@link #compareTo(Version)
+     * compareTo()} and {@link #compareToIgnoreOpt(Version)
+     * compareToIgnoreOpt()} should be used consistently with the
+     * corresponding methods {@link #equals(Object) equals()} and {@link
+     * #equalsIgnoreOpt(Object) equalsIgnoreOpt()}.  </p>
+     *
+     * <p> A <em>short version string</em>, {@code $SVSTR}, often useful in
+     * less formal contexts, is a version number optionally followed by a
+     * pre-release identifier:
+     *
+     * <blockquote><pre>
+     *     $VNUM(-$PRE)?
+     * </pre></blockquote>
+     *
+     * @since  9
+     */
+    public static class Version
+        implements Comparable<Version>
+    {
+        private final List<Integer>     version;
+        private final Optional<String>  pre;
+        private final Optional<Integer> build;
+        private final Optional<String>  optional;
+
+
+        // $VNUM(-$PRE)?(\+($BUILD)?(\-$OPT)?)?
+        // RE limits the format of version strings
+        // ([1-9][0-9]*(?:(?:\.0)*\.[1-9][0-9]*)*)(?:-([a-zA-Z0-9]+))?(?:(\+)(0|[1-9][0-9]*)?)?(?:-([-a-zA-Z0-9.]+))?
+
+        private static final String VNUM
+            = "(?<VNUM>[1-9][0-9]*(?:(?:\\.0)*\\.[1-9][0-9]*)*)";
+        private static final String VNUM_GROUP  = "VNUM";
+
+        private static final String PRE      = "(?:-(?<PRE>[a-zA-Z0-9]+))?";
+        private static final String PRE_GROUP   = "PRE";
+
+        private static final String BUILD
+            = "(?:(?<PLUS>\\+)(?<BUILD>0|[1-9][0-9]*)?)?";
+        private static final String PLUS_GROUP  = "PLUS";
+        private static final String BUILD_GROUP = "BUILD";
+
+        private static final String OPT      = "(?:-(?<OPT>[-a-zA-Z0-9.]+))?";
+        private static final String OPT_GROUP   = "OPT";
+
+        private static final String VSTR_FORMAT
+            = "^" + VNUM + PRE + BUILD + OPT + "$";
+        private static final Pattern VSTR_PATTERN = Pattern.compile(VSTR_FORMAT);
+
+        /**
+         * Constructs a valid <a href="verStr">version string</a> containing
+         * a <a href="#verNum">version number</a> followed by pre-release and
+         * build information.
+         *
+         * @param  s
+         *         A string to be interpreted as a version
+         *
+         * @throws  IllegalArgumentException
+         *          If the given string cannot be interpreted as a valid
+         *          version
+         *
+         * @throws  NullPointerException
+         *          If {@code s} is {@code null}
+         *
+         * @throws  NumberFormatException
+         *          If an element of the version number or the build number
+         *          cannot be represented as an {@link Integer}
+         */
+        private Version(String s) {
+            if (s == null)
+                throw new NullPointerException();
+
+            Matcher m = VSTR_PATTERN.matcher(s);
+            if (!m.matches())
+                throw new IllegalArgumentException("Invalid version string: '"
+                                                   + s + "'");
+
+            // $VNUM is a dot-separated list of integers of arbitrary length
+            List<Integer> list = new ArrayList<>();
+            for (String i : m.group(VNUM_GROUP).split("\\."))
+                list.add(Integer.parseInt(i));
+            version = Collections.unmodifiableList(list);
+
+            pre = Optional.ofNullable(m.group(PRE_GROUP));
+
+            String b = m.group(BUILD_GROUP);
+            // $BUILD is an integer
+            build = (b == null)
+                ? Optional.<Integer>empty()
+                : Optional.ofNullable(Integer.parseInt(b));
+
+            optional = Optional.ofNullable(m.group(OPT_GROUP));
+
+            // empty '+'
+            if ((m.group(PLUS_GROUP) != null) && !build.isPresent()) {
+                if (optional.isPresent()) {
+                    if (pre.isPresent())
+                        throw new IllegalArgumentException("'+' found with"
+                            + " pre-release and optional components:'" + s
+                            + "'");
+                } else {
+                    throw new IllegalArgumentException("'+' found with neither"
+                        + " build or optional components: '" + s + "'");
+                }
+            }
+        }
+
+        /**
+         * Parses the given string as a valid
+         * <a href="#verStr">version string</a> containing a
+         * <a href="#verNum">version number</a> followed by pre-release and
+         * build information.
+         *
+         * @param  s
+         *         A string to interpret as a version
+         *
+         * @throws  IllegalArgumentException
+         *          If the given string cannot be interpreted as a valid
+         *          version
+         *
+         * @throws  NullPointerException
+         *          If the given string is {@code null}
+         *
+         * @throws  NumberFormatException
+         *          If an element of the version number or the build number
+         *          cannot be represented as an {@link Integer}
+         *
+         * @return  The Version of the given string
+         */
+        public static Version parse(String s) {
+            return new Version(s);
+        }
+
+        /**
+         * Returns the <a href="#major">major</a> version number.
+         *
+         * @return  The major version number
+         */
+        public int major() {
+            return version.get(0);
+        }
+
+        /**
+         * Returns the <a href="#minor">minor</a> version number or zero if it
+         * was not set.
+         *
+         * @return  The minor version number or zero if it was not set
+         */
+        public int minor() {
+            return (version.size() > 1 ? version.get(1) : 0);
+        }
+
+        /**
+         * Returns the <a href="#security">security</a> version number or zero
+         * if it was not set.
+         *
+         * @return  The security version number or zero if it was not set
+         */
+        public int security() {
+            return (version.size() > 2 ? version.get(2) : 0);
+        }
+
+        /**
+         * Returns an unmodifiable {@link java.util.List List} of the
+         * integer numerals contained in the <a href="#verNum">version
+         * number</a>.  The {@code List} always contains at least one
+         * element corresponding to the <a href="#major">major version
+         * number</a>.
+         *
+         * @return  An unmodifiable list of the integer numerals
+         *          contained in the version number
+         */
+        public List<Integer> version() {
+            return version;
+        }
+
+        /**
+         * Returns the optional <a href="#pre">pre-release</a> information.
+         *
+         * @return  The optional pre-release information as a String
+         */
+        public Optional<String> pre() {
+            return pre;
+        }
+
+        /**
+         * Returns the <a href="#build">build number</a>.
+         *
+         * @return  The optional build number.
+         */
+        public Optional<Integer> build() {
+            return build;
+        }
+
+        /**
+         * Returns <a href="#opt">optional</a> additional identifying build
+         * information.
+         *
+         * @return  Additional build information as a String
+         */
+        public Optional<String> optional() {
+            return optional;
+        }
+
+        /**
+         * Compares this version to another.
+         *
+         * <p> Each of the components in the <a href="#verStr">version</a> is
+         * compared in the follow order of precedence: version numbers,
+         * pre-release identifiers, build numbers, optional build information.
+         * </p>
+         *
+         * <p> Comparison begins by examining the sequence of version numbers.
+         * If one sequence is shorter than another, then the missing elements
+         * of the shorter sequence are considered to be less than the
+         * corresponding elements of the longer sequence. </p>
+         *
+         * <p> A version with a pre-release identifier is always considered to
+         * be less than a version without one.  Pre-release identifiers are
+         * compared numerically when they consist only of digits, and
+         * lexicographically otherwise.  Numeric identifiers are considered to
+         * be less than non-numeric identifiers.  </p>
+         *
+         * <p> A version without a build number is always less than one with a
+         * build number; otherwise build numbers are compared numerically. </p>
+         *
+         * <p> The optional build information is compared lexicographically.
+         * During this comparison, a version with optional build information is
+         * considered to be greater than a version without one. </p>
+         *
+         * <p> A version is not comparable to any other type of object.
+         *
+         * @param  ob
+         *         The object to be compared
+         *
+         * @return  A negative integer, zero, or a positive integer if this
+         *          {@code Version} is less than, equal to, or greater than the
+         *          given {@code Version}
+         *
+         * @throws  NullPointerException
+         *          If the given object is {@code null}
+         */
+        @Override
+        public int compareTo(Version ob) {
+            return compare(ob, false);
+        }
+
+        /**
+         * Compares this version to another disregarding optional build
+         * information.
+         *
+         * <p> Two versions are compared by examining the version string as
+         * described in {@link #compareTo(Version)} with the exception that the
+         * optional build information is always ignored. </p>
+         *
+         * <p> A version is not comparable to any other type of object.
+         *
+         * @param  ob
+         *         The object to be compared
+         *
+         * @return  A negative integer, zero, or a positive integer if this
+         *          {@code Version} is less than, equal to, or greater than the
+         *          given {@code Version}
+         *
+         * @throws  NullPointerException
+         *          If the given object is {@code null}
+         */
+        public int compareToIgnoreOpt(Version ob) {
+            return compare(ob, true);
+        }
+
+        private int compare(Version ob, boolean ignoreOpt) {
+            if (ob == null)
+                throw new NullPointerException("Invalid argument");
+
+            int ret = compareVersion(ob);
+            if (ret != 0)
+                return ret;
+
+            ret = comparePre(ob);
+            if (ret != 0)
+                return ret;
+
+            ret = compareBuild(ob);
+            if (ret != 0)
+                return ret;
+
+            if (!ignoreOpt)
+                return compareOpt(ob);
+
+            return 0;
+        }
+
+        private int compareVersion(Version ob) {
+            int size = version.size();
+            int oSize = ob.version().size();
+            int min = Math.min(size, oSize);
+            for (int i = 0; i < min; i++) {
+                Integer val = version.get(i);
+                Integer oVal = ob.version().get(i);
+                if (val != oVal)
+                    return val - oVal;
+            }
+            if (size != oSize)
+                return size - oSize;
+            return 0;
+        }
+
+        private int comparePre(Version ob) {
+            Optional<String> oPre = ob.pre();
+            if (!pre.isPresent()) {
+                if (oPre.isPresent())
+                    return 1;
+            } else {
+                if (!oPre.isPresent())
+                    return -1;
+                String val = pre.get();
+                String oVal = oPre.get();
+                if (val.matches("\\d+")) {
+                    return (oVal.matches("\\d+")
+                        ? (new BigInteger(val)).compareTo(new BigInteger(oVal))
+                        : -1);
+                } else {
+                    return (oVal.matches("\\d+")
+                        ? 1
+                        : val.compareTo(oVal));
+                }
+            }
+            return 0;
+        }
+
+        private int compareBuild(Version ob) {
+            Optional<Integer> oBuild = ob.build();
+            if (oBuild.isPresent()) {
+                return (build.isPresent()
+                        ? build.get().compareTo(oBuild.get())
+                        : 1);
+            } else if (build.isPresent()) {
+                return -1;
+            }
+            return 0;
+        }
+
+        private int compareOpt(Version ob) {
+            Optional<String> oOpt = ob.optional();
+            if (!optional.isPresent()) {
+                if (oOpt.isPresent())
+                    return -1;
+            } else {
+                if (!oOpt.isPresent())
+                    return 1;
+                return optional.get().compareTo(oOpt.get());
+            }
+            return 0;
+        }
+
+        /**
+         * Returns a string representation of this version.
+         *
+         * @return  The version string
+         */
+        @Override
+        public String toString() {
+            StringBuilder sb
+                = new StringBuilder(version.stream()
+                    .map(Object::toString)
+                    .collect(Collectors.joining(".")));
+
+            pre.ifPresent(v -> sb.append("-").append(v));
+
+            if (build.isPresent()) {
+                sb.append("+").append(build.get());
+                if (optional.isPresent())
+                    sb.append("-").append(optional.get());
+            } else {
+                if (optional.isPresent()) {
+                    sb.append(pre.isPresent() ? "-" : "+-");
+                    sb.append(optional.get());
+                }
+            }
+
+            return sb.toString();
+        }
+
+        /**
+         * Determines whether this {@code Version} is equal to another object.
+         *
+         * <p> Two {@code Version}s are equal if and only if they represent the
+         * same version string.
+         *
+         * <p> This method satisfies the general contract of the {@link
+         * Object#equals(Object) Object.equals} method. </p>
+         *
+         * @param  ob
+         *         The object to which this {@code Version} is to be compared
+         *
+         * @return  {@code true} if, and only if, the given object is a {@code
+         *          Version} that is identical to this {@code Version}
+         *
+         */
+        @Override
+        public boolean equals(Object ob) {
+            boolean ret = equalsIgnoreOpt(ob);
+            if (!ret)
+                return false;
+
+            Version that = (Version)ob;
+            return (this.optional().equals(that.optional()));
+        }
+
+        /**
+         * Determines whether this {@code Version} is equal to another
+         * disregarding optional build information.
+         *
+         * <p> Two {@code Version}s are equal if and only if they represent the
+         * same version string disregarding the optional build information.
+         *
+         * @param  ob
+         *         The object to which this {@code Version} is to be compared
+         *
+         * @return  {@code true} if, and only if, the given object is a {@code
+         *          Version} that is identical to this {@code Version}
+         *          ignoring the optinal build information
+         *
+         */
+        public boolean equalsIgnoreOpt(Object ob) {
+            if (this == ob)
+                return true;
+            if (!(ob instanceof Version))
+                return false;
+
+            Version that = (Version)ob;
+            return (this.version().equals(that.version())
+                && this.pre().equals(that.pre())
+                && this.build().equals(that.build()));
+        }
+
+        /**
+         * Returns the hash code of this version.
+         *
+         * <p> This method satisfies the general contract of the {@link
+         * Object#hashCode Object.hashCode} method.
+         *
+         * @return  The hashcode of this version
+         */
+        @Override
+        public int hashCode() {
+            int h = 1;
+            int p = 17;
+
+            h = p * h + version.hashCode();
+            h = p * h + pre.hashCode();
+            h = p * h + build.hashCode();
+            h = p * h + optional.hashCode();
+
+            return h;
+        }
+    }
+
 }