//=====================================================================================
//
// File BCast.java v1.0
// A test of interfaces, broadcasters and listeners.
// jlouie 6.96
//
// Name this file BCast.java and compile with 'javac'/'java compiler'
// The bytecode will run as an application or applet!
// This code was tested with Sun's Mac&Win95 JDK 1.02.
// Fonts may not work properly with Netscape.
//
// An interface declares, but does not define interface instance methods. 
// We define an "implementation" as an instance of a class that implements the interface.
// A class that implements an interface must define the abstract interface instance methods.
// This can be viewed as a limited form of multiple inheritence using "mix in" classes 
// derived from base classes "with no data and only virtual functions." (see Core Java)
//
// A broadcaster calls an interface method using a reference to an implementation (listener).
// This enables the broadcaster to call an interface method without needing to 
// "know" the type of the listener or how the listener defines the interface method.
//
// ref. "Core Java" Cornell & Horstmann pp.159 Interfaces and Callbacks
// ref. "Java Report" V1.2 "How do I pass a function pointer to a Java method?"
// see: Observable Class & Observer Interface in the Java API
// see: TestBroadcast.java
//
//======================================================================================


import java.awt.*;
import java.util.*;
import java.applet.*;

public class BCast extends Applet   
{
   public static void main(String[] input)  // application entry point
   {
      BCast bc= new BCast();
      bc.init();
   }
   
   public void init()  // applet entry point
   {
      Point p= new Point(0,0);
      System.out.println("Press  Ctrl-C(Win95) or Apple-Q(Mac) to Quit the interpreter"); 
      // we create instances of classes that implement MyInterface, "implementations".
      ImplementationPlain impp= new ImplementationPlain("ListenerPlain", p);
      p.x= 20;
      ImplementationItalic impi= new ImplementationItalic("ListenerItalic", p);
      p.x= 40;
      ImplementationBold impb= new ImplementationBold("ListenerBold", p);
      p.x=100;
      // create the broadcaster class
      Broadcaster b= new Broadcaster("Broadcaster", p);
      // register each listener with the broadcaster
      b.RegisterListener(impp);
      b.RegisterListener(impi);
      b.RegisterListener(impb);
      // initialize each listener
      b.DoOK();
      // data entered into the broadcaster will be passed to the implementations
      // each implementation overrides the abstract interface instance method Display()
      // each implementation can display the data in a unique way
   }
}


// interface methods can be viewed as abstract instance method declarations that must be
// defined ("implemented") by the implementing class

interface MyInterface
{
   public  void Display(int number);   
}


// the interface _class_ method, CalcAndDisplay, defined here, 
// calls the abstract interface _instance_ method Display()
// for example, one could define a generic sort class method here that calls an 
// abstract instance method compare()

class InterfaceClassMethods   
{
   // a trivial class method as a demonstration
   // this class method does not "know" how to display the output
   // a class that calls this method must pass a reference to an implementation of MyInterface
   static void CalcAndDisplay(MyInterface mi, int number)
   {
      number= (number * number);  // in this case we square the input and pass it to Display()
      mi.Display(number);        // we call the interface method defined in mi
   }
}


// this class takes data and broadcast the data to an array of listeners
// each listener can display the data in a unique way

class Broadcaster extends MyFrame
{
   Vector v;         // store the list of listeners in a vector of objects
   Enumeration e;    // one can iterate the vector list using the Enumeration class
   
   static final int INIT_ELEMENTS= 3;
   
   Broadcaster(String title, Point p)
   {
      super(title, p);
      v= new Vector(INIT_ELEMENTS); // initialize the vector to hold three objects
   }
   
   void RegisterListener(MyInterface mi)
   {
      v.addElement(mi); // add a listener to the vector list, this list can grow
   }
   
   void DoOK()
   {
      String str= tf.getText();  
      try
      {
         number= Integer.parseInt(str);  // may throw an exception
         outLabel.setText("Input was "+Integer.toString(number));
         e= v.elements();
         while (e.hasMoreElements())
         {
            Object o= e.nextElement();
            InterfaceClassMethods.CalcAndDisplay((MyInterface)o, number);
         }
      }
      catch(Exception e)
      {
         outLabel.setText("Input Error");
      }
      tf.selectAll();      
   }  
}

// we extend MyFrame to inherit the generic window behavior
// we implement MyInterface to access the interface class method CalcAndDisplay()
// we must define Display() to enable CalcAndDisplay() to work properly

