io.gravitee.maven.plugins.json.schema.generator.util.ClassFinder.java Source code

Java tutorial

Introduction

Here is the source code for io.gravitee.maven.plugins.json.schema.generator.util.ClassFinder.java

Source

/**
 * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.gravitee.maven.plugins.json.schema.generator.util;

import org.apache.commons.lang3.Validate;

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Utility class to find Classes from a given Path
 *
 * @author Aurlien Bourdon (aurelien.bourdon at gmail.com)
 */
public class ClassFinder {

    /**
     * Java class extension
     */
    private static final String CLASS_EXTENSION = ".class";

    /**
     * Find class names from the given root Path that matching the given list of globs.
     * <p/>
     * Class names are built following the given rule:
     * - Given the root Path: /root/path/
     * - Given the Class Path: /root/path/the/path/to/the/class/Class.class
     * - Then associated Class name would be: the.path.to.the.class.Class
     *
     * @param root  the root Path from which start searching
     * @param globs the glob list to taking into account during path matching
     * @return a list of Paths that match the given list of globs from the root Path
     * @throws IOException if an I/O occurs
     */
    public static List<String> findClassNames(Path root, Globs globs) throws IOException {
        Validate.notNull(root, "Unable to handle null root path");
        Validate.isTrue(root.toFile().isDirectory(), "Unable to handle non existing or non directory root path");
        Validate.notNull(globs, "Unable to handle null globs");

        List<String> matchedClassNames = new ArrayList<>();
        matchedClassNames.addAll(findClassPaths(root, globs).stream()
                .map(path -> ClassUtils.convertClassPathToClassName(path.toString(), root.normalize().toString()))
                .collect(Collectors.toList()));
        return matchedClassNames;
    }

    /**
     * Find class Paths from the given root Path that match the given list of globs.
     *
     * @param root  the root Path from which start searching
     * @param globs the glob list to taking into account during path matching
     * @return a list of Paths that match the given list of globs from the root Path
     * @throws IOException if an I/O occurs
     */
    private static List<Path> findClassPaths(Path root, Globs globs) throws IOException {
        List<Path> matchedPaths = new ArrayList<>();
        Files.walkFileTree(root.normalize(), new GlobsMatchingClassFileVisitor(globs, matchedPaths));
        return matchedPaths;
    }

    /**
     * Visit a given root Path by getting class Paths that match the list of given glob patterns
     */
    private static class GlobsMatchingClassFileVisitor extends SimpleFileVisitor<Path> {

        /**
         * The associated GlobPathMatchers
         */
        private final GlobPathMatchers globPathMatchers;

        /**
         * Path list result
         */
        private final List<Path> matchedPaths;

        /**
         * Create a new instance based on the given included/excluded globs. Results will be stored into the given mactchedPaths
         *
         * @param globs        wrapper to the included/excluded globs configuration
         * @param matchedPaths the structure to store results
         */
        public GlobsMatchingClassFileVisitor(Globs globs, List<Path> matchedPaths) {
            this.globPathMatchers = new GlobPathMatchers(globs);
            this.matchedPaths = matchedPaths;
        }

        /**
         * Simplify glob writing by automatically add the double wildcard pattern
         *
         * @param glob the glob to format
         * @return a new formatted glob
         */
        private static String formatGlob(String glob) {
            glob = "**/" + glob;
            return glob;
        }

        /**
         * Visit the given file and test it against the current globPathMatchers
         *
         * @param path           the path to test against the current globPathMatchers
         * @param fileAttributes the associated file attributes
         * @return always FileVisitResult.CONTINUE
         */
        public FileVisitResult visitFile(Path path, BasicFileAttributes fileAttributes) {
            // If Path is not a class file then continue
            if (!path.getFileName().toString().endsWith(CLASS_EXTENSION)) {
                return FileVisitResult.CONTINUE;
            }

            // Do not keep classes matching the excluded globs
            for (PathMatcher excludedMatcher : globPathMatchers.getExcludedMatchers()) {
                if (excludedMatcher.matches(path)) {
                    return FileVisitResult.CONTINUE;
                }
            }

            // If there is no included paths, then keep them all
            if (globPathMatchers.getIncludedMatchers().isEmpty()) {
                matchedPaths.add(path);
                return FileVisitResult.CONTINUE;
            }

            // Else only keep Path matching the included paths
            for (PathMatcher includedMatcher : globPathMatchers.getIncludedMatchers()) {
                if (includedMatcher.matches(path)) {
                    matchedPaths.add(path);
                    return FileVisitResult.CONTINUE;
                }
            }

            return FileVisitResult.CONTINUE;
        }

        /**
         * Wrapper to included/excluded PathMachers
         */
        private static class GlobPathMatchers {
            /**
             * PathMatcher list of included paths
             */
            private final List<PathMatcher> includedMatchers;

            /**
             * PathMatcher list of excluded paths
             */
            private final List<PathMatcher> excludedMatchers;

            /**
             * Create a new instance based on the given Globs
             *
             * @param globs the based Globs to create included/excluded PathMatchers
             */
            public GlobPathMatchers(Globs globs) {
                this.includedMatchers = new ArrayList<>();
                includedMatchers.addAll(globs.getIncludes().stream()
                        .map(include -> FileSystems.getDefault().getPathMatcher("glob:" + formatGlob(include)))
                        .collect(Collectors.toList()));
                this.excludedMatchers = new ArrayList<>();
                excludedMatchers.addAll(globs.getExcludes().stream()
                        .map(exclude -> FileSystems.getDefault().getPathMatcher("glob:" + formatGlob(exclude)))
                        .collect(Collectors.toList()));
            }

            /**
             * @return the list of included PathMatchers
             */
            public List<PathMatcher> getIncludedMatchers() {
                return includedMatchers;
            }

            /**
             * @return the list of excluded PathMatchers
             */
            public List<PathMatcher> getExcludedMatchers() {
                return excludedMatchers;
            }
        }

    }

}