Package com.google.singletondetector

Source Code of com.google.singletondetector.SingletonDetector

/*
* Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.singletondetector;

import com.google.singletondetector.classpath.ClasspathRoot;
import com.google.singletondetector.classpath.DirectoryClasspathRoot;
import com.google.singletondetector.classpath.JarClasspathRoot;
import com.google.singletondetector.output.FingletonOutputNode;
import com.google.singletondetector.output.HingletonOutputNode;
import com.google.singletondetector.output.MingletonOutputNode;
import com.google.singletondetector.output.OtherOutputNode;
import com.google.singletondetector.output.OutputNode;
import com.google.singletondetector.output.SingletonOutputNode;
import com.google.singletondetector.visitors.SingletonClassVisitor;
import com.google.singletondetector.visitors.SingletonUsageClassVisitor;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
* The main SingletonDetector class, which generates the visitors for each class
* and starts them. Also responsible for constructing the OutputNodes and
* formatting their output.
*
* @author David Rubel
*/
public class SingletonDetector implements Opcodes {
  // Map of all of the classes ready by SingletonDetector
  private Map<String, Clazz> classes = new HashMap<String, Clazz>();

  // The class currently being read by the visitor objects
  private Clazz currClass;

  // The common prefix for all classes
  private String prefix;

  // Command line flags passed in from the main() method
  private Flags flags;

  // Statistics kept on the SingletonDetector
  private Stats stats;

  /**
   * Test constructor, used to pass in a classpath and a list of class names
   * directly, avoiding a directory or jar.  Uses default flags.
   */
  public SingletonDetector(ClasspathRoot classpath, String prefix,
      String... classNames) {
    this(classpath, prefix, new Flags(), classNames);
  }
 
  /**
   * Test constructor, used to pass in a classpath and a list of class names
   * directly, avoiding a directory or jar.
   */
  public SingletonDetector(ClasspathRoot classpath, String prefix, Flags flags,
      String... classNames) {
    this(getClassReaders(classpath, classNames, prefix), prefix, flags);
  }
 
  /**
   * Default constructor, used to pass in a string which represents a directory
   * or a jar.
   */
  public SingletonDetector(String dir, String prefix, Flags flags) throws MalformedURLException {
    this(getClassReaders(dir, prefix, flags), prefix, flags);
  }

  /**
   * Master constructor, called by all other constructors.  Requires a list of
   * ClassReaders, which is generated in the other constructors by different
   * methods.
   */
  public SingletonDetector(List<ClassReader> crlist, String prefix, Flags flags) {
    stats = new Stats();
    this.prefix = prefix;
    this.flags = flags;
   
    // Verbose: Begin processing
    if (flags.isVerbose()) {
      System.out.print("Processing... ");
    }

    // First pass: determine the type of each class (i.e. Singleton), count the
    //             number of read classes and remove enums
    Iterator<ClassReader> iter = crlist.iterator();
    for (ClassReader cr : crlist) {
      cr.accept(new SingletonClassVisitor(this), ClassReader.SKIP_DEBUG);
      stats.incClassesRead();
    }

    // Second pass: determine which special classes each class uses
    for (ClassReader cr : crlist) {
      cr.accept(new SingletonUsageClassVisitor(this), ClassReader.SKIP_DEBUG);
    }
   
    // Third pass: set isDrawn for each class
    for (Clazz cl : classes.values()) {
      cl.setIsDrawn(flags.getThreshold());
    }
   
    // Fourth pass: update isDrawn
    if (flags.getThreshold() > 0) {
      for (Clazz cl : classes.values()) {
        cl.updateIsDrawn();
      }
    }

    // Fifth pass: gather statistics
    if (flags.showBanner() || flags.showStats()) {
      // Get stats on current class
      for (Clazz cl : classes.values()) {
        if (cl.isDrawn()) {
          stats.incClassesDrawn();
 
          if (cl.isSingleton()) {
            stats.incSingletons();
          } else if (cl.isHingleton()) {
            stats.incHingletons();
          } else if (cl.isMingleton()) {
            stats.incMingletons();
          } else if (cl.isFingleton()) {
            stats.incFingletons();
          }
         
          // Get stats for all used classes
          for (Clazz used : cl.getClassesIUse()) {
            if (used.isDrawn()) {   
              if (used.isSingleton()) {
                stats.incSingletonUsers();
              } else if (used.isHingleton()) {
                stats.incHingletonUsers();
              } else if (used.isMingleton()) {
                stats.incMingletonUsers();
              } else if (used.isFingleton()) {
                stats.incFingletonUsers();
              }
            }
          }
        }
      }
    }
   
    // Verbose: end processing
    if (flags.isVerbose()) {
      System.out.println("done.");
    }
  }

