[
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") );
}
}