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     public Map<String,Set<URI>> getPackageArtifacts() {
  95         return packageArtifacts;
  96     }
  97 
  98     @Override @DefinedBy(Api.COMPILER)
  99     public Iterable<JavaFileObject> list(Location location,
 100                                          String packageName,
 101                                          Set<Kind> kinds,
 102                                          boolean recurse) throws IOException {
 103         // Acquire the list of files.
 104         Iterable<JavaFileObject> files = super.list(location, packageName, kinds, recurse);
 105         if (visibleSources.isEmpty()) {
 106             return files;
 107         }
 108         // Now filter!
 109         ListBuffer<JavaFileObject> filteredFiles = new ListBuffer<>();
 110         for (JavaFileObject f : files) {
 111             URI uri = f.toUri();
 112             String t = uri.toString();
 113             if (t.startsWith("jar:")
 114                 || t.endsWith(".class")
 115                 || visibleSources.contains(uri)) {
 116                 filteredFiles.add(f);
 117             }
 118         }
 119         return filteredFiles;
 120     }
 121 
 122     @Override @DefinedBy(Api.COMPILER)
 123     public boolean hasLocation(Location location) {
 124         return super.hasLocation(location);
 125     }
 126 
 127     @Override @DefinedBy(Api.COMPILER)
 128     public JavaFileObject getJavaFileForInput(Location location,
 129                                               String className,
 130                                               Kind kind) throws IOException {
 131         JavaFileObject file = super.getJavaFileForInput(location, className, kind);
 132         if (file == null || visibleSources.isEmpty()) {
 133             return file;
 134         }
 135 
 136         if (visibleSources.contains(file.toUri())) {
 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         if (file == null) return file;
 149         int dp = className.lastIndexOf('.');
 150         String pkg_name = "";
 151         if (dp != -1) {
 152             pkg_name = className.substring(0, dp);
 153         }
 154         // When modules are in use, then the mod_name might be something like "jdk_base"
 155         String mod_name = "";
 156         addArtifact(mod_name+":"+pkg_name, file.toUri());
 157         return file;
 158     }
 159 
 160     @Override @DefinedBy(Api.COMPILER)
 161     public FileObject getFileForInput(Location location,
 162                                       String packageName,
 163                                       String relativeName) throws IOException {
 164         FileObject file =  super.getFileForInput(location, packageName, relativeName);
 165         if (file == null || visibleSources.isEmpty()) {
 166             return file;
 167         }
 168 
 169         if (visibleSources.contains(file.toUri())) {
 170             return file;
 171         }
 172         return null;
 173     }
 174 
 175     @Override @DefinedBy(Api.COMPILER)
 176     public FileObject getFileForOutput(Location location,
 177                                        String packageName,
 178                                        String relativeName,
 179                                        FileObject sibling) throws IOException {
 180         FileObject file = super.getFileForOutput(location, packageName, relativeName, sibling);
 181         if (file == null) return file;
 182         if (location.equals(StandardLocation.NATIVE_HEADER_OUTPUT) &&
 183                 file instanceof JavaFileObject) {
 184            file = new SmartFileObject((JavaFileObject)file, stdout);
 185            packageName = ":" + packageNameFromFileName(relativeName);
 186         }
 187         if (packageName.equals("")) {
 188             packageName = ":";
 189         }
 190         addArtifact(packageName, file.toUri());
 191         return file;
 192     }
 193 
 194     private String packageNameFromFileName(String fn) {
 195         StringBuilder sb = new StringBuilder();
 196         int p = fn.indexOf('_'), pp = 0;
 197         while (p != -1) {
 198             if (sb.length() > 0) sb.append('.');
 199             sb.append(fn.substring(pp,p));
 200             if (p == fn.length()-1) break;
 201             pp = p+1;
 202             p = fn.indexOf('_',pp);
 203         }
 204         return sb.toString();
 205     }
 206 
 207     @Override @DefinedBy(Api.COMPILER)
 208     public void flush() throws IOException {
 209         super.flush();
 210     }
 211 
 212     @Override @DefinedBy(Api.COMPILER)
 213     public void close() throws IOException {
 214         super.close();
 215     }
 216 
 217     void addArtifact(String pkgName, URI art) {
 218         Set<URI> s = packageArtifacts.get(pkgName);
 219         if (s == null) {
 220             s = new HashSet<>();
 221             packageArtifacts.put(pkgName, s);
 222         }
 223         s.add(art);
 224     }
 225 }