Package org.apache.bcel.classfile

Source Code of org.apache.bcel.classfile.JavaClass

/*
* Copyright  2000-2004 The Apache Software Foundation
*
*  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 org.apache.bcel.classfile;


import org.apache.bcel.Constants;
import org.apache.bcel.util.BCELComparator;
import org.apache.bcel.util.SyntheticRepository;
import org.apache.bcel.util.ClassVector;
import org.apache.bcel.util.ClassQueue;
import org.apache.bcel.generic.Type;

import java.io.*;
import java.util.StringTokenizer;

/**
* Represents a Java class, i.e., the data structures, constant pool,
* fields, methods and commands contained in a Java .class file.
* See <a href="ftp://java.sun.com/docs/specs/">JVM specification</a> for details.
* The intent of this class is to represent a parsed or otherwise existing
* class file.  Those interested in programatically generating classes
* should see the <a href="../generic/ClassGen.html">ClassGen</a> class.

* @version $Id: JavaClass.java,v 1.15 2004/12/15 08:28:30 conor Exp $
* @see org.apache.bcel.generic.ClassGen
* @author  <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
*/
public class JavaClass extends AccessFlags implements Cloneable, Node {
  private String file_name;
  private String package_name;
  private String source_file_name = "<Unknown>";
  private int class_name_index;
  private int superclass_name_index;
  private String class_name;
  private String superclass_name;
  private int major, minor; // Compiler version
  private ConstantPool constant_pool; // Constant pool
  private int[] interfaces; // implemented interfaces
  private String[] interface_names;
  private Field[] fields; // Fields, i.e., variables of class
  private Method[] methods; // methods defined in the class
  private Attribute[] attributes; // attributes defined in the class
  private byte source = HEAP; // Generated in memory

  public static final byte HEAP = 1;
  public static final byte FILE = 2;
  public static final byte ZIP = 3;

  static boolean debug = false; // Debugging on/off
  static char sep = '/'; // directory separator

  private static BCELComparator _cmp = new BCELComparator() {
    public boolean equals(Object o1, Object o2) {
      JavaClass THIS = (JavaClass)o1;
      JavaClass THAT = (JavaClass)o2;

      return THIS.getClassName().equals(THAT.getClassName());
    }

    public int hashCode(Object o) {
      JavaClass THIS = (JavaClass)o;
      return THIS.getClassName().hashCode();
    }
  };

  /**
   * In cases where we go ahead and create something,
   * use the default SyntheticRepository, because we
   * don't know any better.
   */
  private transient org.apache.bcel.util.Repository repository =
    SyntheticRepository.getInstance();

  /**
   * Constructor gets all contents as arguments.
   *
   * @param class_name_index Index into constant pool referencing a
   * ConstantClass that represents this class.
   * @param superclass_name_index Index into constant pool referencing a
   * ConstantClass that represents this class's superclass.
   * @param file_name File name
   * @param major Major compiler version
   * @param minor Minor compiler version
   * @param access_flags Access rights defined by bit flags
   * @param constant_pool Array of constants
   * @param interfaces Implemented interfaces
   * @param fields Class fields
   * @param methods Class methods
   * @param attributes Class attributes
   * @param source Read from file or generated in memory?
   */
  public JavaClass(
    int class_name_index,
    int superclass_name_index,
    String file_name,
    int major,
    int minor,
    int access_flags,
    ConstantPool constant_pool,
    int[] interfaces,
    Field[] fields,
    Method[] methods,
    Attribute[] attributes,
    byte source) {
    if (interfaces == null) // Allowed for backward compatibility
      interfaces = new int[0];
    if (attributes == null)
      this.attributes = new Attribute[0];
    if (fields == null)
      fields = new Field[0];
    if (methods == null)
      methods = new Method[0];

    this.class_name_index = class_name_index;
    this.superclass_name_index = superclass_name_index;
    this.file_name = file_name;
    this.major = major;
    this.minor = minor;
    this.access_flags = access_flags;
    this.constant_pool = constant_pool;
    this.interfaces = interfaces;
    this.fields = fields;
    this.methods = methods;
    this.attributes = attributes;
    this.source = source;

    // Get source file name if available
    for (int i = 0; i < attributes.length; i++) {
      if (attributes[i] instanceof SourceFile) {
        source_file_name = ((SourceFile)attributes[i]).getSourceFileName();
        break;
      }
    }

    /* According to the specification the following entries must be of type
     * `ConstantClass' but we check that anyway via the
     * `ConstPool.getConstant' method.
     */
    class_name =
      constant_pool.getConstantString(
        class_name_index,
        Constants.CONSTANT_Class);
    class_name = Utility.compactClassName(class_name, false);

    int index = class_name.lastIndexOf('.');
    if (index < 0)
      package_name = "";
    else
      package_name = class_name.substring(0, index);

    if (superclass_name_index > 0) {
      // May be zero -> class is java.lang.Object
      superclass_name =
        constant_pool.getConstantString(
          superclass_name_index,
          Constants.CONSTANT_Class);
      superclass_name = Utility.compactClassName(superclass_name, false);
    } else
      superclass_name = "java.lang.Object";

    interface_names = new String[interfaces.length];
    for (int i = 0; i < interfaces.length; i++) {
      String str =
        constant_pool.getConstantString(
          interfaces[i],
          Constants.CONSTANT_Class);
      interface_names[i] = Utility.compactClassName(str, false);
    }
  }

