// Purpose.  Visitor design pattern

// 1. Add an accept(Visitor) method to the "element" hierarchy
// 2. Create a "visitor" base class w/ a visit() method for every "element" type
// 3. Create a "visitor" derived class for each "operation" to do on "elements"
// 4. Client creates "visitor" objects and passes each to accept() calls

interface Element {
   public void accept( Visitor v ); // first dispatch     // 1. accept(Visitor)
}                                                         //    interface

class This implements Element {
   public void   accept( Visitor v ) { v.visit( this ); } // 1. accept(Visitor)
   public String thiss()             { return "This"; }   //    implementation
}

class That implements Element {
   public void   accept( Visitor v ) { v.visit( this ); }
   public String that()              { return "That"; }
}

class TheOther implements Element {
   public void   accept( Visitor v ) { v.visit( this ); }
   public String theOther()          { return "TheOther"; }
}

interface Visitor {                                    // 2. Create a "visitor"
   public void visit( This e ); ////// second dispatch //    base class with a
   public void visit( That e );                        //    visit() method for
   public void visit( TheOther e );                    //    every "element"
}                                                      //    type

class UpVisitor implements Visitor {                   // 3. Create a "visitor"
   public void visit( This e ) {                       //    derived class for
      System.out.println( "do Up on " + e.thiss() ); } //    each "operation"
   public void visit( That e ) {                       //    to perform on
      System.out.println( "do Up on " + e.that() ); }  //    "elements"
   public void visit( TheOther e ) {
      System.out.println( "do Up on " + e.theOther() ); }
}

class DownVisitor implements Visitor {
   public void visit( This e ) {
      System.out.println( "do Down on " + e.thiss() ); }
   public void visit( That e ) {
      System.out.println( "do Down on " + e.that() ); }
   public void visit( TheOther e ) {
      System.out.println( "do Down on " + e.theOther() ); }
}

class VisitorDemo {
   public static Element[] list = { new This(), new That(), new TheOther() };
   public static void main( String[] args ) {
      UpVisitor    up   = new UpVisitor();             // 4. Client creates
      DownVisitor  down = new DownVisitor();           //    "visitor" objects
      for (int i=0; i < list.length; i++)              //    and passes each
         list[i].accept( up );                         //    to accept() calls
      for (int i=0; i < list.length; i++)
         list[i].accept( down );
}  }

// do Up on This                // do Down on This
// do Up on That                // do Down on That
// do Up on TheOther            // do Down on TheOther


//===============================================================================

// Purpose.  Double dispatch (within a single hierarchy)
//
// Discussion.  We would like to declare a function like:
//    void process( virtual Base object1, virtual Base object2 )
// that does the right thing based on the type of 2 objects that come from
// a single inheritance hierarchy.  The only problem is that the keyword
// "virtual" may not be used to request dynamic binding for an object being
// passed as an argument.  Java will only "discriminate" the type of an object
// being messaged, not the type of an object being passed.  So in order for
// the type of 2 objects to be discriminated, each object must be the
// receiver of a virtual function call.  Here, when process1() is called on
// the first object, its type becomes "known" at runtime, but the type of
// the second is still UNknown.  process2() is then called on the second
// object, and the identity (and type) of the first object is passed as an
// argument.  Flow of control has now been vectored to the spot where the
// type (and identity) of both objects are known.

public class VisitorSingle {

interface Base {
   void process1( Base secondObject );
   void process2(  A   firstObject  );
   void process2(  B   firstObject  );
   void process2(  C   firstObject  );
}

static class A implements Base {
   public void process1( Base second ) { second.process2( this ); }
   public void process2( A first ) {
      System.out.println( "first is A, second is A" ); }
   public void process2( B first ) {
      System.out.println( "first is B, second is A" ); }
   public void process2( C first ) {
      System.out.println( "first is C, second is A" ); }
}

static class B implements Base {
   public void process1( Base second ) { second.process2( this ); }
   public void process2( A first ) {
      System.out.println( "first is A, second is B" ); }
   public void process2( B first ) {
      System.out.println( "first is B, second is B" ); }
   public void process2( C first ) {
      System.out.println( "first is C, second is B" ); }
}

static class C implements Base {
   public void process1( Base second ) { second.process2( this ); }
   public void process2( A first ) {
      System.out.println( "first is A, second is C" ); }
   public void process2( B first ) {
      System.out.println( "first is B, second is C" ); }
   public void process2( C first ) {
      System.out.println( "first is C, second is C" ); }
}

public static void main( String[] args ) {
   Base array[] = { new A(), new B(), new C() };
   for (int i=0; i < array.length; i++)
      for (int j=0; j < 3; j++)
         array[i].process1( array[j] );
}}

// first is A, second is A
// first is A, second is B
// first is A, second is C
// first is B, second is A
// first is B, second is B
// first is B, second is C
// first is C, second is A
// first is C, second is B
// first is C, second is C


