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

org.jacpfx.rcp.util.ClassFinder Maven / Gradle / Ivy

The newest version!
/*
 * **********************************************************************
 *
 *  Copyright (C) 2010 - 2015
 *
 *  [ClassFinder.java]
 *  JACPFX Project (https://github.com/JacpFX/JacpFX/)
 *  All rights reserved.
 *
 *  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 org.jacpfx.rcp.util;

import java.io.File;
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.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Created with IntelliJ IDEA.
 * User: Andy Moncsek
 * Date: 19.07.13
 * Time: 10:43
 * Find classes for defined packages.
 */
public class ClassFinder {
    /**
     * Defined classpath
     */
    private static final String CLASSPATH = System.getProperty("java.class.path");
    private static final String OS = System.getProperty("os.name").toLowerCase();
    private static final String CLASS_DOT = ".";
    private static final String CLASS_BACKSLASH_DOT = "\\.";
    private static final String CLASS_SLASH = "/";
    private static final String CLASS_DOLLAR = "$";
    private static final String CLASS_FILE = ".class";
    private static final String CLASS_PROJECT_SEPERATOR = "classes";
    private static final int CLASS_PROJECT_SEPERATOR_LENGTH = 8;
    /**
     * List with the directories on the classpath (containing .class files)
     */
    private final List binDirs;
    private final List jarsFiles;

    private final PathMatcher matcher =
            FileSystems.getDefault().getPathMatcher("glob:*.class");

    private static final String FILE_SEPERATOR;

    static {
        FILE_SEPERATOR = isWindows() ? File.separator+File.separator: File.separator;
    }

    private static boolean isWindows() {

        return (OS.contains("win"));

    }

    /**
     * Default constructur initializes the directories indicated by the
     * CLASSPATH, if they are not yet initialized.
     */
    public ClassFinder() {
        binDirs = initClassPathDir();
        jarsFiles = initClassPathJars();
    }

    /**
     * Initialize the directories based on the classpath
     */
    private List initClassPathDir() {
        final String[] cs = CLASSPATH.split(File.pathSeparator);
        final Stream entries = Stream.of(cs);
        return entries
                .map(s -> FileSystems.getDefault().getPath(s))
                .filter(s -> Files.isDirectory(s, LinkOption.NOFOLLOW_LINKS)).collect(Collectors.toList());

    }

    private List initClassPathJars() {
        final String[] cs = CLASSPATH.split(File.pathSeparator);
        final Stream entries = Stream.of(cs);
        return entries
                .map(s -> FileSystems.getDefault().getPath(s))
                .filter(s -> Files.isRegularFile(s, LinkOption.NOFOLLOW_LINKS)).collect(Collectors.toList());

    }

    public List getClasseNamesInPackage
            (Path jar, String packageName) {
        final List classes = new ArrayList();
        packageName = packageName.replaceAll(CLASS_BACKSLASH_DOT, CLASS_SLASH);
        try {
            final JarInputStream jarFile = new JarInputStream
                    (Files.newInputStream(jar));
            while (true) {
                JarEntry jarEntry = jarFile.getNextJarEntry();
                if (jarEntry == null) {
                    break;
                }
                if ((jarEntry.getName().startsWith(packageName)) &&
                        (jarEntry.getName().endsWith(CLASS_FILE))) {
                    classes.add(jarEntry.getName().replaceAll(CLASS_SLASH, CLASS_BACKSLASH_DOT).replace(CLASS_FILE,""));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return classes;
    }


    /**
     * Retrive all classes of the indicated package. The package is searched in
     * all classpath directories that are directories
     *
     * @param packageName name of the package as 'ch.sahits.civ'
     * @return Array of found classes
     * @throws ClassNotFoundException no class was found in classpath
     */
    public Class[] getAll(final String packageName) throws ClassNotFoundException {

        final String packageDir = convertPackege(packageName);
        final List files = new CopyOnWriteArrayList<>();
        final PathMatcher folderMatcher =
                FileSystems.getDefault().getPathMatcher("glob:**" + convertPackageToRegex(packageName) + "**");
        final SimpleFileVisitor visitor = new CollectingFileVisitor(files, folderMatcher);
        binDirs.parallelStream().forEach(dir -> {
            try {
                Files.walkFileTree(dir, visitor);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        final List result = exctractClasses(packageDir, files);
        if(!result.isEmpty())return result.toArray(new Class[result.size()]);

        return findClassesInJar(packageName);

    }

    private Class[] findClassesInJar(final String packageName) {
        // scan jar files
        final List> res = jarsFiles.
                stream().
                map(path -> getClasseNamesInPackage(path, packageName)).
                collect(Collectors.toList());
        final Optional> classes = res.
                stream().
                reduce((a, b) -> {
                    a.addAll(b);
                    return a;
                }).
                map(this::loadClasses);
        if(!classes.isPresent()) return null;
        final List resultTmp = classes.get();
        return resultTmp.toArray(new Class[resultTmp.size()]);
    }

    private List loadClasses(List files) {
        return files
                .stream()
                .map(this::loadClass)
                .filter(clazz -> clazz != null)
                .collect(Collectors.toList());
    }

    private List exctractClasses(final String packageDir, List files) {
        return files.stream()
                .map(Paths::get)
                .map(path -> binDirs.stream()        // find the right classpath of file
                        .filter(path::startsWith)
                        .findFirst()
                        .map(p -> p.relativize(path)))
                .filter(Optional::isPresent)
                .map(Optional::get)
                .filter(path -> path.startsWith(packageDir))
                .map(path -> path.toString().replace(File.separator, CLASS_DOT))
                .map(className -> className.substring(0, className.lastIndexOf(CLASS_FILE)))
                .filter(classFile -> !classFile.contains(CLASS_DOLLAR))
                .map(this::loadClass)
                .filter(clazz -> clazz != null)
                .collect(Collectors.toList());
    }

    private Class loadClass(String file) {
        try {
            return this.getClass().getClassLoader().loadClass(file);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * Convert the package name into a relative directory path
     *
     * @param packageName name of the package as 'ch.sahits.civ'
     * @return relativ directory to the package
     */
    private String convertPackege(String packageName) {

        return packageName.replace(CLASS_DOT, File.separator);
    }

    private String convertPackageToRegex(String packageName) {
        return packageName.replace(CLASS_DOT, FILE_SEPERATOR);
    }

    private class CollectingFileVisitor extends SimpleFileVisitor {
        private final List files;
        private final PathMatcher folderMatcher;

        public CollectingFileVisitor(final List files, final PathMatcher folderMatcher) {
            this.files = files;
            this.folderMatcher = folderMatcher;
        }

        @Override
        public FileVisitResult preVisitDirectory(Path dir,
                                                 BasicFileAttributes attrs) {
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
            final boolean matchesFolder = folderMatcher.matches(file);
            if (matchesFolder && matcher.matches(file.getFileName())) {
                files.add(file.toString());
            }
            return matchesFolder ? FileVisitResult.CONTINUE : FileVisitResult.SKIP_SUBTREE;
        }

        @Override
        public FileVisitResult visitFileFailed(Path file, IOException e) {
            return FileVisitResult.CONTINUE;
        }
    }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy