SmartFileManager.java revision 3376:4c740bddc648
1/*
2 * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.tools.sjavac.comp;
27
28import java.io.IOException;
29import java.io.PrintWriter;
30import java.net.URI;
31import java.nio.file.Path;
32import java.nio.file.Paths;
33import java.util.HashMap;
34import java.util.HashSet;
35import java.util.Map;
36import java.util.Set;
37
38import javax.tools.*;
39import javax.tools.JavaFileObject.Kind;
40
41import com.sun.tools.javac.file.JavacFileManager;
42import com.sun.tools.javac.util.DefinedBy;
43import com.sun.tools.javac.util.DefinedBy.Api;
44import com.sun.tools.javac.util.ListBuffer;
45
46/**
47 * Intercepts reads and writes to the file system to gather
48 * information about what artifacts are generated.
49 *
50 * Traps writes to certain files, if the content written is identical
51 * to the existing file.
52 *
53 * Can also blind out the filemanager from seeing certain files in the file system.
54 * Necessary to prevent javac from seeing some sources where the source path points.
55 *
56 *  <p><b>This is NOT part of any supported API.
57 *  If you write code that depends on this, you do so at your own risk.
58 *  This code and its internal interfaces are subject to change or
59 *  deletion without notice.</b>
60 */
61@com.sun.tools.javac.api.ClientCodeWrapper.Trusted
62public class SmartFileManager extends ForwardingJavaFileManager<JavaFileManager> {
63
64    // Set of sources that can be seen by javac.
65    Set<URI> visibleSources = new HashSet<>();
66    // Map from modulename:packagename to artifacts.
67    Map<String,Set<URI>> packageArtifacts = new HashMap<>();
68
69    public SmartFileManager(JavaFileManager fileManager) {
70        super(fileManager);
71    }
72
73    public void setVisibleSources(Set<URI> s) {
74        visibleSources = s;
75    }
76
77    public void cleanArtifacts() {
78        packageArtifacts = new HashMap<>();
79    }
80
81    /**
82     * Set whether or not to use ct.sym as an alternate to rt.jar.
83     */
84    public void setSymbolFileEnabled(boolean b) {
85        if (!(fileManager instanceof JavacFileManager))
86            throw new IllegalStateException();
87        ((JavacFileManager) fileManager).setSymbolFileEnabled(b);
88    }
89
90    @DefinedBy(Api.COMPILER)
91    public String inferBinaryName(Location location, JavaFileObject file) {
92        return super.inferBinaryName(location, locUnwrap(file));
93    }
94
95
96    public Map<String,Set<URI>> getPackageArtifacts() {
97        return packageArtifacts;
98    }
99
100    @Override @DefinedBy(Api.COMPILER)
101    public Iterable<JavaFileObject> list(Location location,
102                                         String packageName,
103                                         Set<Kind> kinds,
104                                         boolean recurse) throws IOException {
105        // TODO: Do this lazily by returning an iterable with a filtering Iterator
106        // Acquire the list of files.
107        Iterable<JavaFileObject> files = super.list(location, packageName, kinds, recurse);
108        if (visibleSources.isEmpty()) {
109            return locWrapMany(files, location);
110        }
111        // Now filter!
112        ListBuffer<JavaFileObject> filteredFiles = new ListBuffer<>();
113        for (JavaFileObject f : files) {
114            URI uri = f.toUri();
115            String t = uri.toString();
116            if (t.startsWith("jar:")
117                || t.endsWith(".class")
118                || visibleSources.contains(uri)) {
119                filteredFiles.add(f);
120            }
121        }
122
123        return locWrapMany(filteredFiles, location);
124    }
125
126    @Override @DefinedBy(Api.COMPILER)
127    public JavaFileObject getJavaFileForInput(Location location,
128                                              String className,
129                                              Kind kind) throws IOException {
130        JavaFileObject file = super.getJavaFileForInput(location, className, kind);
131        file = locWrap(file, location);
132        if (file == null || visibleSources.isEmpty()) {
133            return file;
134        }
135
136        if (visibleSources.contains(file.toUri()) || isModuleInfo(file)) {
137            return file;
138        }
139        return null;
140    }
141
142    @Override @DefinedBy(Api.COMPILER)
143    public JavaFileObject getJavaFileForOutput(Location location,
144                                               String className,
145                                               Kind kind,
146                                               FileObject sibling) throws IOException {
147        JavaFileObject file = super.getJavaFileForOutput(location, className, kind, sibling);
148        file = locWrap(file, location);
149        if (file == null) return file;
150        int dp = className.lastIndexOf('.');
151        String pkg_name = "";
152        if (dp != -1) {
153            pkg_name = className.substring(0, dp);
154        }
155        // When modules are in use, then the mod_name might be something like "jdk_base"
156        String mod_name = "";
157        addArtifact(mod_name+":"+pkg_name, file.toUri());
158        return file;
159    }
160
161    @Override @DefinedBy(Api.COMPILER)
162    public FileObject getFileForInput(Location location,
163                                      String packageName,
164                                      String relativeName) throws IOException {
165        FileObject file =  super.getFileForInput(location, packageName, relativeName);
166        file = locWrap(file, location);
167        if (file == null || visibleSources.isEmpty()) {
168            return file;
169        }
170
171        if (visibleSources.contains(file.toUri()) || isModuleInfo(file)) {
172            return file;
173        }
174        return null;
175    }
176
177    private boolean isModuleInfo(FileObject fo) {
178        if (fo instanceof JavaFileObject) {
179            JavaFileObject jfo = (JavaFileObject) fo;
180            return jfo.isNameCompatible("module-info", Kind.SOURCE)
181                || jfo.isNameCompatible("module-info", Kind.CLASS);
182        }
183        return false;
184    }
185
186    @Override @DefinedBy(Api.COMPILER)
187    public FileObject getFileForOutput(Location location,
188                                       String packageName,
189                                       String relativeName,
190                                       FileObject sibling) throws IOException {
191        FileObject superFile = super.getFileForOutput(location, packageName, relativeName, sibling);
192        FileObject file = locWrap(superFile, location);
193        if (file == null) return file;
194
195        if (location.equals(StandardLocation.NATIVE_HEADER_OUTPUT) && superFile instanceof JavaFileObject) {
196           file = new SmartFileObject((JavaFileObject) file);
197           packageName = ":" + packageNameFromFileName(relativeName);
198        }
199        if (packageName.equals("")) {
200            packageName = ":";
201        }
202        addArtifact(packageName, file.toUri());
203        return file;
204    }
205
206    @Override @DefinedBy(Api.COMPILER)
207    public Location getModuleLocation(Location location, JavaFileObject fo, String pkgName) throws IOException {
208        return super.getModuleLocation(location, locUnwrap(fo), pkgName);
209    }
210
211    private static String packageNameFromFileName(String fn) {
212        StringBuilder sb = new StringBuilder();
213        int p = fn.indexOf('_'), pp = 0;
214        while (p != -1) {
215            if (sb.length() > 0) sb.append('.');
216            sb.append(fn.substring(pp,p));
217            if (p == fn.length()-1) break;
218            pp = p+1;
219            p = fn.indexOf('_',pp);
220        }
221        return sb.toString();
222    }
223
224    void addArtifact(String pkgName, URI art) {
225        Set<URI> s = packageArtifacts.get(pkgName);
226        if (s == null) {
227            s = new HashSet<>();
228            packageArtifacts.put(pkgName, s);
229        }
230        s.add(art);
231    }
232
233    public static JavaFileObject locWrap(JavaFileObject jfo, Location loc) {
234
235        // From sjavac's perspective platform classes are not interesting and
236        // there is no need to track the location for these file objects.
237        // Also, there exists some jfo instanceof checks which breaks if
238        // the jfos for platform classes are wrapped.
239        if (loc == StandardLocation.PLATFORM_CLASS_PATH)
240            return jfo;
241
242        return jfo == null ? null : new JavaFileObjectWithLocation<>(jfo, loc);
243    }
244
245    private static FileObject locWrap(FileObject fo, Location loc) {
246        if (fo instanceof JavaFileObject)
247            return locWrap((JavaFileObject) fo, loc);
248        return fo == null ? null : new FileObjectWithLocation<>(fo, loc);
249    }
250
251    @DefinedBy(Api.COMPILER)
252    @Override
253    public boolean isSameFile(FileObject a, FileObject b) {
254        return super.isSameFile(locUnwrap(a), locUnwrap(b));
255    }
256
257    private static ListBuffer<JavaFileObject> locWrapMany(Iterable<JavaFileObject> jfos,
258                                                          Location loc) {
259        ListBuffer<JavaFileObject> locWrapped = new ListBuffer<>();
260        for (JavaFileObject f : jfos)
261            locWrapped.add(locWrap(f, loc));
262        return locWrapped;
263    }
264
265    private static FileObject locUnwrap(FileObject fo) {
266        if (fo instanceof FileObjectWithLocation<?>)
267            return ((FileObjectWithLocation<?>) fo).getDelegate();
268        if (fo instanceof JavaFileObjectWithLocation<?>)
269            return ((JavaFileObjectWithLocation<?>) fo).getDelegate();
270        return fo;
271    }
272
273    private static JavaFileObject locUnwrap(JavaFileObject fo) {
274        if (fo instanceof JavaFileObjectWithLocation<?>)
275            return ((JavaFileObjectWithLocation<?>) fo).getDelegate();
276        return fo;
277    }
278}
279