  /**
   * Constructor gets all contents as arguments.
   *
   * @param class_name_index Class name
   * @param superclass_name_index Superclass name
   * @param file_name File name
   * @param major Major compiler version
   * @param minor Minor compiler version
   * @param access_flags Access rights defined by bit flags
   * @param constant_pool Array of constants
   * @param interfaces Implemented interfaces
   * @param fields Class fields
   * @param methods Class methods
   * @param attributes Class attributes
   */
  public JavaClass(
    int class_name_index,
    int superclass_name_index,
    String file_name,
    int major,
    int minor,
    int access_flags,
    ConstantPool constant_pool,
    int[] interfaces,
    Field[] fields,
    Method[] methods,
    Attribute[] attributes) {
    this(
      class_name_index,
      superclass_name_index,
      file_name,
      major,
      minor,
      access_flags,
      constant_pool,
      interfaces,
      fields,
      methods,
      attributes,
      HEAP);
  }

  /**
   * Called by objects that are traversing the nodes of the tree implicitely
   * defined by the contents of a Java class. I.e., the hierarchy of methods,
   * fields, attributes, etc. spawns a tree of objects.
   *
   * @param v Visitor object
   */
  public void accept(Visitor v) {
    v.visitJavaClass(this);
  }

  /* Print debug information depending on `JavaClass.debug'
   */
  static final void Debug(String str) {
    if (debug)
      System.out.println(str);
  }

  /**
   * Dump class to a file.
   *
   * @param file Output file
   * @throws IOException
   */
  public void dump(File file) throws IOException {
    String parent = file.getParent();

    if (parent != null) {
      File dir = new File(parent);

      if (dir != null)
        dir.mkdirs();
    }

    dump(new DataOutputStream(new FileOutputStream(file)));
  }

  /**
   * Dump class to a file named file_name.
   *
   * @param file_name Output file name
   * @exception IOException
   */
  public void dump(String file_name) throws IOException {
    dump(new File(file_name));
  }

  /**
   * @return class in binary format
   */
  public byte[] getBytes() {
    ByteArrayOutputStream s = new ByteArrayOutputStream();
    DataOutputStream ds = new DataOutputStream(s);

    try {
      dump(ds);
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
        ds.close();
      } catch (IOException e2) {
        e2.printStackTrace();
      }
    }

