/*
 * Copyright © 2002 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * California 95054, U.S.A. All rights reserved.  Sun Microsystems, Inc. has
 * intellectual property rights relating to technology embodied in the product
 * that is described in this document. In particular, and without limitation,
 * these intellectual property rights may include one or more of the U.S.
 * patents listed at http://www.sun.com/patents and one or more additional
 * patents or pending patent applications in the U.S. and in other countries.
 * U.S. Government Rights - Commercial software. Government users are subject
 * to the Sun Microsystems, Inc. standard license agreement and applicable
 * provisions of the FAR and its supplements.  Use is subject to license terms.
 * Sun,  Sun Microsystems,  the Sun logo and  Java are trademarks or registered
 * trademarks of Sun Microsystems, Inc. in the U.S. and other countries.  This
 * product is covered and controlled by U.S. Export Control laws and may be
 * subject to the export or import laws in other countries.  Nuclear, missile,
 * chemical biological weapons or nuclear maritime end uses or end users,
 * whether direct or indirect, are strictly prohibited.  Export or reexport
 * to countries subject to U.S. embargo or to entities identified on U.S.
 * export exclusion lists, including, but not limited to, the denied persons
 * and specially designated nationals lists is strictly prohibited.
 */

package org.javacc.jjtree;

import java.io.*;
import java.util.Vector;

import org.javacc.parser.JavaCCGlobals;

class NodeFiles
{
  static void ensure(IO io, String nodeType)
  {
    File file = new File(JJTreeOptions.getJJTreeOutputDirectory(), nodeType + ".java");

    if (nodeType.equals("Node")) {
    } else if (nodeType.equals("SimpleNode")) {
      ensure(io, "Node");
    } else {
      ensure(io, "SimpleNode");
    }

    /* Only build the node file if we're dealing with Node.java, or
       the NODE_BUILD_FILES option is set. */
    if (!(nodeType.equals("Node") || JJTreeOptions.getBuildNodeFiles())) {
      return;
    }

    if (file.exists()) {
      return;
    }

    io.getMsg().println("File \"" + file +
			"\" does not exist.  Will create one.");

    PrintWriter ostr;

    try {
      ostr = new PrintWriter(new BufferedWriter(
				  new FileWriter(file), 8096));

      if (nodeType.equals("Node")) {
	generateNode_java(ostr);
      } else if (nodeType.equals("SimpleNode")) {
	generateSimpleNode_java(ostr);
      } else {
	generateMULTINode_java(ostr, nodeType);
      }
      
      ostr.close();

    } catch (IOException e) {
      throw new Error(e.toString());
    }
  }

  
  static void generatePrologue(PrintWriter ostr, String fileName)
  {
    ostr.println("/* " +
		 JavaCCGlobals.getIdString(JJTreeGlobals.toolList,
					   fileName) +
		 " */");
    ostr.println();
    if (!JJTreeGlobals.packageName.equals("")) {
      ostr.println("package " + JJTreeGlobals.packageName + ";");
      ostr.println();
    }
  }


  static String nodeConstants()
  {
    return JJTreeGlobals.parserName + "TreeConstants";
  }

  static void generateTreeConstants_java()
  {
    String name = nodeConstants();
    File file = new File(JJTreeOptions.getJJTreeOutputDirectory(), name + ".java");

    try {
      PrintWriter ostr = new PrintWriter(new BufferedWriter(
					      new FileWriter(file),
					      8096));

      Vector nodeIds = ASTNodeDescriptor.getNodeIds();
      Vector nodeNames = ASTNodeDescriptor.getNodeNames();

      generatePrologue(ostr, file.toString());
      ostr.println("public interface " + name);
      ostr.println("{");

      for (int i = 0; i < nodeIds.size(); ++i) {
	String n = (String)nodeIds.elementAt(i);
	ostr.println("  public int " + n + " = " + i + ";");
      }

      ostr.println();
      ostr.println();
      
      ostr.println("  public String[] jjtNodeName = {");
      for (int i = 0; i < nodeNames.size(); ++i) {
	String n = (String)nodeNames.elementAt(i);
	ostr.println("    \"" + n + "\",");
      }
      ostr.println("  };");

      ostr.println("}");
      ostr.close();

    } catch (IOException e) {
      throw new Error(e.toString());
    }
  }


  static String visitorClass()
  {
    return JJTreeGlobals.parserName + "Visitor";
  }

