1 /* 2 * Copyright (c) 1999, 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 javax.lang.model.element.Element; 29 import javax.lang.model.element.TypeElement; 30 import java.util.Arrays; 31 import java.util.ArrayList; 32 import java.util.Comparator; 33 import java.util.List; 34 import java.util.LinkedList; 35 import java.util.HashMap; 36 import java.util.HashSet; 37 import java.util.Map; 38 import java.util.Set; 39 40 import com.sun.tools.javac.code.Symbol.ClassSymbol; 41 import com.sun.tools.javac.util.Context; 42 import com.sun.tools.javac.util.Log; 43 import com.sun.tools.javac.util.Name; 44 45 /** Utility class containing dependency information between packages 46 * and the pubapi for a package. 47 * 48 * <p><b>This is NOT part of any supported API. 49 * If you write code that depends on this, you do so at your own 50 * risk. This code and its internal interfaces are subject to change 51 * or deletion without notice.</b></p> 52 */ 53 public class Dependencies { 54 protected static final Context.Key<Dependencies> dependenciesKey = new Context.Key<>(); 55 56 // The log to be used for error reporting. 57 protected Log log; 58 // Map from package name to packages that the package depends upon. 59 protected Map<Name,Set<Name>> deps; 60 // Map from package name to non-sourcefile classes that the package depends upon. 61 protected Map<Name,Set<ClassSymbol>> classDeps; 62 // This is the set of all packages that are supplied 63 // through the java files at the command line. 64 protected Set<Name> explicitPackages; 65 66 // This is the set of all classes found outside of the source, ie the classpath. 67 protected Set<Name> classpathClasses; 68 69 // Map from a class name to its public api. 70 protected Map<Name,List<String>> publicApiPerClass; 71 72 public static Dependencies instance(Context context) { 73 Dependencies instance = context.get(dependenciesKey); 74 if (instance == null) 75 instance = new Dependencies(context); 76 return instance; 77 } 78 79 private Dependencies(Context context) { 80 context.put(dependenciesKey, this); 81 log = Log.instance(context); 82 deps = new HashMap<>(); 83 classDeps = new HashMap<>(); 84 explicitPackages = new HashSet<>(); 85 publicApiPerClass = new HashMap<>(); 86 } 87 88 /** 89 * Fetch the set of dependencies that are relevant to the compile 90 * that has just been performed. I.e. we are only interested in 91 * dependencies for classes that were explicitly compiled. 92 * @return 93 */ 94 public Map<String,Set<String>> getSourcefileDependencies() { 95 Map<String,Set<String>> new_deps = new HashMap<>(); 96 for (Name pkg : explicitPackages) { 97 Set<Name> set = deps.get(pkg); 98 if (set != null) { 99 String pkg_name = pkg.toString(); 100 Set<String> new_set = new_deps.get(pkg_name); 101 if (new_set == null) { 102 new_set = new HashSet<>(); 103 // Modules beware.... 104 new_deps.put(":"+pkg_name, new_set); 105 } 106 for (Name d : set) { 107 new_set.add(":"+d.toString()); 108 } 109 } 110 } 111 return new_deps; 112 } 113 114 /** 115 * Fetch the set of classpath dependencies that our sources depend upon. 116 * @return 117 */ 118 public Map<String,Set<String>> getClasspathDependencies() { 119 Map<String,Set<String>> new_deps = new HashMap<>(); 120 121 for (Name pkg : classDeps.keySet()) { 122 if (explicitPackages.contains(pkg)) { 123 continue; 124 } 125 Set<ClassSymbol> set = classDeps.get(pkg); 126 String pkg_name = pkg.toString(); 127 Set<String> new_set = new_deps.get(pkg_name); 128 if (new_set == null) { 129 new_set = new HashSet<>(); 130 // Modules beware.... 131 new_deps.put(":"+pkg_name, new_set); 132 } 133 for (ClassSymbol c : set) { 134 new_set.add(":. "+c.fullname); 135 } 136 } 137 return new_deps; 138 } 139 140 /** 141 * Convert the map from class names to their pubapi to a map 142 * from package names to their pubapi (which is the sorted concatenation 143 * of all the class pubapis) 144 */ 145 public Map<String,List<String>> getPublicApis() { 146 // The result map, to be returned. 147 Map<String,List<String>> publicApiPerPackage = new HashMap<>(); 148 // Remember the Name for the sortable String version of the name. 149 // I.e. where the dot (.) before the class name is replaced with bang (!). 150 Map<String,Name> backToName = new HashMap<>(); 151 // Sort all the classes on their fullname that includes the package path. 152 // Thus all classes belonging to the same package will be in consecutive order. 153 Name[] names = publicApiPerClass.keySet().toArray(new Name[0]); 154 List<String> fullnames = new ArrayList<>(); 155 for (Name n : names) { 156 String tmp = n.toString(); 157 int p = tmp.lastIndexOf('.'); 158 String s = tmp.substring(0,p)+"!"+tmp.substring(p+1); 159 fullnames.add(s); 160 backToName.put(s, n); 161 } 162 String[] sorted_fullnames = fullnames.toArray(new String[0]); 163 Arrays.sort(sorted_fullnames); 164 // Now sorted_fullnames has a list of classes sorted, but with all classes inside 165 // a package grouped together. This would not happen if we did not use !. 166 String currPkg = ""; 167 List<String> currPublicApi = null; 168 169 for (String n : sorted_fullnames) { 170 int lastBang = n.lastIndexOf('!'); 171 assert(lastBang != -1); 172 String pkgName = n.substring(0, lastBang); 173 if (!pkgName.equals(currPkg)) { 174 if (!currPkg.equals("")) { 175 // Add default module name ":" 176 publicApiPerPackage.put(":"+currPkg, currPublicApi); 177 } 178 currPublicApi = new LinkedList<>(); 179 currPkg = pkgName; 180 } 181 currPublicApi.addAll(publicApiPerClass.get(backToName.get(n))); 182 } 183 if (currPkg != "" && currPublicApi != null) { 184 publicApiPerPackage.put(":"+currPkg, currPublicApi); 185 } 186 return publicApiPerPackage; 187 } 188 189 /** 190 * Visit the api of a source class and construct a pubapi string and 191 * store it into the pubapi_perclass map. 192 */ 193 public void visitPubapiOfSource(TypeElement e) { 194 visitPubapi(e); 195 Name p = ((ClassSymbol)e).packge().fullname; 196 explicitPackages.add(p); 197 } 198 199 /** 200 * Visit the api of a classpath class and construct a pubapi string and 201 * store it into the pubapi_perclass map. 202 */ 203 public void visitPubapiOfClasspath(TypeElement e) { 204 visitPubapi(e); 205 } 206 207 /** 208 * Visit the api of a class and construct a list of api strings and 209 * store it into the pubapi_perclass map. 210 */ 211 private void visitPubapi(TypeElement e) { 212 Name n = ((ClassSymbol)e).fullname; 213 assert(publicApiPerClass.get(n) == null); 214 215 PubapiVisitor v = new PubapiVisitor(); 216 v.construct(e); 217 publicApiPerClass.put(n, v.api); 218 } 219 220 /** 221 * Visit the api of a class and return the constructed pubapi string. 222 */ 223 public static List<String> constructPubapi(TypeElement e, String class_loc_info) { 224 225 PubapiVisitor v = new PubapiVisitor(); 226 if (class_loc_info != null) { 227 v.classLocInfo(class_loc_info); 228 } 229 v.construct(e); 230 return v.api; 231 } 232 233 /** 234 * Collect a package dependency. currPkg is marked as depending on depPkg. 235 */ 236 public void reportPackageDep(Name currPkg, Name depPkg) { 237 if (!currPkg.equals(depPkg)) { 238 Set<Name> theset = deps.get(currPkg); 239 if (theset==null) { 240 theset = new HashSet<>(); 241 deps.put(currPkg, theset); 242 } 243 theset.add(depPkg); 244 } 245 } 246 247 /** 248 * Collect a classpath class dependency. currPkg is marked as depending on depCls. 249 */ 250 public void reportClassDep(ClassSymbol depCls) { 251 String s = depCls.classfile != null ? depCls.classfile.toString() : ""; 252 if (s.startsWith("RegularFileObject[") && 253 s.endsWith(".java]")) { 254 // This was a sourcepath dependency, ignore it. 255 return; 256 } 257 Name pkg = depCls.packge().fullname; 258 Set<ClassSymbol> theset = classDeps.get(pkg); 259 if (theset==null) { 260 theset = new HashSet<>(); 261 classDeps.put(pkg, theset); 262 } 263 theset.add(depCls); 264 } 265 266 }