1 /* 2 * Copyright (c) 2009, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 package com.sun.classanalyzer; 25 26 import java.io.File; 27 import java.util.ArrayList; 28 import java.util.HashMap; 29 import java.util.List; 30 import java.util.Map; 31 import java.util.Set; 32 import java.util.SortedMap; 33 import java.util.TreeMap; 34 import java.util.TreeSet; 35 36 import com.sun.tools.classfile.AccessFlags; 37 38 /** 39 * 40 * @author Mandy Chung 41 */ 42 public class Klass implements Comparable<Klass> { 43 44 private final String classname; 45 private final String packagename; 46 private Module module; 47 private String[] paths; 48 private Map<String, Set<Method>> methods; 49 private AccessFlags accessFlags; 50 private long filesize; 51 private boolean isJavaLangObject; 52 private boolean isPlatformAPI; 53 private boolean isNonCoreAPI; 54 private SortedMap<Klass, Set<ResolutionInfo>> deps; 55 private SortedMap<Klass, Set<ResolutionInfo>> referrers; 56 private List<AnnotatedDependency> annotatedDeps; 57 private Set<String> classForNameRefs; 58 59 private Klass(String classname) { 60 this.classname = classname; 61 this.paths = classname.replace('.', '/').split("/"); 62 this.isJavaLangObject = classname.equals("java.lang.Object"); 63 this.isPlatformAPI = Platform.isPlatformAPI(classname); 64 this.isNonCoreAPI = Platform.isNonCoreAPI(classname); 65 this.deps = new TreeMap<Klass, Set<ResolutionInfo>>(); 66 this.referrers = new TreeMap<Klass, Set<ResolutionInfo>>(); 67 this.methods = new HashMap<String, Set<Method>>(); 68 this.annotatedDeps = new ArrayList<AnnotatedDependency>(); 69 this.classForNameRefs = new TreeSet<String>(); 70 int pos = classname.lastIndexOf('.'); 71 this.packagename = (pos > 0) ? classname.substring(0, pos) : "<unnamed>"; 72 } 73 74 String getBasename() { 75 return paths[paths.length - 1]; 76 } 77 78 String getClassName() { 79 return classname; 80 } 81 82 String getPackageName() { 83 return packagename; 84 } 85 86 String getClassFilePathname() { 87 StringBuilder sb = new StringBuilder(paths[0]); 88 for (int i = 1; i < paths.length; i++) { 89 String p = paths[i]; 90 sb.append(File.separator).append(p); 91 } 92 return sb.append(".class").toString(); 93 } 94 95 boolean isPublic() { 96 return accessFlags == null || accessFlags.is(AccessFlags.ACC_PUBLIC); 97 } 98 99 boolean isPlatformAPI() { 100 return isPlatformAPI; 101 } 102 103 boolean isNonCoreAPI() { 104 return isNonCoreAPI; 105 } 106 107 Module getModule() { 108 return module; 109 } 110 111 void setModule(Module m) { 112 if (module != null) { 113 throw new RuntimeException("Module for " + this + " already set"); 114 } 115 this.module = m; 116 } 117 118 Set<Klass> getReferencedClasses() { 119 return deps.keySet(); 120 } 121 122 Set<Klass> getReferencingClasses() { 123 return referrers.keySet(); 124 } 125 126 void setAccessFlags(int flags) { 127 this.accessFlags = new AccessFlags(flags); 128 } 129 130 void setFileSize(long size) { 131 this.filesize = size; 132 } 133 134 long getFileSize() { 135 return this.filesize; 136 } 137 138 boolean exists() { 139 return filesize > 0; 140 } 141 142 boolean skip(Klass k) { 143 // skip if either class is a root or same class 144 return k.isJavaLangObject || this == k || k.classname.equals(classname); 145 } 146 147 void addDep(Method callee, ResolutionInfo resInfo) { 148 addDep(callee.getKlass(), resInfo); 149 } 150 151 void addDep(Klass ref, ResolutionInfo ri) { 152 if (skip(ref)) { 153 return; 154 } 155 Set<ResolutionInfo> resInfos; 156 if (!deps.containsKey(ref)) { 157 resInfos = new TreeSet<ResolutionInfo>(); 158 deps.put(ref, resInfos); 159 } else { 160 resInfos = deps.get(ref); 161 } 162 resInfos.add(ri); 163 } 164 165 void addReferrer(Method caller, ResolutionInfo resInfo) { 166 addReferrer(caller.getKlass(), resInfo); 167 } 168 169 void addReferrer(Klass k, ResolutionInfo ri) { 170 if (skip(k)) { 171 return; 172 } 173 Set<ResolutionInfo> resInfos; 174 if (!referrers.containsKey(k)) { 175 resInfos = new TreeSet<ResolutionInfo>(); 176 referrers.put(k, resInfos); 177 } else { 178 resInfos = referrers.get(k); 179 } 180 resInfos.add(ri); 181 } 182 183 Method getMethod(String name) { 184 return getMethod(name, ""); 185 } 186 187 Method getMethod(String name, String signature) { 188 Set<Method> set; 189 if (methods.containsKey(name)) { 190 set = methods.get(name); 191 } else { 192 set = new TreeSet<Method>(); 193 methods.put(name, set); 194 } 195 196 for (Method m : set) { 197 if (m.getName().equals(name) && m.getSignature().equals(signature)) { 198 return m; 199 } 200 } 201 Method m = new Method(this, name, signature); 202 set.add(m); 203 return m; 204 } 205 206 @Override 207 public String toString() { 208 return classname; 209 } 210 211 @Override 212 public int compareTo(Klass o) { 213 return classname.compareTo(o.classname); 214 } 215 216 void addAnnotatedDep(AnnotatedDependency dep) { 217 annotatedDeps.add(dep); 218 } 219 220 void addClassForNameReference(String method) { 221 classForNameRefs.add(method); 222 } 223 224 List<AnnotatedDependency> getAnnotatedDeps() { 225 return annotatedDeps; 226 } 227 private static Map<String, Klass> classes = new TreeMap<String, Klass>(); 228 229 static Set<Klass> getAllClasses() { 230 return new TreeSet<Klass>(classes.values()); 231 } 232 233 static Klass findKlassFromPathname(String filename) { 234 String name = filename; 235 if (filename.endsWith(".class")) { 236 name = filename.substring(0, filename.length() - 6); 237 } 238 239 // trim ".class" 240 name = name.replace('/', '.'); 241 for (Klass k : classes.values()) { 242 if (name.endsWith(k.getClassName())) { 243 return k; 244 } 245 } 246 return null; 247 } 248 249 static Klass findKlass(String classname) { 250 return classes.get(classname); 251 } 252 253 static Klass getKlass(String name) { 254 Klass k; 255 String classname = name.replace('/', '.'); 256 if (classname.charAt(classname.length() - 1) == ';') { 257 classname = classname.substring(0, classname.length() - 1); 258 } 259 if (classes.containsKey(classname)) { 260 k = classes.get(classname); 261 } else { 262 k = new Klass(classname); 263 classes.put(classname, k); 264 } 265 return k; 266 } 267 268 public class Method implements Comparable<Method> { 269 270 private final Klass k; 271 private final String method; 272 private final String signature; 273 private long codeLength; 274 // non-primitive types only 275 private final List<Klass> argTypes; 276 private final Klass returnType; 277 boolean isAbstract = false; 278 boolean marked = false; 279 280 public Method(Klass k, String method, String signature) { 281 this(k, method, signature, null, null); 282 } 283 284 public Method(Klass k, String method, String signature, Klass returnType, List<Klass> argTypes) { 285 this.k = k; 286 this.method = method; 287 this.signature = signature; 288 this.argTypes = argTypes; 289 this.returnType = returnType; 290 this.codeLength = 0; 291 } 292 293 public Klass getKlass() { 294 return k; 295 } 296 297 public String getName() { 298 return method; 299 } 300 301 public String getSignature() { 302 return signature; 303 } 304 305 public Klass getReturnType() { 306 return returnType; 307 } 308 309 public List<Klass> argTypes() { 310 return argTypes; 311 } 312 313 public void setCodeLength(long len) { 314 this.codeLength = len; 315 } 316 317 public long getCodeLength() { 318 return codeLength; 319 } 320 321 @Override 322 public boolean equals(Object o) { 323 if (o instanceof Method) { 324 return compareTo((Method) o) == 0; 325 } else { 326 return false; 327 } 328 } 329 330 @Override 331 public int hashCode() { 332 int hash = 3; 333 hash = 71 * hash + (this.k != null ? this.k.hashCode() : 0); 334 hash = 71 * hash + (this.method != null ? this.method.hashCode() : 0); 335 return hash; 336 } 337 338 @Override 339 public String toString() { 340 if (signature.isEmpty()) { 341 return k.classname + "." + method; 342 } else { 343 return signature; 344 } 345 } 346 347 public String toHtmlString() { 348 return toString().replace("<", "<").replace(">", ">"); 349 } 350 351 boolean isClinit() { 352 return method.equals("<clinit>"); 353 } 354 355 public int compareTo(Method m) { 356 if (k == m.getKlass()) { 357 if (method.equals(m.method)) { 358 return signature.compareTo(m.signature); 359 } else { 360 return method.compareTo(m.method); 361 } 362 } else { 363 return k.compareTo(m.getKlass()); 364 } 365 } 366 } 367 }