  static void generateVisitor_java()
  {
    if (!JJTreeOptions.getVisitor()) {
      return;
    }

    String name = visitorClass();
    File file = new File(JJTreeOptions.getJJTreeOutputDirectory(), name + ".java");

    try {
      PrintWriter ostr = new PrintWriter(new BufferedWriter(
					      new FileWriter(file),
					      8096));

      Vector nodeNames = ASTNodeDescriptor.getNodeNames();

      generatePrologue(ostr, file.toString());
      ostr.println("public interface " + name);
      ostr.println("{");

      String ve = JJTreeOptions.getVisitorException();
      if (!ve.equals("")) {
	ve = " throws " + ve;
      }
      
      ostr.println("  public Object visit(SimpleNode node, Object data)" +
		   ve + ";");
      if (JJTreeOptions.getMulti()) {
	for (int i = 0; i < nodeNames.size(); ++i) {
	  String n = (String)nodeNames.elementAt(i);
	  if (n.equals("void")) {
	    continue;
	  }
	  String nodeType = JJTreeOptions.getNodePrefix() + n;
	  ostr.println("  public Object visit(" + nodeType +
		       " node, Object data)" + ve + ";");
	}
      }
      ostr.println("}");
      ostr.close();

    } catch (IOException e) {
      throw new Error(e.toString());
    }
  }


  private static void generateNode_java(PrintWriter ostr)
  {
    generatePrologue(ostr, "Node.java");

    ostr.println("/* All AST nodes must implement this interface.  It provides basic");
    ostr.println("   machinery for constructing the parent and child relationships");
    ostr.println("   between nodes. */");
    ostr.println("");
    ostr.println("public interface Node {");
    ostr.println("");
    ostr.println("  /** This method is called after the node has been made the current");
    ostr.println("    node.  It indicates that child nodes can now be added to it. */");
    ostr.println("  public void jjtOpen();");
    ostr.println("");
    ostr.println("  /** This method is called after all the child nodes have been");
    ostr.println("    added. */");
    ostr.println("  public void jjtClose();");
    ostr.println("");
    ostr.println("  /** This pair of methods are used to inform the node of its");
    ostr.println("    parent. */");
    ostr.println("  public void jjtSetParent(Node n);");
    ostr.println("  public Node jjtGetParent();");
    ostr.println("");
    ostr.println("  /** This method tells the node to add its argument to the node's");
    ostr.println("    list of children.  */");
    ostr.println("  public void jjtAddChild(Node n, int i);");
    ostr.println("");
    ostr.println("  /** This method returns a child node.  The children are numbered");
    ostr.println("     from zero, left to right. */");
    ostr.println("  public Node jjtGetChild(int i);");
    ostr.println("");
    ostr.println("  /** Return the number of children the node has. */");
    ostr.println("  public int jjtGetNumChildren();");

    if (JJTreeOptions.getVisitor()) {
      String ve = JJTreeOptions.getVisitorException();
      if (!ve.equals("")) {
	ve = " throws " + ve;
      }

      ostr.println("");
      ostr.println("  /** Accept the visitor. **/");
      ostr.println("  public Object jjtAccept(" + visitorClass() +
		   " visitor, Object data)" + ve + ";");
    }

    ostr.println("}");

    ostr.close();
  }


