/* --- Copyright (c) Chris Rathman 1999. All rights reserved. -----------------
 > File:        jasper/Jasper.java
 > Purpose:     Write Jasmin and browser files from input java class files
 > Author:      Chris Rathman, 12 June 1999
 > Version:     1.00
 */
package jasper;
import java.io.*;
import java.util.zip.*;

/*=======================================================================
 = Class:         Jasper                                                =
 =                                                                      =
 = Desc:          program to write Jasmin & Browse files                =
 =======================================================================*/
public class Jasper {
   public static final String version = "v1.00";  // jasper software version number

   /*-----------------------------------------------------------------------
    - Method:        main                                                  -
    -                                                                      -
    - Desc:          program entry point                                   -
    -----------------------------------------------------------------------*/
   public static void main(String[] args) {
      String accessPermission;
      boolean jasmin = true;
      boolean browse = false;
      boolean recurse = false;

      java.util.Vector classList = new java.util.Vector();

      for (int i = 0; i < args.length; i++) {
         if (args[i].charAt(0) != '-') {
            // put the class file name into the list of programs to read
            addClassFile(args[i], classList);

         } else {
            if (args[i].equals("--jasmin")) {
               // turn off the output to the jasmin assembly files
               jasmin = false;

            } else if(args[i].equals("-jasmin")) {
               // turn on the output to the jasmin assembly files
               jasmin = true;

            } else if(args[i].equals("-browse")) {
               // enable the output to the browse files
               browse = true;

            } else if(args[i].equals("-recurse")) {
               // recurse through the inheritance and composition for the class
               recurse = true;

            } else if(args[i].equals("-version")) {
               // print out the program name and version number
               printVersion();
               System.exit(0);

            } else if(args[i].equals("--version")) {
               // print out the program name, version number and BSD License
               printVersion();
               printLicense();
               System.exit(0);

            } else if((args[i].equals("-?")) || (args[i].equals("-help"))) {
               // explain the program usage and options which are available
               printVersion();
               printOptions();
               System.exit(0);

            } else {
               // option not recognized so give up
               System.out.println("Unrecognized option: " + args[i]);
               System.out.println("Could not run jasper.");
               printVersion();
               printOptions();
               System.exit(0);
            }
         }
      }

      // read in the files (and add new ones along the way for recurse option
      for (int i = 0; i < classList.size(); i++) {
         // get the next class to read in
         String fileName = (String)classList.elementAt(i);

         // pull in the class
         ClassFile cls = new ClassFile(fileName);

         // output the assembly files
         if (jasmin) cls.jasmin();

         // echo the browse output
         if (browse) browseDump(cls);

         // if recurse, then add classes to list that are referred to by inheritance and composition
         if (recurse) recurseClasses(cls, classList);
      }
   }

   /*-----------------------------------------------------------------------
    - Method:        recurseClasses                                        -
    -                                                                      -
    - Desc:          trundle through browse strings to find class ref's    -
    -----------------------------------------------------------------------*/
   static void recurseClasses(ClassFile cls, java.util.Vector classList) {
      // bring in super class
      String newClass = cls.browseSuper();
      if (!newClass.equals("")) addClassFile(newClass + ".class", classList);

      // bring in interface classes
      String[] newClassArray = cls.browseInterfaces();
      for (int j = 0; j < newClassArray.length; j++) {
         newClass = newClassArray[j];
         addClassFile(newClass + ".class", classList);
      }

      // bring in field types
      newClassArray = cls.browseFields();
      for (int j = 0; j < newClassArray.length; j++) {
         // kill the field access permission attributes
         newClass = stripAccess(newClassArray[j]);

         // reduce the string to the field type
         if (newClass.indexOf(' ') > 0) newClass = newClass.substring(0, newClass.indexOf(' '));

         // strip off any array indexes on the field type
         if (newClass.indexOf('[') > 0) newClass = newClass.substring(0, newClass.indexOf('['));

         // add the class to the list of classes to read
         addClassFile(newClass + ".class", classList);
      }

      // bring in method types
      newClassArray = cls.browseMethods();
      for (int j = 0; j < newClassArray.length; j++) {
         // kill the field access permission attributes
         newClass = stripAccess(newClassArray[j]);

         // reduce the string to the field type
         if (newClass.indexOf(' ') > 0) newClass = newClass.substring(0, newClass.indexOf(' '));

         // strip off any array indexes on the field type
         if (newClass.indexOf('[') > 0) newClass = newClass.substring(0, newClass.indexOf('['));

         // add the class to the list of classes to read
         addClassFile(newClass + ".class", classList);

         // bring in method parameters
         String params = newClassArray[j];
         params = params.substring(params.indexOf('(')+1);
         params = params.substring(0, params.indexOf(')')+1);
         while (params.length() > 1) {
            int n = params.indexOf(',');
            if (n > 0) {
               newClass = params.substring(0, n);
               params = params.substring(n + 2);
            } else {
               newClass = params.substring(0, params.indexOf(')'));
               params = "";
            }
            if (newClass.indexOf('[') > 0) newClass = newClass.substring(0, newClass.indexOf('['));
            addClassFile(newClass + ".class", classList);
         }
      }

      // bring in the fields referenced by class methods
      String[][] newClassRef = cls.browseFieldrefs();
      for (int j = 0; j < newClassRef.length; j++) {
         for (int k = 0; k < newClassRef[j].length; k++) {
            // kill the field access permission attributes
            newClass = stripAccess(newClassRef[j][k]);

            // reduce the string to the field type
            if (newClass.indexOf(' ') > 0) newClass = newClass.substring(0, newClass.indexOf(' '));

            // strip off any array indexes on the field type
            if (newClass.indexOf('[') > 0) newClass = newClass.substring(0, newClass.indexOf('['));

            // add the class to the list of classes to read
            addClassFile(newClass + ".class", classList);

            // get the class name of the field being referred to
            newClass = stripAccess(newClassRef[j][k]);
            if (newClass.lastIndexOf(' ') > 0) newClass = newClass.substring(newClass.lastIndexOf(' ') + 1);

            // strip off the field name
            if (newClass.lastIndexOf('.') > 0) newClass = newClass.substring(0, newClass.lastIndexOf('.'));

            // add the class to the list of classes to read
            addClassFile(newClass + ".class", classList);
         }
      }

      // bring in the methods referenced by class methods
      newClassRef = cls.browseMethodrefs();
      for (int j = 0; j < newClassRef.length; j++) {
         for (int k = 0; k < newClassRef[j].length; k++) {
            // kill the field access permission attributes
            newClass = stripAccess(newClassRef[j][k]);

            // reduce the string to the method return type
            if (newClass.indexOf(' ') > 0) newClass = newClass.substring(0, newClass.indexOf(' '));

            // strip off any array indexes on the return type
            if (newClass.indexOf('[') > 0) newClass = newClass.substring(0, newClass.indexOf('['));

            if (!newClass.equals("new")) {
               // add the class to the list of classes to read
               addClassFile(newClass + ".class", classList);

               // get the class name of the method being referred to
               newClass = stripAccess(newClassRef[j][k]);
               if (newClass.indexOf('(') > 0) newClass = newClass.substring(0, newClass.indexOf('('));
               if (newClass.lastIndexOf(' ') > 0) newClass = newClass.substring(newClass.lastIndexOf(' ') + 1);

               // strip off the method name
               if (newClass.lastIndexOf('.') > 0) newClass = newClass.substring(0, newClass.lastIndexOf('.'));

               // add the class to the list of classes to read
               addClassFile(newClass + ".class", classList);
            }

            // bring in method parameters
            String params = newClassRef[j][k];
            params = params.substring(params.indexOf('(')+1);
            params = params.substring(0, params.indexOf(')')+1);
            while (params.length() > 1) {
               int n = params.indexOf(',');
               if (n > 0) {
                  newClass = params.substring(0, n);
                  params = params.substring(n + 2);
               } else {
                  newClass = params.substring(0, params.indexOf(')'));
                  params = "";
               }
               if (newClass.indexOf('[') > 0) newClass = newClass.substring(0, newClass.indexOf('['));
               addClassFile(newClass + ".class", classList);
            }
         }
      }

      // bring in the interface methods referenced by class methods
      newClassRef = cls.browseMethodrefs();
      for (int j = 0; j < newClassRef.length; j++) {
         for (int k = 0; k < newClassRef[j].length; k++) {
            // kill the field access permission attributes
            newClass = stripAccess(newClassRef[j][k]);

            // reduce the string to the method return type
            if (newClass.indexOf(' ') > 0) newClass = newClass.substring(0, newClass.indexOf(' '));

            // strip off any array indexes on the return type
            if (newClass.indexOf('[') > 0) newClass = newClass.substring(0, newClass.indexOf('['));

            if (!newClass.equals("new")) {
               // add the class to the list of classes to read
               addClassFile(newClass + ".class", classList);

               // get the class name of the method being referred to
               newClass = stripAccess(newClassRef[j][k]);
               if (newClass.indexOf('(') > 0) newClass = newClass.substring(0, newClass.indexOf('('));
               if (newClass.lastIndexOf(' ') > 0) newClass = newClass.substring(newClass.lastIndexOf(' ') + 1);

               // strip off the method name
               if (newClass.lastIndexOf('.') > 0) newClass = newClass.substring(0, newClass.lastIndexOf('.'));

               // add the class to the list of classes to read
               addClassFile(newClass + ".class", classList);
            }

            // bring in method parameters
            String params = newClassRef[j][k];
            params = params.substring(params.indexOf('(')+1);
            params = params.substring(0, params.indexOf(')')+1);
            while (params.length() > 1) {
               int n = params.indexOf(',');
               if (n > 0) {
                  newClass = params.substring(0, n);
                  params = params.substring(n + 2);
               } else {
                  newClass = params.substring(0, params.indexOf(')'));
                  params = "";
               }
               if (newClass.indexOf('[') > 0) newClass = newClass.substring(0, newClass.indexOf('['));
               addClassFile(newClass + ".class", classList);
            }
         }
      }
   }

   /*-----------------------------------------------------------------------
    - Method:        addClassFile                                          -
    -                                                                      -
    - Desc:          if class not in process list, then add it             -
    -----------------------------------------------------------------------*/
   static void addClassFile(String name, java.util.Vector classList) {
      // normalize the file name
      String fileName = ClassFile.parseFileDir(name) + ClassFile.parseFileName(name) +
         "." + ClassFile.parseFileExt(name);

      // don't add the primitive types since there is no class file associated with them
      if (fileName.equals(".class")) return;
      if (fileName.equals("byte.class")) return;
      if (fileName.equals("short.class")) return;
      if (fileName.equals("int.class")) return;
      if (fileName.equals("long.class")) return;
      if (fileName.equals("boolean.class")) return;
      if (fileName.equals("float.class")) return;
      if (fileName.equals("double.class")) return;
      if (fileName.equals("char.class")) return;
      if (fileName.equals("void.class")) return;

      // check if file has already been read in
      for (int i = 0; i < classList.size(); i++) {
         if (((String)classList.elementAt(i)).equals(fileName)) return;
      }

      // add class to the list of those read
      classList.add(fileName);
   }

   /*-----------------------------------------------------------------------
    - Method:        stripAccess                                           -
    -                                                                      -
    - Desc:          strip access permission from fields and methods       -
    -                   element[i][0] = permission string to match         -
    -                   element[i][1] = field/method as synthetic          -
    -----------------------------------------------------------------------*/
   static String stripAccess(String newClass) {
      // loop until all the known access flags are stripped
      boolean flag = true;
      while (flag) {
         flag = false;
         for (int i = 0; i < accessStrings.length; i++) {
            String s = accessStrings[i][0];
            if (newClass.substring(0, s.length()).equals(s)) {
               // strip the attribute away from the string
               newClass = newClass.substring(s.length());
               flag = true;

               // check if this is a synthetic field/method
               boolean synthetic = accessStrings[i][1].equals("1");
               if (synthetic) return "";
            }
         }
      }
      return newClass;
   }

   /*-----------------------------------------------------------------------
    - Method:        accessStrings                                         -
    -                                                                      -
    - Desc:          array of access permission strings used for strip     -
    -                   element[i][0] = permission string to match         -
    -                   element[i][1] = synthetic field/method) ("1"=true) -
    -----------------------------------------------------------------------*/
   static String[][] accessStrings = {
      {"public ",       "0"},
      {"private ",      "0"},
      {"protected ",    "0"},
      {"static ",       "0"},
      {"final ",        "0"},
      {"synchronized ", "0"},
      {"volatile ",     "0"},
      {"transient ",    "0"},
      {"native ",       "0"},
      {"abstract ",     "0"},
      {"#deprecated# ", "0"},
      {"#synthetic# ",  "1"},
   };

