package org.jetbrains.jps.incremental.java; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.tools.*; import java.io.File; import java.io.IOException; import java.net.URI; import java.util.Collections; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * @author Eugene Zhuravlev * Date: 9/24/11 */ class JavacFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> { private final Context myContext; private Map<File, Set<File>> myOutputsMap = Collections.emptyMap(); static interface Context { StandardJavaFileManager getStandardFileManager(); void consumeOutputFile(OutputFileObject obj); void reportMessage(final Diagnostic.Kind kind, String message); void ensurePendingTasksCompleted(); } public JavacFileManager(Context context) { super(context.getStandardFileManager()); myContext = context; } // todo: check if reading source files can be optimized public boolean setOutputDirectories(final Map<File, Set<File>> outputDirToSrcRoots) { for (File outputDir : outputDirToSrcRoots.keySet()) { // this will validate output dirs if (!setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(outputDir))) { return false; } } myOutputsMap = outputDirToSrcRoots; return true; } public boolean setLocation(Location location, Iterable<? extends File> path) { try { getStdManager().setLocation(location, path); } catch (IOException e) { myContext.reportMessage(Diagnostic.Kind.ERROR, e.getMessage()); return false; } return true; } public boolean isSameFile(FileObject a, FileObject b) { if (a instanceof OutputFileObject && b instanceof OutputFileObject) { return a.equals(b); } return super.isSameFile(a, b); } public Iterable<JavaFileObject> list(Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException { return super.list(location, packageName, kinds, recurse); } public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { if (kind != JavaFileObject.Kind.SOURCE && kind != JavaFileObject.Kind.CLASS) { throw new IllegalArgumentException("Invalid kind " + kind); } return getFileForOutput(location, kind, externalizeFileName(className, kind), className, sibling); } public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException { final StringBuilder name = new StringBuilder(); if (packageName.isEmpty()) { name.append(relativeName); } else { name.append(externalizeFileName(packageName)).append(File.separatorChar).append(relativeName); } final String fileName = name.toString(); return getFileForOutput(location, getKind(fileName), fileName, null, sibling); } private OutputFileObject getFileForOutput(Location location, JavaFileObject.Kind kind, String fileName, @Nullable String className, FileObject sibling) throws IOException { JavaFileObject src = null; if (sibling instanceof JavaFileObject) { final JavaFileObject javaFileObject = (JavaFileObject)sibling; if (javaFileObject.getKind() == JavaFileObject.Kind.SOURCE) { src = javaFileObject; } } File dir = getSingleOutputDirectory(location, src); if (location == StandardLocation.CLASS_OUTPUT) { if (dir == null) { throw new IOException("Output directory is not specified"); } } else if (location == StandardLocation.SOURCE_OUTPUT) { if (dir == null) { dir = getSingleOutputDirectory(StandardLocation.CLASS_OUTPUT, src); if (dir == null) { throw new IOException("Neither class output directory nor source output are specified"); } } } final File file = (dir == null? new File(fileName) : new File(dir, fileName)); return new OutputFileObject(myContext, file, kind, className, src); } private File getSingleOutputDirectory(final Location loc, final JavaFileObject sourceFile) { if (loc == StandardLocation.CLASS_OUTPUT) { if (myOutputsMap.size() > 1 && sourceFile != null) { // multiple outputs case final File outputDir = findOutputDir(new File(sourceFile.toUri())); if (outputDir != null) { return outputDir; } } } final Iterable<? extends File> location = getStdManager().getLocation(loc); if (location != null) { final Iterator<? extends File> it = location.iterator(); if (it.hasNext()) { return it.next(); } } return null; } private File findOutputDir(File src) { File file = src.getParentFile(); while (file != null) { for (Map.Entry<File, Set<File>> entry : myOutputsMap.entrySet()) { if (entry.getValue().contains(file)) { return entry.getKey(); } } file = file.getParentFile(); } return null; } @NotNull private StandardJavaFileManager getStdManager() { return fileManager; } public Iterable<? extends JavaFileObject> toJavaFileObjects(Iterable<? extends File> files) { return getStdManager().getJavaFileObjectsFromFiles(files); } public void cleanupResources() { try { fileManager.close(); } catch (IOException e) { e.printStackTrace(); // todo } } private static URI toURI(String outputDir, String name, JavaFileObject.Kind kind) { return createUri("file:///" + outputDir.replace('\\','/') + "/" + name.replace('.', '/') + kind.extension); } private static URI createUri(String url) { return URI.create(url.replaceAll(" ","%20")); } private static JavaFileObject.Kind getKind(String name) { if (name.endsWith(JavaFileObject.Kind.CLASS.extension)){ return JavaFileObject.Kind.CLASS; } if (name.endsWith(JavaFileObject.Kind.SOURCE.extension)) { return JavaFileObject.Kind.SOURCE; } if (name.endsWith(JavaFileObject.Kind.HTML.extension)) { return JavaFileObject.Kind.HTML; } return JavaFileObject.Kind.OTHER; } private static String externalizeFileName(CharSequence cs, JavaFileObject.Kind kind) { return externalizeFileName(cs) + kind.extension; } private static String externalizeFileName(CharSequence name) { return name.toString().replace('.', File.separatorChar); } }