  private static void generateSimpleNode_java(PrintWriter ostr)
  {
    generatePrologue(ostr, "SimpleNode.java");

    ostr.print("public class SimpleNode");
    if (!JJTreeOptions.getNodeExtends().equals(""))
       ostr.print(" extends " + JJTreeOptions.getNodeExtends());
    ostr.println(" implements Node {");
    ostr.println("  protected Node parent;");
    ostr.println("  protected Node[] children;");
    ostr.println("  protected int id;");
    ostr.println("  protected " + JJTreeGlobals.parserName + " parser;");
    ostr.println("");
    ostr.println("  public SimpleNode(int i) {");
    ostr.println("    id = i;");
    ostr.println("  }");
    ostr.println("");
    ostr.println("  public SimpleNode(" + JJTreeGlobals.parserName + " p, int i) {");
    ostr.println("    this(i);");
    ostr.println("    parser = p;");
    ostr.println("  }");
    ostr.println("");

    if (JJTreeOptions.getNodeFactory()) {
      ostr.println("  public static Node jjtCreate(int id) {");
      ostr.println("    return new SimpleNode(id);");
      ostr.println("  }");
      ostr.println("");
      ostr.println("  public static Node jjtCreate(" + JJTreeGlobals.parserName + " p, int id) {");
      ostr.println("    return new SimpleNode(p, id);");
      ostr.println("  }");
      ostr.println("");
    }

    ostr.println("  public void jjtOpen() {");
    ostr.println("  }");
    ostr.println("");
    ostr.println("  public void jjtClose() {");
    ostr.println("  }");
    ostr.println("  ");
    ostr.println("  public void jjtSetParent(Node n) { parent = n; }");
    ostr.println("  public Node jjtGetParent() { return parent; }");
    ostr.println("");
    ostr.println("  public void jjtAddChild(Node n, int i) {");
    ostr.println("    if (children == null) {");
    ostr.println("      children = new Node[i + 1];");
    ostr.println("    } else if (i >= children.length) {");
    ostr.println("      Node c[] = new Node[i + 1];");
    ostr.println("      System.arraycopy(children, 0, c, 0, children.length);");
    ostr.println("      children = c;");
    ostr.println("    }");
    ostr.println("    children[i] = n;");
    ostr.println("  }");
    ostr.println("");
    ostr.println("  public Node jjtGetChild(int i) {");
    ostr.println("    return children[i];");
    ostr.println("  }");
    ostr.println("");
    ostr.println("  public int jjtGetNumChildren() {");
    ostr.println("    return (children == null) ? 0 : children.length;");
    ostr.println("  }");
    ostr.println("");

    if (JJTreeOptions.getVisitor()) {
      String ve = JJTreeOptions.getVisitorException();
      if (!ve.equals("")) {
	ve = " throws " + ve;
      }
      ostr.println("  /** Accept the visitor. **/");
      ostr.println("  public Object jjtAccept(" + visitorClass() +
		   " visitor, Object data)" + ve + " {");
      ostr.println("    return visitor.visit(this, data);");
      ostr.println("  }");
      ostr.println("");

      ostr.println("  /** Accept the visitor. **/");
      ostr.println("  public Object childrenAccept(" + visitorClass() +
		   " visitor, Object data)" + ve + " {");
      ostr.println("    if (children != null) {");
      ostr.println("      for (int i = 0; i < children.length; ++i) {");
      ostr.println("        children[i].jjtAccept(visitor, data);");
      ostr.println("      }");
      ostr.println("    }");
      ostr.println("    return data;");
      ostr.println("  }");
      ostr.println("");
    }

    ostr.println("  /* You can override these two methods in subclasses of SimpleNode to");
    ostr.println("     customize the way the node appears when the tree is dumped.  If");
    ostr.println("     your output uses more than one line you should override");
    ostr.println("     toString(String), otherwise overriding toString() is probably all");
    ostr.println("     you need to do. */");
    ostr.println("");
    ostr.println("  public String toString() { return " + nodeConstants() + ".jjtNodeName[id]; }");
    ostr.println("  public String toString(String prefix) { return prefix + toString(); }");

    ostr.println("");
    ostr.println("  /* Override this method if you want to customize how the node dumps");
    ostr.println("     out its children. */");
    ostr.println("");
    ostr.println("  public void dump(String prefix) {");
    ostr.println("    System.out.println(toString(prefix));");
    ostr.println("    if (children != null) {");
    ostr.println("      for (int i = 0; i < children.length; ++i) {");
    ostr.println("	SimpleNode n = (SimpleNode)children[i];");
    ostr.println("	if (n != null) {");
    ostr.println("	  n.dump(prefix + \" \");");
    ostr.println("	}");
    ostr.println("      }");
    ostr.println("    }");
    ostr.println("  }");
    ostr.println("}");
    ostr.println("");
  }


  private static void generateMULTINode_java(PrintWriter ostr, String nodeType)
  {
    generatePrologue(ostr, nodeType + ".java");

    ostr.println("public class " + nodeType + " extends SimpleNode {");
    ostr.println("  public " + nodeType + "(int id) {");
    ostr.println("    super(id);");
    ostr.println("  }");
    ostr.println();
    ostr.println("  public " + nodeType + "(" + JJTreeGlobals.parserName + " p, int id) {");
    ostr.println("    super(p, id);");
    ostr.println("  }");
    ostr.println();

    if (JJTreeOptions.getNodeFactory()) {
      ostr.println("  public static Node jjtCreate(int id) {");
      ostr.println("      return new " + nodeType + "(id);");
      ostr.println("  }");
      ostr.println();
      ostr.println("  public static Node jjtCreate(" +
		   JJTreeGlobals.parserName + " p, int id) {");
      ostr.println("      return new " + nodeType + "(p, id);");
      ostr.println("  }");
    }

    if (JJTreeOptions.getVisitor()) {
      String ve = JJTreeOptions.getVisitorException();
      if (!ve.equals("")) {
	ve = " throws " + ve;
      }
      ostr.println("");
      ostr.println("  /** Accept the visitor. **/");
      ostr.println("  public Object jjtAccept(" + visitorClass() +
		   " visitor, Object data)" + ve + " {");
      ostr.println("    return visitor.visit(this, data);");
      ostr.println("  }");
    }

    ostr.println("}");
    ostr.close();
  }

}


/*end*/



