// Purpose.  Inheritance run amok

public class DecoratorBefore {

static class A { public void doIt() { System.out.print( 'A' ); } }

static class AwithX extends A {
   public  void doIt() { super.doIt();  doX(); }
   private void doX()  { System.out.print( 'X' ); }
}

static class AwithY extends A {
   public void doIt() { super.doIt();  doY(); }
   public void doY()  { System.out.print( 'Y' ); }
}

static class AwithZ extends A {
   public void doIt() { super.doIt();  doZ(); }
   public void doZ()  { System.out.print( 'Z' ); }
}

static class AwithXY extends AwithX {
   private AwithY obj = new AwithY();
   public void doIt() {
      super.doIt();
      obj.doY();
}  }

static class AwithXYZ extends AwithX {
   private AwithY obj1 = new AwithY();
   private AwithZ obj2 = new AwithZ();
   public void doIt() {
      super.doIt();
      obj1.doY();
      obj2.doZ();
}  }

public static void main( String[] args ) {
   A[] array = { new AwithX(), new AwithXY(), new AwithXYZ() };
   for (int i=0; i < array.length; i++) {
      array[i].doIt();
      System.out.print( "  " );
}  }  }

// AX  AXY  AXYZ



// Purpose.  Decorator design pattern

public class DecoratorAfter {

interface I { void doIt(); }

static class A implements I { public void doIt() { System.out.print( 'A' ); } }

static abstract class D implements I {
   private I core;
   public D( I inner ) { core = inner; }
   public void doIt()  { core.doIt();  }
}

static class X extends D {
   public X( I inner ) { super( inner ); }
   public void doIt()  {
      super.doIt();
      doX();
   }
   private void doX() { System.out.print( 'X' ); }
}

static class Y extends D {
   public Y( I inner ) { super( inner ); }
   public void doIt()  {
      super.doIt();
      doY();
   }
   private void doY() { System.out.print( 'Y' ); }
}

static class Z extends D {
   public Z( I inner ) { super( inner ); }
   public void doIt()  {
      super.doIt();
      doZ();
   }
   private void doZ() { System.out.print( 'Z' ); }
}

public static void main( String[] args ) {
   I[] array = { new X( new A() ), new Y( new X( new A() ) ),
                 new Z( new Y( new X( new A() ) ) ) };
   for (int i=0; i < array.length; i++) {
      array[i].doIt();
      System.out.print( "  " );
}  }  }

// AX  AXY  AXYZ



// Purpose.  Decorator design pattern

// 1. Create a "lowest common denominator" that makes classes interchangeable
// 2. Create a second level base class for optional functionality
// 3. "Core" class and "Decorator" class declare an "isa" relationship
// 4. Decorator class "hasa" instance of the "lowest common denominator"
// 5. Decorator class delegates to the "hasa" object
// 6. Create a Decorator derived class for each optional embellishment
// 7. Decorator derived classes delegate to base class AND add extra stuf
// 8. Client has the responsibility to compose desired configurations

interface Widget { void draw(); }             // 1. "lowest common denominator"

class TextField implements Widget {           // 3. "Core" class with "isa" rel
   private int width, height;
   public TextField( int w, int h ) {
      width  = w;
      height = h;
   }
   public void draw() {
      System.out.println( "TextField: " + width + ", " + height );
}  }
                                                // 2. Second level base class
abstract class Decorator implements Widget {    //    with "isa" relationship
   private Widget wid;                          // 4. "hasa" relationship
   public Decorator( Widget w ) { wid = w; }
   public void draw()           { wid.draw(); } // 5. Delegation
}

class BorderDecorator extends Decorator {          // 6. Optional embellishment
   public BorderDecorator( Widget w ) {
      super( w );
   }
   public void draw() {
      super.draw();                                // 7. Delegate to base class
      System.out.println( "   BorderDecorator" );  //    and add extra stuff
}  }

class ScrollDecorator extends Decorator {          // 6. Optional embellishment
   public ScrollDecorator( Widget w ) {
      super( w );
   }
   public void draw() {
      super.draw();                                // 7. Delegate to base class
      System.out.println( "   ScrollDecorator" );  //    and add extra stuff
}  }

public class DecoratorDemo {
   public static void main( String[] args ) {
      // 8. Client has the responsibility to compose desired configurations
      Widget aWidget = new BorderDecorator(
                          new BorderDecorator(
                             new ScrollDecorator(
                                new TextField( 80, 24 ))));
      aWidget.draw();
}  }

// TextField: 80, 24
//    ScrollDecorator
//    BorderDecorator
//    BorderDecorator



// Decorator - authenticate, input, encrypt, authenticate, decrypt, output

import java.io.*;

public class DecoratorStream {
  static BufferedReader in = new BufferedReader( new InputStreamReader( System.in ) );

  interface LCD {
    void write( String[] s );
    void read(  String[] s );
  }

  static class Core implements LCD {
    public void write( String[] s ) {
      System.out.print( "INPUT:    " );
      try {
        s[0] = in.readLine();
      } catch (IOException ex) { ex.printStackTrace(); }
    }
    public void read( String[] s ) {
      System.out.println( "Output:   " + s[0] );
  } }

  static class Decorator implements LCD {
    private LCD inner;
    public Decorator( LCD i ) { inner = i; }
    public void write( String[] s ) { inner.write( s ); }
    public void read(  String[] s ) { inner.read(  s ); }
  }

  static class Authenticate extends Decorator {
    public Authenticate( LCD inner ) { super( inner ); }
    public void write(  String[] s ) {
      System.out.print( "PASSWORD: " );
      try {
        in.readLine();
      } catch (IOException ex) { ex.printStackTrace(); }
      super.write( s );
    }
    public void read( String[] s ) {
      System.out.print( "PASSWORD: " );
      try {
        in.readLine();
      } catch (IOException ex) { ex.printStackTrace(); }
      super.read( s );
  } }

  static class Scramble extends Decorator {
    public Scramble( LCD inner ) { super( inner ); }
    public void write( String[] s ) {
      super.write( s );
      System.out.println( "encrypt:" );
      StringBuffer sb = new StringBuffer( s[0] );
      for (int i=0; i < sb.length(); i++)
        sb.setCharAt( i, (char) (sb.charAt( i ) - 5) );
      s[0] = sb.toString();
    }
    public void read( String[] s ) {
      StringBuffer sb = new StringBuffer( s[0] );
      for (int i=0; i < sb.length(); i++)
        sb.setCharAt( i, (char) (sb.charAt( i ) + 5) );
      s[0] = sb.toString();
      System.out.println( "decrypt:" );
      super.read( s );
  } }

  public static void main( String[] args ) {
    LCD stream = new Authenticate( new Scramble( new Core() ) );
    String[] str = { new String() };
    stream.write( str );
    System.out.println( "main:     " + str[0] );
    stream.read( str );
} }

// PASSWORD: secret
// INPUT:    the quick brown fox
// encrypt:
// main:     oc`lpd^f]mjriajs
// PASSWORD: secret
// decrypt:
// Output:   the quick brown fox