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

com.thomasjensen.checkstyle.addons.util.Util Maven / Gradle / Ivy

The newest version!
package com.thomasjensen.checkstyle.addons.util;
/*
 * Checkstyle-Addons - Additional Checkstyle checks
 * Copyright (c) 2015-2022, the Checkstyle Addons contributors
 *
 * This program is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License, version 3, as published by the Free
 * Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this
 * program.  If not, see .
 */

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;


/**
 * Utility class.
 */
public final class Util
{
    /** A pattern that never matches (and does so efficiently) */
    public static final Pattern NEVER_MATCH = Pattern.compile("^(?!x)x");

    /** Size of the file I/O buffer in bytes */
    private static final int IO_BUFFER_SIZE_BYTES = 8000;



    private Util()
    {
        super();
    }



    /**
     * Determine the text of the first direct IDENT child node.
     *
     * @param pAst an AST
     * @return the first encountered IDENT text, or null if none was found
     */
    @CheckForNull
    public static String getFirstIdent(@Nonnull final DetailAST pAst)
    {
        String result = null;
        DetailAST ast = pAst.findFirstToken(TokenTypes.IDENT);
        if (ast != null) {
            result = ast.getText();
        }
        return result;
    }



    /**
     * Determine the full identifier of the current element on the AST. The identifier is built from DOT and IDENT
     * elements found directly below the specified element. Other elements encountered are ignored.
     *
     * @param pAst an AST
     * @return the full identifier constructed from either the first encountered IDENT or DOT; null if no
     * identifier could be constructed
     */
    @CheckForNull
    public static String getFullIdent(@Nonnull final DetailAST pAst)
    {
        String result = null;
        DetailAST ast = checkTokens(pAst.getFirstChild(), TokenTypes.DOT, TokenTypes.IDENT);
        if (ast != null) {
            StringBuilder sb = new StringBuilder();
            if (getFullIdentInternal(ast, sb)) {
                result = sb.toString();
            }
        }
        return result;
    }



    private static boolean getFullIdentInternal(@Nonnull final DetailAST pDotOrIdent, @Nonnull final StringBuilder pSb)
    {
        // pDotOrIdent was an IDENT
        if (pDotOrIdent.getType() == TokenTypes.IDENT) {
            pSb.append(pDotOrIdent.getText());
            return true;
        }

        // pDotOrIdent was a DOT
        DetailAST ast = checkTokens(pDotOrIdent.getFirstChild(), TokenTypes.DOT, TokenTypes.IDENT);
        if (ast != null) {
            getFullIdentInternal(ast, pSb);
            pSb.append('.');
            ast = checkTokens(ast.getNextSibling(), TokenTypes.IDENT);
            if (ast != null) {
                getFullIdentInternal(ast, pSb);
                return true;
            }
        }
        return false;
    }



    @CheckForNull
    private static DetailAST checkTokens(@Nonnull final DetailAST pAst, @Nonnull final int... pTokens)
    {
        for (DetailAST ast = pAst; ast != null; ast = ast.getNextSibling()) {
            for (int token : pTokens) {
                if (token == ast.getType()) {
                    return ast;
                }
            }
        }
        return null;
    }



    /**
     * Quietly close the given resource, ignoring any exceptions.
     *
     * @param pCloseable the resource to close
     */
    public static void closeQuietly(@Nullable final Closeable pCloseable)
    {
        if (pCloseable != null) {
            try {
                pCloseable.close();
            }
            catch (IOException e) {
                // ignore
            }
        }
    }



    /**
     * Read all bytes from an InputStream into a byte array. Can be replaced with Files.readAllBytes() once
     * the code is migrated to Java 7.
     *
     * @param pInputStream the input stream
     * @return the complete contents read from the input stream
     *
     * @throws IOException I/O error
     */
    public static byte[] readBytes(@Nonnull final InputStream pInputStream)
        throws IOException
    {
        ByteArrayOutputStream baos = null;
        BufferedInputStream bis = null;
        try {
            baos = new ByteArrayOutputStream(IO_BUFFER_SIZE_BYTES);
            bis = new BufferedInputStream(pInputStream, IO_BUFFER_SIZE_BYTES);
            byte[] buffer = new byte[IO_BUFFER_SIZE_BYTES];
            for (int bytesRead = bis.read(buffer); bytesRead > 0; bytesRead = bis.read(buffer)) {
                baos.write(buffer, 0, bytesRead);
            }
        }
        finally {
            closeQuietly(bis);
            closeQuietly(baos);
        }

        return baos.toByteArray();
    }



