All Together

// Code displayed in this color is for tracing only

import java.awt.*;

abstract class Stream implements Runnable {
   DisplayIt trace;        // only for tracing - not normally used
   Object value;
   Thread runner = null, nextThread = null;
   
   public Stream ()  {  }
   
   synchronized public void putIt (Object t) {
      value = t;
      notify();
      try { wait(); } catch (Exception e) {  }
   }

   synchronized public Object next () {
      if (runner != null)  notify();
      else {
         runner = new Thread(this);
	 runner.start();
      }

      try { wait(); } catch (Exception e) {  }

      return value;
   }
}

class IntObject implements Object {
   int number;
   
   public IntObject (int n) { number = n; }
   public int valueOf () { return number; }
}

class Successor extends Stream {
   int number;
   
   public Successor(int n, DisplayIt t) { number = n; trace = t;  }   
   
   public void run () {
      trace.appendText(this+":","Connected","Number: "+number);
      
      while (true) {
         trace.appendText(this+":","Run (1)","Returned "+number);
         putIt((Object)new IntObject(number++));	 
      }
   }
}

class Times extends Stream {
   int number;
   Stream stream;
   
   public Times (int n, Stream s, DisplayIt t) { stream = s;  number = n;  trace = t; }   

   public void run () {
      trace.appendText(this+":","Connected", number+" * "+stream);
      
      while (true) {
         int n = ((IntObject)(stream.next())).valueOf();
         trace.appendText(this+":","Run (1)","Returned "+(number*n));
         putIt(new IntObject(number*n));
      }
   }
}   

class Merge extends Stream {
   Object val1, val2;
   Stream stream1;
   Stream stream2;
   
   public Merge (Stream s1, Stream s2, DisplayIt t)  { stream1 = s1; stream2 = s2; trace = t; }

   public void traceText (int i, Object v) {
      if (v == null) trace.appendText(this+":","Run ("+i+")","Returned null");
      else {
         int n = ((IntObject)(v)).valueOf();
         trace.appendText(this+":","Run ("+i+")","Returned "+n);
      }
   }

   public void run () {
      trace.appendText(this+":","Connected: ","to: "+stream1+" and "+stream2);
      
      val1 = stream1.next();
      val2 = stream2.next();
      
      while (true) {
         if (val1 == null && val2 == null) {
            traceText(1, null);
            putIt(null);
         } else if (val1 == null) {
            traceText(2, val2);
            putIt(val2);
            val2 = stream2.next();
         } else if (val2 == null) {
            traceText(3,val1);
            putIt(val1);
            val1 = stream1.next();
         } else if (((IntObject)(val1)).valueOf() < ((IntObject)(val2)).valueOf()) {
            traceText(4,val1);
            putIt(val1);
            val1 = stream1.next();
         } else {
            traceText(5,val2);
            putIt(val2);
            val2 = stream2.next();
         }
      }
   }
}

class Hamming extends Stream {
   int primes[], index;
   
   public Hamming (int p[], int i, DisplayIt t) {  primes = p;  index = i; trace = t; }

   public String makeParam () {
      String param = new String();
      for (int i=index ; i > 0 ; i--) param += primes[i]+", ";
      param += primes[0];
      return param;
   }

   public void run () {
      trace.appendText(this+":","Connected",makeParam());
      trace.appendText(this+":","Run (1)","Returned "+primes[index]);
      putIt(new IntObject(primes[index]));

      Stream s = new Times(primes[index], new Hamming(primes, index, trace), trace);
      if (index != 0) s = new Merge(s, new Hamming(primes, index-1, trace), trace);
      
      while (true) {
         Object token = s.next();
         trace.appendText(this+":","Run (2)","Returned "+((IntObject)(token)).valueOf());
         putIt(token);
      }
   }
}

class DisplayIt {
   JTextArea text;
   FontMetrics fm;
   int c1p = 150;
   int c2p = 100;
   String blanks = new String("                                        ");

   public DisplayIt(JTextArea t, FontMetrics f) { text = t; fm = f; }

   synchronized public void append (String c1, String c2, String c3) {
      int l1 = fm.stringWidth(c1);
      int l2 = fm.stringWidth(c2);
      int b1 = (c1p-l1)/4;
      int b2 = (c2p-l2)/4;
      String str = new String(c1+blanks.substring(0, b1-1)+"\t"+
                              c2+blanks.substring(0, b2-1)+"\t"+
                              c3);
      text.append(str+"\n");
   }
}

public class S extends Applet implements ActionListener {
   Stream str[];
   JButton but[];
   JTextField out[];
   Object object;
   JTextArea trace;
   DisplayIt stream;
   
