All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jboss.classfilewriter.ClassFile Maven / Gradle / Ivy

There is a newer version: 62
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 *
 * Copyright 2012 Red Hat, 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 org.jboss.classfilewriter;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.jboss.classfilewriter.annotations.AnnotationBuilder;
import org.jboss.classfilewriter.annotations.AnnotationsAttribute;
import org.jboss.classfilewriter.attributes.Attribute;
import org.jboss.classfilewriter.constpool.ConstPool;
import org.jboss.classfilewriter.util.ByteArrayDataOutputStream;
import org.jboss.classfilewriter.util.DescriptorUtils;

/**
 * @author Stuart Douglas
 */
public class ClassFile implements WritableEntry {

    private final String name;

    private final String superclass;

    private final int accessFlags;

    private final int version;

    private final ConstPool constPool = new ConstPool();

    private final List interfaces = new ArrayList();

    private final Set fields = new HashSet();

    private final Set methods = new HashSet();

    private byte[] bytecode;

    private final List attributes = new ArrayList();

    private final AnnotationsAttribute runtimeVisibleAnnotationsAttribute;

    private final ClassLoader classLoader;

    private final ClassFactory classFactory;

    @Deprecated
    public ClassFile(String name, String superclass, String... interfaces) {
        this(name, AccessFlag.of(AccessFlag.SUPER, AccessFlag.PUBLIC), superclass, null, interfaces);
    }

    @Deprecated
    public ClassFile(String name, int accessFlags, String superclass, String... interfaces) {
        this(name, accessFlags, superclass, null, interfaces);
    }

    @Deprecated
    public ClassFile(String name, String superclass, ClassLoader classLoader, String... interfaces) {
        this(name, AccessFlag.of(AccessFlag.SUPER, AccessFlag.PUBLIC), superclass, classLoader, interfaces);
    }

    @Deprecated
    public ClassFile(String name, int accessFlags, String superclass, ClassLoader classLoader, String... interfaces) {
        this(name, accessFlags, superclass, JavaVersions.JAVA_6, classLoader, interfaces);
    }

    @Deprecated
    public ClassFile(String name, int accessFlags, String superclass, int version, ClassLoader classLoader, String... interfaces) {
        if(version > JavaVersions.JAVA_6 && classLoader == null) {
            throw new IllegalArgumentException("ClassLoader must be specified if version is greater than Java 6");
        }
        this.version = version;
        this.classLoader = classLoader;
        this.classFactory = null; // allowed to be null for backward compatibility reasons
        this.name = name.replace('/', '.'); // store the name in . form
        this.superclass = superclass;
        this.accessFlags = accessFlags;
        this.interfaces.addAll(Arrays.asList(interfaces));
        this.runtimeVisibleAnnotationsAttribute = new AnnotationsAttribute(AnnotationsAttribute.Type.RUNTIME_VISIBLE, constPool);
        this.attributes.add(runtimeVisibleAnnotationsAttribute);
    }

    public ClassFile(String name, String superclass, ClassLoader classLoader, ClassFactory classFactory, String... interfaces) {
        this(name, AccessFlag.of(AccessFlag.SUPER, AccessFlag.PUBLIC), superclass, classLoader, classFactory, interfaces);
    }

    public ClassFile(String name, int accessFlags, String superclass, ClassLoader classLoader, ClassFactory classFactory, String... interfaces) {
        this(name, accessFlags, superclass, JavaVersions.JAVA_6, classLoader, classFactory, interfaces);
    }

    public ClassFile(String name, int accessFlags, String superclass, int version, ClassLoader classLoader, ClassFactory classFactory, String... interfaces) {
        if(version > JavaVersions.JAVA_6 && classLoader == null) {
            throw new IllegalArgumentException("ClassLoader must be specified if version is greater than Java 6");
        }
        if (classFactory == null) {
            throw new IllegalArgumentException("ClassFactory must be specified");
        }
        this.version = version;
        this.classLoader = classLoader;
        this.classFactory = classFactory;
        this.name = name.replace('/', '.'); // store the name in . form
        this.superclass = superclass;
        this.accessFlags = accessFlags;
        this.interfaces.addAll(Arrays.asList(interfaces));
        this.runtimeVisibleAnnotationsAttribute = new AnnotationsAttribute(AnnotationsAttribute.Type.RUNTIME_VISIBLE, constPool);
        this.attributes.add(runtimeVisibleAnnotationsAttribute);
    }

    public void addInterface(String iface) {
        this.interfaces.add(iface);
    }

    // fields
    /**
     * Adds a field with the given name and descriptor.
     *
     */
    public ClassField addField(int accessFlags, String name, String descriptor) {
        return addField(accessFlags, name, descriptor, null);
    }

    public ClassField addField(int accessFlags, String name, String descriptor, String signature) {
        ClassField field = new ClassField((short) accessFlags, name, descriptor, this, constPool);
        if (fields.contains(field)) {
            throw new DuplicateMemberException("Field  already exists. Field: " + name + " Descriptor:" + signature);
        }
        fields.add(field);
        field.setSignature(signature);
        return field;
    }

    public ClassField addField(int accessFlags, String name, Class type) {
        return addField(accessFlags, name, DescriptorUtils.makeDescriptor(type));
    }

    public ClassField addField(int accessFlags, String name, Class type, String genericSignature) {
        return addField(accessFlags, name, DescriptorUtils.makeDescriptor(type), genericSignature);
    }

    public ClassField addField(Field field) {
        ClassField classField = addField((short) field.getModifiers(), field.getName(), field.getType(), null);
        for (Annotation annotation : field.getDeclaredAnnotations()) {
            classField.getRuntimeVisibleAnnotationsAttribute().addAnnotation(
                    AnnotationBuilder.createAnnotation(constPool, annotation));
        }
        return classField;

    }

