com.netspective.commons.lang.ValueBeanGeneratorClassLoader.java Source code

Java tutorial

Introduction

Here is the source code for com.netspective.commons.lang.ValueBeanGeneratorClassLoader.java

Source

/*
 * Copyright (c) 2000-2004 Netspective Communications LLC. All rights reserved.
 *
 * Netspective Communications LLC ("Netspective") permits redistribution, modification and use of this file in source
 * and binary form ("The Software") under the Netspective Source License ("NSL" or "The License"). The following
 * conditions are provided as a summary of the NSL but the NSL remains the canonical license and must be accepted
 * before using The Software. Any use of The Software indicates agreement with the NSL.
 *
 * 1. Each copy or derived work of The Software must preserve the copyright notice and this notice unmodified.
 *
 * 2. Redistribution of The Software is allowed in object code form only (as Java .class files or a .jar file
 *    containing the .class files) and only as part of an application that uses The Software as part of its primary
 *    functionality. No distribution of the package is allowed as part of a software development kit, other library,
 *    or development tool without written consent of Netspective. Any modified form of The Software is bound by these
 *    same restrictions.
 *
 * 3. Redistributions of The Software in any form must include an unmodified copy of The License, normally in a plain
 *    ASCII text file unless otherwise agreed to, in writing, by Netspective.
 *
 * 4. The names "Netspective", "Axiom", "Commons", "Junxion", and "Sparx" are trademarks of Netspective and may not be
 *    used to endorse or appear in products derived from The Software without written consent of Netspective.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT A WARRANTY OF ANY KIND. ALL EXPRESS OR IMPLIED REPRESENTATIONS AND
 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT,
 * ARE HEREBY DISCLAIMED.
 *
 * NETSPECTIVE AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE OR ANY THIRD PARTY AS A
 * RESULT OF USING OR DISTRIBUTING THE SOFTWARE. IN NO EVENT WILL NETSPECTIVE OR ITS LICENSORS BE LIABLE FOR ANY LOST
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE, EVEN
 * IF IT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 */
package com.netspective.commons.lang;

import java.io.IOException;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.bcel.Constants;
import org.apache.bcel.generic.BranchInstruction;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.FieldGen;
import org.apache.bcel.generic.InstructionFactory;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.PUSH;
import org.apache.bcel.generic.Type;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * A classloader that generates a value object bean based on one or more interfaces. Each getter/setter in the interfaces
 * is implemented in the generated bean with private field names using the suffix of the set/get methods. This class
 * allows you to manage an entire bean by simply defining an interface. There is special handling for creating value
 * beans that need to be settable (mutable) by a ResultSet instance.
 * <p/>
 * Assume the following interfaces:
 * <p/>
 * public interface BeanGeneratorTestInterface
 * {
 * public String getString();
 * public int getInt();
 * public short getShort();
 * public byte getByte();
 * public char getChar();
 * public Integer getInteger();
 * public boolean getBoolean();
 * public float getFloat();
 * public Date getDate();
 * public double getDouble();
 * public Object getObject();
 * }
 * <p/>
 * public interface BeanGeneratorTestMutableInterface
 * {
 * public void setString(String value);
 * public void setInt(int value);
 * public void setInteger(Integer value);
 * public void setShort(short value);
 * public void setByte(byte value);
 * public void setChar(char value);
 * public void setBoolean(boolean bool);
 * public void setFloat(float value);
 * public void setDate(Date value);
 * public void setDouble(double value);
 * public void setObject(Object value);
 * <p/>
 * public void setColumnsByName(ResultSet rs, ValueBeanFieldIndexTranslator translator);
 * public void setColumnsByName(ResultSet rs, ValueBeanFieldNameTranslator translator);
 * }
 * <p/>
 * If the programmer defines the above two interfaces (they can also be combined into one if desired), then a call like
 * the following will automatically generate a class on the fly:
 * <p/>
 * final String className = "com.netspective.commons.lang.BeanGeneratorTestImpl";
 * ValueBeanGeneratorClassLoader bgcl = ValueBeanGeneratorClassLoader.getInstance(className, new Class[]
 * { BeanGeneratorTestInterface.class, BeanGeneratorTestMutableInterface.class });
 * <p/>
 * The auto-generated class that can manage the values as fields and the special handlers setColumnsByName(ResultSet, ?)
 * will automatically assign values to the fields using the items in the ResultSet using optional name or index
 * translators. The index version is the fastest.
 */
