package edu.mit.ai.psg.jeva.examples;

import edu.mit.ai.psg.jeva.*;
import edu.mit.ai.psg.strings.*;
import java.util.*;
import java.io.PrintWriter;

/** <tt><a href="TraceEvalHookBase.java.html">TraceEvalHookBase.java</a></tt>
    provides indented printing for trace output.

    <p>Call {@link #trace(IJevaNode, Object)} from the visit method
    of the type(s) of node you want traced (default is to trace
    all nodes: {@link #visit(IJevaNode, Object)}).  Subclasses override
    visit methods to specify which nodes they want traced.

    <p>{@link #trace(IJevaNode, Object)} will set up a printwriter with
    the appropriate indenting, and calls
    {@link #indentedTrace(PrintWriter, IJevaNode, Object)}.
    Subclasses can override this trace method to specify how they want
    the node to print out.

    <p>Default output stream is System.err.

    @author CarlManning, caroma@ai.mit.edu<br>
    Copyright (c) 1999 Massachusetts Institute of Technology **/ 
public abstract class TraceEvalHookBase extends JevaVisitorBase {
  class PrintWriterStack extends Stack {
    public PrintWriter popWriter() { return (PrintWriter) super.pop(); }
    public PrintWriter topWriter() { return (PrintWriter) super.peek(); }
  }
  final PrintWriterStack printWriterStack = new PrintWriterStack();
  // --- constructors ---
  public TraceEvalHookBase() {
    printWriterStack.push(new PrintWriter(System.err));
  }
  public TraceEvalHookBase(PrintWriter pw) {
    printWriterStack.push(pw);
  }
  /** default method: trace all nodes.  To trace less, override this method
      with one that simply <tt>return null;</tt>, and override the methods 
      for the nodes you want to trace to just call
      {@link #trace(IJevaNode, Object) return trace(node, data);} **/
  public Object visit(IJevaNode node, Object data) {
    return trace(node, data);
  }    
  // --- trace method ---
  /** prints out "->" on calls, and "<-" on results, and calls 
      {@link #indentedTrace(PrintWriter, IJevaNode, Object)}
      with an appropriately indenting printwriter. **/
  public Object trace(IJevaNode node, Object data) {
    Object result = null;
    if (data instanceof EvalMethods.EvalHookPreData) {
      PrintWriter pw = printWriterStack.topWriter();
      pw.print("->");
      PrintWriter iw = new IndentWriter(pw, "| ");
      printWriterStack.push(iw);
      result = indentedTrace(iw, node, data);
    } else if (data instanceof EvalMethods.PreApplyData) {
      PrintWriter pw = printWriterStack.topWriter();      
      result = indentedTrace(pw, node, data);
    } else if (data instanceof EvalMethods.EvalHookPostData) {
      if (printWriterStack.size() > 1)
	// don't remove last writer, to prevent errors if hook changed during
	// middle of computation.
	printWriterStack.popWriter().flush();
      PrintWriter pw = printWriterStack.topWriter();      
      pw.print("<- ");
      PrintWriter iw = new IndentWriter(pw, "   ");
      result = indentedTrace(iw, node, data);
      iw.flush();
      pw.flush();
    }
    return result;
  }
  /** Override this method to customize how to print out node. **/
  public abstract Object indentedTrace(PrintWriter pw,
				       IJevaNode node, Object data);
}
