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("<", "&lt;").replace(">", "&gt;");
 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 }