Back

package com.futureshocked.classloader;

import java.util.HashMap;
import java.util.List;
import java.util.Iterator;
import java.util.ArrayList;
/**
 * A contain for {@link InterceptedMethod}, which also has <tt>boolean</tt>
 *   methods that will let {@link DebuggerClassLoader} know if a class should
 *   be modified, and {@link DebuggerClassVisitor} know if a method should be
 *   modified.
 */
public class ClassTransforms {
  /**
   * A HashMap that contains <tt>String</tt> keys, consisting of class names,
   *   that map to <tt>List</tt> objects that hold {@link InterceptedMethod}.
   */
  private HashMap classes;

  /**
   * Constructor that simply creates a new <tt>HashMap</tt> for
   * <tt>this.classes</tt>.
   */
  public ClassTransforms() {
    classes = new HashMap();
  }

  /**
   * Checks <tt>classes</tt> for a key equal to <tt>className</tt>. Since a
   *   <tt>HashMap</tt> is checked it should be the fastest way to check if
   *   a class needs to be modified, which is important since this will be
   *   called for every loaded class.
   * @param className The name of the class about to be loaded.
   * @return <tt>true</tt> if the class needs to be modified.
   */
  public boolean modifyClass(String className) {
    return classes.containsKey(className);
  }

  /**
   * Checks to see if a method needs to be modified.
   * @param className The name of the class the method belongs to.
   * @param method The method being checked.
   * @param desc The description of the method.
   * @return <tt>true</tt> if the method needs to be modified.
   */
  public boolean modifyMethod(String className, String method, String desc) {
    return (getInterceptedMethod(className, method, desc) != null);
  }

  /**
   * Returns an {@link InterceptedMethod} that matches <tt>className</tt>,
   *   <tt>method</tt>, and <tt>desc</tt>. See {@link InterceptedMethod} for
   *   information related to matching.
   * @param className The name of the class the method belongs to.
   * @param method The name of the method.
   * @param desc The description of the method - see
   *   {@link ApplicationDebugger} for information about the format.
   * @return <tt>null</tt> if the {@link InterceptedMethod} is not found, the
   *   correct object if it's found.
   */
  public InterceptedMethod getInterceptedMethod(String className, String method,
      String desc) {
    InterceptedMethod im = null;

    if (modifyClass(className)) {
      List methods = getMethodsByClass(className);
      if (methods != null) {
        Iterator i = methods.iterator();
        while ((i.hasNext()) && (im == null)) {
          InterceptedMethod wantedIm = (InterceptedMethod) i.next();
          // See InterceptedMethod.matches() for information on the matching
          //   algorithm
          if (wantedIm.matches(className, method, desc))
            im = wantedIm;
        }
      }
    }
    return im;
  }

  /**
   * Returns all {@link InterceptedMethod} objects that belong to
   *   <tt>className</tt>.
   * @param className The name of the class being queried.
   * @return <tt>null</tt> if no methods are to be intercepted, a <tt>List</tt>
   *   of {@link InterceptedMethod} objects if any methods are to be
   *   intercepted.
   */
  public List getMethodsByClass(String className) {
    List methods = null;

    if (classes.containsKey(className))
      methods = (List) classes.get(className);

    return methods;
  }

  /**
   * Add a new method to be intercepted. Format should be:<br>
   *  <tt>java.lang.String testMethod(int arg1, com.user.package.Test arg2)</tt>
   * @param code A code style method description.
   * @return {@link InterceptedMethod} that will match the user specified
   *   method.
   */
  public InterceptedMethod addCodeStyleInterceptedMethod(String code) {
    MethodDescriptionUtil mdu = new MethodDescriptionUtil(code);

    return addInterceptedMethod(mdu.getClassName(), mdu.getMethodName(),
        mdu.getDescription());
  }

  /**
   * Add a new method to be intercepted. This method should be used only if
   *   you understand the format for <tt>desc</tt>.
   * @param className The name of the class the method belongs to.
   * @param method The name of the method to be intercepted.
   * @param desc The description of the method, see {@link ApplicationDebugger}
   *   for an example of the format, or the ASM documentation.
   * @return {@link InterceptedMethod} that will match the user specified
   *   method.
   */
  public InterceptedMethod addInterceptedMethod(String className, String method,
      String desc) {
    InterceptedMethod im = new InterceptedMethod(className, method, desc);

    addInterceptedMethod(im);
    return im;
  }

  /**
   * Adds an already created {@link InterceptedMethod} object to this container.
   * @param im {@link InterceptedMethod} object
   */
  public void addInterceptedMethod(InterceptedMethod im) {
    List methods = getMethodsByClass(im.getClassName());

    if (methods == null) {
      methods = new ArrayList();
    }

    methods.add(im);
    classes.put(im.getClassName(), methods);
  }

  /**
   * Removes an {@link InterceptedMethod} from this container.<br><br>
   * This method matches using reference equality (==), rather than
   *   the <tt>InterceptedMethod.matches()</tt> method, so to remove an object
   *   the exact object should first be obtained from this container, rather
   *   than creating a new {@link InterceptedMethod} with the same values.
   * @param im
   */
  public void removeInterceptedMethod(InterceptedMethod im) {
    List methods = getMethodsByClass(im.getClassName());

    if (methods != null) {
      Iterator i = methods.iterator();
      while (i.hasNext()) {
        InterceptedMethod matchedIm = (InterceptedMethod) i.next();
        // maybe change this to matches()?
        if (matchedIm == im)
          methods.remove(matchedIm);
      }

      if (methods.size() < 1)
        methods = null;

      classes.put(im.getClassName(), methods);
    }
  }

}

Top