   public Panel textBox(String label, int t) {
      JPanel p = new JPanel();
      p.setLayout(new BorderLayout());
      p.add("North", new JLabel(label));
      p.add("Center", out[t] = new JTextField());
      return p;
   }
   
   public Panel buttonBox(String label, int b) {
      JPanel p = new JPanel();
      p.setLayout(new BorderLayout());
      p.add("North", new JLabel(""));
      p.add("Center", but[b] = new JButton(label));
      but[b].addActionListener(this);
      return p;
   }

   public void init () {
      but = new JButton[6];
      str = new Stream[6];
      out = new JTextField[6];
      
      setLayout(new BorderLayout(10,10));
      
      JPanel q = new JPanel();
      q.setLayout(new GridLayout(2,5,10,0));
      q.add(textBox("Successor",1));
      q.add(textBox("Times 3", 2));
      q.add(textBox("Times 5", 3));
      q.add(textBox("Merge (*3, *5)", 4));
      q.add(textBox("Hamming (3,5,11)", 5));
      q.add(buttonBox("Start", 1));
      q.add(buttonBox("Start", 2));
      q.add(buttonBox("Start", 3));
      q.add(buttonBox("Start", 4));
      q.add(buttonBox("Start", 5));
      add("North", q);
      
      JPanel y = new JPanel();
      y.setLayout(new BorderLayout());
      y.add("Center", textBox("Current Stream:", 0));
      y.add("East", but[0] = new JButton("Clear"));
      
      JPanel p0 = new JPanel();
      p0.setLayout(new BorderLayout());
      p0.add("North", new JLabel("Trace"));
      p0.add("Center", trace = new JTextArea(4,60));

      JPanel x = new JPanel();
      x.setLayout(new BorderLayout());
      x.add("North", y);
      x.add("Center", p0);
      add("Center", x);
      
      trace.setEditable(false);
      out[0].setEditable(false);

      setBackground(new Color(240, 240, 220));
      trace.setBackground(new Color(255, 255, 240));
      out[0].setBackground(new Color(255, 255, 240));
      but[0].setBackground(new Color(0, 170, 255));
      but[0].setForeground(Color.black);

      for (int i=1 ; i < 6 ; i++) {
         out[i].setEditable(false);
         out[i].setBackground(new Color(255, 255, 240));
         but[i].setBackground(Color.green);
      }

      stream = new DisplayIt(trace, getGraphics().getFontMetrics());
      
      str[1] = new Successor(1, stream);
      str[2] = new Times(3, new Successor(1, stream), stream);
      str[3] = new Times(5, new Successor(1, stream), stream);
      str[4]  = new Merge(new Times(3, new Successor(1, stream), stream), new Times(5, new Successor(1, stream), stream), stream);
      int a[] = new int[3]; a[0] = 11; a[1] = 5; a[2] = 3;
      str[5] = new Hamming(a, 2, stream);
   }

   public void shave() {
      int l = trace.getText().length();
      if (l > 5000) {
         String str = new String(trace.getText().substring(l-4000, l));
         trace.setText("");
         trace.append(str);
      }
   }

   public void setButtonColor (int b) {
      for (int i=1 ; i < 6 ; i++)
         if (i == b && but[i].getBackground().equals(Color.green))
            but[i].setBackground(Color.red);
         else if (i != b && but[i].getBackground().equals(Color.red))
            but[i].setBackground(Color.green);
   }

   public void showTitle (int i) {
      switch (i) {
      case 1:
         out[0].setText("new Successor(1)");
         break;
      case 2:
         out[0].setText("new Times(3, new Successor(1))");
         break;
      case 3:
         out[0].setText("new Times(5, new Successor(1))");
         break;
      case 4:
         out[0].setText("new Merge(new Times(3, new Successor(1)), new Times(5, new Successor(1)))");
         break;
      case 5:
         out[0].setText("new Hamming(a, 2)   int a[] = new int[3]; a[0]=11; a[1]=5; a[2]=3;");
         break;
      }
   }

   public void actionPerformed (ActionEvent evt) {
      if (evt.getSource() == but[0]) trace.setText("");
      for (int i=1 ; i < 6 ; i++) {
         if (evt.getSource() == but[i]) {
            shave();
            but[i].setText("Next");
            setButtonColor(i);
            showTitle(i);
            object = str[i].next();
            int n = ((IntObject)(object)).valueOf();
            stream.append("","["+n+"]","");
            out[i].setText(out[i].getText()+" "+String.valueOf(n));
         }
      }
   }
}