package mandelbrot.dependency.analyzer; import java.io.File; import java.io.FileNotFoundException; import java.nio.file.Paths; import java.util.Collection; import java.util.Comparator; import java.util.Scanner; import java.util.regex.Matcher; import java.util.regex.Pattern; import mandelbrot.dependency.data.ModuleDescriptor; import mandelbrot.dependency.data.ModuleKey; import mandelbrot.dependency.data.ModuleKey.ModuleType; import mandelbrot.ocamljava_maven_plugin.util.FileMappings; import org.apache.maven.plugin.AbstractMojo; import com.google.common.base.Objects; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import com.google.common.collect.SortedSetMultimap; import com.google.common.collect.TreeMultimap; public class DependencyExtractor { private static final String OPEN_KEYWORD = "open"; static final Pattern MODULE_REGEX_START_OF_LINE = Pattern .compile("[\\w\\=\\s\\+\\-]*?([A-Z][a-zA-z0-9]+)\\.[\\w]*?"); private final AbstractMojo abstractMojo; private final SortedSetMultimap<String, ModuleDescriptor> moduleToFilePath = TreeMultimap .create(new Comparator<String>() { @Override public int compare(final String paramT1, final String paramT2) { return paramT1.compareTo(paramT2); } }, new Comparator<ModuleDescriptor>() { @Override public int compare(final ModuleDescriptor s1, final ModuleDescriptor s2) { if (Objects.equal(s1, s2)) return 0; if (s1 == null) return 1; if (s2 == null) return -1; final ModuleType type1 = s1.getModuleType(); final ModuleType type2 = s2.getModuleType(); int cmp = ModuleType.dependencyCompareTo().compare( type1, type2); if (cmp != 0) return cmp; cmp = s1.getModuleName().compareTo(s2.getModuleName()); if (cmp != 0) return cmp; final Optional<File> fromFile = s1.getModuleFile(); final Optional<File> fromFile2 = s2.getModuleFile(); if (fromFile.isPresent() && !fromFile2.isPresent()) return -1; if (!fromFile.isPresent() && fromFile2.isPresent()) return 1; return fromFile.get().compareTo(fromFile2.get()); }}); private final boolean scanningEnabled; public DependencyExtractor(final AbstractMojo abstractMojo) { this(abstractMojo, true); } public DependencyExtractor(final AbstractMojo abstractMojo, final boolean scanningEnabled) { this.abstractMojo = Preconditions.checkNotNull(abstractMojo); this.scanningEnabled = scanningEnabled; } public Multimap<String, Optional<String>> groupSourcesByModuleDependencies( final Collection<String> sources, final File prefixToTruncate) { Preconditions.checkNotNull(sources); final ImmutableMultimap.Builder<String, Optional<String>> builder = ImmutableMultimap .builder(); moduleToFilePath.clear(); for (final String source : sources) { final File sourceFile = new File(source); // TODO move this util method to separate class final Optional<String> moduleNameOfSource = Analyzer .moduleNameOfSource(source); if (moduleNameOfSource.isPresent()) { moduleToFilePath.put(moduleNameOfSource.get(), new ModuleDescriptor.Builder() .setModuleFile(sourceFile) // .setModuleFile(new File(sourceFile.getPath().replace(prefixToTruncate.getPath(), ""))) .setModuleKey(ModuleKey.fromFile(sourceFile)) .setJavaPackageName(FileMappings.toPackage(prefixToTruncate, source)) .build()); // Hackish but convenient to add self to grouping so it appears // in the multimaps key set, but still // depend on nothing. builder.put(moduleNameOfSource.get(), Optional.<String> absent()); if (scanningEnabled) scan(builder, source, sourceFile, moduleNameOfSource); } } return builder.build(); } private void scan( final ImmutableMultimap.Builder<String, Optional<String>> builder, final String source, final File sourceFile, final Optional<String> moduleNameOfSource) { Scanner scanner = null; try { scanner = new Scanner(sourceFile); while (scanner.hasNext()) { final String line = scanner.nextLine(); if (line == null) continue; if (line.startsWith(OPEN_KEYWORD) && !line.equals(OPEN_KEYWORD)) { final String moduleName = extractModuleName(line); if (isValidModuleName(moduleName)) { builder.put(moduleNameOfSource.get(), Optional.of(moduleName)); } } final Matcher matcher = MODULE_REGEX_START_OF_LINE .matcher(line); if (!matcher.matches()) continue; for (int i = 1; i <= matcher.groupCount(); i++) { final String moduleName = extractModuleName(matcher .group(i)); if (isValidModuleName(moduleName)) { builder.put(moduleNameOfSource.get(), Optional.of(moduleName)); } } } } catch (final FileNotFoundException e) { abstractMojo.getLog().error("problem with file: " + source, e); return; } finally { if (scanner != null) scanner.close(); } } public SortedSetMultimap<String, ModuleDescriptor> getModuleToFilePath() { return Multimaps.unmodifiableSortedSetMultimap(moduleToFilePath); } private String extractModuleName(String line) { if (line.startsWith(OPEN_KEYWORD)) line = line.substring(OPEN_KEYWORD.length(), line.length()); return line.replace(";", "").trim().toLowerCase(); } // TODO make much stricter private boolean isValidModuleName(final String moduleName) { return moduleName != null && !moduleName.trim().isEmpty() && !moduleName.contains(" "); } public Multimap<String, Optional<String>> groupSourcesByModuleDependencies(Collection<String> sources) { return groupSourcesByModuleDependencies(sources, Paths.get("").toFile()); } }