JMU
Reflection for Creating Decorators
in Java


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


Review
An Inconvenient Aspect of the Decorator Pattern
Overcoming this Inconvience
Using Reflection to Create the Source for a Decorator
javaexamples/decorator/DecoratorMaker.java
        import java.awt.*;
import java.io.*;
import java.lang.reflect.*;

/**
 * A partial implementation of an application that can be used to
 * create a decorator (in the sense of the Decorator Pattern) for
 * a given class.
 *
 * This application uses reflection to create a class that has all of
 * the methods of the given class with calls to the same method in the
 * decorated object.
 *
 * Notes:
 *
 * 1.  The current implementation does not consider exceptions.
 *
 * 2.  The current implementation does not consider parameterized types.
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 0.1
 */
public class DecoratorMaker
{
    /**
     * The entry points
     *
     * @param args  args[0] is the name of the class to decorate
     */
    public static void main(String[] args) throws Exception
    {
       createDecoratorFor(args[0]);       
    }
    
    /**
     * Create a decorator for the given class
     *
     * @param name  The name of the class
     */
    private static void createDecoratorFor(String name) throws Exception
    {
       Class         parent, returnType, toDecorate;
       Class[]       parameters;       
       int           modifiers;       
       Method[]      methods;
       PrintStream   out;       
       String        decoratorString, nameString, paramString;
       String        returnTypeString, typedParamString;
       

       toDecorate = Class.forName(name);

       // Create an abstract decorator for the parent
       parent  = toDecorate.getSuperclass();
       
       if (parent != null) createDecoratorFor(parent.getCanonicalName());

       decoratorString = "Abstract"+toDecorate.getSimpleName()+"Decorator";
       
       out = new PrintStream(new FileOutputStream(decoratorString+".java"));
       
       out.print("public abstract class " + decoratorString);       
       if (parent != null) 
          out.print(" extends Abstract"+parent.getSimpleName()+"Decorator");
       out.println();
       
                          
       out.println("{");
       
       // Attributes
       out.println("protected "+toDecorate.getCanonicalName()+
                   " decorated;");
       out.println();
       

       // Constructor
       out.println("public "+decoratorString+"("+
                   toDecorate.getCanonicalName()+" decorated)");
       out.println("{");
       if (parent != null)
          out.println("\tsuper(decorated);");       
       out.println("\tthis.decorated = decorated;");
       out.println("}");
       out.println();
       
       
       methods = toDecorate.getDeclaredMethods();
       
       for (int i=0; i<methods.length; i++)
       {
          modifiers  = methods[i].getModifiers();

          //  Don't include final, native, private, or protected methods
          if (!Modifier.isFinal(modifiers) && !Modifier.isNative(modifiers) &&
              !Modifier.isPrivate(modifiers) && !Modifier.isProtected(modifiers))
          {
             // Modifiers
             out.print(Modifier.toString(modifiers));
          
             // Return type
             returnType = methods[i].getReturnType();
             returnTypeString = returnType.getCanonicalName();          
             out.print(" " + returnTypeString);

             // Name
             nameString = methods[i].getName();
             out.print(" " + nameString);

             // Parameters
             paramString = "(";
             typedParamString = "(";
             parameters = methods[i].getParameterTypes();
             if (parameters.length > 0)
             {
                paramString      += "param0";
                typedParamString += parameters[0].getCanonicalName() + " " + 
                   "param0";
             }
          
             for (int p=1; p<parameters.length; p++)
             {
                paramString      += ", " +  "param"+p;
                typedParamString += ", " + parameters[p].getCanonicalName() +" "+
                   "param"+p;
             }
             paramString += ")";
             typedParamString += ")";
             out.print(typedParamString);

             // Body
             if (Modifier.isAbstract(modifiers))
                out.println(";");
             else
             {
                out.println();             
                out.println("{");
                if (returnTypeString.equals("void"))
                   out.println("\tdecorated."+nameString+paramString+";");
                else
                   out.println("\treturn decorated."+nameString+paramString+";");
                out.println("}");
             }
             out.println();
          }
       }
       
       // End of the class
       out.println("}");

       out.close();       
    }
    
    
}