public static Class makeEventAdapterClass()

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


  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;
  }