//===============================================================================

// Purpose.  VisitorComposite1 is a basic Composite implementation with one
// recursive traversal method.  VisitorComposite2 is a non-Visitor implementation
// that models "parsing" the hierarchical Composite with the collect() recursive
// traversal method.  VisitorComposite3 is a Visitor implementation.
//
// Highlights.  VisitorComposite2 changes interface Component into an abstract
// class.  It requires protected static members.  VisitorComposite3 is "open for
// extension, but closed for modification".  The interface Component remains an
// interface.  Now that "collect" is an object, many of them can be created and
// can operate simultaneously (the previous static attributes would have required
// significant extra effort to provide this functionality).  Drawback: the public
// interface of Leaf and Composite had to be extended.

import java.util.*;

interface Component { void traverse(); }

class Leaf implements Component {
  private int number;
  public Leaf( int num ) { number = num; }
  public void traverse() { System.out.print( number + " " ); }
}

class Composite implements Component {
  private static char next = 'a';
  private List children = new ArrayList();
  private char letter = next++;

  public void add( Component c ) { children.add( c ); }
  public void traverse() {
    System.out.print( letter + " " );
    for (int i=0; i < children.size(); i++)
      ((Component)children.get(i)).traverse();
} }

public class VisitorComposite1 {
  public static void main( String[] args ) {
    Composite[] containers = new Composite[3];

    for (int i=0; i < containers.length; i++) {
      containers[i] = new Composite();
      for (int j=1; j < 4; j++)
        containers[i].add( new Leaf( i * containers.length + j ) );
    }

    for (int i=1; i < containers.length; i++)
      containers[0].add( containers[i] );

    containers[0].traverse();
    System.out.println();
} }

// a 1 2 3 b 4 5 6 c 7 8 9

//------------------------------ VisitorComposite2 ------------------------------

import java.util.*;

abstract class Component {
  protected static StringBuffer letters = new StringBuffer();
  protected static StringBuffer numbers = new StringBuffer();

  public abstract void traverse();
  public abstract void collect();
  public static String getLetters() { return letters.toString(); }
  public static String getNumbers() { return numbers.toString(); }
}

class Leaf extends Component {
  private int number;
  public Leaf( int num ) { number = num; }
  public void traverse() { System.out.print( number + " " ); }
  public void collect()  { numbers.append( number ); }
}

class Composite extends Component {
  private static char next = 'a';
  private List children = new ArrayList();
  private char letter = next++;

  public void add( Component c ) { children.add( c ); }
  public void traverse() {
    System.out.print( letter + " " );
    for (int i=0; i < children.size(); i++)
      ((Component)children.get(i)).traverse();
  }
  public void collect() {
    letters.append( letter );
    for (int i=0; i < children.size(); i++)
      ((Component)children.get(i)).collect();
} }

public class VisitorComposite2 {
  public static void main( String[] args ) {
    Composite[] containers = new Composite[3];

    for (int i=0; i < containers.length; i++) {
      containers[i] = new Composite();
      for (int j=1; j < 4; j++)
        containers[i].add( new Leaf( i * containers.length + j ) );
    }

    for (int i=1; i < containers.length; i++)
      containers[0].add( containers[i] );

    containers[0].traverse();
    System.out.println();

    containers[0].collect();
    System.out.print( "letters are - " + Component.getLetters() );
    System.out.println( ", numbers are - " + Component.getNumbers() );
} }

// a 1 2 3 b 4 5 6 c 7 8 9
// letters are - abc, numbers are - 123456789

//------------------------------ VisitorComposite3 ------------------------------

import java.util.*;

interface Component {
  void traverse();
  void accept( Visitor v );
}

class Leaf implements Component {
  private int number;
  public Leaf( int num )          { number = num; }
  public void traverse()          { System.out.print( number + " " ); }
  public void accept( Visitor v ) { v.visit( this ); }
  public int  getNumber()         { return number; }
}

class Composite implements Component {
  private static char next = 'a';
  private List children = new ArrayList();
  private char letter = next++;

  public void add( Component c ) { children.add( c ); }
  public void traverse() {
    System.out.print( letter + " " );
    for (int i=0; i < children.size(); i++)
      ((Component)children.get(i)).traverse();
  }
  public void accept( Visitor v ) {
    v.visit( this );
    for (int i=0; i < children.size(); i++)
      ((Component)children.get(i)).accept( v );
  }
  public char getLetter() { return letter; }
}

interface Visitor {
  void visit( Leaf l );
  void visit( Composite c );
}

class CollectVisitor implements Visitor {
  private StringBuffer letters = new StringBuffer();
  private StringBuffer numbers = new StringBuffer();

  public void   visit( Composite c ) { letters.append( c.getLetter() ); }
  public void   visit( Leaf l )      { numbers.append( l.getNumber() ); }
  public String getLetters()         { return letters.toString(); }
  public String getNumbers()         { return numbers.toString(); }
}

