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