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

com.thomasjensen.checkstyle.addons.util.CheckstyleApiFixer 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.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
import com.puppycrawl.tools.checkstyle.api.FileContents;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;


/**
 * The public API of the Checkstyle tool changes frequently, and many releases of Checkstyle are incompatible with their
 * predecessors. This class uses reflection in order to allow the Checkstyle Addons code to work with all dependency
 * configurations. It simply provides a stable interface with workarounds for all supported Checkstyle versions.
 */
public class CheckstyleApiFixer
{
    private final AbstractCheck check;

    private final File currentFileNameMockFile;



    /**
     * Constructor.
     *
     * @param pCheck the check for which this instance of the API fixer shall run
     */
    public CheckstyleApiFixer(@Nonnull final AbstractCheck pCheck)
    {
        this(pCheck, null);
    }



    /**
     * Constructor.
     *
     * @param pCheck the check for which this instance of the API fixer shall run
     * @param pCurrentFileNameMockFile file to use as result of {@link #getCurrentFileName()} in unit tests
     */
    @SuppressFBWarnings("EI_EXPOSE_REP2")
    public CheckstyleApiFixer(@Nonnull final AbstractCheck pCheck, @Nullable final String pCurrentFileNameMockFile)
    {
        check = pCheck;
        currentFileNameMockFile = pCurrentFileNameMockFile != null ? new File(pCurrentFileNameMockFile) : null;
    }



    /**
     * Wrapper for FileContents.getFileName() (Checkstyle issue #1205).
     *
     * @return the currently analyzed file as returned by Checkstyle
     * @throws UnsupportedOperationException no known variant of FileContents.getFileName() could be
     *     found
     */
    @CheckForNull
    @SuppressWarnings({"JavaReflectionMemberAccess", "deprecation"})
    public File getCurrentFileName()
    {
        if (currentFileNameMockFile != null) {
            return currentFileNameMockFile;
        }

        // the remainder of this method is a workaround for Checkstyle issue #1205
        // https://github.com/checkstyle/checkstyle/issues/1205
        final FileContents fileContents = check.getFileContents();
        Method getFilename = null;
        try {
            getFilename = fileContents.getClass().getMethod("getFileName");
        }
        catch (NoSuchMethodException e) {
            try {
                getFilename = fileContents.getClass().getMethod("getFilename");
            }
            catch (NoSuchMethodException e1) {
                throw new UnsupportedOperationException("FileContents.getFilename()", e1);
            }
        }

        String filename = null;
        try {
            filename = (String) getFilename.invoke(fileContents);
        }
        catch (IllegalAccessException | InvocationTargetException e) {
            throw new UnsupportedOperationException("FileContents.getFilename()", e);
        }

        File result = filename != null ? new File(filename) : null;
        return result;
    }



    /**
     * Returns the name of a token for a given ID.
     *
     * @param pTokenId the ID of the token name to get
     * @return a token name as returned by Checkstyle
     * @throws UnsupportedOperationException no known variant of getTokenName() could be found
     */
    @Nonnull
    public String getTokenName(final int pTokenId)
    {
        final List searchClasses = Arrays.asList("com.puppycrawl.tools.checkstyle.utils.TokenUtil",
            "com.puppycrawl.tools.checkstyle.utils.TokenUtils", TokenTypes.class.getName(),
            "com.puppycrawl.tools.checkstyle.Utils");
        Method getTokenName = null;

        for (final String className : searchClasses) {
            try {
                final Class utilsClass = Class.forName(className);
                getTokenName = utilsClass.getMethod("getTokenName", int.class);
                break;
            }
            catch (ClassNotFoundException | NoSuchMethodException e) {
                // ignore
            }
        }
        if (getTokenName == null) {
            throw new UnsupportedOperationException("getTokenName() - method not found");
        }

        String result = null;
        try {
            result = (String) getTokenName.invoke(null, pTokenId);
        }
        catch (IllegalAccessException | InvocationTargetException e) {
            throw new UnsupportedOperationException(getTokenName.getDeclaringClass().getName() + ".getTokenName()", e);
        }
        return result;
    }



    /**
     * Determine if the given token is the root token of Checkstyle's AST. This means it's either an EOF
     * token or a COMPILATION_UNIT token. This must be performed reflectively, because in Checkstyle 9.0,
     * the EOF token was replaced with COMPILATION_UNIT.
     *
     * @param pToken the token to check
     * @return flag indicating root token
     * @throws IllegalStateException TokenTypes class has neither an EOF nor a
     *     COMPILATION_UNIT field
     */
    @SuppressWarnings("JavaReflectionMemberAccess")
    public boolean isRootToken(final int pToken)
    {
        try {
            Field field = TokenTypes.class.getField("COMPILATION_UNIT");
            return ((Integer) field.get(null)).intValue() == pToken;
        }
        catch (NoSuchFieldException e) {
            try {
                Field field = TokenTypes.class.getField("EOF");
                return ((Integer) field.get(null)).intValue() == pToken;
            }
            catch (NoSuchFieldException ex) {
                throw new IllegalStateException("TokenTypes class has no EOF or COMPILATION_UNIT field", ex);
            }
            catch (IllegalAccessException ex) {
                throw new IllegalStateException(e);
            }
        }
        catch (IllegalAccessException e) {
            throw new IllegalStateException(e);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy