import java.io.*;

//============================================================================
// Generates a stub Java file that can run as either an applet or an
// application, and a stub HTML file that loads the associated class file.
//
// Change javac to match the executable java compiler location on
// your system, and browser to match the location of the browser
// (appletviewer or netscape) you want to use. These only get used with
// the -test command line flag.
//
// width and height are the default values if none are supplied
// via -width and -height.
//
// The latest version of this code is at
//  <http://www.apl.jhu.edu/~hall/java/JavaStub.java>. The documentation
// is at <http://www.apl.jhu.edu/~hall/java/JavaStub.html>.
// 4/96 Marty Hall. hall@apl.jhu.edu.

public class JavaStub {
  //-------------------------------------------------------------------
  // Customize these for the local system. See above.
  
  String javac = "/usr/local/WWW/Java/bin/javac";          
  String browser = "/usr/local/WWW/Java/bin/appletviewer"; 
  int width=500, height=400;
  
  //-------------------------------------------------------------------
  // Internally used. Turn debug on if you are having trouble with
  // -test. Set -testResult true if you want it automatically.
  
  boolean addMain = true;
  boolean justPrint = false;
  boolean testResult = false;
  static final boolean debug = false; 
  String baseFilename, javaFilename, htmlFilename;
  
  //-------------------------------------------------------------------
  // Top-level driver. First entry in args should be the base
  // Filename, followed by the other command line args. See usage().
  
  public static void main(String[] args) {
    JavaStub stub = new JavaStub();
    stub.generateStubs(args);
  }

  //-------------------------------------------------------------------
  // The real meat of the class. This is the method to call
  // if using this class from a different class.
  // First entry in args should be the base filename, followed by the
  // other command line args. See usage().
  
  public void generateStubs(String[] args) {
    parseArgs(args);
    writeJavaFile(baseFilename, width, height, justPrint, addMain);
    writeHtmlFile(baseFilename, justPrint, width, height);
    if(testResult)
      viewResult(javaFilename, htmlFilename, javac, browser);
    printSeparator();
  }

  //-------------------------------------------------------------------
  // Constructors to let you override width/height or
  // width/height/javac/browswer. You would likely only use
  // this if using JavaStub from another class rather than as
  // a standalone application.

  public JavaStub() {}
  
  public JavaStub(int width, int height) {
    this.width = width;
    this.height = height;
  }

  public JavaStub(int width, int height, String javac, String browser) {
    this.width = width;
    this.height = height;
    this.javac = javac;
    this.browser = browser;
  }
  
  //-------------------------------------------------------------------
  // Check the command line arguments and set internal flags as a result.
  
  void parseArgs(String[] args) {
    if (args.length == 0) {
      System.out.println("You must supply a base filename");
      usage();
    }
    baseFilename = args[0];
    for(int I=1; I<args.length; I++) {
      if (args[I].equals("-nomain"))
	addMain = false;
      else if (args[I].equals("-print"))
	justPrint = true;
      else if (args[I].equals("-test"))
	testResult = true;
      else if (args[I].equals("-width")) {
	if (I==args.length) {
	  System.out.println("No value specified for '-width'");
	  usage();
	}
	width = Integer.parseInt(args[++I]);
      }
      else if (args[I].equals("-height")) {
	if (I==args.length) {
	  System.out.println("No value specified for '-height'");
	  usage();
	}
	height = Integer.parseInt(args[++I]);
      }
      else {
	System.out.println("Unrecognized flag `" + args[I] + "'.");
	usage();
      }
    }
  }
    
  //-------------------------------------------------------------------
  // Print usage message then exit. Called when illegal flags supplied
  // or base filename omitted.
  
  void usage() {
    System.out.println
      ("\nUsage: java JavaStub <Base Filename>\n" +
       "         [-width <Width> -height <Height> -nomain -print -test]\n" +
       "         <Base Filename> should NOT include the file extension.");
    printSeparator();
    System.exit(1);
  }

  //-------------------------------------------------------------------
  // Generates an HTML file with an APPLET entry that loads
  // the class file based on base filename.
  //
  // Passing the filename and print-only flag in instead of referring
  // to them internally (since they are part of this class) lets
  // other classes call writeHtmlFile more easily.
  
  public void writeHtmlFile(String baseFilename, boolean justPrint,
			    int width, int height) {
    htmlFilename = baseFilename + ".html";
    String classFilename = baseFilename + ".class";
    confirmWrite(htmlFilename);
    PrintStream File1 = null;
    if (justPrint) {
      File1 = System.out;
      File1.println("\n------------------------ " +
		    htmlFilename + " ------------------------\n");
    }
    else {
      FileOutputStream F1 = null;
      try {
	F1 = new FileOutputStream(htmlFilename);
      } catch(IOException E) {};
      if (F1 == null) {
	System.out.println("Error: could not open " +
			   htmlFilename + " for output.");
	System.exit(2);
      }
      File1 = new PrintStream(F1);
      System.out.println
	("Generating HTML file " + htmlFilename + "\n" +
	 "that loads " + classFilename +
	 " with WIDTH=" + width + ", and HEIGHT=" + height);
    }
    File1.println
      ("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n" +
       "\n" +
       "<!-- Template generated by JavaStub: -->\n" +
       "<!-- http://www.apl.jhu.edu/~hall/java/JavaStub.html -->\n" +
       "\n" +
       "<HTML>\n" +
       "<HEAD>\n" +
       "  <TITLE>" + baseFilename + "</TITLE>\n" +
       "</HEAD>\n" +
       "\n" +
       "<BODY>\n" +
       "<H1>" + baseFilename + "</H1>\n" +
       "\n" +
       "<APPLET CODE=\"" + classFilename +
                     "\" WIDTH=" + width + " HEIGHT=" + height + ">\n" +
       "  <B>Error! You must use a Java enabled browser.</B>\n" +
       "</APPLET>\n" +
       "\n" +
       "</BODY>\n" +
       "</HTML>\n"
       );
  }
  
  //-------------------------------------------------------------------
  // Generates a stub Java file based on base filename.
  
  public void writeJavaFile(String baseFilename, int width, int height,
			    boolean justPrint, boolean addMain) {
    javaFilename = baseFilename + ".java";
    printSeparator();
    confirmWrite(javaFilename);
    PrintStream File1 = null;
    if (justPrint) {
      File1 = System.out;
      File1.println("\n------------------------ " +
		    javaFilename + " ------------------------\n");
    }
    else {
      FileOutputStream F1 = null;
      try {
	F1 = new FileOutputStream(javaFilename);
      } catch(IOException E) {};
      if (F1 == null) {
	System.out.println("Error: could not open " +
			   javaFilename + " for output.");
	System.exit(3);
      }
      File1 = new PrintStream(F1);
      System.out.println
	("Generating Java file " + javaFilename + "\n" +
	 "that can be run as either an applet or an application.");
    }
    File1.println
      ("import java.applet.Applet;\n" +
       "import java.awt.*;\n");
    if (addMain)
      File1.println(
       "import java.net.*;                 // for getImage & getAudioClip\n" +
       "import sun.applet.AppletAudioClip; // for AppletAudioClip\n");
    File1.println(
       "\n" +
       "public class " + baseFilename + " extends Applet {\n" +
       "\n" +
       "  //--------------------------------------------------------------\n" +
       "\n" +
       "  public void init() {\n" +
       "  }\n" +
       "\n" +
       "  //--------------------------------------------------------------\n" +
       "  // A placeholder for paint(). REPLACE THIS WITH YOUR CODE.\n" +
       "\n" +
       "  public void paint(Graphics g) {\n" +
       "    int width = size().width,\n" +
       "        height = size().height;\n" +
       "    Font font = new Font(\"TimesRoman\", Font.BOLD, 40);\n" +
       "\n" +
       "    g.setColor(Color.red);\n" +
       "    g.fillRect(0, 0, width, height);\n" +
       "    g.setColor(Color.yellow);\n" +
       "    g.setFont(font);\n" +
       "    g.drawString" +
               "(\"" + baseFilename + " -- stub paint()\", 0, height-50);\n" +
       "    g.drawString" +
               "(\"generated by JavaStub\", 0, height-10);\n" +
       "  }\n" +
       "\n");
    if(addMain)
      File1.println(
       "  //--------------------------------------------------------------\n" +
       "  // Lets this be run as either an applet or an application.\n" +
       "  // Generated by JavaStub: " +
                         "http://www.apl.jhu.edu/~hall/java/JavaStub.html\n" +
       "\n" +
       "  public static void main(String[] args) {\n" +
       "    Frame mainFrame = new Frame(\"" + baseFilename + "\");\n" +
       "    " + baseFilename + " app = new " + baseFilename + "();\n" +
       "    app.init();\n" +
       "    mainFrame.resize(" + width + "," + height + ");\n" +
       "    mainFrame.add(\"Center\", app);\n" +
       "    mainFrame.show();\n" +
       "    mainFrame.repaint();\n" +
       "  }\n" +
       "  //--------------------------------------------------------------\n" +
       "  // Only needed if running standalone, but doesn't hurt anyhow.\n" +
       "\n" +
       "  public Image getImage(URL url) {\n" +
       "    return(Toolkit.getDefaultToolkit().getImage(url));\n" +
       "  }\n" +
       "\n" +
       "  //--------------------------------------------------------------\n" +
       "  // Also only needed if running standalone. Adapted from\n" +
       "  // Jef Poskanzer's MainFrame class\n" +
       "  // (http://www.acme.com/java/software/Acme.MainFrame.html)\n" +
       "  // Using MainFrame will give you menus, showStatus,\n" +
       "  // getCodebase, etc. \n" +
       "\n" +
       "  public java.applet.AudioClip getAudioClip(URL url) {\n" +
       "    return new AppletAudioClip(url);\n" +
       "  }\n" +
       "\n");
    File1.println(
       "  //--------------------------------------------------------------\n" +
       "\n" +
       "} // class " + baseFilename);
    printSeparator();
  }
       
  //-------------------------------------------------------------------
  // If file already exists, confirm before overwriting it.
  // Also, to avoid having to parse pathname delimiters, users must supply
  // a local filename, not an absolute one.
  
  public void confirmWrite(String filename) {
    File file = new File(filename);
    if (file.isAbsolute()) {
      System.out.println
	("You must specify a filename in the current directory,\n" +
	 "not an absolute pathname.");
      System.exit(3);
    }
    if (file.isFile()) {
      System.out.print(filename + " already exists. Overwrite it? (y or n) ");
      System.out.flush();
      DataInputStream stdin = new DataInputStream(System.in);
      String answer = "n";
      try { answer = stdin.readLine();
      } catch(Exception e) {};
      if (answer.equals("y") || answer.equals("Y") || answer.equals("yes"))
	return;
      else if (answer.equals("n") || answer.equals("N") || answer.equals("no"))
	System.exit(4);
      else {
	System.out.println("Please answer 'y' or 'n'.");
	confirmWrite(filename);
      }
    }
  }
  
  //-------------------------------------------------------------------

  public void viewResult(String javaFilename, String htmlFilename,
			 String javac, String browser) {
    File javacFile = new File(javac);
    if (!javacFile.isFile()) {
      System.out.println
	("Error: javac not found in " + javac + ".\n" +
	 "Change 'javac' and 'browser' at the top of JavaStub.java,\n" +
	 "recompile JavaStub, and try again.");
      System.exit(5);
    }
    File browserFile = new File(browser);
    if (!browserFile.isFile()) {
      System.out.println
	("Error: Browser not found in " + browser + ".\n" +
	 "Change 'browser' and 'javac' at the top of JavaStub.java,\n" +
	 "recompile JavaStub, and try again.");
      System.exit(6);
    }
    exec(javac + " " + javaFilename);
    exec(browser + " " + htmlFilename);
  }
  
  //-------------------------------------------------------------------
  // Call javac or the browser. If debug is false (as normal),
  // this doesn't print anything unless the exec has an error.
  // If debug is true, it prints all the output of the subprocess.
  // The waitFor() is required to make sure that the process doing
  // the compilation is finished before the process starting the browser
  // on the results kicks in.
  
  public void exec(String command) {
    printSeparator();
    System.out.println("Executing '" + command + "'.");
    try {
      Process p  = Runtime.getRuntime().exec(command);
      if(debug) {
	DataInputStream commandResult =
	  new DataInputStream(new BufferedInputStream(p.getInputStream()));
	String s = null;
	try {
	  while ((s = commandResult.readLine()) != null)
	    System.out.println("Output: " + s);
	} catch (Exception e) {}
      }
      else {
	try {
	  int returnVal = p.waitFor();
	  if (returnVal != 0) {
	    System.out.println("Error executing '" + command + "'.");
	    System.exit(5);
	  }
	}
	catch (InterruptedException i) {
	  System.out.println(command + " interrupted!");
	}
      }
    }
    catch (Exception e) {
      System.out.println("Error doing exec(" + command + "): " + e);
    }
  }
  
  //-------------------------------------------------------------------

  public void printSeparator() {
    System.out.println("--------------------------------------------------");
  }

  //-------------------------------------------------------------------

} // class JavaStub