public class ValueBeanGeneratorClassLoader extends ClassLoader {
    private static final Log log = LogFactory.getLog(ValueBeanGeneratorClassLoader.class);
    private static final Map INSTANCES = new HashMap();

    public static synchronized ValueBeanGeneratorClassLoader getInstance(final String className,
            final Class[] interfaces) throws IOException, ClassNotFoundException {
        ValueBeanGeneratorClassLoader bgcl = (ValueBeanGeneratorClassLoader) INSTANCES.get(className);
        if (bgcl == null) {
            bgcl = new ValueBeanGeneratorClassLoader(interfaces[0].getClassLoader(), className, interfaces);
            INSTANCES.put(className, bgcl);
        }

        return bgcl;
    }

    private final String className;
    private final Class[] interfaces;
    private final Class generatedClass;

    public ValueBeanGeneratorClassLoader(String className, Class[] interfaces)
            throws IOException, ClassNotFoundException {
        this.className = className;
        this.interfaces = interfaces;
        final byte[] byteCode = generateSimpleBean();
        this.generatedClass = defineClass(className, byteCode, 0, byteCode.length);
    }

    public ValueBeanGeneratorClassLoader(ClassLoader parent, String className, Class[] interfaces)
            throws IOException, ClassNotFoundException {
        super(parent);
        this.className = className;
        this.interfaces = interfaces;
        final byte[] byteCode = generateSimpleBean();
        this.generatedClass = defineClass(className, byteCode, 0, byteCode.length);
    }

    public Class getGeneratedClass() {
        return generatedClass;
    }

    protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
        if (name.equals(className))
            return generatedClass;
        else
            return super.loadClass(name, resolve);
    }

    protected byte[] generateSimpleBean() throws IOException, ClassNotFoundException {
        List interfaceNames = new ArrayList();
        for (int i = 0; i < interfaces.length; i++)
            interfaceNames.add(interfaces[i].getName());

        final BeanUtil[] beanUtils = new BeanUtil[interfaces.length];
        for (int i = 0; i < interfaces.length; i++) {
            final Class intf = interfaces[i];
            final BeanUtil beanUtil = new BeanUtil(intf);
            beanUtils[i] = new BeanUtil(intf);

            for (Iterator mutatorIter = beanUtil.generators.iterator(); mutatorIter.hasNext();) {
                SingleFieldBytecodeGenerator generator = (SingleFieldBytecodeGenerator) mutatorIter.next();
                if (generator instanceof IndexMappedResultSetMutatorBytecodeGenerator)
                    interfaceNames.add(ValueBeanIndexedResultSetAssignable.class.getName());
            }
        }

        final ClassGen classGen = new ClassGen(className, "java.lang.Object", className + ".java",
                Constants.ACC_PUBLIC | Constants.ACC_SUPER,
                (String[]) interfaceNames.toArray(new String[interfaceNames.size()]));
        final ConstantPoolGen constantsPool = classGen.getConstantPool();
        final InstructionFactory instructionFactory = new InstructionFactory(classGen, constantsPool);

        // generate the default constructor
        classGen.addEmptyConstructor(Constants.ACC_PUBLIC);

        for (int i = 0; i < interfaces.length; i++) {
            final BeanUtil beanUtil = beanUtils[i];

            for (Iterator mutatorIter = beanUtil.generators.iterator(); mutatorIter.hasNext();) {
                SingleFieldBytecodeGenerator generator = (SingleFieldBytecodeGenerator) mutatorIter.next();
                generator.generateBytecode(instructionFactory, constantsPool, classGen);
            }
        }

        return classGen.getJavaClass().getBytes();
    }

    /**
     * Convert runtime java.lang.Class to BCEL Type object.
     *
     * @param cl Java class
     *
     * @return corresponding Type object
     */
    public static Type getBCELType(java.lang.Class cl) {
        if (cl == null) {
            throw new IllegalArgumentException("Class must not be null");
        }

        /* That's an amzingly easy case, because getName() returns
         * the signature. That's what we would have liked anyway.
         */
        if (cl.isArray()) {
            return Type.getType(cl.getName());
        } else if (cl.isPrimitive()) {
            if (cl == Integer.TYPE) {
                return Type.INT;
            } else if (cl == Void.TYPE) {
                return Type.VOID;
            } else if (cl == Double.TYPE) {
                return Type.DOUBLE;
            } else if (cl == Float.TYPE) {
                return Type.FLOAT;
            } else if (cl == Boolean.TYPE) {
                return Type.BOOLEAN;
            } else if (cl == Byte.TYPE) {
                return Type.BYTE;
            } else if (cl == Short.TYPE) {
                return Type.SHORT;
            } else if (cl == Long.TYPE) {
                return Type.LONG;
            } else if (cl == Character.TYPE) {
                return Type.CHAR;
            } else {
                throw new IllegalStateException("Ooops, what primitive type is " + cl);
            }
        } else { // "Real" class
            return new ObjectType(cl.getName());
        }
    }

    protected interface ClassField {
        public String getFieldName();

        public Class getFieldType();

        public InvokeInstruction getResultSetAccessorByColNameInvokeInstruction(
                InstructionFactory instructionFactory);

        public InvokeInstruction getResultSetAccessorByColIndexInvokeInstruction(
                InstructionFactory instructionFactory);
    }

    protected interface SingleFieldBytecodeGenerator {
        public void generateBytecode(InstructionFactory instructionFactory, ConstantPoolGen constantsPool,
                ClassGen classGen);
    }

    protected interface MultiFieldBytecodeGenerator extends SingleFieldBytecodeGenerator {
    }

    protected class SimpleFieldAccessorSingleFieldBytecodeGenerator implements SingleFieldBytecodeGenerator {
        private final BeanMethod beanMethod;

        public SimpleFieldAccessorSingleFieldBytecodeGenerator(BeanMethod beanMethod) {
            this.beanMethod = beanMethod;
        }

        public void generateBytecode(InstructionFactory instructionFactory, ConstantPoolGen constantsPool,
                ClassGen classGen) {
            final InstructionList il = new InstructionList();
            final Type returnType = getBCELType(beanMethod.method.getReturnType());
            final MethodGen accessor = new MethodGen(Constants.ACC_PUBLIC, returnType, Type.NO_ARGS,
                    new String[] {}, beanMethod.method.getName(), className, il, constantsPool);

            il.append(InstructionFactory.createLoad(Type.OBJECT, 0));
            il.append(instructionFactory.createFieldAccess(className, beanMethod.fieldName, returnType,
                    Constants.GETFIELD));
            il.append(InstructionFactory.createReturn(returnType));
            accessor.setMaxStack();
            accessor.setMaxLocals();
            classGen.addMethod(accessor.getMethod());
            il.dispose();
        }
    }

    protected class SimpleFieldMutatorSingleFieldBytecodeGenerator implements SingleFieldBytecodeGenerator {
        private final BeanMethod beanMethod;

        public SimpleFieldMutatorSingleFieldBytecodeGenerator(BeanMethod beanMethod) {
            this.beanMethod = beanMethod;
        }

        public void generateBytecode(InstructionFactory instructionFactory, ConstantPoolGen constantsPool,
                ClassGen classGen) {
            final Type paramType = getBCELType(beanMethod.method.getParameterTypes()[0]);
            classGen.addField(
                    new FieldGen(Constants.ACC_PRIVATE, paramType, beanMethod.fieldName, constantsPool).getField());

            final InstructionList il = new InstructionList();
            final MethodGen mutator = new MethodGen(Constants.ACC_PUBLIC, Type.VOID, new Type[] { paramType },
                    new String[] { beanMethod.fieldName }, beanMethod.method.getName(), className, il,
                    constantsPool);

            il.append(InstructionFactory.createLoad(Type.OBJECT, 0));
            il.append(InstructionFactory.createLoad(paramType, 1));
            il.append(instructionFactory.createFieldAccess(className, beanMethod.fieldName, paramType,
                    Constants.PUTFIELD));
            il.append(InstructionFactory.createReturn(Type.VOID));
            mutator.setMaxStack();
            mutator.setMaxLocals();
            classGen.addMethod(mutator.getMethod());
            il.dispose();
        }
    }

    protected class IndexMappedResultSetMutatorBytecodeGenerator implements MultiFieldBytecodeGenerator {
        private final BeanMethod beanMethod;
        private final Set classFields;

        public IndexMappedResultSetMutatorBytecodeGenerator(BeanMethod beanMethod, Set classFields) {
            this.beanMethod = beanMethod;
            this.classFields = classFields;
        }

        public void generateBytecode(InstructionFactory instructionFactory, ConstantPoolGen constantsPool,
                ClassGen classGen) {
            final InstructionList il = new InstructionList();
            final MethodGen method = new MethodGen(Constants.ACC_PUBLIC, Type.VOID,
                    new Type[] { new ObjectType("java.sql.ResultSet"),
                            new ObjectType(ValueBeanFieldIndexTranslator.class.getName()) },
                    new String[] { "rs", "mapper" }, beanMethod.method.getName(), className, il, constantsPool);

            BranchInstruction ifle = null;

            for (Iterator i = classFields.iterator(); i.hasNext();) {
                final ClassField field = (ClassField) i.next();
                final InvokeInstruction resultSetAccessorByColIndexInvokeInstruction = field
                        .getResultSetAccessorByColIndexInvokeInstruction(instructionFactory);

                if (resultSetAccessorByColIndexInvokeInstruction == null)
                    continue;

                /* PSEUDO CODE:
                 *   int columnIndex = mapper.getTranslatedIndex("fieldName");
                 *   if(columnIndex > 0)
                 *      this.fieldName = rs.getXXX(columnIndex);
                 */

                InstructionHandle start = il.append(InstructionFactory.createLoad(Type.OBJECT, 2));
                if (ifle != null)
                    ifle.setTarget(start);

                il.append(new PUSH(constantsPool, field.getFieldName()));
                il.append(instructionFactory.createInvoke(ValueBeanFieldIndexTranslator.class.getName(),
                        "getTranslatedIndex", Type.INT, new Type[] { Type.STRING }, Constants.INVOKEINTERFACE));
                il.append(InstructionFactory.createStore(Type.INT, 3));
                il.append(InstructionFactory.createLoad(Type.INT, 3));
                ifle = InstructionFactory.createBranchInstruction(Constants.IFLE, null);
                il.append(ifle);
                il.append(InstructionFactory.createLoad(Type.OBJECT, 0));
                il.append(InstructionFactory.createLoad(Type.OBJECT, 1));
                il.append(InstructionFactory.createLoad(Type.INT, 3));
                il.append(resultSetAccessorByColIndexInvokeInstruction);
                il.append(instructionFactory.createFieldAccess(className, field.getFieldName(),
                        getBCELType(field.getFieldType()), Constants.PUTFIELD));
            }

            InstructionHandle exit = il.append(InstructionFactory.createReturn(Type.VOID));
            if (ifle != null)
                ifle.setTarget(exit);

            method.setMaxStack();
            method.setMaxLocals();

            classGen.addMethod(method.getMethod());
            il.dispose();
        }
    }

    protected class NameMappedResultSetMutatorBytecodeGenerator implements MultiFieldBytecodeGenerator {
        private final BeanMethod beanMethod;
        private final Set classFields;

        public NameMappedResultSetMutatorBytecodeGenerator(BeanMethod beanMethod, Set classFields) {
            this.beanMethod = beanMethod;
            this.classFields = classFields;
        }

        public void generateBytecode(InstructionFactory instructionFactory, ConstantPoolGen constantsPool,
                ClassGen classGen) {
            final InstructionList il = new InstructionList();
            final MethodGen method = new MethodGen(Constants.ACC_PUBLIC, Type.VOID,
                    new Type[] { new ObjectType("java.sql.ResultSet"),
                            new ObjectType(ValueBeanFieldNameTranslator.class.getName()) },
                    new String[] { "rs", "mapper" }, beanMethod.method.getName(), className, il, constantsPool);

            BranchInstruction ifNullBranch = null;

            for (Iterator i = classFields.iterator(); i.hasNext();) {
                final ClassField field = (ClassField) i.next();
                final InvokeInstruction resultSetAccessorByColNameInvokeInstruction = field
                        .getResultSetAccessorByColNameInvokeInstruction(instructionFactory);

                if (resultSetAccessorByColNameInvokeInstruction == null)
                    continue;

                /* PSEUDO CODE:
                 *   String columnName = mapper.getTranslatedName("fieldName");
                 *   if(columnName != null)
                 *       this.fieldName = rs.getString(columnName);
                 */

                InstructionHandle start = il.append(InstructionFactory.createLoad(Type.OBJECT, 2));
                if (ifNullBranch != null)
                    ifNullBranch.setTarget(start);

                il.append(new PUSH(constantsPool, field.getFieldName()));
                il.append(instructionFactory.createInvoke(ValueBeanFieldIndexTranslator.class.getName(),
                        "getTranslatedName", Type.STRING, new Type[] { Type.STRING }, Constants.INVOKEINTERFACE));
                il.append(InstructionFactory.createStore(Type.OBJECT, 3));
                il.append(InstructionFactory.createLoad(Type.OBJECT, 3));
                ifNullBranch = InstructionFactory.createBranchInstruction(Constants.IFNULL, null);
                il.append(ifNullBranch);
                il.append(InstructionFactory.createLoad(Type.OBJECT, 0));
                il.append(InstructionFactory.createLoad(Type.OBJECT, 1));
                il.append(InstructionFactory.createLoad(Type.OBJECT, 3));
                il.append(resultSetAccessorByColNameInvokeInstruction);
                il.append(instructionFactory.createFieldAccess(className, field.getFieldName(),
                        getBCELType(field.getFieldType()), Constants.PUTFIELD));
            }

            InstructionHandle exit = il.append(InstructionFactory.createReturn(Type.VOID));
            if (ifNullBranch != null)
                ifNullBranch.setTarget(exit);

            method.setMaxStack();
            method.setMaxLocals();

            classGen.addMethod(method.getMethod());
            il.dispose();
        }
    }

    protected class BeanMethod implements ClassField {
        private final String fieldName;
        private final Method method;
        private final SingleFieldBytecodeGenerator generator;

        public BeanMethod(Method method, Set classFields) {
            this.method = method;
            final String methodName = method.getName();
            final Class[] parameterTypes = method.getParameterTypes();
            if (methodName.startsWith("get") && parameterTypes.length == 0) {
                this.fieldName = methodName.substring(3);
                this.generator = new SimpleFieldAccessorSingleFieldBytecodeGenerator(this);
            } else if (methodName.startsWith("set")) {
                if (parameterTypes.length == 1) {
                    this.fieldName = methodName.substring(3);
                    this.generator = new SimpleFieldMutatorSingleFieldBytecodeGenerator(this);
                } else if (parameterTypes.length == 2 && parameterTypes[0].equals(ResultSet.class)
                        && parameterTypes[1].equals(ValueBeanFieldIndexTranslator.class)) {
                    this.fieldName = null;
                    this.generator = new IndexMappedResultSetMutatorBytecodeGenerator(this, classFields);
                } else if (parameterTypes.length == 2 && parameterTypes[0].equals(ResultSet.class)
                        && parameterTypes[1].equals(ValueBeanFieldNameTranslator.class)) {
                    this.fieldName = null;
                    this.generator = new NameMappedResultSetMutatorBytecodeGenerator(this, classFields);
                } else {
                    fieldName = null;
                    generator = null;
                }
            } else {
                fieldName = null;
                generator = null;
            }
        }

        public String getFieldName() {
            return fieldName;
        }

        public Class getFieldType() {
            return method.getParameterTypes()[0];
        }

        public boolean equals(Object obj) {
            if (super.equals(obj))
                return fieldName.equals(((BeanMethod) obj).fieldName);

            return false;
        }

        public SingleFieldBytecodeGenerator getGenerator() {
            return generator;
        }

        public InvokeInstruction getResultSetAccessorByColNameInvokeInstruction(
                InstructionFactory instructionFactory) {
            Class cl = getFieldType();
            if (cl == null) {
                throw new IllegalArgumentException("Class must not be null");
            }

            /* That's an amzingly easy case, because getName() returns
             * the signature. That's what we would have liked anyway.
             */
            if (cl.isArray()) {
                throw new IllegalArgumentException("Arrays not allowed yet");
            } else if (cl.isAssignableFrom(String.class)) {
                return instructionFactory.createInvoke("java.sql.ResultSet", "getString", Type.STRING,
                        new Type[] { Type.STRING }, Constants.INVOKEINTERFACE);
            } else if (cl.isAssignableFrom(java.sql.Timestamp.class)) {
                return instructionFactory.createInvoke("java.sql.ResultSet", "getTimestamp",
                        new ObjectType("java.sql.Timestamp"), new Type[] { Type.STRING },
                        Constants.INVOKEINTERFACE);
            } else if (cl.isAssignableFrom(java.sql.Date.class)) {
                return instructionFactory.createInvoke("java.sql.ResultSet", "getDate",
                        new ObjectType("java.sql.Date"), new Type[] { Type.STRING }, Constants.INVOKEINTERFACE);
            } else if (cl.isAssignableFrom(java.sql.Time.class)) {
                return instructionFactory.createInvoke("java.sql.ResultSet", "getTime",
                        new ObjectType("java.sql.Time"), new Type[] { Type.STRING }, Constants.INVOKEINTERFACE);
            } else if (cl.isAssignableFrom(java.util.Date.class)) {
                return instructionFactory.createInvoke("java.sql.ResultSet", "getDate",
                        new ObjectType("java.util.Date"), new Type[] { Type.STRING }, Constants.INVOKEINTERFACE);
            } else if (cl.isPrimitive()) {
                if (cl == Integer.TYPE) {
                    return instructionFactory.createInvoke("java.sql.ResultSet", "getInt", Type.INT,
                            new Type[] { Type.STRING }, Constants.INVOKEINTERFACE);
                } else if (cl == Double.TYPE) {
                    return instructionFactory.createInvoke("java.sql.ResultSet", "getDouble", Type.DOUBLE,
                            new Type[] { Type.STRING }, Constants.INVOKEINTERFACE);
                } else if (cl == Float.TYPE) {
                    return instructionFactory.createInvoke("java.sql.ResultSet", "getFloat", Type.FLOAT,
                            new Type[] { Type.STRING }, Constants.INVOKEINTERFACE);
                } else if (cl == Boolean.TYPE) {
                    return instructionFactory.createInvoke("java.sql.ResultSet", "getBoolean", Type.BOOLEAN,
                            new Type[] { Type.STRING }, Constants.INVOKEINTERFACE);
                } else if (cl == Byte.TYPE) {
                    return instructionFactory.createInvoke("java.sql.ResultSet", "getByte", Type.BYTE,
                            new Type[] { Type.STRING }, Constants.INVOKEINTERFACE);
                } else if (cl == Short.TYPE) {
                    return instructionFactory.createInvoke("java.sql.ResultSet", "getShort", Type.SHORT,
                            new Type[] { Type.STRING }, Constants.INVOKEINTERFACE);
                } else if (cl == Long.TYPE) {
                    return instructionFactory.createInvoke("java.sql.ResultSet", "getLong", Type.LONG,
                            new Type[] { Type.STRING }, Constants.INVOKEINTERFACE);
                } else {
                    log.error("Primitive does not have ResultSet mapping: " + cl);
                    return null;
                }
            } else {
                return instructionFactory.createInvoke("java.sql.ResultSet", "getObject",
                        new ObjectType(cl.getName()), new Type[] { Type.STRING }, Constants.INVOKEINTERFACE);
            }
        }

        public InvokeInstruction getResultSetAccessorByColIndexInvokeInstruction(
                InstructionFactory instructionFactory) {
            Class cl = getFieldType();
            if (cl == null) {
                throw new IllegalArgumentException("Class must not be null");
            }

            /* That's an amzingly easy case, because getName() returns
             * the signature. That's what we would have liked anyway.
             */
            if (cl.isArray()) {
                throw new IllegalArgumentException("Arrays not allowed yet");
            } else if (cl.isAssignableFrom(String.class)) {
                return instructionFactory.createInvoke("java.sql.ResultSet", "getString", Type.STRING,
                        new Type[] { Type.INT }, Constants.INVOKEINTERFACE);
            } else if (cl.isAssignableFrom(java.sql.Timestamp.class)) {
                return instructionFactory.createInvoke("java.sql.ResultSet", "getTimestamp",
                        new ObjectType("java.sql.Timestamp"), new Type[] { Type.INT }, Constants.INVOKEINTERFACE);
            } else if (cl.isAssignableFrom(java.sql.Date.class)) {
                return instructionFactory.createInvoke("java.sql.ResultSet", "getDate",
                        new ObjectType("java.sql.Date"), new Type[] { Type.INT }, Constants.INVOKEINTERFACE);
            } else if (cl.isAssignableFrom(java.sql.Time.class)) {
                return instructionFactory.createInvoke("java.sql.ResultSet", "getTime",
                        new ObjectType("java.sql.Time"), new Type[] { Type.INT }, Constants.INVOKEINTERFACE);
            } else if (cl.isAssignableFrom(java.util.Date.class)) {
                return instructionFactory.createInvoke("java.sql.ResultSet", "getDate",
                        new ObjectType("java.util.Date"), new Type[] { Type.INT }, Constants.INVOKEINTERFACE);
            } else if (cl.isPrimitive()) {
                if (cl == Integer.TYPE) {
                    return instructionFactory.createInvoke("java.sql.ResultSet", "getInt", Type.INT,
                            new Type[] { Type.INT }, Constants.INVOKEINTERFACE);
                } else if (cl == Double.TYPE) {
                    return instructionFactory.createInvoke("java.sql.ResultSet", "getDouble", Type.DOUBLE,
                            new Type[] { Type.INT }, Constants.INVOKEINTERFACE);
                } else if (cl == Float.TYPE) {
                    return instructionFactory.createInvoke("java.sql.ResultSet", "getFloat", Type.FLOAT,
                            new Type[] { Type.INT }, Constants.INVOKEINTERFACE);
                } else if (cl == Boolean.TYPE) {
                    return instructionFactory.createInvoke("java.sql.ResultSet", "getBoolean", Type.BOOLEAN,
                            new Type[] { Type.INT }, Constants.INVOKEINTERFACE);
                } else if (cl == Byte.TYPE) {
                    return instructionFactory.createInvoke("java.sql.ResultSet", "getByte", Type.BYTE,
                            new Type[] { Type.INT }, Constants.INVOKEINTERFACE);
                } else if (cl == Short.TYPE) {
                    return instructionFactory.createInvoke("java.sql.ResultSet", "getShort", Type.SHORT,
                            new Type[] { Type.INT }, Constants.INVOKEINTERFACE);
                } else if (cl == Long.TYPE) {
                    return instructionFactory.createInvoke("java.sql.ResultSet", "getLong", Type.LONG,
                            new Type[] { Type.INT }, Constants.INVOKEINTERFACE);
                } else {
                    log.error("Primitive does not have ResultSet mapping: " + cl);
                    return null;
                }
            } else {
                return instructionFactory.createInvoke("java.sql.ResultSet", "getObject",
                        new ObjectType(cl.getName()), new Type[] { Type.INT }, Constants.INVOKEINTERFACE);
            }
        }
    }

    protected class BeanUtil {
        private Class clazz;
        private Set classFields = new HashSet();
        private Set generators = new HashSet();

        public BeanUtil(Class clazz) {
            this.clazz = clazz;
            final Method[] methods = clazz.getMethods();
            for (int m = 0; m < methods.length; m++) {
                final Method method = methods[m];
                final BeanMethod beanMethod = new BeanMethod(method, classFields);
                if (beanMethod.generator != null) {
                    generators.add(beanMethod.generator);
                    if (beanMethod.generator instanceof SimpleFieldMutatorSingleFieldBytecodeGenerator)
                        classFields.add(beanMethod);
                }
            }
        }
    }
}