class ImplementationBold extends MyFrame implements MyInterface // Displays bold
{  
   ImplementationBold(String title, Point p)
   {
      super(title, p);
      InterfaceClassMethods.CalcAndDisplay(this, DEF_INT);
   }
   
   // define the abstract interface method here
   public void Display(int number)
   {
      Font f= outLabel.getFont();
      f= new Font(f.getName(), Font.BOLD, f.getSize());
      outLabel.setFont(f);
      outLabel.setText(Integer.toString(number));  
   }
   
   void DoOK()
   {
      String str= tf.getText();  
      try
      {
         number= Integer.parseInt(str);  // may throw an exception
         outLabel.setText("Input was "+Integer.toString(number));
         InterfaceClassMethods.CalcAndDisplay(this, number);
      }
      catch(Exception e)
      {
         outLabel.setText("Input Error");
      }
      tf.selectAll();      
   }  
}

class ImplementationItalic extends MyFrame implements MyInterface // Displays italic
{  
   ImplementationItalic(String title, Point p)
   {
      super(title, p);
      InterfaceClassMethods.CalcAndDisplay(this, DEF_INT);
   }
   
   public void Display(int number)
   {
      Font f= outLabel.getFont();
      f= new Font(f.getName(), Font.ITALIC, f.getSize());
      outLabel.setFont(f);
      outLabel.setText(Integer.toString(number));  
   }
   
   void DoOK()
   {
      String str= tf.getText();  
      try
      {
         number= Integer.parseInt(str);  // may throw an exception
         outLabel.setText("Input was "+Integer.toString(number));
         InterfaceClassMethods.CalcAndDisplay(this, number);
      }
      catch(Exception e)
      {
         outLabel.setText("Input Error");
      }
      tf.selectAll();      
   }  
}
class ImplementationPlain extends MyFrame implements MyInterface // Displays plain
{  
   ImplementationPlain(String title, Point p)
   {
      super(title, p);
      InterfaceClassMethods.CalcAndDisplay(this, DEF_INT);
   }
   
   public void Display(int number)
   {
      Font f= outLabel.getFont();
      f= new Font(f.getName(), Font.PLAIN, f.getSize());
      outLabel.setFont(f);
      outLabel.setText(Integer.toString(number));  
   }
   
   void DoOK()
   {
      String str= tf.getText();  
      try
      {
         number= Integer.parseInt(str);  // may throw an exception
         outLabel.setText("Input was "+Integer.toString(number));
         InterfaceClassMethods.CalcAndDisplay(this, number);
      }
      catch(Exception e)
      {
         outLabel.setText("Input Error");
      }
      tf.selectAll();      
   }  
}

// this class provides input/output window functionality
// DoOK() is declared abstract so MyFrame is abstract
// one cannot create an instance of MyFrame

abstract class MyFrame extends Frame
{
   Label outLabel;
   TextField tf;
   int number;
   
   static final int DEF_INT= 2;
   
   MyFrame(String title, Point p)
   {
      super(title);
      setLayout(new GridLayout(3,2,10,10));
      add(new Label("Enter Integer: ", Label.RIGHT));
      tf= new TextField(Integer.toString(DEF_INT),20);
      add(tf);
      add(new Label("Output: ", Label.RIGHT));
      outLabel= new Label("",Label.LEFT);
      add(outLabel);
      add(new Button("OK"));
      add(new Button("Cancel"));
      this.move(p.x, p.y);
      this.resize(400,400);    
      this.pack();         
      this.show();
      tf.requestFocus();
      tf.selectAll();
   }
   
   public boolean handleEvent(Event event) 
   {
      switch (event.id)
      {
         case Event.WINDOW_DESTROY:
            if (this.isShowing()) this.hide();
            this.dispose();
            return true;
      }
      return super.handleEvent(event);  // calls action
   }

   public boolean action(Event evt, Object obj)  
   {  
      char ch;
      String str= (String)obj;
      
      if (evt.target instanceof Button)
      {
         ch= str.charAt(0);
         switch (ch)
         {
            case 'O':            // OK       
               DoOK();
               return true;
            case 'C':            // Cancel
               if (this.isShowing()) this.hide();
               this.dispose();
               return true;
         }
      }
      
      if (evt.target instanceof TextField)  DoOK();      
      return super.action(evt, obj);   
   }
   
   abstract void DoOK();
}