    // methods

    public ClassMethod addMethod(int accessFlags, String name, String returnType, String... parameters) {
        ClassMethod method = new ClassMethod(name, returnType, parameters, accessFlags, this);
        if (methods.contains(method)) {
            throw new DuplicateMemberException("Method  already exists. Method: " + name + " Parameters:"
                    + Arrays.toString(parameters) + " Return Type: " + returnType);
        }
        methods.add(method);
        return method;
    }

    /**
     * Adds a method with the same signiture as the given method, including exception types
     * 

* The new method will have the same modifier as the original method, except that the abstract and native flags will be * stripped. *

* TODO: annotations and signiture attribute */ public ClassMethod addMethod(Method method) { ClassMethod classMethod = addMethod(method.getModifiers() & (~AccessFlag.ABSTRACT) & (~AccessFlag.NATIVE), method .getName(), DescriptorUtils.makeDescriptor(method.getReturnType()), DescriptorUtils.parameterDescriptors(method .getParameterTypes())); for (Class e : method.getExceptionTypes()) { classMethod.addCheckedExceptions((Class) e); } for (Annotation annotation : method.getDeclaredAnnotations()) { classMethod.getRuntimeVisibleAnnotationsAttribute().addAnnotation( AnnotationBuilder.createAnnotation(constPool, annotation)); } int count = 0; for (Annotation[] parameterAnnotations : method.getParameterAnnotations()) { for (Annotation annotation : parameterAnnotations) { classMethod.getRuntimeVisibleParameterAnnotationsAttribute().addAnnotation(count, AnnotationBuilder.createAnnotation(constPool, annotation)); } count++; } return classMethod; } /** * Adds a constructor with the same signiture as the given constrcutor, including exception types *

* TODO: annotations and signiture attribute */ public ClassMethod addConstructor(Constructor method) { ClassMethod classMethod = addMethod(method.getModifiers(), "", "V", DescriptorUtils.parameterDescriptors(method .getParameterTypes())); for (Class e : method.getExceptionTypes()) { classMethod.addCheckedExceptions((Class) e); } for (Annotation annotation : method.getDeclaredAnnotations()) { classMethod.getRuntimeVisibleAnnotationsAttribute().addAnnotation( AnnotationBuilder.createAnnotation(constPool, annotation)); } int count = 0; for (Annotation[] parameterAnnotations : method.getParameterAnnotations()) { for (Annotation annotation : parameterAnnotations) { classMethod.getRuntimeVisibleParameterAnnotationsAttribute().addAnnotation(count, AnnotationBuilder.createAnnotation(constPool, annotation)); } count++; } return classMethod; } public void write(ByteArrayDataOutputStream stream) throws IOException { // first make sure everything we need is in the const pool int nameIndex = constPool.addClassEntry(name); int superClassIndex = constPool.addClassEntry(superclass); List interfaceIndexes = new ArrayList(interfaces.size()); for (String i : interfaces) { interfaceIndexes.add(constPool.addClassEntry(i)); } stream.writeInt(0xCAFEBABE);// magic stream.writeInt(version); constPool.write(stream); stream.writeShort(accessFlags); stream.writeShort(nameIndex); stream.writeShort(superClassIndex); stream.writeShort(interfaceIndexes.size()); // interface count for (int i : interfaceIndexes) { stream.writeShort(i); } stream.writeShort(fields.size()); // field count for (ClassField field : fields) { field.write(stream); } stream.writeShort(methods.size()); // method count for (ClassMethod method : methods) { method.write(stream); } stream.writeShort(attributes.size()); // attribute count for (Attribute attribute : attributes) { attribute.write(stream); } } public Class define() { return defineInternal(classLoader, null); } @Deprecated public Class define(ClassLoader loader) { return defineInternal(loader, null); } public Class define(ProtectionDomain domain) { return defineInternal(classLoader, domain); } /** * Definines the class using the given ClassLoader and ProtectionDomain */ @Deprecated public Class define(ClassLoader loader, ProtectionDomain domain) { return defineInternal(loader, domain); } private Class defineInternal(ClassLoader loader, ProtectionDomain domain) { byte[] b = toBytecode(); final ClassFactory classFactory = this.classFactory == null ? DefaultClassFactory.INSTANCE : this.classFactory; return classFactory.defineClass(loader, name, b, 0, b.length, domain); } public byte[] toBytecode() { // TODO: throw illegal state exception if the class file is modified after writing if (bytecode == null) { try { ByteArrayDataOutputStream out = new ByteArrayDataOutputStream(); write(out); bytecode = out.getBytes(); } catch (IOException e) { throw new RuntimeException(e); } } return bytecode; } public ClassLoader getClassLoader() { return classLoader; } public ConstPool getConstPool() { return constPool; } /** * returns the type descriptor for the class * * @return */ public String getDescriptor() { return DescriptorUtils.makeDescriptor(name); } public AnnotationsAttribute getRuntimeVisibleAnnotationsAttribute() { return runtimeVisibleAnnotationsAttribute; } /** * Returns the generated class name * * @return The generated class name */ public String getName() { return name; } /** * * @return The generated superclass name */ public String getSuperclass() { return superclass; } /** * * @return The interfaces implemented by this class */ public List getInterfaces() { return Collections.unmodifiableList(interfaces); } /** * * @return This class's fields */ public Set getFields() { return Collections.unmodifiableSet(fields); } /** * * @return This classes methods */ public Set getMethods() { return Collections.unmodifiableSet(methods); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy