org.bsc.maven.plugin.findclass.ClasspathDescriptor.java Source code

Java tutorial

Introduction

Here is the source code for org.bsc.maven.plugin.findclass.ClasspathDescriptor.java

Source

/*
 * Copyright 2010 Ning, Inc.
 *
 * Ning licenses this file to you 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 org.bsc.maven.plugin.findclass;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.maven.plugin.MojoExecutionException;

public class ClasspathDescriptor {
    private static final Pattern[] DEFAULT_IGNORED_RESOURCES = { Pattern.compile("(META-INF/)?ASL2\\.0(\\.TXT)?"),
            Pattern.compile("META-INF/DEPENDENCIES(\\.TXT)?"), Pattern.compile("META-INF/DISCLAIMER(\\.TXT)?"),
            Pattern.compile("(META-INF/)?[A-Z_-]*LICENSE.*"), Pattern.compile("META-INF/MANIFEST\\.MF"),
            Pattern.compile("META-INF/INDEX\\.LIST"), Pattern.compile("META-INF/MAVEN/.*"),
            Pattern.compile("META-INF/PLEXUS/.*"), Pattern.compile("META-INF/SERVICES/.*"),
            Pattern.compile("(META-INF/)?NOTICE(\\.TXT)?"), Pattern.compile("META-INF/README"),
            Pattern.compile("OSGI-INF/.*"), Pattern.compile("README(\\.TXT)?"), Pattern.compile(".*PACKAGE\\.HTML"),
            Pattern.compile(".*OVERVIEW\\.HTML"), Pattern.compile("META-INF/SPRING\\.HANDLERS"),
            Pattern.compile("META-INF/SPRING\\.SCHEMAS"), Pattern.compile("META-INF/SPRING\\.TOOLING") };

    private static final Set<String> IGNORED_LOCAL_DIRECTORIES = new HashSet<>();

    private static final Map<File, Cached> CACHED_BY_ELEMENT = new HashMap<>();

    static {
        IGNORED_LOCAL_DIRECTORIES.add(".GIT");
        IGNORED_LOCAL_DIRECTORIES.add(".SVN");
        IGNORED_LOCAL_DIRECTORIES.add(".HG");
        IGNORED_LOCAL_DIRECTORIES.add(".BZR");
    }

    private final Map<String, Set<File>> classesWithElements = new TreeMap<>();

    private final Map<String, Set<File>> resourcesWithElements = new TreeMap<>();

    private boolean useDefaultResourceIgnoreList = true;

    private Pattern[] ignoredResourcesPatterns = null;

    public boolean isUseDefaultResourceIgnoreList() {
        return useDefaultResourceIgnoreList;
    }

    public void setUseDefaultResourceIgnoreList(final boolean useDefaultResourceIgnoreList) {
        this.useDefaultResourceIgnoreList = useDefaultResourceIgnoreList;
    }

    public void setIgnoredResources(final String[] ignoredResources) throws MojoExecutionException {
        if (ignoredResources != null) {
            ignoredResourcesPatterns = new Pattern[ignoredResources.length];

            try {
                for (int i = 0; i < ignoredResources.length; i++) {
                    ignoredResourcesPatterns[i] = Pattern.compile(ignoredResources[i].toUpperCase());
                }
            } catch (final PatternSyntaxException pse) {
                throw new MojoExecutionException("Error compiling resourceIgnore pattern: " + pse.getMessage());
            }
        }
    }

    public void add(final File element) throws IOException {
        if (!element.exists()) {
            throw new FileNotFoundException("Path " + element + " doesn't exist");
        }
        if (element.isDirectory()) {
            addDirectory(element);
        } else {
            addArchive(element);
        }
    }

    public Set<String> getClasss() {
        return Collections.unmodifiableSet(classesWithElements.keySet());
    }

    public Set<String> getResources() {
        return Collections.unmodifiableSet(resourcesWithElements.keySet());
    }

    public Set<File> getElementsHavingClass(final String className) {
        final Set<File> elements = classesWithElements.get(className);

        return elements == null ? null : Collections.unmodifiableSet(elements);
    }

    public Set<File> getElementsHavingResource(final String resource) {
        final Set<File> elements = resourcesWithElements.get(resource);

        return elements == null ? null : Collections.unmodifiableSet(elements);
    }

    private void addDirectory(final File element) {
        addDirectory(element, null, element);
    }

    private void addDirectory(final File element, final String parentPackageName, final File directory) {
        if (addCached(element)) {
            return;
        }

        final List<String> classes = new ArrayList<>();
        final List<String> resources = new ArrayList<>();
        final File[] files = directory.listFiles();
        final String pckgName = element.equals(directory) ? null
                : (parentPackageName == null ? "" : parentPackageName + ".") + directory.getName();

        if (files != null && files.length > 0) {
            for (File file : files) {
                if (file.isDirectory() && !IGNORED_LOCAL_DIRECTORIES.contains(file.getName().toUpperCase())) {
                    addDirectory(element, pckgName, file);
                } else if (file.isFile()) {
                    if ("class".equals(FilenameUtils.getExtension(file.getName()))) {
                        final String className = (pckgName == null ? "" : pckgName + ".")
                                + FilenameUtils.getBaseName(file.getName());
                        classes.add(className);
                        addClass(className, element);
                    } else {
                        final String resourcePath = (pckgName == null ? "" : pckgName.replace('.', '/') + "/")
                                + file.getName();
                        resources.add(resourcePath);
                        addResource(resourcePath, element);
                    }
                }
            }
        }

        CACHED_BY_ELEMENT.put(element, new Cached(classes, resources));
    }

    private void addArchive(final File element) throws IOException {
        if (addCached(element)) {
            return;
        }

        final List<String> classes = new ArrayList<>();
        final List<String> resources = new ArrayList<>();
        InputStream input = null;
        ZipInputStream zipInput = null;

        try {
            input = element.toURI().toURL().openStream();
            zipInput = new ZipInputStream(input);

            ZipEntry entry;

            while ((entry = zipInput.getNextEntry()) != null) {
                if (!entry.isDirectory()) {
                    final String name = entry.getName();

                    if ("class".equals(FilenameUtils.getExtension(name))) {
                        final String className = FilenameUtils.removeExtension(name).replace('/', '.').replace('\\',
                                '.');

                        classes.add(className);
                        addClass(className, element);
                    } else {
                        final String resourcePath = name.replace('\\', File.separatorChar);

                        resources.add(resourcePath);
                        addResource(resourcePath, element);
                    }
                }
            }

            CACHED_BY_ELEMENT.put(element, new Cached(classes, resources));
        } finally {
            if (zipInput != null) {
                // this will also close the wrapped stream
                IOUtils.closeQuietly(zipInput);
            } else if (input != null) {
                IOUtils.closeQuietly(input);
            }
        }
    }

    private void addClass(final String className, final File element) {
        if (className.indexOf('$') < 0) {
            Set<File> elements = classesWithElements.get(className);

            if (elements == null) {
                elements = new HashSet<>();
                classesWithElements.put(className, elements);
            }
            elements.add(element);
        }
    }

    private void addResource(final String path, final File element) {
        if (!ignore(path)) {
            Set<File> elements = resourcesWithElements.get(path);

            if (elements == null) {
                elements = new HashSet<>();
                resourcesWithElements.put(path, elements);
            }
            elements.add(element);
        }
    }

    private boolean ignore(final String path) {
        final String uppercasedPath = path.toUpperCase().replace(File.separatorChar, '/');

        // Unless it has been turned off...
        if (useDefaultResourceIgnoreList) {
            //  check whether the path is in the list of default ignores
            for (Pattern ignored_resource : DEFAULT_IGNORED_RESOURCES) {
                if (ignored_resource.matcher(uppercasedPath).matches()) {
                    return true;
                }
            }
        }

        // check whether there is an user supplied ignore pattern.
        if (ignoredResourcesPatterns != null) {
            for (Pattern ignoredResourcesPattern : ignoredResourcesPatterns) {
                if (ignoredResourcesPattern.matcher(uppercasedPath).matches()) {
                    return true;
                }
            }
        }

        return false;
    }

    private boolean addCached(final File element) {
        final Cached cached = CACHED_BY_ELEMENT.get(element);

        if (cached == null) {
            return false;
        }

        for (String className : cached.getClasses()) {
            addClass(className, element);
        }

        for (String resourceName : cached.getResources()) {
            addResource(resourceName, element);
        }

        return true;
    }

    private static class Cached {
        private final List<String> classes;
        private final List<String> resources;

        private Cached(final List<String> classes, final List<String> resources) {
            this.classes = classes;
            this.resources = resources;
        }

        public List<String> getClasses() {
            return classes;
        }

        public List<String> getResources() {
            return resources;
        }
    }
}