  public void setCurrClass(String name) {
    String label = removePrefix(name);
    currClass = classes.get(label);
    if (currClass == null) {
      currClass = new Clazz(label);
      classes.put(currClass.getName(), currClass);
    }
  }

  public Clazz getCurrClass() {
    return currClass;
  }

  public Clazz getClass(String name) {
    return classes.get(removePrefix(name));
  }

  public String getPrefix() {
    return prefix;
  }

  public void setPrefix(String prefix) {
    this.prefix = prefix;
  }

  public String[] splitDesc(String desc) {
    String[] ret = new String[2];
    if (desc.startsWith("(")) {
      ret[0] = desc.substring(1, desc.indexOf(')'));
      ret[1] = desc.substring(desc.indexOf(')') + 1);
      ret[1] = ret[1].substring(0, ret[1].length() - 1);
    }
    return ret;
  }

  public String removePrefix(String str) {
    String ret = str;
    if (str.startsWith(prefix)) {
      ret = ret.substring(prefix.length());
    }
    return ret;
  }
 
  /*************************************************************************/
  /*                         CLASS READER METHODS                          */
  /*************************************************************************/
 
  private static ClassReader getClassReader(ClasspathRoot classpath,
      String className) throws IOException {
    String resourceName = className;
    if (!resourceName.endsWith(".class")) {
      resourceName += ".class";
    }
    ClassReader cr =
        new ClassReader(classpath.getResourceAsStream(resourceName));
    return cr;
  }

  private static boolean validcr(ClassReader cr, String prefix) {
    return !cr.getSuperName().equals("java/lang/Enum")
        && cr.getClassName().startsWith(prefix);
  }

  private static List<ClassReader> getClassReaders(ClasspathRoot classpath,
      String[] classNames, String prefix) {
    List<ClassReader> crlist = new ArrayList<ClassReader>();
    for (String className : classNames) {
      ClassReader cr;
      try {
        cr = getClassReader(classpath, className);
        if (validcr(cr, prefix)) {
          crlist.add(cr);
        }
      } catch (IOException e) {
        System.out.println("Failed to read " + className);
      }
    }
    return crlist;
  }
 
  private static List<ClassReader> getClassReaders(String dir, String prefix,
      Flags flags) throws MalformedURLException {
    List<ClassReader> crlist = new ArrayList<ClassReader>();

    // Get the classpath for the classes directory or jar
    URL root = new File(dir).toURI().toURL();
    ClasspathRoot classpath;
    if (dir.endsWith(".jar")) {
      classpath = new JarClasspathRoot(root);
    } else {
      classpath = new DirectoryClasspathRoot(root);
    }

    // Get the class readers for each class
    buildCrlist(root, classpath, prefix, "", crlist, flags.isVerbose());
   
    return crlist;
  }
 
  private static void buildCrlist(URL root, ClasspathRoot classpath,
      String prefix, String packageName, List<ClassReader> crlist,
      boolean verbose) throws MalformedURLException {
    for (String resource : classpath.getResources(packageName)) {
      if (resource.endsWith(".class")) {
        String className = packageName + resource;
        //className = className.replace(".class", "").replace('/', '.');
        if (!className.contains("$")) {
          ClassReader cr;
          try {
            cr = getClassReader(classpath, className);
            if (validcr(cr, prefix)) {
              if (verbose) {
                System.out.println("Found: "
                    + cr.getClassName().replace("/", "."));
              }
              crlist.add(cr);
            }
          } catch (IOException e) {
            System.out.println("Failed to read " + className);
          }
        }
      } else if (resource.endsWith(".jar")) {
        String temp = root.getPath() + packageName + resource;
        URL jarRoot = new File(temp).toURI().toURL();
        ClasspathRoot jarPath = new JarClasspathRoot(jarRoot);
        buildCrlist(jarRoot, jarPath, prefix, "", crlist, verbose);
      } else {
        buildCrlist(root, classpath, prefix, packageName + resource + "/", crlist,
            verbose);
      }
    }
  }

  /*************************************************************************/
  /*                          VISITOR METHODS                              */
  /*************************************************************************/

  public void addStaticField(String name, String desc) {
    // Visitor guarantees field is private, static and not final
    if (desc.startsWith("L")) {
      desc = desc.substring(1, desc.length() - 1);
      currClass.addStaticField(name, removePrefix(desc));
    }
  }

  public void visitStaticField(String name, String desc) {
    // Visitor guarantees field is public and static
    currClass.visitStaticField(name, desc, flags);
  }

  public void visitStaticMethod(String name, String desc) {
    String[] ret = splitDesc(desc);
    String params = ret[0];
    String returnType = ret[1];
    if (returnType.startsWith("L")) {
      returnType = returnType.substring(1);
      if (!returnType.startsWith("java/")
          && returnType.startsWith(prefix)) {
        currClass.visitStaticMethod(name, params, removePrefix(returnType), flags);
      }
    }
  }