public class VisitorComposite3 {
  public static void main( String[] args ) {
    Composite[] containers = new Composite[3];

    for (int i=0; i < containers.length; i++) {
      containers[i] = new Composite();
      for (int j=1; j < 4; j++)
        containers[i].add( new Leaf( i * containers.length + j ) );
    }

    for (int i=1; i < containers.length; i++)
      containers[0].add( containers[i] );

    containers[0].traverse();
    System.out.println();

    CollectVisitor anOperation = new CollectVisitor();
    containers[0].accept( anOperation );
    System.out.print( "letters are - " + anOperation.getLetters() );
    System.out.println( ", numbers are - " + anOperation.getNumbers() );
} }

// a 1 2 3 b 4 5 6 c 7 8 9
// letters are - abc, numbers are - 123456789


//===============================================================================

// Problem.  "If you want to add a new Visitable object, you have to change
// the Visitor interface, and then implement that method in each of your
// Visitors."
//
// Solution.  With the ReflectiveVisitor, you only need one method in the
// Visitor interface - visit(Object).  All other visit() methods can be
// added later as point-to-point coupling is required.

import java.lang.reflect.Method;


////////// The "element" hierarchy //////////

interface Element {
   public void accept( ReflectiveVisitor v );
}
class This implements Element {
   public void   accept( ReflectiveVisitor v ) { v.visit( this ); }
   public String thiss()                       { return "This"; }
}
class That implements Element {
   public void   accept( ReflectiveVisitor v ) { v.visit( this ); }
   public String that()                        { return "That"; }
}
class TheOther implements Element {
   public void   accept( ReflectiveVisitor v ) { v.visit( this ); }
   public String theOther()                    { return "TheOther"; }
}


////////// The "operation" hierarchy //////////

abstract class ReflectiveVisitor {
  abstract public void visit( Object o );

  public void visitTheOther( TheOther e ) {
    System.out.println( "ReflectiveVisitor: do Base on " + e.theOther() );
  }

  // 1. Look for visitElementClassName() in the current class
  // 2. Look for visitElementClassName() in superclasses
  // 3. Look for visitElementClassName() in interfaces
  // 4. Look for visitObject() in current class
  protected Method getMethod( Class c ) {
    Class  newc = c;
    Method m    = null;
    while (m == null  &&  newc != Object.class) {
      String method = newc.getName();
      method = "visit" + method.substring( method.lastIndexOf('.') + 1 );
      try {
        m = getClass().getMethod( method, new Class[] { newc } );
      } catch (NoSuchMethodException ex) {
        newc = newc.getSuperclass();
    } }
    if (newc == Object.class) {
      // System.out.println( "Searching for interfaces" );
      Class[] interfaces = c.getInterfaces();
      for (int i=0; i < interfaces.length; i++) {
        String method = interfaces[i].getName();
        method = "visit" + method.substring( method.lastIndexOf('.') + 1 );
        try {
          m = getClass().getMethod( method, new Class[] { interfaces[i] } );
        } catch (NoSuchMethodException ex) { }
    } }
    if (m == null)
      try {
        m = getClass().getMethod( "visitObject", new Class[] { Object.class } );
      } catch (Exception ex) { }
    return m;
} }

class UpVisitor extends ReflectiveVisitor {
  public void visit( Object o ) {
    try {
      getMethod( o.getClass() ).invoke( this, new Object[] { o } );
    } catch (Exception ex) {
      System.out.println( "UpVisitor - no appropriate visit() method" );
  } }
  public void visitThis( This e ) {
    System.out.println( "UpVisitor: do Up on " + e.thiss() );
  }
  public void visitObject( Object e ) {
    System.out.println( "UpVisitor: generic visitObject() method" );
} }

class DownVisitor extends ReflectiveVisitor {
  public void visit( Object o ) {
    try {
      getMethod( o.getClass() ).invoke( this, new Object[] { o } );
    } catch (Exception ex) {
      System.out.println( "DownVisitor - no appropriate visit() method" );
  } }
  public void visitThat( That e ) {
    System.out.println( "DownVisitor: do Down on " + e.that() );
} }


class VisitorDemo {
  public static void main( String[] args ) {
    Element[]    list = { new This(), new That(), new TheOther() };
    UpVisitor    up   = new UpVisitor();
    DownVisitor  down = new DownVisitor();
    for (int i=0; i < list.length; i++)
      list[i].accept( up );
    for (int i=0; i < list.length; i++)
      list[i].accept( down );
} }

// UpVisitor: do Up on This
// UpVisitor: generic visitObject() method
// ReflectiveVisitor: do Base on TheOther
// DownVisitor - no appropriate visit() method
// DownVisitor: do Down on That
// ReflectiveVisitor: do Base on TheOther