    /**
     * Calls getCanonicalFile() on the given File; if that doesn't work, call getAbsoluteFile() on it. Thus, the
     * resulting file is not guaranteed to exist. Separator characters are standardized to {@link File#separatorChar}.
     *
     * @param pFile a file
     * @return the canonical representation, or the absolute representation
     */
    @Nonnull
    public static File canonize(@Nonnull final File pFile)
    {
        String resultPath = null;
        try {
            resultPath = pFile.getCanonicalPath();
        }
        catch (IOException e) {
            resultPath = pFile.getAbsolutePath();
        }
        return new File(standardizeSlashes(resultPath));
    }



    /**
     * Standardizes all slashes and backslashes in the given String to {@link File#separatorChar}.
     *
     * @param pPath a String
     * @return a new String which is pPath with standardized path separator characters
     */
    @Nonnull
    public static String standardizeSlashes(@Nonnull final String pPath)
    {
        final String goodSlash = File.separator;
        final String badSlash = File.separatorChar == '/' ? "\\" : "/";
        // The JDK also assumes there are only these two options.
        String result = pPath.replaceAll(Pattern.quote(badSlash), Matcher.quoteReplacement(goodSlash));
        return result;
    }



    /**
     * Search for a String in a collection.
     *
     * @param pIterable the list of String to be searched
     * @param pSearched the String to search for
     * @param pCaseSensitive if comparisons should be case sensitive
     * @return true if found, false otherwise
     */
    public static boolean containsString(@Nullable final Iterable pIterable, @Nullable final String pSearched,
        final boolean pCaseSensitive)
    {
        boolean result = false;
        if (pSearched != null && pIterable != null) {
            for (String s : pIterable) {
                if (stringEquals(pSearched, s, pCaseSensitive)) {
                    result = true;
                    break;
                }
            }
        }
        return result;
    }



    /**
     * Determine the equality of two Strings, optionally ignoring case.
     *
     * @param pStr1 the first String
     * @param pStr2 the second String
     * @param pCaseSensitive if the comparison should be case sensitive
     * @return true if the String are equal, false otherwise
     */
    public static boolean stringEquals(@Nullable final String pStr1, @Nullable final String pStr2,
        final boolean pCaseSensitive)
    {
        boolean result = false;
        if (pStr1 == null && pStr2 == null) {
            result = true;
        }
        else if (pStr1 != null) {
            if (pCaseSensitive) {
                result = pStr1.equals(pStr2);
            }
            else {
                result = pStr1.equalsIgnoreCase(pStr2);
            }
        }
        return result;
    }



    /**
     * Creates a new immutable {@link HashSet} which contains a union of the two given sets.
     *
     * @param pColl1 first set
     * @param pColl2 second set
     * @param  type of all contained elements
     * @return union set
     */
    @Nonnull
    public static  Set union(@Nullable final Set pColl1, @Nullable final Set pColl2)
    {
        final Set result = new HashSet<>();
        if (pColl1 != null) {
            result.addAll(pColl1);
        }
        if (pColl2 != null) {
            result.addAll(pColl2);
        }
        return Collections.unmodifiableSet(result);
    }



    /**
     * Find the left-most token in the given AST. The left-most token is the token with the smallest column number. Only
     * tokens which are located on the same line as the given AST are considered.
     *
     * @param pAst the root of a subtree. This token is also considered for the result.
     * @return the left-most token
     */
    @Nonnull
    public static DetailAST findLeftMostTokenInLine(@Nonnull final DetailAST pAst)
    {
        return findLeftMostTokenInLineInternal(pAst, pAst.getLineNo(), pAst.getColumnNo());
    }



    @Nonnull
    private static DetailAST findLeftMostTokenInLineInternal(@Nonnull final DetailAST pAst, final int pLine,
        final int pColumn)
    {
        DetailAST result = pAst;
        int col = pColumn;
        for (DetailAST ast = pAst.getFirstChild(); ast != null; ast = ast.getNextSibling()) {
            if (ast.getLineNo() > pLine) {
                break;
            }
            int currentCol = ast.getColumnNo();
            if (currentCol < col) {
                col = currentCol;
                result = ast;
            }

            DetailAST subTree = findLeftMostTokenInLineInternal(ast, pLine, col);
            currentCol = subTree.getColumnNo();
            if (currentCol < col) {
                col = currentCol;
                result = subTree;
            }
        }
        return result;
    }



    /**
     * Variant of {@link Enum#valueOf} that ignores value case.
     *
     * @param pValue the String value
     * @param pEnumClass the class object of the enum type
     * @param  the enum type
     * @return the enum value
     *
     * @throws IllegalArgumentException the given String value does not match a valid enum value
     */
    @Nonnull
    public static > E valueOfIgnoreCase(@Nonnull final String pValue,
        @Nonnull final Class pEnumClass)
    {
        for (E e : pEnumClass.getEnumConstants()) {
            if (e.name().equalsIgnoreCase(pValue)) {
                return e;
            }
        }
        throw new IllegalArgumentException("illegal value for " + pEnumClass.getSimpleName() + ": " + pValue);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy