public static Class makeEventAdapterClass()

in src/main/java/org/apache/bsf/util/event/generator/EventAdapterGenerator.java [202:554]


    public static Class makeEventAdapterClass(final Class listenerType, final boolean writeClassFile) {
        logger.debug("EventAdapterGenerator");

        if (EVENTLISTENER.isAssignableFrom(listenerType)) {
            boolean exceptionable = false;
            boolean nonExceptionable = false;
            byte[] constantPool = null;
            short cpBaseIndex;
            short cpCount = 0;
            short cpExceptionBaseIndex;
            short exceptionableCount;
            short nonExceptionableCount;

            /* Derive Names */
            final String listenerTypeName = listenerType.getName();
            logger.debug("ListenerTypeName: " + listenerTypeName);
            final String adapterClassName = CLASSPACKAGE
                    + (listenerTypeName.endsWith("Listener") ? listenerTypeName.substring(0, listenerTypeName.length() - 8) : listenerTypeName).replace('.',
                            '_')
                    + "Adapter";
            String finalAdapterClassName = adapterClassName;
            Class cached = null;
            int suffixIndex = 0;

            do {
                if (null != (cached = ldr.getLoadedClass(finalAdapterClassName))) {
                    logger.debug("cached:  " + cached);
                    try {
                        if (!listenerType.isAssignableFrom(cached)) {
                            finalAdapterClassName = adapterClassName + "_" + suffixIndex++;
                        } else {
                            return cached;
                        }
                    } catch (final VerifyError ex) {
                        System.err.println(ex.getMessage());
                        ex.printStackTrace();
                        return cached;
                    }
                }
            } while (cached != null);

            final String eventListenerName = listenerTypeName.replace('.', '/');

            /* method stuff */
            final java.lang.reflect.Method[] lms = listenerType.getMethods();

            /* ****************************************************************************************** */
            // Listener interface
            // Class name
            cpCount += 4;

            // cp item 17
            constantPool = Bytecode.addUtf8(constantPool, eventListenerName);

            // cp item 18
            constantPool = Bytecode.addUtf8(constantPool, finalAdapterClassName);

            // cp item 19
            constantPool = Bytecode.addClass(constantPool, (short) 17);

            // cp item 20
            constantPool = Bytecode.addClass(constantPool, (short) 18);

            // do we have nonExceptionalble event, exceptionable or both
            for (int i = 0; i < lms.length; ++i) {
                final Class[] exceptionTypes = lms[i].getExceptionTypes();
                if (0 < exceptionTypes.length) {
                    exceptionable = true;
                } else {
                    nonExceptionable = true;
                }
            } /* End for */

            /* ****************************************************************************************** */
            // optional inclusion of nonexceptional events affects exceptional events indices

            nonExceptionableCount = 0;
            if (nonExceptionable) {
                nonExceptionableCount = 3;
                cpCount += nonExceptionableCount;

                // cp item 21
                constantPool = Bytecode.addUtf8(constantPool, "processEvent");

                // cp item 22
                constantPool = Bytecode.addNameAndType(constantPool, (short) 21, (short) 8);

                // cp item 23
                constantPool = Bytecode.addInterfaceMethodRef(constantPool, (short) 12, (short) 22);
            }

            /* ****************************************************************************************** */
            // optional inclusion of exceptional events affects CP Items which follow for specific methods

            exceptionableCount = 0;
            if (exceptionable) {
                final int classIndex = BASECPCOUNT + cpCount + 1;
                final int nameIndex = BASECPCOUNT + cpCount + 0;
                final int natIndex = BASECPCOUNT + cpCount + 3;

                exceptionableCount = 5;
                cpCount += exceptionableCount;

                // cp item 24 or 21
                constantPool = Bytecode.addUtf8(constantPool, "processExceptionableEvent");

                // cp item 25 or 22
                constantPool = Bytecode.addUtf8(constantPool, "java/lang/Exception");

                // cp item 26 or 23
                constantPool = Bytecode.addClass(constantPool, (short) classIndex);

                // cp item 27 or 24
                constantPool = Bytecode.addNameAndType(constantPool, (short) nameIndex, (short) 8);

                // cp item 28 or 25
                constantPool = Bytecode.addInterfaceMethodRef(constantPool, (short) 12, (short) natIndex);

            }

            // base index for method cp references
            cpBaseIndex = (short) (BASECPCOUNT + cpCount);
            logger.debug("cpBaseIndex: " + cpBaseIndex);

            for (int i = 0; i < lms.length; ++i) {
                final String eventMethodName = lms[i].getName();
                final String eventName = lms[i].getParameterTypes()[0].getName().replace('.', '/');
                cpCount += 3;
                // cp items for event methods
                constantPool = Bytecode.addUtf8(constantPool, eventMethodName);
                constantPool = Bytecode.addUtf8(constantPool, ("(L" + eventName + ";)V"));
                constantPool = Bytecode.addString(constantPool, (short) (BASECPCOUNT + cpCount - 3));
            } /* End for */

            final boolean[] propertyChangeFlag = new boolean[lms.length];
            int cpIndexPCE = 0;
            for (int i = 0; i < lms.length; ++i) {
                final String eventName = lms[i].getParameterTypes()[0].getName().replace('.', '/');
                // cp items for PropertyChangeEvent special handling
                if (eventName.equalsIgnoreCase("java/beans/PropertyChangeEvent")) {
                    propertyChangeFlag[i] = true;
                    if (0 == cpIndexPCE) {
                        constantPool = Bytecode.addUtf8(constantPool, eventName);
                        constantPool = Bytecode.addUtf8(constantPool, "getPropertyName");
                        constantPool = Bytecode.addUtf8(constantPool, "()Ljava/lang/String;");
                        constantPool = Bytecode.addClass(constantPool, (short) (BASECPCOUNT + cpCount));
                        constantPool = Bytecode.addNameAndType(constantPool, (short) (BASECPCOUNT + cpCount + 1), (short) (BASECPCOUNT + cpCount + 2));
                        constantPool = Bytecode.addMethodRef(constantPool, (short) (BASECPCOUNT + cpCount + 3), (short) (BASECPCOUNT + cpCount + 4));
                        cpCount += 6;
                        cpIndexPCE = BASECPCOUNT + cpCount - 1;

                    }
                } else {
                    propertyChangeFlag[i] = false;
                }
            } /* End for */

            cpExceptionBaseIndex = (short) (BASECPCOUNT + cpCount);
            logger.debug("cpExceptionBaseIndex: " + cpExceptionBaseIndex);

            final int excpIndex[][] = new int[lms.length][];
            for (int i = 0; i < lms.length; ++i) {
                final Class[] exceptionTypes = lms[i].getExceptionTypes();
                excpIndex[i] = new int[exceptionTypes.length];
                for (int j = 0; j < exceptionTypes.length; j++) {
                    constantPool = Bytecode.addUtf8(constantPool, exceptionTypes[j].getName().replace('.', '/'));
                    constantPool = Bytecode.addClass(constantPool, (short) (BASECPCOUNT + cpCount));
                    excpIndex[i][j] = BASECPCOUNT + cpCount + 1;
                    cpCount += 2;
                }
            } /* End for */
            /* end constant pool */

            /* ************************************************************************************************ */
            // put the Class byte array together

            /* start */
            byte[] newClass = CLASSHEADER; // magic, version (fixed)
            final short count = (short) (BASECPCOUNT + cpCount);
            newClass = ByteUtility.addBytes(newClass, count); // constant_pool_count (variable)
            newClass = ByteUtility.addBytes(newClass, BASECP); // constant_pool (fixed)
            newClass = ByteUtility.addBytes(newClass, constantPool); // constant_pool (variable)
            newClass = ByteUtility.addBytes(newClass, FIXEDCLASSBYTES); // see FIXEDCLASSBYTES (fixed)
            newClass = ByteUtility.addBytes(newClass, (short) (lms.length + 1)); // method_count (variable)
            newClass = ByteUtility.addBytes(newClass, INITMETHOD); // constructor <init> (fixed)
            // methods

            /* ****************************************************************************************** */
            /* loop over listener methods from listenerType */
            for (int i = 0; i < lms.length; ++i) {
                newClass = ByteUtility.addBytes(newClass, (short) 1); // access_flags (fixed)
                newClass = ByteUtility.addBytes(newClass, (short) (cpBaseIndex + 3 * i + 0)); // name_index (variable)
                newClass = ByteUtility.addBytes(newClass, (short) (cpBaseIndex + 3 * i + 1)); // descriptor_index (variable)
                newClass = ByteUtility.addBytes(newClass, (short) 1); // attribute_count (fixed)
                newClass = ByteUtility.addBytes(newClass, (short) 3); // attribute_name_index code(fixed)

                // Code Attribute Length
                int length = 32;
                if (0 < excpIndex[i].length) {
                    length += 5 + 8 * (1 + excpIndex[i].length);
                }
                if (propertyChangeFlag[i]) {
                    length += 2;
                }
                newClass = ByteUtility.addBytes(newClass, (long) length); // attribute_length (variable)

                // start code attribute
                newClass = ByteUtility.addBytes(newClass, (short) 6); // max_stack (fixed)
                newClass = ByteUtility.addBytes(newClass, (short) 3); // max_locals (fixed)

                // Code Length
                length = 20;
                if (exceptionable && 0 < excpIndex[i].length) {
                    length += 5;
                }
                if (propertyChangeFlag[i]) {
                    length += 2;
                }
                newClass = ByteUtility.addBytes(newClass, (long) length); // code_length (variable)

                // start code
                newClass = ByteUtility.addBytes(newClass, (byte) 0x2A); // aload_0 (fixed)
                newClass = ByteUtility.addBytes(newClass, (byte) 0xB4); // getfield (fixed)
                newClass = ByteUtility.addBytes(newClass, (short) 15); // index (fixed)

                if (propertyChangeFlag[i]) { // the propertyName is passed as the first parameter
                    newClass = ByteUtility.addBytes(newClass, (byte) 0x2B); // aload_1 (fixed)
                    newClass = ByteUtility.addBytes(newClass, (byte) 0xB6); // invokevirtual (fixed)
                    newClass = ByteUtility.addBytes(newClass, (short) cpIndexPCE); // methodref (variable)
                } else { // the eventMethodName is passed as the first parameter
                         // Target for method invocation.
                    newClass = ByteUtility.addBytes(newClass, (byte) 0x12); // ldc (fixed)
                    newClass = ByteUtility.addBytes(newClass, (byte) (cpBaseIndex + 3 * i + 2)); // index (byte) (variable)
                }

                newClass = ByteUtility.addBytes(newClass, (byte) 0x04); // iconst_1 (fixed)
                newClass = ByteUtility.addBytes(newClass, (byte) 0xBD); // anewarray (fixed)
                newClass = ByteUtility.addBytes(newClass, (short) 10); // Class java/lang/Object (fixed)
                newClass = ByteUtility.addBytes(newClass, (byte) 0x59); // dup (fixed)
                newClass = ByteUtility.addBytes(newClass, (byte) 0x03); // iconst_0 (fixed)
                newClass = ByteUtility.addBytes(newClass, (byte) 0x2B); // aload_1 (fixed)
                newClass = ByteUtility.addBytes(newClass, (byte) 0x53); // aastore (fixed)
                newClass = ByteUtility.addBytes(newClass, (byte) 0xB9); // invokeinterface (fixed)

                // index to processEvent or processExceptionableEvent method
                length = 23; // actually an index into cp
                if (exceptionable && nonExceptionable) { // interface method index
                    if (0 < lms[i].getExceptionTypes().length) {
                        length += 5;
                    }
                } else if (exceptionable) {
                    length += 2;
                }
                newClass = ByteUtility.addBytes(newClass, (short) length); // index (process??????...) (variable)

                newClass = ByteUtility.addBytes(newClass, (byte) 0x03); // iconst_0 (fixed)
                newClass = ByteUtility.addBytes(newClass, (byte) 0x00); // noop (fixed)
                newClass = ByteUtility.addBytes(newClass, (byte) 0xB1); // return (fixed)

                if (exceptionable && 0 < excpIndex[i].length) { // exception code
                    newClass = ByteUtility.addBytes(newClass, (byte) 0x4D); // astore_2 (fixed)
                    newClass = ByteUtility.addBytes(newClass, (byte) 0x2C); // aload_2 (fixed)
                    newClass = ByteUtility.addBytes(newClass, (byte) 0xBF); // athrow (fixed)
                    newClass = ByteUtility.addBytes(newClass, (byte) 0x57); // pop (fixed)
                    newClass = ByteUtility.addBytes(newClass, (byte) 0xB1); // return (fixed)
                    // end code

                    // exception table
                    length = excpIndex[i].length;
                    newClass = ByteUtility.addBytes(newClass, (short) (1 + length)); // exception_table_length (variable)
                    for (int j = 0; j < length; j++) { // catch exception types and rethrow
                        newClass = ByteUtility.addBytes(newClass, (short) 0); // start_pc (fixed)
                        if (propertyChangeFlag[i]) {
                            newClass = ByteUtility.addBytes(newClass, (short) 21); // end_pc (fixed)
                            newClass = ByteUtility.addBytes(newClass, (short) 22); // handler_pc (fixed)
                        } else {
                            newClass = ByteUtility.addBytes(newClass, (short) 19); // end_pc (fixed)
                            newClass = ByteUtility.addBytes(newClass, (short) 20); // handler_pc (fixed)
                        }
                        newClass = ByteUtility.addBytes(newClass, (short) excpIndex[i][j]); // catch_type (variable)
                    }
                    // catch "exception" and trap it
                    newClass = ByteUtility.addBytes(newClass, (short) 0); // start_pc (fixed)
                    if (propertyChangeFlag[i]) {
                        newClass = ByteUtility.addBytes(newClass, (short) 21); // end_pc (fixed)
                        newClass = ByteUtility.addBytes(newClass, (short) 25); // handler_pc (fixed)
                    } else {
                        newClass = ByteUtility.addBytes(newClass, (short) 19); // end_pc (fixed)
                        newClass = ByteUtility.addBytes(newClass, (short) 23); // handler_pc (fixed)
                    }
                    if (nonExceptionable) {
                        newClass = ByteUtility.addBytes(newClass, (short) 26);
                    } // catch_type (fixed)
                    else // or
                    {
                        newClass = ByteUtility.addBytes(newClass, (short) 23);
                    } // catch_type (fixed)
                } else {
                    newClass = ByteUtility.addBytes(newClass, (short) 0);
                } // exception_table_length (fixed)
                // attributes on the code attribute (none)
                newClass = ByteUtility.addBytes(newClass, (short) 0); // attribute_count (fixed)
                // end code attribute

            } /* End for */
            // Class Attributes (none for this)
            newClass = ByteUtility.addBytes(newClass, (short) 0); // attribute_count (fixed)
            /* done */

            logger.debug("adapterName: " + finalAdapterClassName);
            logger.debug("cpCount: " + count + " = " + BASECPCOUNT + " + " + cpCount);
            logger.debug("methodCount: " + (lms.length + 1));
            // output to disk class file
            /* ****************************************************************************************** */

            // now create the class and load it
            // return the Class.

            if (writeClassFile) {
                try {
                    // removed "WRITEDIRECTORY+", as this path is already part of 'finalAdapterClassName'
                    final FileOutputStream fos = new FileOutputStream(finalAdapterClassName + ".class");
                    fos.write(newClass);
                    fos.close();
                } catch (final IOException ex) {
                    System.err.println(ex.getMessage());
                    ex.printStackTrace();
                }

                try {
                    final Class ret = ldr.loadClass(finalAdapterClassName);
                    logger.debug("EventAdapterGenerator: " + ret.getName() + " dynamically generated");
                    return ret;
                } catch (final ClassNotFoundException ex) {
                    System.err.println(ex.getMessage());
                    ex.printStackTrace();
                }
            }

            try {
                final Class ret = ldr.defineClass(finalAdapterClassName, newClass);
                logger.debug("EventAdapterGenerator: " + ret.getName() + " dynamically generated");
                return ret;
            }

            catch (final Throwable ex) // rgf, 2012-01-15
            {
                System.err.println(ex.getMessage());
                ex.printStackTrace();
            }
        }
        return null;
    }