SmartFileManager.java revision 2958:27da0c3ac83a
1/*
2 * Copyright (c) 2012, 2015, 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.util.HashMap;
32import java.util.HashSet;
33import java.util.Map;
34import java.util.Set;
35
36import javax.tools.*;
37import javax.tools.JavaFileObject.Kind;
38
39import com.sun.tools.javac.file.JavacFileManager;
40import com.sun.tools.javac.util.DefinedBy;
41import com.sun.tools.javac.util.DefinedBy.Api;
42import com.sun.tools.javac.util.ListBuffer;
43
44/**
45 * Intercepts reads and writes to the file system to gather
46 * information about what artifacts are generated.
47 *
48 * Traps writes to certain files, if the content written is identical
49 * to the existing file.
50 *
51 * Can also blind out the filemanager from seeing certain files in the file system.
52 * Necessary to prevent javac from seeing some sources where the source path points.
53 *
54 *  <p><b>This is NOT part of any supported API.
55 *  If you write code that depends on this, you do so at your own risk.
56 *  This code and its internal interfaces are subject to change or
57 *  deletion without notice.</b>
58 */
59@com.sun.tools.javac.api.ClientCodeWrapper.Trusted
60public class SmartFileManager extends ForwardingJavaFileManager<JavaFileManager> {
61
62    // Set of sources that can be seen by javac.
63    Set<URI> visibleSources = new HashSet<>();
64    // Map from modulename:packagename to artifacts.
65    Map<String,Set<URI>> packageArtifacts = new HashMap<>();
66    // Where to print informational messages.
67    PrintWriter stdout;
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    public void setLog(PrintWriter pw) {
82        stdout = pw;
83    }
84
85    /**
86     * Set whether or not to use ct.sym as an alternate to rt.jar.
87     */
88    public void setSymbolFileEnabled(boolean b) {
89        if (!(fileManager instanceof JavacFileManager))
90            throw new IllegalStateException();
91        ((JavacFileManager) fileManager).setSymbolFileEnabled(b);
92    }
93
94    @DefinedBy(Api.COMPILER)
95    public String inferBinaryName(Location location, JavaFileObject file) {
96        return super.inferBinaryName(location, locUnwrap(file));
97    }
98
99
100    public Map<String,Set<URI>> getPackageArtifacts() {
101        return packageArtifacts;
102    }
103
104    @Override @DefinedBy(Api.COMPILER)
105    public Iterable<JavaFileObject> list(Location location,
106                                         String packageName,
107                                         Set<Kind> kinds,
108                                         boolean recurse) throws IOException {
109        // TODO: Do this lazily by returning an iterable with a filtering Iterator
110        // Acquire the list of files.
111        Iterable<JavaFileObject> files = super.list(location, packageName, kinds, recurse);
112        if (visibleSources.isEmpty()) {
113            return locWrapMany(files, location);
114        }
115        // Now filter!
116        ListBuffer<JavaFileObject> filteredFiles = new ListBuffer<>();
117        for (JavaFileObject f : files) {
118            URI uri = f.toUri();
119            String t = uri.toString();
120            if (t.startsWith("jar:")
121                || t.endsWith(".class")
122                || visibleSources.contains(uri)) {
123                filteredFiles.add(f);
124            }
125        }
126
127        return locWrapMany(filteredFiles, location);
128    }
129
130    @Override @DefinedBy(Api.COMPILER)
131    public JavaFileObject getJavaFileForInput(Location location,
132                                              String className,
133                                              Kind kind) throws IOException {
134        JavaFileObject file = super.getJavaFileForInput(location, className, kind);
135        file = locWrap(file, location);
136        if (file == null || visibleSources.isEmpty()) {
137            return file;
138        }
139
140        if (visibleSources.contains(file.toUri())) {
141            return file;
142        }
143        return null;
144    }
145
146    @Override @DefinedBy(Api.COMPILER)
147    public JavaFileObject getJavaFileForOutput(Location location,
148                                               String className,
149                                               Kind kind,
150                                               FileObject sibling) throws IOException {
151        JavaFileObject file = super.getJavaFileForOutput(location, className, kind, sibling);
152        file = locWrap(file, location);
153        if (file == null) return file;
154        int dp = className.lastIndexOf('.');
155        String pkg_name = "";
156        if (dp != -1) {
157            pkg_name = className.substring(0, dp);
158        }
159        // When modules are in use, then the mod_name might be something like "jdk_base"
160        String mod_name = "";
161        addArtifact(mod_name+":"+pkg_name, file.toUri());
162        return file;
163    }
164
165    @Override @DefinedBy(Api.COMPILER)
166    public FileObject getFileForInput(Location location,
167                                      String packageName,
168                                      String relativeName) throws IOException {
169        FileObject file =  super.getFileForInput(location, packageName, relativeName);
170        file = locWrap(file, location);
171        if (file == null || visibleSources.isEmpty()) {
172            return file;
173        }
174
175        if (visibleSources.contains(file.toUri())) {
176            return file;
177        }
178        return null;
179    }
180
181    @Override @DefinedBy(Api.COMPILER)
182    public FileObject getFileForOutput(Location location,
183                                       String packageName,
184                                       String relativeName,
185                                       FileObject sibling) throws IOException {
186        FileObject superFile = super.getFileForOutput(location, packageName, relativeName, sibling);
187        FileObject file = locWrap(superFile, location);
188        if (file == null) return file;
189
190        if (location.equals(StandardLocation.NATIVE_HEADER_OUTPUT) && superFile instanceof JavaFileObject) {
191           file = new SmartFileObject((JavaFileObject) file, stdout);
192           packageName = ":" + packageNameFromFileName(relativeName);
193        }
194        if (packageName.equals("")) {
195            packageName = ":";
196        }
197        addArtifact(packageName, file.toUri());
198        return file;
199    }
200
201    private static String packageNameFromFileName(String fn) {
202        StringBuilder sb = new StringBuilder();
203        int p = fn.indexOf('_'), pp = 0;
204        while (p != -1) {
205            if (sb.length() > 0) sb.append('.');
206            sb.append(fn.substring(pp,p));
207            if (p == fn.length()-1) break;
208            pp = p+1;
209            p = fn.indexOf('_',pp);
210        }
211        return sb.toString();
212    }
213
214    void addArtifact(String pkgName, URI art) {
215        Set<URI> s = packageArtifacts.get(pkgName);
216        if (s == null) {
217            s = new HashSet<>();
218            packageArtifacts.put(pkgName, s);
219        }
220        s.add(art);
221    }
222
223    public static JavaFileObject locWrap(JavaFileObject jfo, Location loc) {
224
225        // From sjavac's perspective platform classes are not interesting and
226        // there is no need to track the location for these file objects.
227        // Also, there exists some jfo instanceof checks which breaks if
228        // the jfos for platform classes are wrapped.
229        if (loc == StandardLocation.PLATFORM_CLASS_PATH)
230            return jfo;
231
232        return jfo == null ? null : new JavaFileObjectWithLocation<>(jfo, loc);
233    }
234
235    private static FileObject locWrap(FileObject fo, Location loc) {
236        if (fo instanceof JavaFileObject)
237            return locWrap((JavaFileObject) fo, loc);
238        return fo == null ? null : new FileObjectWithLocation<>(fo, loc);
239    }
240
241    @DefinedBy(Api.COMPILER)
242    @Override
243    public boolean isSameFile(FileObject a, FileObject b) {
244        return super.isSameFile(locUnwrap(a), locUnwrap(b));
245    }
246
247    private static ListBuffer<JavaFileObject> locWrapMany(Iterable<JavaFileObject> jfos,
248                                                          Location loc) {
249        ListBuffer<JavaFileObject> locWrapped = new ListBuffer<>();
250        for (JavaFileObject f : jfos)
251            locWrapped.add(locWrap(f, loc));
252        return locWrapped;
253    }
254
255    private static FileObject locUnwrap(FileObject fo) {
256        if (fo instanceof FileObjectWithLocation<?>)
257            return ((FileObjectWithLocation<?>) fo).getDelegate();
258        if (fo instanceof JavaFileObjectWithLocation<?>)
259            return ((JavaFileObjectWithLocation<?>) fo).getDelegate();
260        return fo;
261    }
262
263    private static JavaFileObject locUnwrap(JavaFileObject fo) {
264        if (fo instanceof JavaFileObjectWithLocation<?>)
265            return ((JavaFileObjectWithLocation<?>) fo).getDelegate();
266        return fo;
267    }
268}
269