package driver; import static util.FileUtils.glob; import java.io.IOException; import java.nio.file.Path; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import files.RootedSourcePath; import files.Package; import files.RelativeSourcePath; /** * Handles the association between relative paths and {@link SourceFile}. Each * {@link SourceFile} instance is uniquely identified by a set of * {@link RelativeSourcePath} instances. Those instances might however not * compare equal, due to subclassing. * * All {@link SourceFile} instances should be obtained from the instance of this * class held in {@Context#repo}, which is the only one who * should create them. */ public class SourceRepository { /****************************************************************************/ private final Map<Path, SourceFile> files = new HashMap<>(); /***************************************************************************** * Associate relative paths to corresponding rooted source paths known to * exist. */ private final Map<Path, RootedSourcePath> hints = new HashMap<>(); /***************************************************************************** * Returns the CTimeFile associated to the given path, creating it if needed. */ SourceFile get(RelativeSourcePath path) { return get(path.relativePath()); } /***************************************************************************** * Returns the CTimeFile associated to the given path, creating it if needed. */ private SourceFile get(Path path) { SourceFile out = files.get(path); if (out == null) { out = createNewFile(path); } return out; } /***************************************************************************** * Indicates that the given paths are known to exists. */ void hint(List<RootedSourcePath> paths) { for (RootedSourcePath rpath : paths) { assert rpath.absolutePath().toFile().exists(); RootedSourcePath old = hints.put(rpath.relativePath(), rpath); if (old != null && !old.equals(rpath)) { reportAmbiguity( rpath.relativePath(), old.absolutePath(), rpath.absolutePath()); } } } /***************************************************************************** * Creates a new SourceFile from the given path. Throws an error if no source * file can be found matching the path, or if the relative path matches * multiple files. */ private SourceFile createNewFile(Path relative) { RootedSourcePath rpath = hints.get(relative); if (rpath == null) { rpath = findRootedPathFor(relative); if (rpath == null) { throw new Error("No file matching relative path \"" + relative + "\""); } } SourceFile out = new SourceFile(rpath); files.put(relative, out); return out; } /***************************************************************************** * Search among {@link Config#roots} for a file matching $relative. */ private RootedSourcePath findRootedPathFor(Path relative) { RootedSourcePath rpath = null; for (Path root : Config.get().roots) { Path absolute = root.resolve(relative); if (absolute.toFile().exists()) { RootedSourcePath rpath2 = new RootedSourcePath(root, absolute, false); if (rpath != null) { reportAmbiguity(rpath, rpath2); } rpath = rpath2; } } return rpath; } /****************************************************************************/ private void reportAmbiguity(RootedSourcePath rpath1, RootedSourcePath rpath2) { reportAmbiguity( rpath1.relativePath(), rpath1.absolutePath(), rpath2.absolutePath()); } /****************************************************************************/ private void reportAmbiguity(Path rel, Path absOne, Path absTwo) { throw new Error("Multiple file matching relative path \"" + rel + "\" : \n - " + absOne + "\n - " + absTwo); } /***************************************************************************** * Find all source files with the given extension under the given directory * (not recursively) and return them. */ Collection<SourceFile> findAll(Package pkg, String extension) { Path pkgPath = pkg.relativePath(); Set<SourceFile> out = new HashSet<>(); for (Path root : Config.get().roots) { List<Path> paths; try { paths = glob(root.resolve(pkgPath), "*." + extension); } catch (IOException e) { throw new Error( "Error while retrieving source files from package " + pkg, e); } for (Path path : paths) { Path relative = pkgPath.resolve(path.toFile().getName()); SourceFile file = get(relative); out.add(file); } } return out; } }