Package org.aspectj.apache.bcel.generic

Source Code of org.aspectj.apache.bcel.generic.ClassGen

package org.aspectj.apache.bcel.generic;

/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation.  All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in
*    the documentation and/or other materials provided with the
*    distribution.
*
* 3. The end-user documentation included with the redistribution,
*    if any, must include the following acknowledgment:
*       "This product includes software developed by the
*        Apache Software Foundation (http://www.apache.org/)."
*    Alternately, this acknowledgment may appear in the software itself,
*    if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
*    "Apache BCEL" must not be used to endorse or promote products
*    derived from this software without prior written permission. For
*    written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
*    "Apache BCEL", nor may "Apache" appear in their name, without
*    prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation.  For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.lang.reflect.Modifier;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.aspectj.apache.bcel.Constants;
import org.aspectj.apache.bcel.classfile.Attribute;
import org.aspectj.apache.bcel.classfile.ConstantPool;
import org.aspectj.apache.bcel.classfile.Field;
import org.aspectj.apache.bcel.classfile.JavaClass;
import org.aspectj.apache.bcel.classfile.Method;
import org.aspectj.apache.bcel.classfile.Modifiers;
import org.aspectj.apache.bcel.classfile.SourceFile;
import org.aspectj.apache.bcel.classfile.Utility;
import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen;
import org.aspectj.apache.bcel.classfile.annotation.RuntimeInvisAnnos;
import org.aspectj.apache.bcel.classfile.annotation.RuntimeVisAnnos;

/**
* Template class for building up a java class. May be initialized with an existing java class.
*
* @see JavaClass
* @version $Id: ClassGen.java,v 1.15 2009/09/15 19:40:14 aclement Exp $
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
*
*         Upgraded, Andy Clement 9th Mar 06 - calculates SUID
*/
public class ClassGen extends Modifiers implements Cloneable {

  private String classname;
  private String superclassname;
  private String filename;
  private int classnameIndex = -1;
  private int superclassnameIndex = -1;
  private int major = Constants.MAJOR_1_1;
  private int minor = Constants.MINOR_1_1;
  private ConstantPool cpool;
  private List<Field> fieldsList = new ArrayList<Field>();
  private List<Method> methodsList = new ArrayList<Method>();
  private List<Attribute> attributesList = new ArrayList<Attribute>();
  private List<String> interfaceList = new ArrayList<String>();
  private List<AnnotationGen> annotationsList = new ArrayList<AnnotationGen>();

  public ClassGen(String classname, String superclassname, String filename, int modifiers, String[] interfacenames,
      ConstantPool cpool) {
    this.classname = classname;
    this.superclassname = superclassname;
    this.filename = filename;
    this.modifiers = modifiers;
    this.cpool = cpool;
    if (filename != null) {
      addAttribute(new SourceFile(cpool.addUtf8("SourceFile"), 2, cpool.addUtf8(filename), cpool));
    }
    this.classnameIndex = cpool.addClass(classname);
    this.superclassnameIndex = cpool.addClass(superclassname);
    if (interfacenames != null) {
      for (String interfacename : interfacenames) {
        addInterface(interfacename);
      }
    }
  }

  public ClassGen(String classname, String superclassname, String filename, int modifiers, String[] interfacenames) {
    this(classname, superclassname, filename, modifiers, interfacenames, new ConstantPool());
  }

  public ClassGen(JavaClass clazz) {
    classnameIndex = clazz.getClassNameIndex();
    superclassnameIndex = clazz.getSuperclassNameIndex();
    classname = clazz.getClassName();
    superclassname = clazz.getSuperclassName();
    filename = clazz.getSourceFileName();
    modifiers = clazz.getModifiers();
    cpool = clazz.getConstantPool().copy();
    major = clazz.getMajor();
    minor = clazz.getMinor();

    Method[] methods = clazz.getMethods();
    Field[] fields = clazz.getFields();
    String[] interfaces = clazz.getInterfaceNames();

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

    // OPTIMIZE Could make unpacking lazy, done on first reference
    Attribute[] attributes = clazz.getAttributes();
    for (Attribute attr : attributes) {
      if (attr instanceof RuntimeVisAnnos) {
        RuntimeVisAnnos rva = (RuntimeVisAnnos) attr;
        List<AnnotationGen> annos = rva.getAnnotations();
        for (AnnotationGen a : annos) {
          annotationsList.add(new AnnotationGen(a, cpool, false));
        }
      } else if (attr instanceof RuntimeInvisAnnos) {
        RuntimeInvisAnnos ria = (RuntimeInvisAnnos) attr;
        List<AnnotationGen> annos = ria.getAnnotations();
        for (AnnotationGen anno : annos) {
          annotationsList.add(new AnnotationGen(anno, cpool, false));
        }
      } else {
        attributesList.add(attr);
      }
    }

    for (int i = 0; i < methods.length; i++) {
      addMethod(methods[i]);
    }

    for (int i = 0; i < fields.length; i++) {
      addField(fields[i]);
    }
  }

  /**
   * @return build and return a JavaClass
   */
  public JavaClass getJavaClass() {
    int[] interfaces = getInterfaces();
    Field[] fields = getFields();
    Method[] methods = getMethods();

    Collection<Attribute> attributes = null;
    if (annotationsList.size() == 0) {
      attributes = attributesList;
    } else {
      // TODO: Sometime later, trash any attributes called 'RuntimeVisibleAnnotations' or 'RuntimeInvisibleAnnotations'
      attributes = new ArrayList<Attribute>();
      attributes.addAll(Utility.getAnnotationAttributes(cpool, annotationsList));
      attributes.addAll(attributesList);
    }

    // Must be last since the above calls may still add something to it
    ConstantPool cp = this.cpool.getFinalConstantPool();

    return new JavaClass(classnameIndex, superclassnameIndex, filename, major, minor, modifiers, cp, interfaces, fields,
        methods, attributes.toArray(new Attribute[attributes.size()]));// OPTIMIZE avoid toArray()?
  }

  public void addInterface(String name) {
    interfaceList.add(name);
  }

  public void removeInterface(String name) {
    interfaceList.remove(name);
  }

  public int getMajor() {
    return major;
  }

  public void setMajor(int major) {
    this.major = major;
  }

  public void setMinor(int minor) {
    this.minor = minor;
  }

  public int getMinor() {
    return minor;
  }

  public void addAttribute(Attribute a) {
    attributesList.add(a);
  }

  public void addAnnotation(AnnotationGen a) {
    annotationsList.add(a);
  }

  public void addMethod(Method m) {
    methodsList.add(m);
  }

  /**
   * Convenience method.
   *
   * Add an empty constructor to this class that does nothing but calling super().
   *
   * @param access rights for constructor
   */
  public void addEmptyConstructor(int access_flags) {
    InstructionList il = new InstructionList();
    il.append(InstructionConstants.THIS); // Push `this'
    il.append(new InvokeInstruction(Constants.INVOKESPECIAL, cpool.addMethodref(superclassname, "<init>", "()V")));
    il.append(InstructionConstants.RETURN);

    MethodGen mg = new MethodGen(access_flags, Type.VOID, Type.NO_ARGS, null, "<init>", classname, il, cpool);
    mg.setMaxStack(1);
    mg.setMaxLocals();
    addMethod(mg.getMethod());
  }

  /**
   * Add a field to this class.
   *
   * @param f field to add
   */
  public void addField(Field f) {
    fieldsList.add(f);
  }

  public boolean containsField(Field f) {
    return fieldsList.contains(f);
  }

  /**
   * @return field object with given name, or null if not found
   */
  public Field containsField(String name) {
    for (Field field : fieldsList) {
      if (field.getName().equals(name)) {
        return field;
      }
    }
    return null;
  }

  /**
   * @return method object with given name and signature, or null if not found
   */
  public Method containsMethod(String name, String signature) {
    for (Method method : methodsList) {
      if (method.getName().equals(name) && method.getSignature().equals(signature)) {
        return method;
      }
    }
    return null;
  }

  public void removeAttribute(Attribute a) {
    attributesList.remove(a);
  }

  public void removeAnnotation(AnnotationGen a) {
    annotationsList.remove(a);
  }

  public void removeMethod(Method m) {
    methodsList.remove(m);
  }

  /**
   * Replace given method with new one. If the old one does not exist add the new_ method to the class anyway.
   */
  public void replaceMethod(Method old, Method new_) {
    if (new_ == null)
      throw new ClassGenException("Replacement method must not be null");

    int i = methodsList.indexOf(old);

    if (i < 0)
      methodsList.add(new_);
    else
      methodsList.set(i, new_);
  }

  /**
   * Replace given field with new one. If the old one does not exist add the new_ field to the class anyway.
   */
  public void replaceField(Field old, Field new_) {
    if (new_ == null)
      throw new ClassGenException("Replacement method must not be null");

    int i = fieldsList.indexOf(old);

    if (i < 0)
      fieldsList.add(new_);
    else
      fieldsList.set(i, new_);
  }

  public void removeField(Field f) {
    fieldsList.remove(f);
  }

  public String getClassName() {
    return classname;
  }

  public String getSuperclassName() {
    return superclassname;
  }

  public String getFileName() {
    return filename;
  }

  public void setClassName(String name) {
    classname = name.replace('/', '.');
    classnameIndex = cpool.addClass(name);
  }

  public void setSuperclassName(String name) {
    superclassname = name.replace('/', '.');
    superclassnameIndex = cpool.addClass(name);
  }

  public Method[] getMethods() {
    Method[] methods = new Method[methodsList.size()];
    methodsList.toArray(methods);
    return methods;
  }

  public void setMethods(Method[] methods) {
    methodsList.clear();
    for (int m = 0; m < methods.length; m++)
      addMethod(methods[m]);
  }

  public void setFields(Field[] fs) {
    fieldsList.clear();
    for (int m = 0; m < fs.length; m++)
      addField(fs[m]);
  }

  public void setMethodAt(Method method, int pos) {
    methodsList.set(pos, method);
  }

  public Method getMethodAt(int pos) {
    return methodsList.get(pos);
  }

  public String[] getInterfaceNames() {
    int size = interfaceList.size();
    String[] interfaces = new String[size];

    interfaceList.toArray(interfaces);
    return interfaces;
  }

  public int[] getInterfaces() {
    int size = interfaceList.size();
    int[] interfaces = new int[size];

    for (int i = 0; i < size; i++)
      interfaces[i] = cpool.addClass(interfaceList.get(i));

    return interfaces;
  }

  public Field[] getFields() {
    Field[] fields = new Field[fieldsList.size()];
    fieldsList.toArray(fields);
    return fields;
  }

  public Collection<Attribute> getAttributes() {
    return attributesList;
  }

  // J5TODO: Should we make calling unpackAnnotations() lazy and put it in here?
  public AnnotationGen[] getAnnotations() {
    AnnotationGen[] annotations = new AnnotationGen[annotationsList.size()];
    annotationsList.toArray(annotations);
    return annotations;
  }

  public ConstantPool getConstantPool() {
    return cpool;
  }

  public void setConstantPool(ConstantPool constant_pool) {
    cpool = constant_pool;
  }

  public void setClassNameIndex(int class_name_index) {
    this.classnameIndex = class_name_index;
    classname = cpool.getConstantString(class_name_index, Constants.CONSTANT_Class).replace('/', '.');
  }

  public void setSuperclassNameIndex(int superclass_name_index) {
    this.superclassnameIndex = superclass_name_index;
    superclassname = cpool.getConstantString(superclass_name_index, Constants.CONSTANT_Class).replace('/', '.');
  }

  public int getSuperclassNameIndex() {
    return superclassnameIndex;
  }

  public int getClassNameIndex() {
    return classnameIndex;
  }

  @Override
  public Object clone() {
    try {
      return super.clone();
    } catch (CloneNotSupportedException e) {
      System.err.println(e);
      return null;
    }
  }

  public final boolean isAnnotation() {
    return (modifiers & Constants.ACC_ANNOTATION) != 0;
  }

  public final boolean isEnum() {
    return (modifiers & Constants.ACC_ENUM) != 0;
  }

  /**
   * Calculate the SerialVersionUID for a class.
   */
  public long getSUID() {
    try {

      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      DataOutputStream dos = new DataOutputStream(baos);

      // 1. classname
      dos.writeUTF(getClassName());

      // 2. classmodifiers: ACC_PUBLIC, ACC_FINAL, ACC_INTERFACE, and ACC_ABSTRACT
      int classmods = 0;
      classmods |= (isPublic() ? Constants.ACC_PUBLIC : 0);
      classmods |= (isFinal() ? Constants.ACC_FINAL : 0);
      classmods |= (isInterface() ? Constants.ACC_INTERFACE : 0);

      if (isAbstract()) {
        // if an interface then abstract is only set if it has methods
        if (isInterface()) {
          if (methodsList.size() > 0)
            classmods |= Constants.ACC_ABSTRACT;
        } else {
          classmods |= Constants.ACC_ABSTRACT;
        }
      }

      dos.writeInt(classmods);

      // 3. ordered list of interfaces
      String[] names = getInterfaceNames();
      if (names != null) {
        Arrays.sort(names);
        for (int i = 0; i < names.length; i++)
          dos.writeUTF(names[i]);
      }

      // 4. ordered list of fields (ignoring private static and private transient fields):
      // (relevant modifiers are ACC_PUBLIC, ACC_PRIVATE,
      // ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_VOLATILE,
      // ACC_TRANSIENT)
      List<Field> relevantFields = new ArrayList<Field>();
      for (Field field : fieldsList) {
        if (!(field.isPrivate() && field.isStatic()) && !(field.isPrivate() && field.isTransient())) {
          relevantFields.add(field);
        }
      }
      Collections.sort(relevantFields, new FieldComparator());
      int relevantFlags = Constants.ACC_PUBLIC | Constants.ACC_PRIVATE | Constants.ACC_PROTECTED | Constants.ACC_STATIC
          | Constants.ACC_FINAL | Constants.ACC_VOLATILE | Constants.ACC_TRANSIENT;
      for (Field f : relevantFields) {
        dos.writeUTF(f.getName());
        dos.writeInt(relevantFlags & f.getModifiers());
        dos.writeUTF(f.getType().getSignature());
      }

      // some up front method processing: discover clinit, init and ordinary methods of interest:
      List<Method> relevantMethods = new ArrayList<Method>();
      List<Method> relevantCtors = new ArrayList<Method>();
      boolean hasClinit = false;
      for (Method m : methodsList) {
        boolean couldBeInitializer = m.getName().charAt(0) == '<';
        if (couldBeInitializer && m.getName().equals("<clinit>")) {
          hasClinit = true;
        } else if (couldBeInitializer && m.getName().equals("<init>")) {
          if (!m.isPrivate())
            relevantCtors.add(m);
        } else {
          if (!m.isPrivate())
            relevantMethods.add(m);
        }
      }
      Collections.sort(relevantCtors, new ConstructorComparator());
      Collections.sort(relevantMethods, new MethodComparator());

      // 5. If a class initializer exists, write out the following:
      // 1. The name of the method, <clinit>.
      // 2. The modifier of the method, java.lang.reflect.Modifier.STATIC, written as a 32-bit integer.
      // 3. The descriptor of the method, ()V.
      if (hasClinit) {
        dos.writeUTF("<clinit>");
        dos.writeInt(Modifier.STATIC);
        dos.writeUTF("()V");
      }

      // for methods and constructors:
      // ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED,
      // ACC_NATIVE, ACC_ABSTRACT and ACC_STRICT
      relevantFlags = Constants.ACC_PUBLIC | Constants.ACC_PRIVATE | Constants.ACC_PROTECTED | Constants.ACC_STATIC
          | Constants.ACC_FINAL | Constants.ACC_SYNCHRONIZED | Constants.ACC_NATIVE | Constants.ACC_ABSTRACT
          | Constants.ACC_STRICT;

      // 6. sorted non-private constructors
      for (Method ctor : relevantCtors) {
        dos.writeUTF(ctor.getName()); // <init>
        dos.writeInt(relevantFlags & ctor.getModifiers());
        dos.writeUTF(ctor.getSignature().replace('/', '.'));
      }

      // 7. sorted non-private methods
      for (Method m : relevantMethods) {
        dos.writeUTF(m.getName());
        dos.writeInt(relevantFlags & m.getModifiers());
        dos.writeUTF(m.getSignature().replace('/', '.'));
      }
      dos.flush();
      dos.close();
      byte[] bs = baos.toByteArray();
      MessageDigest md = MessageDigest.getInstance("SHA");
      byte[] result = md.digest(bs);

      long suid = 0L;
      int pos = result.length > 8 ? 7 : result.length - 1; // use the bytes we have
      while (pos >= 0) {
        suid = suid << 8 | ((long) result[pos--] & 0xff);
      }

      // if it was definetly 8 everytime...
      // long suid = ((long)(sha[0]&0xff) | (long)(sha[1]&0xff) << 8 |
      // (long)(sha[2]&0xff) << 16 | (long)(sha[3]&0xff) << 24 |
      // (long)(sha[4]&0xff) << 32 | (long)(sha[5]&0xff) << 40 |
      // (long)(sha[6]&0xff) << 48 | (long)(sha[7]&0xff) << 56);
      return suid;
    } catch (Exception e) {
      e.printStackTrace();
      throw new RuntimeException("Unable to calculate suid for " + getClassName() + ": " + e.toString());
    }
  }

  private static class FieldComparator implements Comparator<Field> {
    public int compare(Field f0, Field f1) {
      return f0.getName().compareTo(f1.getName());
    }
  }

  private static class ConstructorComparator implements Comparator<Method> {
    public int compare(Method m0, Method m1) {
      // can ignore the name...
      return (m0).getSignature().compareTo(m1.getSignature());
    }
  }

  private static class MethodComparator implements Comparator<Method> {
    public int compare(Method m0, Method m1) {
      int result = m0.getName().compareTo(m1.getName());
      if (result == 0) {
        result = m0.getSignature().compareTo(m1.getSignature());
      }
      return result;
    }
  }

  public boolean hasAttribute(String attributeName) {
    for (Attribute attr : attributesList) {
      if (attr.getName().equals(attributeName)) {
        return true;
      }
    }
    return false;
  }

  public Attribute getAttribute(String attributeName) {
    for (Attribute attr : attributesList) {
      if (attr.getName().equals(attributeName)) {
        return attr;
      }
    }
    return null;
  }
}
TOP

Related Classes of org.aspectj.apache.bcel.generic.ClassGen

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.