   /*-----------------------------------------------------------------------
    - Method:        browseDump                                            -
    -                                                                      -
    - Desc:          echo the browse                                       -
    -----------------------------------------------------------------------*/
   static void browseDump(ClassFile cls) {
      String[] ix = cls.browseInterfaces();
      String[] fx = cls.browseFields();
      String[] mx = cls.browseMethods();
      String[][] mf = cls.browseFieldrefs();
      String[][] mm = cls.browseMethodrefs();
      String[][] mi = cls.browseInterfaceMethodrefs();
      String[][] ic = cls.browseInnerClasses();

      System.out.println("+++++++++++++++++++++++");
      System.out.println("   SourceFile = " + cls.browseSourceFile());
      System.out.println("   class      = " + cls.browseClass());
      System.out.println("   extends    = " + cls.browseSuper());
      for (int i = 0; i < ix.length; i++) System.out.println("   implements = " + ix[i]);
      for (int i = 0; i < fx.length; i++) System.out.println("   field      = " + fx[i]);
      for (int i = 0; i < mx.length; i++) {
         System.out.println("   method     = " + mx[i]);
         for (int j = 0; j < mf[i].length; j++) System.out.println("      fields     = " + mf[i][j]);
         for (int j = 0; j < mm[i].length; j++) System.out.println("      methods    = " + mm[i][j]);
         for (int j = 0; j < mi[i].length; j++) System.out.println("      interfaces = " + mi[i][j]);
      }
      for (int i = 0; i < ic.length; i++) {
         System.out.println("innerClass[" + i + "] = " + ic[i][0]);
         System.out.println("outerClass[" + i + "] = " + ic[i][1]);
         System.out.println("innerName[" + i + "]  = " + ic[i][2]);
      }
   }

   /*-----------------------------------------------------------------------
    - Method:        printVersion                                          -
    -                                                                      -
    - Desc:          echo the program name and version number              -
    -----------------------------------------------------------------------*/
   private static void printVersion() {
      System.out.println("Jasper Version " + version +
         "  Copyright (c) Chris Rathman 1999. All rights reserved.");
      System.out.println("Syntax:  java [java-options] jasper/Jasper [jasper-options] files.class");
      System.out.println("");
   }

   /*-----------------------------------------------------------------------
    - Method:        printOptions                                          -
    -                                                                      -
    - Desc:          echo the program options                              -
    -----------------------------------------------------------------------*/
   private static void printOptions() {
      System.out.println("Jasper recognizes the following options:");
      System.out.println("   --jasmin   Disable jasmin file output");
      System.out.println("   -jasmin    Enable jasmin file output (default)");
      System.out.println("   -browse    Enable output to the browse files");
      System.out.println("   -recurse   Recurse through the inheritance and composition for the class");
      System.out.println("   -help      View Jasper help");
      System.out.println("   -version   View Jasper version number");
      System.out.println("   --version  View Jasper license");
      System.out.println("");
   }

   /*-----------------------------------------------------------------------
    - Method:        printLicense                                          -
    -                                                                      -
    - Desc:          Echo the software license                             -
    -----------------------------------------------------------------------*/
   static void printLicense() {
      System.out.println("");
      System.out.println("Redistribution and use in source and binary forms, with or without");
      System.out.println("modification, are permitted provided that the following conditions");
      System.out.println("are met:");
      System.out.println("");
      System.out.println("  1. Redistributions of source code must retain the above copyright");
      System.out.println("     notice, this list of conditions and the following disclaimer.");
      System.out.println("  2. Redistributions in binary form must reproduce the above copyright");
      System.out.println("     notice, this list of conditions and the following disclaimer in the");
      System.out.println("     documentation and/or other materials provided with the distribution.");
      System.out.println("  3. All advertising materials mentioning features or use of this software");
      System.out.println("     must display the following acknowledgement:");
      System.out.println("       This product includes software developed by Chris Rathman and");
      System.out.println("       its contributors.");
      System.out.println("  4. Neither the name of Chris Rathman nor the names of its contributors");
      System.out.println("     may be used to endorse or promote products derived from this software");
      System.out.println("     without specific prior written permission.");
      System.out.println("");
      System.out.println("THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''");
      System.out.println("AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE");
      System.out.println("IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE");
      System.out.println("ARE DISCLAIMED. IN NO EVENT SHALL CHRIS RATHMAN OR CONTRIBUTORS BE LIABLE FOR");
      System.out.println("ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES");
      System.out.println("(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;");
      System.out.println("LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND");
      System.out.println("ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT");
      System.out.println("(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS");
      System.out.println("SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.");
      System.out.println("");
   }
}

Chris Rathman / Chris.Rathman@gmail.com