package polyglot.frontend; import java.io.*; import java.util.*; import polyglot.main.Report; import polyglot.util.InternalCompilerError; /** A <code>SourceLoader</code> is responsible for loading source files. */ public class SourceLoader { protected ExtensionInfo sourceExt; protected Collection sourcePath; /** 0 if unknown, 1 if case insensitive, -1 if not. */ protected int caseInsensitive; /** Set of sources already loaded. An attempt to load a source * already loaded will cause an IOException. */ protected Set loadedSources; /** * This is a map from Files (of directories) to Set[String]s, which * records the first level of contents of the directory. This cache * is used to avoid a number of File.exists() calls. */ protected Map directoryContentsCache; public SourceLoader(ExtensionInfo sourceExt, Collection sourcePath) { this.sourcePath = sourcePath; this.sourceExt = sourceExt; this.directoryContentsCache = new HashMap(); this.caseInsensitive = 0; this.loadedSources = new HashSet(); } /** Load a source from a specific file. */ public FileSource fileSource(String fileName) throws IOException { // If we haven't done so already, // determine if the file system is case insensitive setCaseInsensitive(fileName); File sourceFile = new File(fileName); if (! sourceFile.exists()) { throw new FileNotFoundException(fileName); } if (loadedSources.contains(fileKey(sourceFile))) { throw new FileNotFoundException(fileName); } loadedSources.add(fileKey(sourceFile)); String[] exts = sourceExt.fileExtensions(); boolean ok = false; for (int i = 0; i < exts.length; i++) { String ext = exts[i]; if (fileName.endsWith("." + ext)) { ok = true; break; } } if (! ok) { String extString = ""; for (int i = 0; i < exts.length; i++) { if (exts.length == 2 && i == exts.length-1) { extString += " or "; } else if (exts.length != 1 && i == exts.length-1) { extString += ", or "; } else if (i != 0) { extString += ", "; } extString = extString + "\"." + exts[i] + "\""; } if (exts.length == 1) { throw new IOException("Source \"" + fileName + "\" does not have the extension " + extString + "."); } else { throw new IOException("Source \"" + fileName + "\" does not have any of the extensions " + extString + "."); } } if (Report.should_report(Report.frontend, 2)) Report.report(2, "Loading class from " + sourceFile); return new FileSource(sourceFile); } /** * The current user directory. We make it static so we don't need to * keep on making copies of it. */ static File current_dir = null; /** * The current user directory. */ protected static File current_dir() { if (current_dir == null) { current_dir = new File(System.getProperty("user.dir")); } return current_dir; } /** Check if a directory for a package exists. */ public boolean packageExists(String name) { String fileName = name.replace('.', File.separatorChar); /* Search the source path. */ for (Iterator i = sourcePath.iterator(); i.hasNext(); ) { File directory = (File) i.next(); File f = new File(directory, fileName); if (f.exists() && f.isDirectory()) { return true; } } return false; } /** Load the source file for the given class name using the source path. */ public FileSource classSource(String className) { /* Search the source path. */ String[] exts = sourceExt.fileExtensions(); for (int k = 0; k < exts.length; k++) { String fileName = className.replace('.', File.separatorChar) + "." + exts[k]; for (Iterator i = sourcePath.iterator(); i.hasNext(); ) { File directory = (File) i.next(); Set dirContents = (Set)directoryContentsCache.get(directory); if (dirContents == null) { dirContents = new HashSet(); directoryContentsCache.put(directory, dirContents); if (directory.exists()) { String[] contents = directory.list(); for (int j = 0; j < contents.length; j++) { dirContents.add(contents[j]); } } } // check if the source file exists in the directory int index = fileName.indexOf(File.separatorChar); if (index < 0) index = fileName.length(); String firstPart = fileName.substring(0, index); if (dirContents.contains(firstPart)) { // the directory contains at least the first part of the // file path. We will check if this file exists. File sourceFile; if (directory != null && directory.equals(current_dir())) { sourceFile = new File(fileName); } else { sourceFile = new File(directory, fileName); } // Skip it if already loaded if (loadedSources.contains(fileKey(sourceFile))) { continue; } try { if (Report.should_report(Report.frontend, 2)) Report.report(2, "Loading " + className + " from " + sourceFile); FileSource s = new FileSource(sourceFile); loadedSources.add(fileKey(sourceFile)); return s; } catch (IOException e) { } } } } return null; } public Object fileKey(File file) { setCaseInsensitive(file.getAbsolutePath()); if (caseInsensitive()) { return file.getAbsolutePath().toLowerCase(); } return file.getAbsolutePath(); } /** Is the file system case insensitive. */ public boolean caseInsensitive() { if (caseInsensitive == 0) { throw new InternalCompilerError("unknown case sensitivity"); } return caseInsensitive == 1; } protected void setCaseInsensitive(String fileName) { if (caseInsensitive != 0) { return; } // File.equals doesn't work correctly on the Mac. // So, get the list of files in the same directory // as sourceFile. Check if the sourceFile with two // different cases exists but only appears in the list once. File f1 = new File(fileName.toUpperCase()); File f2 = new File(fileName.toLowerCase()); if (f1.equals(f2)) { caseInsensitive = 1; } else if (f1.exists() && f2.exists()) { boolean f1Exists = false; boolean f2Exists = false; File dir; if (f1.getParent() != null) { dir = new File(f1.getParent()); } else { dir = current_dir(); } File[] ls = dir.listFiles(); for (int i = 0; i < ls.length; i++) { if (f1.equals(ls[i])) { f1Exists = true; } if (f2.equals(ls[i])) { f2Exists = true; } } if (! f1Exists || ! f2Exists) { caseInsensitive = 1; } else { // There are two files. caseInsensitive = -1; } } else { caseInsensitive = -1; } } protected String canonicalize(String fileName) { return fileName; } }