Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[jdt-ui-dev] Enhancement: New Class Wizard supplies framework for decorator design pattern

Hi Folks,

I have an idea for an enhancement to the New Class Wizard that would relieve those using the decorator design pattern of a great deal of labor.  When creating a new class that implements one or more interfaces with generation of "Inherited abstract methods" stubs enabled, it would be great if there were additionally the option to populate those method stubs with "pass-through" calls to the internally stored decorated object.  For example, if we were creating a class MyBopper implementing the interface Bopper,

public interface Bopper {
   int bop( double intensity );
}

it would be great if Eclipse could write the following code for you

public class MyBopper {

   private Bopper source;

   public MyBopper( Bopper source ) {
      this.source = source;
   }

   public int bop( double intensity ) {
      return source(intensity);
   }

}

I've written code to generate such classes below.  Hope this interests others as well!  I'd be glad to add an enhancement ticket to Bugzilla but don't have an account.

Best Regards,
Clark

example code follows:

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;

import java.lang.reflect.*;
import java.sql.PreparedStatement;

/**
 * Code generator that creates classes for decorating the functionality of objects
 * @author Clark Williams
 *
 * Note that it is easy to make an adapter for a class (as opposed to an
 * interface): just create a subclass with matching constructors.  This is
 * already done by the Eclipse New Class Wizard, so there is no reason to
 * use the DecoratorWriter to decorate types other than interfaces.   
 */
public class DecoratorWriter {

    /**
     * Write a template code for a decorator class for objects of a specified interface
     * @param interfaceType the Class object representing the interface to be adapted by the generated class
     * @param packageName package of the new class
     * @param decoratorName name of the new class
     * @param outstr stream to write the class template to
     */
    static public void write( Class<?> interfaceType, String packageName, String decoratorName, OutputStream outstr ) {
        if( !interfaceType.isInterface() )
        {
            throw new IllegalArgumentException("Parameter interfaceType must represent an interface");
        }
       
        // Prepare name of base interface with any generic type arguments
        String typeargs = "";
        if( interfaceType.getTypeParameters().length > 0 )
        {
            typeargs = "<";
            boolean first = true;
            for( TypeVariable<?> tp : interfaceType.getTypeParameters() )
            {
                if( !first )
                    typeargs += ",";
                typeargs = typeargs + tp.toString();
                first = false;
            }
            typeargs += ">";
        }
        String interfaceName = interfaceType.getCanonicalName()+typeargs;
       
        PrintStream out = new PrintStream(outstr);
       
        out.println( "package " + packageName + ";" );
        out.println();
        // import would produce warning, since we always refer to interfaceType with full interface name
        //out.println( "import " + interfaceType.getCanonicalName() + ";" );
        //out.println();

        out.println( "/**" );
        out.println( " * @author " + System.getenv("user") );
        out.println( " * " );
        out.println( " */" );
        out.println( "public class " + decoratorName + typeargs + " implements " + interfaceName + " { " );
        out.println( );
        out.println( "\tprivate " + interfaceName + " source;" );
        out.println( );
        out.println( "\t/**" );
        out.println( "\t * Apply a " + decoratorName + " adapter to the supplied " + interfaceType.getName() + " object" );
        out.println( "\t * @param source The object providing the base functionality to be adapted" );
        out.println( "\t */" );           
        out.println( "\tpublic " + decoratorName + "( " + interfaceName + " source ) {" );
        out.println( "\t\tthis.source = source;");
        out.println( "\t}");
        for( Method m : interfaceType.getMethods() )               
        {
            if( Modifier.isPublic(m.getModifiers()) )
            {
                out.println( );
                out.println( "\t/* (non-Javadoc)" );   
                out.print( "\t * @see " + m.getDeclaringClass().getCanonicalName() + "#" + m.getName() + "(" );
                {
                boolean first = true;
                for( Class<?> param : m.getParameterTypes() )
                {
                    if( !first )
                        out.print(",");
                    first = false;
                    out.print( param.getCanonicalName() );
                }
                }
                out.println( ")" );                   
                out.println( "\t */" );
                out.println( "\t@Override ");                   
                if( m.isAnnotationPresent(Deprecated.class) )
                {
                    // avoid warning
                    // Note: only works if RetentionPolicy is such that
                    //    annotations are preserved at runtime
                    out.println( "\t@Deprecated" );
                }
                int methModifiers = m.getModifiers();
                // Remove "abstract"
                methModifiers = methModifiers & (~Modifier.ABSTRACT);
                out.print( "\t" + Modifier.toString(methModifiers) + " " );
                // method type arguments, if any
                if( m.getTypeParameters().length > 0 )
                {
                    out.print( "<" );
                    boolean first = true;
                    for( TypeVariable<?> tp : m.getTypeParameters() )
                    {
                        if( !first )
                            out.print(",");
                        out.print(tp.toString());
                        first = false;
                    }
                    out.print("> ");
                }
                // return type
                if( m.getGenericReturnType() ==  m.getReturnType() )
                    out.print( m.getReturnType().getCanonicalName() );
                else
                    out.print( m.getGenericReturnType().toString() );
                out.print( " " + m.getName() );
                out.print( "(" );
                {
                    Type[] genparams = m.getGenericParameterTypes();
                    Class<?>[] params = m.getParameterTypes();
                    for( int argnum = 0; argnum < params.length; ++argnum )
                    {
                        if( argnum > 0 )
                            out.print(",");           
                        if( genparams[argnum] == params[argnum ])
                            out.print( " " + params[argnum].getCanonicalName() + " arg" + Integer.toString(argnum) );
                        else
                            out.print( " " + genparams[argnum].toString() + " arg" + Integer.toString(argnum) );
                    }               
                }
                out.print( " )" );
                if( m.getExceptionTypes().length > 0 )
                {
                    out.print( " throws");
                    boolean first = true;
                    for( Class<?> except : m.getExceptionTypes() )
                    {
                        if( !first )
                            out.print( "," );
                        first = false;                           
                        out.print( " " + except.getCanonicalName() );
                    }
                }
                out.println(" {");
                // Method Body
                out.print( "\t\t" );
                if( m.getReturnType() != void.class )
                {
                    out.print( "return " );
                }
                out.print( "source." + m.getName() + "(" );
                for( int argnum = 0; argnum < m.getParameterTypes().length; ++argnum )
                {
                    if( argnum > 0 )
                        out.print(",");
                    out.print( " arg" + Integer.toString(argnum) );
                }
                out.println(  " );" );
                   
                out.println( "\t}" );
            }
        }           
        out.println( );
        out.println( "}" );
    }
   
    // test
    public static void main( String[] args ) throws IOException
    {
        File outputdir = new File("src/test");
        if( !outputdir.exists() )
            outputdir.mkdirs();
        write( PreparedStatement.class, "test", "PrepStmtAdapter", new FileOutputStream("src/test/PrepStmtAdapter.java") );
        write( Comparable.class, "test", "UberComparable", new FileOutputStream("src/test/UberComparable.java") );
    }
}



Back to the top