1/*
2 * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package java.lang;
27
28import java.io.PrintStream;
29import java.util.Arrays;
30import java.util.List;
31import java.util.Map;
32import java.util.Optional;
33
34class VersionProps {
35
36    private static final String launcher_name =
37        "@@LAUNCHER_NAME@@";
38
39    private static final String java_version =
40        "@@VERSION_SHORT@@";
41
42    private static final String java_version_date =
43        "@@VERSION_DATE@@";
44
45    // This field is read by HotSpot
46    private static final String java_runtime_name =
47        "@@RUNTIME_NAME@@";
48
49    // This field is read by HotSpot
50    private static final String java_runtime_version =
51        "@@VERSION_STRING@@";
52
53    private static final String VERSION_NUMBER =
54        "@@VERSION_NUMBER@@";
55
56    private static final String VERSION_SPECIFICATION =
57        "@@VERSION_SPECIFICATION@@";
58
59    private static final String VERSION_BUILD =
60        "@@VERSION_BUILD@@";
61
62    private static final String VERSION_PRE =
63        "@@VERSION_PRE@@";
64
65    private static final String VERSION_OPT =
66        "@@VERSION_OPT@@";
67
68    private static final boolean isLTS =
69        "@@VERSION_OPT@@".startsWith("LTS");
70
71    private static final String CLASSFILE_MAJOR_MINOR =
72        "@@VERSION_CLASSFILE_MAJOR@@.@@VERSION_CLASSFILE_MINOR@@";
73
74    private static final String VENDOR =
75        "@@VENDOR@@";
76
77    private static final String VENDOR_URL =
78        "@@VENDOR_URL@@";
79
80    // The remaining VENDOR_* fields must not be final,
81    // so that they can be redefined by jlink plugins
82
83    // This field is read by HotSpot
84    private static String VENDOR_VERSION =
85        "@@VENDOR_VERSION_STRING@@";
86
87    private static String VENDOR_URL_BUG =
88        "@@VENDOR_URL_BUG@@";
89
90    // This field is read by HotSpot
91    private static String VENDOR_URL_VM_BUG =
92        "@@VENDOR_URL_VM_BUG@@";
93
94    /**
95     * Initialize system properties using build provided values.
96     *
97     * @param props Map instance in which to insert the properties
98     */
99    public static void init(Map<String, String> props) {
100        props.put("java.version", java_version);
101        props.put("java.version.date", java_version_date);
102        props.put("java.runtime.version", java_runtime_version);
103        props.put("java.runtime.name", java_runtime_name);
104        if (!VENDOR_VERSION.isEmpty())
105            props.put("java.vendor.version", VENDOR_VERSION);
106
107        props.put("java.class.version", CLASSFILE_MAJOR_MINOR);
108
109        props.put("java.specification.version", VERSION_SPECIFICATION);
110        props.put("java.specification.name", "Java Platform API Specification");
111        props.put("java.specification.vendor", "Oracle Corporation");
112
113        props.put("java.vendor", VENDOR);
114        props.put("java.vendor.url", VENDOR_URL);
115        props.put("java.vendor.url.bug", VENDOR_URL_BUG);
116    }
117
118    private static int parseVersionNumber(String version, int prevIndex, int index) {
119        if (index - prevIndex > 1 &&
120            Character.digit(version.charAt(prevIndex), 10) <= 0)
121            throw new IllegalArgumentException("Leading zeros not supported (" +
122                    version.substring(prevIndex, index) + ")");
123        return Integer.parseInt(version, prevIndex, index, 10);
124    }
125
126    // This method is reflectively used by regression tests.
127    static List<Integer> parseVersionNumbers(String version) {
128        // Let's find the size of an array required to hold $VNUM components
129        int size = 0;
130        int prevIndex = 0;
131        do {
132            prevIndex = version.indexOf('.', prevIndex) + 1;
133            size++;
134        } while (prevIndex > 0);
135        Integer[] verNumbers = new Integer[size];
136
137        // Fill in the array with $VNUM components
138        int n = 0;
139        prevIndex = 0;
140        int index = version.indexOf('.');
141        while (index > -1) {
142            verNumbers[n] = parseVersionNumber(version, prevIndex, index);
143            prevIndex = index + 1; // Skip the period
144            index = version.indexOf('.', prevIndex);
145            n++;
146        }
147        verNumbers[n] = parseVersionNumber(version, prevIndex, version.length());
148
149        if (verNumbers[0] == 0 || verNumbers[n] == 0)
150            throw new IllegalArgumentException("Leading/trailing zeros not allowed (" +
151                    Arrays.toString(verNumbers) + ")");
152
153        return List.of(verNumbers);
154    }
155
156    static List<Integer> versionNumbers() {
157        return parseVersionNumbers(VERSION_NUMBER);
158    }
159
160    static Optional<String> pre() {
161        return optionalOf(VERSION_PRE);
162    }
163
164    static Optional<Integer> build() {
165        return VERSION_BUILD.isEmpty() ?
166                Optional.empty() :
167                Optional.of(Integer.parseInt(VERSION_BUILD));
168    }
169
170    static Optional<String> optional() {
171        return optionalOf(VERSION_OPT);
172    }
173
174    // Treat empty strings as value not being present
175    private static Optional<String> optionalOf(String value) {
176        if (!value.isEmpty()) {
177            return Optional.of(value);
178        } else {
179            return Optional.empty();
180        }
181    }
182
183    /**
184     * In case you were wondering this method is called by java -version.
185     */
186    public static void print(boolean err) {
187        print(err, false);
188    }
189
190    /**
191     * This is the same as print except that it adds an extra line-feed
192     * at the end, typically used by the -showversion in the launcher
193     */
194    public static void println(boolean err) {
195        print(err, true);
196    }
197
198    /**
199     * Print version info.
200     */
201    private static void print(boolean err, boolean newln) {
202        PrintStream ps = err ? System.err : System.out;
203
204        /* First line: platform version. */
205        if (err) {
206            ps.println(launcher_name + " version \"" + java_version + "\""
207                       + " " + java_version_date
208                       + (isLTS ? " LTS" : ""));
209        } else {
210            /* Use a format more in line with GNU conventions */
211            ps.println(launcher_name + " " + java_version
212                       + " " + java_version_date
213                       + (isLTS ? " LTS" : ""));
214        }
215
216        /* Second line: runtime version (ie, libraries). */
217        String jdk_debug_level = System.getProperty("jdk.debug", "release");
218        if ("release".equals(jdk_debug_level)) {
219           /* Do not show debug level "release" builds */
220            jdk_debug_level = "";
221        } else {
222            jdk_debug_level = jdk_debug_level + " ";
223        }
224
225        String vendor_version = (VENDOR_VERSION.isEmpty()
226                                 ? "" : " " + VENDOR_VERSION);
227
228        ps.println(java_runtime_name + vendor_version
229                   + " (" + jdk_debug_level + "build " + java_runtime_version + ")");
230
231        /* Third line: JVM information. */
232        String java_vm_name    = System.getProperty("java.vm.name");
233        String java_vm_version = System.getProperty("java.vm.version");
234        String java_vm_info    = System.getProperty("java.vm.info");
235        ps.println(java_vm_name + vendor_version
236                   + " (" + jdk_debug_level + "build " + java_vm_version + ", "
237                            + java_vm_info + ")");
238
239    }
240
241}
242