org.openhab.tools.analysis.checkstyle.PackageExportsNameCheck.java Source code

Java tutorial

Introduction

Here is the source code for org.openhab.tools.analysis.checkstyle.PackageExportsNameCheck.java

Source

/**
 * Copyright (c) 2010-2018 by the respective copyright holders.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package org.openhab.tools.analysis.checkstyle;

import static org.openhab.tools.analysis.checkstyle.api.CheckConstants.MANIFEST_EXTENSION;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ivy.osgi.core.BundleInfo;
import org.openhab.tools.analysis.checkstyle.api.AbstractStaticCheck;

import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
import com.puppycrawl.tools.checkstyle.api.FileText;

/**
 * Checks if all packages that are not exported are marked as internal.
 *
 * @author Aleksandar Kovachev - Initial contribution
 * @author Petar Valchev - Added a verification of non existent packages
 * @author Svilen Valkanov - Refactored the check
 */
public class PackageExportsNameCheck extends AbstractStaticCheck {
    private static final String CORRECT_NAMING_OF_NOT_EXPORTED_PACKAGES_MESSAGE = "The package %s"
            + " should be marked as \"internal\" if it is not exported.";

    private static final String PACKAGE_PATTERN = "[a-zA-Z0-9\\._-]*";

    private final Log logger = LogFactory.getLog(PackageExportsNameCheck.class);

    private String[] sourceDirectories;
    private String[] excludedPackages;

    /**
     * Sets the configuration property for source directories.
     *
     * @param sourceDirectories - source directories
     */
    public void setSourceDirectories(String[] sourceDirectories) {
        this.sourceDirectories = sourceDirectories;
    }

    /**
     * Sets the configuration property for excluding packages.
     *
     * @param excludePackages - excluded packages
     */
    public void setExcludedPackages(String[] excludePackages) {
        this.excludedPackages = excludePackages;
    }

    public PackageExportsNameCheck() {
        setFileExtensions(MANIFEST_EXTENSION);
    }

    @Override
    protected void processFiltered(File manifestFile, FileText fileText) throws CheckstyleException {
        BundleInfo bundleInfo = parseManifestFromFile(fileText);
        Set<String> uniqueManifestExports = bundleInfo.getExports().stream().map(export -> export.getName())
                .collect(Collectors.toSet());

        File projectDirectory = manifestFile.getParentFile().getParentFile();
        Path projectDirectoryPath = projectDirectory.toPath();

        Set<String> sourcePackages = new HashSet<>();
        try {
            for (String sourcePath : sourceDirectories) {
                Path relativeSourcePath = Paths.get(sourcePath);
                Path sourceDirectoryPath = projectDirectoryPath.resolve(relativeSourcePath);
                sourcePackages.addAll(getFilteredPackagesFromSourceDirectory(sourceDirectoryPath));
            }

            sourcePackages.removeAll(uniqueManifestExports);
            for (String packageName : sourcePackages) {
                log(0, String.format(CORRECT_NAMING_OF_NOT_EXPORTED_PACKAGES_MESSAGE, packageName));
            }
        } catch (IOException e) {
            logger.error("Problem occurred while processing directories. "
                    + "The check will exit without logging any warnings!", e);
        }
    }

    /**
     * Filter and return the packages from the source directory. Only not excluded packages will be returned.
     *
     * @param sourcePath - The full path of the source directory
     * @return {@link Set } of {@link String }s with the package names.
     * @throws IOException if an I/O error is thrown while visiting files
     */
    private Set<String> getFilteredPackagesFromSourceDirectory(Path sourcePath) throws IOException {
        Set<String> packages = new HashSet<>();
        // No symbolic links are expected in the source directory
        if (Files.exists(sourcePath, LinkOption.NOFOLLOW_LINKS)) {
            Files.walkFileTree(sourcePath, new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) {
                    Path packageRelativePath = sourcePath.relativize(path.getParent());
                    String packageName = packageRelativePath.toString()
                            .replaceAll(Matcher.quoteReplacement(File.separator), ".");

                    if (!isExcluded(packageName)) {
                        packages.add(packageName);
                    }
                    return FileVisitResult.CONTINUE;
                }
            });
        }

        return packages;
    }

    /**
     * Checks if the package should be excluded
     *
     * @param packageName - The package name to be checked.
     * @return <b>true</b> if the package name should be excluded
     */
    private boolean isExcluded(String packageName) {
        for (String excludePackageName : excludedPackages) {
            excludePackageName = excludePackageName.replaceAll("\\.\\*", PACKAGE_PATTERN);

            Pattern pattern = Pattern.compile(excludePackageName);
            Matcher matcher = pattern.matcher(packageName);

            if (matcher.matches()) {
                return true;
            }
        }
        return false;
    }
}