All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.checkerframework.javacutil.SystemUtil Maven / Gradle / Ivy

Go to download

The Checker Framework enhances Java's type system to make it more powerful and useful. This lets software developers detect and prevent errors in their Java programs. The Checker Framework includes compiler plug-ins ("checkers") that find bugs or verify their absence. It also permits you to write your own compiler plug-ins.

There is a newer version: 3.42.0-eisop4
Show newest version
package org.checkerframework.javacutil;

import com.google.common.base.Splitter;
import com.sun.tools.javac.main.Option;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Options;

import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.common.value.qual.IntVal;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.processing.ProcessingEnvironment;

/** This file contains basic utility functions. */
public class SystemUtil {

    /** Do not instantiate. */
    private SystemUtil() {
        throw new AssertionError("Class SystemUtil cannot be instantiated.");
    }

    /** A splitter that splits on periods. The result contains no empty strings. */
    public static final Splitter DOT_SPLITTER = Splitter.on('.').omitEmptyStrings();

    /** A splitter that splits on commas. The result contains no empty strings. */
    public static final Splitter COMMA_SPLITTER = Splitter.on(',').omitEmptyStrings();

    /** A splitter that splits on colons. The result contains no empty strings. */
    public static final Splitter COLON_SPLITTER = Splitter.on(':').omitEmptyStrings();

    /**
     * A splitter that splits on {@code File.pathSeparator}. The result contains no empty strings.
     */
    public static final Splitter PATH_SEPARATOR_SPLITTER =
            Splitter.on(File.pathSeparator).omitEmptyStrings();

    /**
     * Like {@code System.getProperty}, but splits on the path separator and never returns null.
     *
     * @param propName a system property name
     * @return the paths in the system property; may be an empty array
     */
    public static final List getPathsProperty(String propName) {
        String propValue = System.getProperty(propName);
        if (propValue == null) {
            return Collections.emptyList();
        } else {
            return PATH_SEPARATOR_SPLITTER.splitToList(propValue);
        }
    }

    /**
     * Calls {@code InputStream.available()}, but returns null instead of throwing an IOException.
     *
     * @param is an input stream
     * @return {@code is.available()}, or null if that throws an exception
     */
    public static @Nullable Integer available(InputStream is) {
        try {
            return is.available();
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * Returns true if the first {@code readLimit} bytes of the input stream consist only of
     * whitespace.
     *
     * @param is an input stream
     * @param readLimit how many bytes to look ahead in the input stream
     * @return null if {@code !is.markSupported()}; otherwise, true if the first {@code readLimit}
     *     characters of the input stream consist only of whitespace
     */
    public static @Nullable Boolean isWhitespaceOnly(InputStream is, int readLimit) {
        if (!is.markSupported()) {
            return null;
        }
        try {
            is.mark(readLimit * 4); // each character is at most 4 bytes, usually much less
            for (int bytesRead = 0; bytesRead < readLimit; bytesRead++) {
                int nextCodePoint = readCodePoint(is);
                if (nextCodePoint == -1) {
                    return true;
                } else if (Character.isWhitespace(nextCodePoint)) {
                    // do nothing, continue loop
                } else {
                    return false;
                }
            }
            return true;
        } finally {
            try {
                is.reset();
            } catch (IOException e) {
                // Do nothing.
            }
        }
    }

    // From https://stackoverflow.com/a/54513347 .
    /**
     * Reads a Unicode code point from an input stream.
     *
     * @param is an input stream
     * @return the Unicode code point for the next character in the input stream
     */
    public static int readCodePoint(InputStream is) {
        try {
            int nextByte = is.read();
            if (nextByte == -1) {
                return -1;
            }
            byte firstByte = (byte) nextByte;
            int byteCount = getByteCount(firstByte);
            if (byteCount == 1) {
                return nextByte;
            }
            byte[] utf8Bytes = new byte[byteCount];
            utf8Bytes[0] = (byte) nextByte;
            for (int i = 1;
                    i < byteCount;
                    i++) { // Get any subsequent bytes for this UTF-8 character.
                nextByte = is.read();
                utf8Bytes[i] = (byte) nextByte;
            }
            int codePoint = new String(utf8Bytes, StandardCharsets.UTF_8).codePointAt(0);
            return codePoint;
        } catch (IOException e) {
            throw new Error("input stream = " + is, e);
        }
    }

    // From https://stackoverflow.com/a/54513347 .
    /**
     * Returns the number of bytes in a UTF-8 character based on the bit pattern of the supplied
     * byte. The only valid values are 1, 2 3 or 4. If the byte has an invalid bit pattern an
     * IllegalArgumentException is thrown.
     *
     * @param b the first byte of a UTF-8 character
     * @return the number of bytes for this UTF-* character
     * @throws IllegalArgumentException if the bit pattern is invalid
     */
    private static @IntVal({1, 2, 3, 4}) int getByteCount(byte b) throws IllegalArgumentException {
        if ((b >= 0)) return 1; // Pattern is 0xxxxxxx.
        if ((b >= (byte) 0b11000000) && (b <= (byte) 0b11011111)) return 2; // Pattern is 110xxxxx.
        if ((b >= (byte) 0b11100000) && (b <= (byte) 0b11101111)) return 3; // Pattern is 1110xxxx.
        if ((b >= (byte) 0b11110000) && (b <= (byte) 0b11110111)) return 4; // Pattern is 11110xxx.
        throw new IllegalArgumentException(); // Invalid first byte for UTF-8 character.
    }

    /** The major version number of the Java runtime (JRE), such as 8, 11, or 17. */
    @SuppressWarnings(
            "deprecation") // remove @SuppressWarnings when getJreVersion() isn't deprecated
    public static final int jreVersion = getJreVersion();

    // Keep in sync with BCELUtil.java (in the bcel-util project).
    /**
     * Returns the major version number from the "java.version" system property, such as 8, 11, or
     * 17.
     *
     * 

This is different from the version passed to the compiler via {@code --release}; use * {@link #getReleaseValue(ProcessingEnvironment)} to get that version. * *

Two possible formats of the "java.version" system property are considered. Up to Java 8, * from a version string like `1.8.whatever`, this method extracts 8. Since Java 9, from a * version string like `11.0.1`, this method extracts 11. * *

Starting in Java 9, there is the int {@code Runtime.version().feature()}, but that does * not exist on JDK 8. * * @return the major version of the Java runtime * @deprecated use field {@link #jreVersion} instead */ @Deprecated // 2022-07-14 not for removal, just to make private (and then it won't be // deprecated) public static int getJreVersion() { String version = System.getProperty("java.version"); // Up to Java 8, from a version string like "1.8.whatever", extract "8". if (version.startsWith("1.")) { return Integer.parseInt(version.substring(2, 3)); } // Since Java 9, from a version string like "11.0.1" or "11-ea" or "11u25", extract "11". // The format is described at http://openjdk.org/jeps/223 . Pattern newVersionPattern = Pattern.compile("^(\\d+).*$"); Matcher newVersionMatcher = newVersionPattern.matcher(version); if (newVersionMatcher.matches()) { String v = newVersionMatcher.group(1); assert v != null : "@AssumeAssertion(nullness): inspection"; return Integer.parseInt(v); } throw new RuntimeException( "Could not determine version from property java.version=" + version); } /** * Returns the release value passed to the compiler or null if release was not passed. * * @param env the ProcessingEnvironment * @return the release value or null if none was passed */ public static @Nullable String getReleaseValue(ProcessingEnvironment env) { Context ctx = ((JavacProcessingEnvironment) env).getContext(); Options options = Options.instance(ctx); return options.get(Option.RELEASE); } /** * Returns the pathname to the tools.jar file, or null if it does not exist. Returns null on * Java 9 and later. * * @return the pathname to the tools.jar file, or null */ public static @Nullable String getToolsJar() { if (jreVersion > 8) { return null; } String javaHome = System.getenv("JAVA_HOME"); if (javaHome == null) { String javaHomeProperty = System.getProperty("java.home"); if (javaHomeProperty.endsWith(File.separator + "jre")) { javaHome = javaHomeProperty.substring(javaHomeProperty.length() - 4); } else { // Could also determine the location of javac on the path... throw new Error("Can't infer Java home; java.home=" + javaHomeProperty); } } File toolsJarFile = new File(new File(javaHome, "lib"), "tools.jar"); if (!toolsJarFile.exists()) { throw new Error( String.format( "File does not exist: %s ; JAVA_HOME=%s ; java.home=%s", toolsJarFile, javaHome, System.getProperty("java.home"))); } return toolsJarFile.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy