package org.jboss.windup.bootstrap.commands.windup; import java.io.IOException; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.jboss.windup.bootstrap.commands.AbstractListCommand; import org.jboss.windup.bootstrap.commands.Command; import org.jboss.windup.bootstrap.commands.CommandPhase; import org.jboss.windup.bootstrap.commands.CommandResult; import org.jboss.windup.bootstrap.commands.FurnaceDependent; import org.jboss.windup.exec.configuration.options.InputPathOption; import org.jboss.windup.rules.apps.java.scan.operation.packagemapping.PackageNameMappingRegistry; import org.jboss.windup.util.ClassNameUtil; import org.jboss.windup.util.PackageComparator; import org.jboss.windup.util.PackageFrequencyTrie; import org.jboss.windup.util.PackageFrequencyTrieVisitor; import org.jboss.windup.util.PathUtil; import org.jboss.windup.util.ZipUtil; /** * @author <a href="mailto:jesse.sightler@gmail.com">Jesse Sightler</a> */ public class DiscoverPackagesCommand extends AbstractListCommand implements Command, FurnaceDependent { private final List<String> arguments; public DiscoverPackagesCommand(List<String> arguments) { this.arguments = arguments; } @Override public CommandResult execute() { String input = null; for (int i = 0; i < this.arguments.size(); i++) { String argument = this.arguments.get(i); if (argument.equalsIgnoreCase("--" + InputPathOption.NAME)) { if (this.arguments.size() > (i + 1)) { input = this.arguments.get(i + 1); break; } } } if (input == null) { System.err.println(); System.err.println("ERROR: --input must be specified"); return CommandResult.EXIT; } final Map<String, Integer> classes = findClasses(Paths.get(input)); PackageNameMappingRegistry packageNameMappingRegistry = getFurnace().getAddonRegistry().getServices(PackageNameMappingRegistry.class).get(); packageNameMappingRegistry.loadPackageMappings(); Map<String, String> packageToOrganization = new TreeMap<>(new PackageComparator()); PackageFrequencyTrie frequencyTrie = new PackageFrequencyTrie(); for (String qualifiedName : classes.keySet()) { String packageName = ClassNameUtil.getPackageName(qualifiedName); String organization = packageNameMappingRegistry.getOrganizationForPackage(packageName); if (organization == null) frequencyTrie.addClass(qualifiedName); else packageToOrganization.put(packageName, organization); } System.out.println("Known Packages:"); System.out.println("======================="); System.out.println(); for (Map.Entry<String, String> organizationPackage : packageToOrganization.entrySet()) { System.out.println(organizationPackage.getKey() + " - " + organizationPackage.getValue()); } System.out.println(); System.out.println("Unknown Packages:"); System.out.println("======================="); frequencyTrie.visit(new PackageFrequencyTrieVisitor() { @Override public void visit(PackageFrequencyTrie trie, int depth) { String packageName = trie.getPackageName(); int recursiveClassCount = trie.getClassCount(true); if (depth == 1 || (depth > 1 && recursiveClassCount > 100)) System.out.println(packageName + " - Classes: " + recursiveClassCount); if (depth == 0 && trie.getClassCount(false) > 0) { System.out.println("Default Package - Classes: " + trie.getClassCount(false)); } } }); return CommandResult.EXIT; } @Override public CommandPhase getPhase() { return CommandPhase.PRE_EXECUTION; } /** * Recursively scan the provided path and return a list of all Java packages contained therein. */ private static Map<String, Integer> findClasses(Path path) { List<String> paths = findPaths(path, true); Map<String, Integer> results = new HashMap<>(); for (String subPath : paths) { if (subPath.endsWith(".java") || subPath.endsWith(".class")) { String qualifiedName = PathUtil.classFilePathToClassname(subPath); addClassToMap(results, qualifiedName); } } return results; } private static void addClassToMap(Map<String, Integer> map, String className) { Integer count = map.get(className); if (count == null) map.put(className, 1); else map.put(className, count + 1); } /** * Find all paths within the given file (or folder). */ private static Collection<String> findPaths(Path path) { List<String> paths = findPaths(path, false); Collections.sort(paths); return paths; } private static List<String> findPaths(Path path, boolean relativeOnly) { List<String> results = new ArrayList<>(); results.add(path.normalize().toAbsolutePath().toString()); if (Files.isDirectory(path)) { try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path)) { for (Path child : directoryStream) { results.addAll(findPaths(child, relativeOnly)); } } catch (IOException e) { System.err.println("Could not read file: " + path + " due to: " + e.getMessage()); } } else if (Files.isRegularFile(path) && ZipUtil.endsWithZipExtension(path.toString())) { results.addAll(ZipUtil.scanZipFile(path, relativeOnly)); } return results; } }