1 /*
   2  * Copyright (c) 2012, 2014, 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 
  26 package com.sun.tools.sjavac.comp;
  27 
  28 import java.io.IOException;
  29 import java.io.PrintWriter;
  30 import java.net.URI;
  31 import java.util.HashMap;
  32 import java.util.HashSet;
  33 import java.util.Map;
  34 import java.util.Set;
  35 
  36 import javax.tools.*;
  37 import javax.tools.JavaFileObject.Kind;
  38 
  39 import com.sun.tools.javac.file.JavacFileManager;
  40 import com.sun.tools.javac.util.DefinedBy;
  41 import com.sun.tools.javac.util.DefinedBy.Api;
  42 import 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
  60 public 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     private static JavaFileObject locWrap(JavaFileObject jfo, Location loc) {
 224         return jfo == null ? null : new JavaFileObjectWithLocation<>(jfo, loc);
 225     }
 226 
 227     private static FileObject locWrap(FileObject fo, Location loc) {
 228         if (fo instanceof JavaFileObject)
 229             return locWrap((JavaFileObject) fo, loc);
 230         return fo == null ? null : new FileObjectWithLocation<>(fo, loc);
 231     }
 232 
 233     @DefinedBy(Api.COMPILER)
 234     @Override
 235     public boolean isSameFile(FileObject a, FileObject b) {
 236         return super.isSameFile(locUnwrap(a), locUnwrap(b));
 237     }
 238 
 239     private static ListBuffer<JavaFileObject> locWrapMany(Iterable<JavaFileObject> jfos,
 240                                                           Location loc) {
 241         ListBuffer<JavaFileObject> locWrapped = new ListBuffer<>();
 242         for (JavaFileObject f : jfos)
 243             locWrapped.add(locWrap(f, loc));
 244         return locWrapped;
 245     }
 246 
 247     private static FileObject locUnwrap(FileObject fo) {
 248         if (fo instanceof FileObjectWithLocation<?>)
 249             return ((FileObjectWithLocation<?>) fo).getDelegate();
 250         if (fo instanceof JavaFileObjectWithLocation<?>)
 251             return ((JavaFileObjectWithLocation<?>) fo).getDelegate();
 252         return fo;
 253     }
 254 
 255     private static JavaFileObject locUnwrap(JavaFileObject fo) {
 256         if (fo instanceof JavaFileObjectWithLocation<?>)
 257             return ((JavaFileObjectWithLocation<?>) fo).getDelegate();
 258         return fo;
 259     }
 260 }