do not convert spaces for local paths
[idea/community.git] / jps / jps-builders / src / org / jetbrains / jps / javac / JavacFileManager.java
1 package org.jetbrains.jps.javac;
2
3 import com.intellij.openapi.util.io.FileUtil;
4 import org.jetbrains.annotations.NotNull;
5 import org.jetbrains.annotations.Nullable;
6 import org.jetbrains.jps.incremental.Paths;
7
8 import javax.tools.*;
9 import java.io.File;
10 import java.io.IOException;
11 import java.util.Collections;
12 import java.util.Iterator;
13 import java.util.Map;
14 import java.util.Set;
15
16 /**
17  * @author Eugene Zhuravlev
18  *         Date: 9/24/11
19  */
20 class JavacFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {
21
22   private final Context myContext;
23   private Map<File, Set<File>> myOutputsMap = Collections.emptyMap();
24
25   interface Context {
26     boolean isCanceled();
27
28     StandardJavaFileManager getStandardFileManager();
29
30     void consumeOutputFile(@NotNull OutputFileObject obj);
31
32     void reportMessage(final Diagnostic.Kind kind, String message);
33   }
34
35   public JavacFileManager(Context context) {
36     super(context.getStandardFileManager());
37     myContext = context;
38   }
39
40   public boolean setOutputDirectories(final Map<File, Set<File>> outputDirToSrcRoots) {
41     for (File outputDir : outputDirToSrcRoots.keySet()) {
42       // this will validate output dirs
43       if (!setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(outputDir))) {
44         return false;
45       }
46     }
47     myOutputsMap = outputDirToSrcRoots;
48     return true;
49   }
50
51   public boolean setLocation(Location location, Iterable<? extends File> path) {
52     try {
53       getStdManager().setLocation(location, path);
54     }
55     catch (IOException e) {
56       myContext.reportMessage(Diagnostic.Kind.ERROR, e.getMessage());
57       return false;
58     }
59     return true;
60   }
61
62   public boolean isSameFile(FileObject a, FileObject b) {
63     if (a instanceof OutputFileObject && b instanceof OutputFileObject) {
64       return a.equals(b);
65     }
66     return super.isSameFile(a, b);
67   }
68
69   public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
70     if (kind != JavaFileObject.Kind.SOURCE && kind != JavaFileObject.Kind.CLASS) {
71       throw new IllegalArgumentException("Invalid kind " + kind);
72     }
73     return getFileForOutput(location, kind, externalizeFileName(className, kind), className, sibling);
74   }
75
76   public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException {
77     final StringBuilder name = new StringBuilder();
78     if (packageName.isEmpty()) {
79       name.append(relativeName);
80     }
81     else {
82       name.append(externalizeFileName(packageName)).append(File.separatorChar).append(relativeName);
83     }
84     final String fileName = name.toString();
85     return getFileForOutput(location, getKind(fileName), fileName, null, sibling);
86   }
87
88   private OutputFileObject getFileForOutput(Location location, JavaFileObject.Kind kind, String fileName, @Nullable String className, FileObject sibling) throws IOException {
89     JavaFileObject src = null;
90     if (sibling instanceof JavaFileObject) {
91       final JavaFileObject javaFileObject = (JavaFileObject)sibling;
92       if (javaFileObject.getKind() == JavaFileObject.Kind.SOURCE) {
93         src = javaFileObject;
94       }
95     }
96
97     File dir = getSingleOutputDirectory(location, src);
98
99     if (location == StandardLocation.CLASS_OUTPUT) {
100       if (dir == null) {
101         throw new IOException("Output directory is not specified");
102       }
103     }
104     else if (location == StandardLocation.SOURCE_OUTPUT) {
105       if (dir == null) {
106         dir = getSingleOutputDirectory(StandardLocation.CLASS_OUTPUT, src);
107         if (dir == null) {
108           throw new IOException("Neither class output directory nor source output are specified");
109         }
110       }
111     }
112     final File file = (dir == null? new File(fileName).getAbsoluteFile() : new File(dir, fileName));
113     return new OutputFileObject(myContext, dir, fileName, file, kind, className, src != null? src.toUri() : null);
114   }
115
116   private File getSingleOutputDirectory(final Location loc, final JavaFileObject sourceFile) {
117     if (loc == StandardLocation.CLASS_OUTPUT) {
118       if (myOutputsMap.size() > 1 && sourceFile != null) {
119         // multiple outputs case
120         final File outputDir = findOutputDir(Paths.convertToFile(sourceFile.toUri()));
121         if (outputDir != null) {
122           return outputDir;
123         }
124       }
125     }
126
127     final Iterable<? extends File> location = getStdManager().getLocation(loc);
128     if (location != null) {
129       final Iterator<? extends File> it = location.iterator();
130       if (it.hasNext()) {
131         return it.next();
132       }
133     }
134     return null;
135   }
136
137   private File findOutputDir(File src) {
138     File file = FileUtil.getParentFile(src);
139     while (file != null) {
140       for (Map.Entry<File, Set<File>> entry : myOutputsMap.entrySet()) {
141         if (entry.getValue().contains(file)) {
142           return entry.getKey();
143         }
144       }
145       file = FileUtil.getParentFile(file);
146     }
147     return null;
148   }
149
150   @NotNull
151   private StandardJavaFileManager getStdManager() {
152     return fileManager;
153   }
154
155   public Iterable<? extends JavaFileObject> toJavaFileObjects(Iterable<? extends File> files) {
156     return getStdManager().getJavaFileObjectsFromFiles(files);
157   }
158
159   @Override
160   public void close() {
161     try {
162       super.close();
163     }
164     catch (IOException e) {
165       throw new RuntimeException(e);
166     }
167     finally {
168       myOutputsMap = Collections.emptyMap();
169     }
170   }
171
172   private static JavaFileObject.Kind getKind(String name) {
173     if (name.endsWith(JavaFileObject.Kind.CLASS.extension)){
174       return JavaFileObject.Kind.CLASS;
175     }
176     if (name.endsWith(JavaFileObject.Kind.SOURCE.extension)) {
177       return JavaFileObject.Kind.SOURCE;
178     }
179     if (name.endsWith(JavaFileObject.Kind.HTML.extension)) {
180       return JavaFileObject.Kind.HTML;
181     }
182     return JavaFileObject.Kind.OTHER;
183   }
184
185   private static String externalizeFileName(CharSequence cs, JavaFileObject.Kind kind) {
186     return externalizeFileName(cs) + kind.extension;
187   }
188
189   private static String externalizeFileName(CharSequence name) {
190     return name.toString().replace('.', File.separatorChar);
191   }
192
193 }