    return s.toByteArray();
  }

  /**
   * Dump Java class to output stream in binary format.
   *
   * @param file Output stream
   * @exception IOException
   */
  public void dump(OutputStream file) throws IOException {
    dump(new DataOutputStream(file));
  }

  /**
   * Dump Java class to output stream in binary format.
   *
   * @param file Output stream
   * @exception IOException
   */
  public void dump(DataOutputStream file) throws IOException {
    file.writeInt(0xcafebabe);
    file.writeShort(minor);
    file.writeShort(major);

    constant_pool.dump(file);

    file.writeShort(access_flags);
    file.writeShort(class_name_index);
    file.writeShort(superclass_name_index);

    file.writeShort(interfaces.length);
    for (int i = 0; i < interfaces.length; i++)
      file.writeShort(interfaces[i]);

    file.writeShort(fields.length);
    for (int i = 0; i < fields.length; i++)
      fields[i].dump(file);

    file.writeShort(methods.length);
    for (int i = 0; i < methods.length; i++)
      methods[i].dump(file);

    if (attributes != null) {
      file.writeShort(attributes.length);
      for (int i = 0; i < attributes.length; i++)
        attributes[i].dump(file);
    } else
      file.writeShort(0);

    file.close();
  }

  /**
   * @return Attributes of the class.
   */
  public Attribute[] getAttributes() {
    return attributes;
  }

  /**
   * @return Class name.
   */
  public String getClassName() {
    return class_name;
  }

  /**
   * @return Package name.
   */
  public String getPackageName() {
    return package_name;
  }

  /**
   * @return Class name index.
   */
  public int getClassNameIndex() {
    return class_name_index;
  }

  /**
   * @return Constant pool.
   */
  public ConstantPool getConstantPool() {
    return constant_pool;
  }

  /**
   * @return Fields, i.e., variables of the class. Like the JVM spec
   * mandates for the classfile format, these fields are those specific to
   * this class, and not those of the superclass or superinterfaces.
   */
  public Field[] getFields() {
    return fields;
  }

  /**
   * @return File name of class, aka SourceFile attribute value
   */
  public String getFileName() {
    return file_name;
  }

  /**
   * @return Names of implemented interfaces.
   */
  public String[] getInterfaceNames() {
    return interface_names;
  }

  /**
   * @return Indices in constant pool of implemented interfaces.
   */
  public int[] getInterfaceIndices() {
    return interfaces;
  }

  /**
   * @return Major number of class file version.
   */
  public int getMajor() {
    return major;
  }

  /**
   * @return Methods of the class.
   */
  public Method[] getMethods() {
    return methods;
  }

  /**
   * @return A org.apache.bcel.classfile.Method corresponding to
   * java.lang.reflect.Method if any
   */
  public Method getMethod(java.lang.reflect.Method m) {
    for (int i = 0; i < methods.length; i++) {
      Method method = methods[i];

      if (m.getName().equals(method.getName())
        && (m.getModifiers() == method.getModifiers())
        && Type.getSignature(m).equals(method.getSignature())) {
        return method;
      }
    }

    return null;
  }

  /**
   * @return Minor number of class file version.
   */
  public int getMinor() {
    return minor;
  }

  /**
   * @return sbsolute path to file where this class was read from
   */
  public String getSourceFileName() {
    return source_file_name;
  }

  /**
   * @return Superclass name.
   */
  public String getSuperclassName() {
    return superclass_name;
  }

  /**
   * @return Class name index.
   */
  public int getSuperclassNameIndex() {
    return superclass_name_index;
  }

  static {
    // Debugging ... on/off
    String debug = System.getProperty("JavaClass.debug");

    if (debug != null)
      JavaClass.debug = new Boolean(debug).booleanValue();

    // Get path separator either / or \ usually
    String sep = System.getProperty("file.separator");

    if (sep != null)
      try {
        JavaClass.sep = sep.charAt(0);
      } catch (StringIndexOutOfBoundsException e) {
      } // Never reached
  }

  /**
   * @param attributes .
   */
  public void setAttributes(Attribute[] attributes) {
    this.attributes = attributes;
  }

  /**
   * @param class_name .
   */
  public void setClassName(String class_name) {
    this.class_name = class_name;
  }

  /**
   * @param class_name_index .
   */
  public void setClassNameIndex(int class_name_index) {
    this.class_name_index = class_name_index;
  }

  /**
   * @param constant_pool .
   */
  public void setConstantPool(ConstantPool constant_pool) {
    this.constant_pool = constant_pool;
  }

  /**
   * @param fields .
   */
  public void setFields(Field[] fields) {
    this.fields = fields;
  }

  /**
   * Set File name of class, aka SourceFile attribute value
   */
  public void setFileName(String file_name) {
    this.file_name = file_name;
  }

  /**
   * @param interface_names .
   */
  public void setInterfaceNames(String[] interface_names) {
    this.interface_names = interface_names;
  }

  /**
   * @param interfaces .
   */
  public void setInterfaces(int[] interfaces) {
    this.interfaces = interfaces;
  }

  /**
   * @param major .
   */
  public void setMajor(int major) {
    this.major = major;
  }

  /**
   * @param methods .
   */
  public void setMethods(Method[] methods) {
    this.methods = methods;
  }

  /**
   * @param minor .
   */
  public void setMinor(int minor) {
    this.minor = minor;
  }

  /**
   * Set absolute path to file this class was read from.
   */
  public void setSourceFileName(String source_file_name) {
    this.source_file_name = source_file_name;
  }

  /**
   * @param superclass_name .
   */
  public void setSuperclassName(String superclass_name) {
    this.superclass_name = superclass_name;
  }

  /**
   * @param superclass_name_index .
   */
  public void setSuperclassNameIndex(int superclass_name_index) {
    this.superclass_name_index = superclass_name_index;
  }

  /**
   * @return String representing class contents.
   */
  public String toString() {
    String access = Utility.accessToString(access_flags, true);
    access = access.equals("") ? "" : (access + " ");

    StringBuffer buf =
      new StringBuffer(
        access
          + Utility.classOrInterface(access_flags)
          + " "
          + class_name
          + " extends "
          + Utility.compactClassName(superclass_name, false)
          + '\n');
    int size = interfaces.length;

    if (size > 0) {
      buf.append("implements\t\t");

      for (int i = 0; i < size; i++) {
        buf.append(interface_names[i]);
        if (i < size - 1)
          buf.append(", ");
      }

      buf.append('\n');
    }

    buf.append("filename\t\t" + file_name + '\n');
    buf.append("compiled from\t\t" + source_file_name + '\n');
    buf.append("compiler version\t" + major + "." + minor + '\n');
    buf.append("access flags\t\t" + access_flags + '\n');
    buf.append("constant pool\t\t" + constant_pool.getLength() + " entries\n");
    buf.append("ACC_SUPER flag\t\t" + isSuper() + "\n");

    if (attributes.length > 0) {
      buf.append("\nAttribute(s):\n");
      for (int i = 0; i < attributes.length; i++)
        buf.append(indent(attributes[i]));
    }

    if (fields.length > 0) {
      buf.append("\n" + fields.length + " fields:\n");
      for (int i = 0; i < fields.length; i++)
        buf.append("\t" + fields[i] + '\n');
    }

    if (methods.length > 0) {
      buf.append("\n" + methods.length + " methods:\n");
      for (int i = 0; i < methods.length; i++)
        buf.append("\t" + methods[i] + '\n');
    }

    return buf.toString();
  }

  private static final String indent(Object obj) {
    StringTokenizer tok = new StringTokenizer(obj.toString(), "\n");
    StringBuffer buf = new StringBuffer();

    while (tok.hasMoreTokens())
      buf.append("\t" + tok.nextToken() + "\n");

    return buf.toString();
  }

  /**
   * @return deep copy of this class
   */
  public JavaClass copy() {
    JavaClass c = null;

    try {
      c = (JavaClass)clone();
    } catch (CloneNotSupportedException e) {
    }

    c.constant_pool = constant_pool.copy();
    c.interfaces = (int[])interfaces.clone();
    c.interface_names = (String[])interface_names.clone();

    c.fields = new Field[fields.length];
    for (int i = 0; i < fields.length; i++)
      c.fields[i] = fields[i].copy(c.constant_pool);

    c.methods = new Method[methods.length];
    for (int i = 0; i < methods.length; i++)
      c.methods[i] = methods[i].copy(c.constant_pool);

    c.attributes = new Attribute[attributes.length];
    for (int i = 0; i < attributes.length; i++)
      c.attributes[i] = attributes[i].copy(c.constant_pool);

    return c;
  }

  public final boolean isSuper() {
    return (access_flags & Constants.ACC_SUPER) != 0;
  }

  public final boolean isClass() {
    return (access_flags & Constants.ACC_INTERFACE) == 0;
  }

  /** @return returns either HEAP (generated), FILE, or ZIP
   */
  public final byte getSource() {
    return source;
  }

  /********************* New repository functionality *********************/

  /**
   * Gets the ClassRepository which holds its definition. By default
   * this is the same as SyntheticRepository.getInstance();
   */
  public org.apache.bcel.util.Repository getRepository() {
    return repository;
  }

  /**
   * Sets the ClassRepository which loaded the JavaClass.
   * Should be called immediately after parsing is done.
   */
  public void setRepository(org.apache.bcel.util.Repository repository) {
    this.repository = repository;
  }

  /** Equivalent to runtime "instanceof" operator.
   *
   * @return true if this JavaClass is derived from the super class
   * @throws ClassNotFoundException if superclasses or superinterfaces
   *   of this object can't be found
   */
  public final boolean instanceOf(JavaClass super_class)
    throws ClassNotFoundException {

    if (this.equals(super_class))
      return true;

    JavaClass[] super_classes = getSuperClasses();

    for (int i = 0; i < super_classes.length; i++) {
      if (super_classes[i].equals(super_class)) {
        return true;
      }
    }

    if (super_class.isInterface()) {
      return implementationOf(super_class);
    }

    return false;
  }

  /**
   * @return true, if this class is an implementation of interface inter
   * @throws ClassNotFoundException if superclasses or superinterfaces
   *   of this class can't be found
   */
  public boolean implementationOf(JavaClass inter)
    throws ClassNotFoundException {

    if (!inter.isInterface()) {
      throw new IllegalArgumentException(
        inter.getClassName() + " is no interface");
    }

    if (this.equals(inter)) {
      return true;
    }

    JavaClass[] super_interfaces = getAllInterfaces();

    for (int i = 0; i < super_interfaces.length; i++) {
      if (super_interfaces[i].equals(inter)) {
        return true;
      }
    }

    return false;
  }

  /**
   * @return the superclass for this JavaClass object, or null if this
   * is java.lang.Object
   * @throws ClassNotFoundException if the superclass can't be found
   */
  public JavaClass getSuperClass() throws ClassNotFoundException {
    if ("java.lang.Object".equals(getClassName())) {
      return null;
    }

    return repository.loadClass(getSuperclassName());
  }

  /**
   * @return list of super classes of this class in ascending order, i.e.,
   * java.lang.Object is always the last element
   * @throws ClassNotFoundException if any of the superclasses can't be found
   */
  public JavaClass[] getSuperClasses() throws ClassNotFoundException {
    JavaClass clazz = this;
    ClassVector vec = new ClassVector();

    for (clazz = clazz.getSuperClass();
      clazz != null;
      clazz = clazz.getSuperClass()) {
      vec.addElement(clazz);
    }

    return vec.toArray();
  }

  /**
   * Get interfaces directly implemented by this JavaClass.
   */
  public JavaClass[] getInterfaces() throws ClassNotFoundException {
    String[] interfaces = getInterfaceNames();
    JavaClass[] classes = new JavaClass[interfaces.length];

    for (int i = 0; i < interfaces.length; i++) {
      classes[i] = repository.loadClass(interfaces[i]);
    }

    return classes;
  }

  /**
   * Get all interfaces implemented by this JavaClass (transitively).
   */
  public JavaClass[] getAllInterfaces() throws ClassNotFoundException {
    ClassQueue queue = new ClassQueue();
    ClassVector vec = new ClassVector();

    queue.enqueue(this);

    while (!queue.empty()) {
      JavaClass clazz = queue.dequeue();

      JavaClass souper = clazz.getSuperClass();
      JavaClass[] interfaces = clazz.getInterfaces();

      if (clazz.isInterface()) {
        vec.addElement(clazz);
      } else {
        if (souper != null) {
          queue.enqueue(souper);
        }
      }

      for (int i = 0; i < interfaces.length; i++) {
        queue.enqueue(interfaces[i]);
      }
    }

    return vec.toArray();
  }
 
  /**
   * @return Comparison strategy object
   */
  public static BCELComparator getComparator() {
    return _cmp;
  }

  /**
   * @param comparator Comparison strategy object
   */
  public static void setComparator(BCELComparator comparator) {
    _cmp = comparator;
  }

  /**
   * Return value as defined by given BCELComparator strategy.
   * By default two JavaClass objects are said to be equal when
   * their class names are equal.
   *
   * @see java.lang.Object#equals(java.lang.Object)
   */
  public boolean equals(Object obj) {
    return _cmp.equals(this, obj);
  }

  /**
   * Return value as defined by given BCELComparator strategy.
   * By default return the hashcode of the class name.
   *
   * @see java.lang.Object#hashCode()
   */
  public int hashCode() {
    return _cmp.hashCode(this);
  }
}
TOP

Related Classes of org.apache.bcel.classfile.JavaClass

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.