  public void invokeStatic(String referencedClass, String desc) {
    String[] ret = splitDesc(desc);
    String params = ret[0];
    String returnType = ret[1];
    if (returnType.startsWith("L")) {
      returnType = returnType.substring(1);
      returnType = removePrefix(returnType);
      referencedClass = removePrefix(referencedClass);

      Clazz cl = classes.get(referencedClass);
      if (cl != null && cl != currClass
          && (currClass.isSpecial() || !flags.ignoreOthers())) {
        if (returnType.equals(referencedClass) && cl.isSingleton()) {
          currClass.addClassIUse(cl);
        } else if (cl.doIHingle(returnType) && cl.isHingleton()) {
          currClass.addClassIUse(cl);
        } else if (params.equals("") && cl.isMingleton()) {
          currClass.addClassIUse(cl);
        }
      }
    }

  }

  public void fieldInstruction(String owner) {
    Clazz cl = classes.get(removePrefix(owner));
    if (cl != null && cl != currClass && cl.isFingleton()) {
      currClass.addClassIUse(cl);
    }
  }

  /*************************************************************************/
  /*                             OUTPUT METHODS                            */
  /*************************************************************************/

  public String getOutput(boolean pad) {
    return stats.getOutput(flags, pad);
  }

  public String getGraphMlOutput() {
    HashMap<String, OutputNode> nodes = getOutputNodes();
    String nodeOutput = "";
    String edgeOutput = "";
   
    // Verbose: begin output
    if (flags.isVerbose()) {
      System.out.print("Generating output graph... ");
    }
   
    // Fill buffers
    for (OutputNode node : nodes.values()) {
      nodeOutput += node.getGraphMlNode();
      edgeOutput += node.getGraphMlEdges();
    }

    String banner = "";
    if (flags.showBanner()) {
      banner =
          "    <node id=\"banner\">\n" + "      <data key=\"d0\">\n"
              + "        <y:ShapeNode>\n"
              + "          <y:Fill color = \"#0000FF\"/>\n"
              + "          <y:NodeLabel textColor=\"#FFFFFF\" fontSize=\"24\">"
              + stats.getOutput(flags, false).replace("\n", "&#xA;")
              + "</y:NodeLabel>\n"
              + "          <y:Shape type=\"rectangle\"/>\n"
              + "        </y:ShapeNode>\n" + "      </data>\n"
              + "    </node>\n";
    }
   
    // Verbose: end output
    if (flags.isVerbose()) {
      System.out.println("done.");
    }

    return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        + "<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns/graphml\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns/graphml http://www.yworks.com/xml/schema/graphml/1.0/ygraphml.xsd\">\n"
        + "<key id=\"d0\" for=\"node\" yfiles.type=\"nodegraphics\"/>\n"
        + "<key id=\"d1\" for=\"edge\" yfiles.type=\"edgegraphics\"/>\n"
        + "  <graph id=\"SingletonDetector\" edgedefault=\"directed\">\n"
        + banner + nodeOutput + edgeOutput + "  </graph>\n" + "</graphml>";
  }

  private HashMap<String, OutputNode> getOutputNodes() {
    HashMap<String, OutputNode> nodes = new HashMap<String, OutputNode>();

    for (String className : classes.keySet()) {
      Clazz clazz = classes.get(className);

      if (clazz.isDrawn()) {
        if (!nodes.containsKey(className)) {
          nodes.put(className, newOutputNode(clazz));
        }

        // Process all classes that this class uses
        for (Clazz usedClazz : clazz.getClassesIUse()) {
          if (usedClazz.isDrawn()) {
            String usedName = usedClazz.getName();

            // Add the using class
            if (!nodes.containsKey(usedName)) {
              nodes.put(usedName, newOutputNode(usedClazz));
            }

            // Add the edge
            nodes.get(className).addEdge(nodes.get(usedName));
          }
        }
      }
    }

    return nodes;
  }

  private OutputNode newOutputNode(Clazz clazz) {
    OutputNode node;

    if (clazz.isSingleton()) {
      node = new SingletonOutputNode(clazz.getName());
    } else if (clazz.isHingleton()) {
      String classIHingle = clazz.getClassIHingle();
      node = new HingletonOutputNode(clazz.getName(), classIHingle);
    } else if (clazz.isMingleton()) {
      node = new MingletonOutputNode(clazz.getName());
    } else if (clazz.isFingleton()) {
      node = new FingletonOutputNode(clazz.getName());
    } else {
      node = new OtherOutputNode(clazz.getName());
    }

    return node;
  }
}
TOP

Related Classes of com.google.singletondetector.SingletonDetector

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.