Main Page   Packages   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members  

Utility.java

00001 package de.fub.bytecode.classfile;
00002 
00003 import de.fub.bytecode.Constants;
00004 import de.fub.bytecode.util.ByteSequence;
00005 import java.io.*;
00006 import java.util.Vector;
00007 
00008 /**
00009  * Utility functions that do not really belong to any class in particular.
00010  *
00011  * @version $Id: Utility.java,v 1.1.1.1 2002/01/24 03:44:00 pserver Exp $
00012  * @author  <A HREF="http://www.inf.fu-berlin.de/~dahm">M. Dahm</A>
00013  */
00014 public abstract class Utility implements Constants {
00015   private static int consumed_chars; /* How many chars have been consumed
00016                       * during parsing in signatureToString().
00017                       * Read by methodSignatureToString().
00018                       * Set by side effect,but only internally.
00019                       */
00020   private static boolean wide=false; /* The `WIDE' instruction is used in the
00021                       * byte code to allow 16-bit wide indices
00022                       * for local variables. This opcode
00023                       * precedes an `ILOAD', e.g.. The opcode
00024                       * immediately following takes an extra
00025                       * byte which is combined with the
00026                       * following byte to form a
00027                       * 16-bit value.
00028                       */
00029   /**
00030    * Convert bit field of flags into string such as `static final'.
00031    *
00032    * @param  access_flags Access flags
00033    * @return String representation of flags
00034    */
00035   public static final String accessToString(int access_flags)
00036   {
00037     return accessToString(access_flags, false);
00038   }  
00039   /**
00040    * Convert bit field of flags into string such as `static final'.
00041    *
00042    * Special case: Classes compiled with new compilers and with the
00043    * `ACC_SUPER' flag would be said to be "synchronized". This is
00044    * because SUN used the same value for the flags `ACC_SUPER' and
00045    * `ACC_SYNCHRONIZED'. 
00046    *
00047    * @param  access_flags Access flags
00048    * @param  for_class access flags are for class qualifiers ?
00049    * @return String representation of flags
00050    */
00051   public static final String accessToString(int access_flags, 
00052                         boolean for_class)
00053   {
00054     StringBuffer buf = new StringBuffer();
00055 
00056     int p = 0;
00057     for(int i=0; p < MAX_ACC_FLAG; i++) { // Loop through known flags
00058       p = pow2(i);
00059 
00060       if((access_flags & p) != 0) {
00061     /* Special case: Classes compiled with new compilers and with the
00062      * `ACC_SUPER' flag would be said to be "synchronized". This is
00063      * because SUN used the same value for the flags `ACC_SUPER' and
00064      * `ACC_SYNCHRONIZED'.
00065      */
00066     if(for_class && ((p == ACC_SUPER) || (p == ACC_INTERFACE)))
00067       continue;     
00068 
00069     buf.append(ACCESS_NAMES[i] + " ");
00070       }
00071     }
00072 
00073     return buf.toString().trim();
00074   }  
00075   /**
00076    * Convert (signed) byte to (unsigned) short value, i.e. all negative
00077    * values become positive.
00078    */
00079   private static final short byteToShort(byte b) {
00080     return (b < 0)? (short)(256 + b) : (short)b;
00081   }  
00082   /**
00083    * @return "class" or "interface", depending on the ACC_INTERFACE flag
00084    */
00085   public static final String classOrInterface(int access_flags) {
00086     return ((access_flags & ACC_INTERFACE) != 0)? "interface" : "class";
00087   }  
00088   /**
00089    * @return `flag' with bit `i' set to 0
00090    */
00091   public static final int clearBit(int flag, int i) {
00092     int bit = pow2(i); 
00093     return (flag & bit) == 0? flag : flag ^ bit; 
00094   }  
00095   public static final String codeToString(byte[] code, 
00096                       ConstantPool constant_pool, 
00097                       int index, int length) {
00098     return codeToString(code, constant_pool, index, length, true);
00099   }  
00100   /**
00101    * Disassemble a byte array of JVM byte codes starting from code line 
00102    * `index' and return the dissambled string representation. Decode only
00103    * `num' opcodes (including their operands), use -1 if you want to
00104    * decompile everything.
00105    *
00106    * @param  code byte code array
00107    * @param  constant_pool Array of constants
00108    * @param  index offset in `code' array
00109    * <EM>(number of opcodes, not bytes!)</EM>
00110    * @param  length number of opcodes to decompile, -1 for all
00111    * @param  verbose be verbose, e.g. print constant pool index
00112    * @return String representation of byte codes
00113    */
00114   public static final String codeToString(byte[] code, 
00115                       ConstantPool constant_pool, 
00116                       int index, int length, boolean verbose)
00117   {
00118     StringBuffer buf    = new StringBuffer(code.length * 20); // Should be sufficient
00119     ByteSequence stream = new ByteSequence(code);
00120 
00121     try {
00122       for(int i=0; i < index; i++) // Skip `index' lines of code
00123     codeToString(stream, constant_pool, verbose);
00124 
00125       for(int i=0; stream.available() > 0; i++) {
00126     if((length < 0) || (i < length)) {
00127       String indices = fillup(stream.getIndex() + ":", 6, true, ' ');
00128       buf.append(indices + codeToString(stream, constant_pool, verbose) + '\n');
00129     }
00130       }
00131     } catch(IOException e) {
00132       System.out.println(buf.toString());
00133       e.printStackTrace();
00134       throw new ClassFormatError("Byte code error: " + e);
00135     }
00136 
00137     return buf.toString();
00138   }  
00139   public static final String codeToString(ByteSequence bytes, ConstantPool constant_pool)
00140     throws IOException
00141   {
00142     return codeToString(bytes, constant_pool, true);
00143   }  
00144   /**
00145    * Disassemble a stream of byte codes and return the
00146    * string representation.
00147    *
00148    * @param  stream data input stream
00149    * @param  constant_pool Array of constants
00150    * @param  verbose be verbose, e.g. print constant pool index
00151    * @return String representation of byte code
00152    */
00153   public static final String codeToString(ByteSequence bytes,
00154                       ConstantPool constant_pool, boolean verbose)
00155        throws IOException
00156   {
00157     short        opcode = (short)bytes.readUnsignedByte();
00158     int          default_offset=0, low, high, npairs;
00159     int          index, vindex, constant;
00160     int[]        match, jump_table;
00161     int          no_pad_bytes=0, offset;
00162     StringBuffer buf = new StringBuffer(OPCODE_NAMES[opcode]);
00163 
00164     /* Special case: Skip (0-3) padding bytes, i.e. the
00165      * following bytes are 4-byte-aligned
00166      */
00167     if((opcode == TABLESWITCH) || (opcode == LOOKUPSWITCH)) {
00168       int remainder = bytes.getIndex() % 4;
00169       no_pad_bytes  = (remainder == 0)? 0 : 4 - remainder;
00170 
00171       for(int i=0; i < no_pad_bytes; i++) {
00172     byte b;
00173 
00174     if((b=bytes.readByte()) != 0)
00175       System.err.println("Ooops. Padding byte != 0 " + b);
00176       }
00177 
00178       // Both cases have a field default_offset in common
00179       default_offset = bytes.readInt();
00180     }
00181 
00182     switch(opcode) {
00183       /* Table switch has variable length arguments.
00184        */
00185     case TABLESWITCH:
00186       low  = bytes.readInt();
00187       high = bytes.readInt();
00188 
00189       offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
00190       default_offset += offset;
00191 
00192       buf.append("\tdefault = " + default_offset + ", low = " + low + 
00193          ", high = " + high + "(");
00194 
00195       jump_table = new int[high - low + 1];
00196       for(int i=0; i < jump_table.length; i++) {
00197     jump_table[i] = offset + bytes.readInt();
00198     buf.append(jump_table[i]);
00199 
00200     if(i < jump_table.length - 1)
00201       buf.append(", ");
00202       }
00203       buf.append(")");
00204 
00205       break;
00206 
00207       /* Lookup switch has variable length arguments.
00208        */
00209     case LOOKUPSWITCH: {
00210 
00211       npairs = bytes.readInt();
00212       offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
00213       
00214       match      = new int[npairs];
00215       jump_table = new int[npairs];
00216       default_offset += offset;
00217 
00218       buf.append("\tdefault = " + default_offset + ", npairs = " + npairs +
00219          " (");
00220 
00221       for(int i=0; i < npairs; i++) {
00222     match[i]      = bytes.readInt();
00223 
00224     jump_table[i] = offset + bytes.readInt();
00225 
00226     buf.append("(" + match[i] + ", " + jump_table[i] + ")");
00227 
00228     if(i < npairs - 1)
00229       buf.append(", ");
00230       }
00231       buf.append(")");
00232     }
00233     break;
00234 
00235     /* Two address bytes + offset from start of byte stream form the
00236      * jump target
00237      */
00238     case GOTO:      case IFEQ:      case IFGE:      case IFGT:
00239     case IFLE:      case IFLT:      case JSR: case IFNE:
00240     case IFNONNULL: case IFNULL:    case IF_ACMPEQ:
00241     case IF_ACMPNE: case IF_ICMPEQ: case IF_ICMPGE: case IF_ICMPGT:
00242     case IF_ICMPLE: case IF_ICMPLT: case IF_ICMPNE:
00243       buf.append("\t\t#" + ((bytes.getIndex() - 1) + bytes.readShort()));
00244       break;
00245       
00246       /* 32-bit wide jumps
00247        */
00248     case GOTO_W: case JSR_W:
00249       buf.append("\t\t#" + ((bytes.getIndex() - 1) + bytes.readInt()));
00250       break;
00251 
00252       /* Index byte references local variable (register)
00253        */
00254     case ALOAD:  case ASTORE: case DLOAD:  case DSTORE: case FLOAD:
00255     case FSTORE: case ILOAD:  case ISTORE: case LLOAD:  case LSTORE:
00256     case RET: 
00257       if(wide) {
00258     vindex = bytes.readUnsignedShort();
00259     wide=false; // Clear flag
00260       }
00261       else
00262     vindex = bytes.readUnsignedByte();
00263 
00264       buf.append("\t\t%" + vindex);
00265       break;
00266 
00267       /*
00268        * Remember wide byte which is used to form a 16-bit address in the
00269        * following instruction. Relies on that the method is called again with
00270        * the following opcode.
00271        */
00272     case WIDE:
00273       wide      = true;
00274       buf.append("\t(wide)");
00275       break;
00276 
00277       /* Array of basic type.
00278        */
00279     case NEWARRAY:
00280       buf.append("\t\t<" + TYPE_NAMES[bytes.readByte()] + ">");
00281       break;
00282 
00283       /* Access object/class fields.
00284        */
00285     case GETFIELD: case GETSTATIC: case PUTFIELD: case PUTSTATIC:
00286       index = bytes.readUnsignedShort();
00287       buf.append("\t\t" +
00288          constant_pool.constantToString(index, CONSTANT_Fieldref) +
00289          (verbose? " (" + index + ")" : ""));
00290       break;
00291       
00292       /* Operands are references to classes in constant pool
00293        */
00294     case NEW:
00295     case CHECKCAST: case INSTANCEOF:
00296       index = bytes.readUnsignedShort();
00297       buf.append("\t\t<" + constant_pool.constantToString(index,
00298                             CONSTANT_Class) +
00299          ">" + (verbose? " (" + index + ")" : ""));
00300       break;
00301 
00302       /* Operands are references to methods in constant pool
00303        */
00304     case INVOKESPECIAL: case INVOKESTATIC: case INVOKEVIRTUAL:
00305       index = bytes.readUnsignedShort();
00306       buf.append("\t" + constant_pool.constantToString(index,
00307                                CONSTANT_Methodref) +
00308          (verbose? " (" + index + ")" : ""));
00309       break;
00310 
00311     case INVOKEINTERFACE:
00312       index = bytes.readUnsignedShort();
00313       int nargs = bytes.readUnsignedByte(); // historical, redundant
00314       buf.append("\t" + 
00315          constant_pool.constantToString(index,
00316                         CONSTANT_InterfaceMethodref) +
00317          (verbose? " (" + index + ")\t" : "") + nargs + "\t" + 
00318          bytes.readUnsignedByte()); // Last byte is a reserved space
00319       break;
00320     
00321       /* Operands are references to items in constant pool
00322        */
00323     case LDC_W: case LDC2_W:
00324       index = bytes.readUnsignedShort();
00325 
00326       buf.append("\t\t" + constant_pool.constantToString
00327          (index, constant_pool.getConstant(index).getTag()) +
00328          (verbose? " (" + index + ")" : ""));
00329       break;
00330 
00331     case LDC:
00332       index = bytes.readUnsignedByte();
00333 
00334       buf.append("\t\t" + 
00335          constant_pool.constantToString
00336          (index, constant_pool.getConstant(index).getTag()) +
00337          (verbose? " (" + index + ")" : ""));
00338       break;
00339     
00340       /* Array of references.
00341        */
00342     case ANEWARRAY:
00343       index = bytes.readUnsignedShort();
00344       
00345       buf.append("\t\t<" + compactClassName(constant_pool.getConstantString
00346                       (index, CONSTANT_Class), false) +
00347          ">" + (verbose? " (" + index + ")": ""));
00348       break;
00349     
00350       /* Multidimensional array of references.
00351        */
00352     case MULTIANEWARRAY: {
00353       index          = bytes.readUnsignedShort();
00354       int dimensions = bytes.readUnsignedByte();
00355 
00356       buf.append("\t<" + compactClassName(constant_pool.getConstantString
00357                       (index, CONSTANT_Class), false) +
00358          ">\t" + dimensions + (verbose? " (" + index + ")" : ""));
00359     }
00360     break;
00361 
00362     /* Increment local variable.
00363      */
00364     case IINC:
00365       if(wide) {
00366     vindex   = bytes.readUnsignedShort();
00367     constant = bytes.readShort();
00368     wide     = false;
00369       }
00370       else {
00371     vindex   = bytes.readUnsignedByte();
00372     constant = bytes.readByte();
00373       }
00374       buf.append("\t\t%" + vindex + "\t" + constant);
00375       break;
00376 
00377     default:
00378       if(NO_OF_OPERANDS[opcode] > 0) {
00379     for(int i=0; i < TYPE_OF_OPERANDS[opcode].length; i++) {
00380       buf.append("\t\t");
00381       switch(TYPE_OF_OPERANDS[opcode][i]) {
00382       case T_BYTE:  buf.append(bytes.readByte()); break;
00383       case T_SHORT: buf.append(bytes.readShort());       break;
00384       case T_INT:   buf.append(bytes.readInt());         break;
00385                           
00386       default: // Never reached
00387         System.err.println("Unreachable default case reached!");
00388         System.exit(-1);
00389       }
00390     }
00391       }
00392     }
00393 
00394     return buf.toString();
00395   }  
00396   /**
00397    * Shorten long class names, <em>java/lang/String</em> becomes 
00398    * <em>String</em>.
00399    *
00400    * @param str The long class name
00401    * @return Compacted class name
00402    */
00403   public static final String compactClassName(String str) {
00404     return compactClassName(str, true);
00405   }  
00406   /**
00407    * Shorten long class name <em>str</em>, i.e. chop off the <em>prefix</em>,
00408    * if the
00409    * class name starts with this string and the flag <em>chopit</em> is true.
00410    * Slashes <em>/</em> are converted to dots <em>.</em>.
00411    *
00412    * @param str The long class name
00413    * @param prefix The prefix the get rid off
00414    * @param chopit Flag that determines whether chopping is executed or not
00415    * @return Compacted class name
00416    */
00417   public static final String compactClassName(String str, 
00418                           String prefix,
00419                           boolean chopit)
00420   {
00421     int len = prefix.length();
00422 
00423     str = str.replace('/', '.'); // Is `/' on all systems, even DOS
00424 
00425     if(chopit) {
00426       // If string starts with `prefix' and contains no further dots
00427       if(str.startsWith(prefix) &&
00428      (str.substring(len).indexOf('.') == -1))
00429     str = str.substring(len);
00430     }
00431     
00432     return str;
00433   }  
00434   /**
00435    * Shorten long class names, <em>java/lang/String</em> becomes 
00436    * <em>java.lang.String</em>,
00437    * e.g.. If <em>chopit</em> is <em>true</em> the prefix <em>java.lang</em>
00438    * is also removed.
00439    *
00440    * @param str The long class name
00441    * @param chopit Flag that determines whether chopping is executed or not
00442    * @return Compacted class name
00443    */
00444   public static final String compactClassName(String str, boolean chopit) {
00445     return compactClassName(str, "java.lang.", chopit);
00446   }  
00447   static final boolean equals(byte[] a, byte[] b) {
00448     int size;
00449 
00450     if((size=a.length) != b.length)
00451       return false;
00452 
00453     for(int i=0; i < size; i++)
00454       if(a[i] != b[i])
00455     return false;
00456 
00457     return true;
00458   }  
00459   /**
00460    * Fillup char with up to length characters with char `fill' and justify it left or right.
00461    *
00462    * @param str string to format
00463    * @param length length of desired string
00464    * @param left format left or right
00465    * @param fill fill character
00466    * @return formatted string
00467    */
00468   public static final String fillup(String str, int length, boolean left_justify, char fill) {
00469     int    len = length - str.length();
00470     char[] buf = new char[(len < 0)? 0 : len];
00471 
00472     for(int j=0; j < buf.length; j++)
00473       buf[j] = fill;
00474 
00475     if(left_justify)
00476       return str + new String(buf);    
00477     else
00478       return new String(buf) + str;
00479   }  
00480   /**
00481    * Return a string for an integer justified left or right and filled up with
00482    * `fill' characters if necessary.
00483    *
00484    * @param i integer to format
00485    * @param length length of desired string
00486    * @param left format left or right
00487    * @param fill fill character
00488    * @return formatted int
00489    */
00490   public static final String format(int i, int length, boolean left_justify, char fill) {
00491     return fillup(Integer.toString(i), length, left_justify, fill);
00492   }  
00493   private static final boolean is_digit(char ch) {
00494     return (ch >= '0') && (ch <= '9');
00495   }  
00496   private static final boolean is_space(char ch) {
00497     return (ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n');
00498   }  
00499   /**
00500    * @return true, if bit `i' in `flag' is set
00501    */
00502   public static final boolean isSet(int flag, int i) {
00503     return (flag & pow2(i)) != 0;
00504   }  
00505   /**
00506    * @param  signature    Method signature
00507    * @return Array of argument types
00508    * @throw  ClassFormatError  
00509    */
00510   public static final String[] methodSignatureArgumentTypes(String signature)
00511     throws ClassFormatError 
00512   {
00513     return methodSignatureArgumentTypes(signature, true);
00514   }  
00515   /**
00516    * @param  signature    Method signature
00517    * @param chopit Shorten class names ?
00518    * @return Array of argument types
00519    * @throw  ClassFormatError  
00520    */
00521   public static final String[] methodSignatureArgumentTypes(String signature,
00522                                 boolean chopit)
00523     throws ClassFormatError
00524   {
00525     Vector   vec = new Vector();
00526     int      index;
00527     String[] types;
00528 
00529     try { // Read all declarations between for `(' and `)'
00530       if(signature.charAt(0) != '(')
00531     throw new ClassFormatError("Invalid method signature: " + signature);
00532 
00533       index = 1; // current string position
00534 
00535       while(signature.charAt(index) != ')') {
00536     vec.addElement(signatureToString(signature.substring(index), chopit));
00537     index += consumed_chars; // update position
00538       }
00539     } catch(StringIndexOutOfBoundsException e) { // Should never occur
00540       throw new ClassFormatError("Invalid method signature: " + signature);
00541     }
00542     
00543     types = new String[vec.size()];
00544     vec.copyInto(types);
00545     return types;
00546   }  
00547   /**
00548    * @param  signature    Method signature
00549    * @return return type of method
00550    * @throw  ClassFormatError  
00551    */
00552   public static final String methodSignatureReturnType(String signature)
00553        throws ClassFormatError 
00554   {
00555     return methodSignatureReturnType(signature, true);
00556   }  
00557   /**
00558    * @param  signature    Method signature
00559    * @param chopit Shorten class names ?
00560    * @return return type of method
00561    * @throw  ClassFormatError  
00562    */
00563   public static final String methodSignatureReturnType(String signature,
00564                                boolean chopit)
00565        throws ClassFormatError
00566   {
00567     int    index;
00568     String type;
00569 
00570     try {
00571       // Read return type after `)'
00572       index = signature.lastIndexOf(')') + 1; 
00573       type = signatureToString(signature.substring(index), chopit);
00574     } catch(StringIndexOutOfBoundsException e) { // Should never occur
00575       throw new ClassFormatError("Invalid method signature: " + signature);
00576     }
00577 
00578     return type;
00579   }  
00580   /**
00581    * Converts method signature to string with all class names compacted.
00582    *
00583    * @param signature to convert
00584    * @param name of method
00585    * @param access flags of method
00586    * @return Human readable signature
00587    */
00588   public static final String methodSignatureToString(String signature,
00589                              String name,
00590                              String access) {
00591     return methodSignatureToString(signature, name, access, true);
00592   }  
00593   /**
00594    * A return­type signature represents the return value from a method.
00595    * It is a series of bytes in the following grammar:
00596    *
00597    * <return_signature> ::= <field_type> | V
00598    *
00599    * The character V indicates that the method returns no value. Otherwise, the
00600    * signature indicates the type of the return value.
00601    * An argument signature represents an argument passed to a method:
00602    *
00603    * <argument_signature> ::= <field_type>
00604    *
00605    * A method signature represents the arguments that the method expects, and
00606    * the value that it returns.
00607    * <method_signature> ::= (<arguments_signature>) <return_signature>
00608    * <arguments_signature>::= <argument_signature>*
00609    *
00610    * This method converts such a string into a Java type declaration like
00611    * `void main(String[])' and throws a `ClassFormatError' when the parsed 
00612    * type is invalid.
00613    *
00614    * @param  signature    Method signature
00615    * @param  name         Method name
00616    * @param  access       Method access rights
00617    * @return Java type declaration
00618    * @throw  ClassFormatError  
00619    */
00620   public static final String methodSignatureToString(String signature,
00621                              String name,
00622                              String access,
00623                              boolean chopit)
00624        throws ClassFormatError
00625   {
00626     StringBuffer buf = new StringBuffer("(");
00627     String       type;
00628     int          index;
00629 
00630     try { // Read all declarations between for `(' and `)'
00631       if(signature.charAt(0) != '(')
00632     throw new ClassFormatError("Invalid method signature: " + signature);
00633 
00634       index = 1; // current string position
00635 
00636       while(signature.charAt(index) != ')') {
00637     buf.append(signatureToString(signature.substring(index), chopit) +
00638            ", ");
00639     index += consumed_chars; // update position
00640       }
00641 
00642       index++; // update position
00643 
00644       // Read return type after `)'
00645       type = signatureToString(signature.substring(index), chopit);
00646 
00647     } catch(StringIndexOutOfBoundsException e) { // Should never occur
00648       throw new ClassFormatError("Invalid method signature: " + signature);
00649     }
00650 
00651     if(buf.length() > 1)               // Tack off the extra ", "
00652       buf.setLength(buf.length() - 2);
00653 
00654     buf.append(")");
00655 
00656     return access + ((access.length() > 0)?" " : "") + // May be an empty string
00657       type + " " + name + buf.toString();
00658   }  
00659   /**
00660    * Converts string containing the method return and argument types 
00661    * to a byte code method signature.
00662    *
00663    * @param  ret Return type of method
00664    * @param  argv Types of method arguments
00665    * @return Byte code representation of method type
00666    */
00667   public final static String methodTypeToSignature(String ret, String[] argv)
00668     throws ClassFormatError
00669   {
00670     StringBuffer buf = new StringBuffer("(");
00671     String       str;
00672 
00673     if(argv != null)
00674       for(int i=0; i < argv.length; i++) {
00675     str = typeToSignature(argv[i]);
00676 
00677     if(str.endsWith("V")) // void can't be a method argument
00678       throw new ClassFormatError("Invalid type: " + argv[i]);
00679 
00680     buf.append(str);
00681       }
00682 
00683     str = typeToSignature(ret);
00684 
00685     buf.append(")" + str);
00686 
00687     return buf.toString();
00688   }  
00689   // Guess what this does
00690   private static final int pow2(int n) {
00691     return 1 << n;
00692   }  
00693   public static final String printArray(Object[] obj) {
00694     return printArray(obj, true);
00695   }  
00696   public static final String printArray(Object[] obj, boolean braces) {
00697     if(obj == null)
00698       return null;
00699 
00700     StringBuffer buf = new StringBuffer();
00701     if(braces)
00702       buf.append('{');
00703 
00704     for(int i=0; i < obj.length; i++) {
00705       if(obj[i] != null)
00706     buf.append(obj[i].toString());
00707       else
00708     buf.append("null");
00709 
00710       if(i < obj.length - 1)
00711     buf.append(", ");
00712     }
00713 
00714     if(braces)
00715       buf.append('}');
00716 
00717     return buf.toString();
00718   }  
00719   public static final void printArray(PrintStream out, Object[] obj) {
00720     out.println(printArray(obj, true));
00721   }  
00722   public static final void printArray(PrintWriter out, Object[] obj) {
00723     out.println(printArray(obj, true));
00724   }  
00725   /**
00726    * Replace all occurences of <em>old</em> in <em>str</em> with <em>new</em>.
00727    *
00728    * @param str String to permute
00729    * @param old String to be replaced
00730    * @param new Replacement string
00731    * @return new String object
00732    */
00733   public static final String replace(String str, String old, String new_) {
00734     int          index, old_index;
00735     StringBuffer buf = new StringBuffer();
00736 
00737     try {
00738       if((index = str.indexOf(old)) != -1) { // `old' found in str
00739     old_index = 0;                       // String start offset
00740       
00741     // While we have something to replace
00742     while((index = str.indexOf(old, old_index)) != -1) {
00743       buf.append(str.substring(old_index, index)); // append prefix
00744       buf.append(new_);                            // append replacement
00745           
00746       old_index = index + old.length(); // Skip `old'.length chars
00747     }
00748 
00749     buf.append(str.substring(old_index)); // append rest of string
00750     str = buf.toString();   
00751       }
00752     } catch(StringIndexOutOfBoundsException e) { // Should not occur
00753       System.err.println(e);
00754     }
00755 
00756     return str;
00757   }  
00758   /**
00759    * @return `flag' with bit `i' set to 1
00760    */
00761   public static final int setBit(int flag, int i) {
00762     return flag | pow2(i); 
00763   }  
00764   /**
00765    * Converts signature to string with all class names compacted.
00766    *
00767    * @param signature to convert
00768    * @return Human readable signature
00769    */
00770   public static final String signatureToString(String signature) {
00771     return signatureToString(signature, true);
00772   }  
00773   /**
00774    * The field signature represents the value of an argument to a function or 
00775    * the value of a variable. It is a series of bytes generated by the 
00776    * following grammar:
00777    *
00778    * <PRE>
00779    * <field_signature> ::= <field_type>
00780    * <field_type>      ::= <base_type>|<object_type>|<array_type>
00781    * <base_type>       ::= B|C|D|F|I|J|S|Z
00782    * <object_type>     ::= L<fullclassname>;
00783    * <array_type>      ::= [<field_type>
00784    *
00785    * The meaning of the base types is as follows:
00786    * B byte signed byte
00787    * C char character
00788    * D double double precision IEEE float
00789    * F float single precision IEEE float
00790    * I int integer
00791    * J long long integer
00792    * L<fullclassname>; ... an object of the given class
00793    * S short signed short
00794    * Z boolean true or false
00795    * [<field sig> ... array
00796    * </PRE>
00797    *
00798    * This method converts this string into a Java type declaration such as
00799    * `String[]' and throws a `ClassFormatError' when the parsed type is 
00800    * invalid.
00801    *
00802    * @param  signature    Class signature
00803    * @return Java type declaration
00804    * @throw  ClassFormatError
00805    */
00806   public static final String signatureToString(String signature,
00807                            boolean chopit)
00808        throws ClassFormatError
00809   {
00810     consumed_chars = 1; // This is the default, read just one char like `B'
00811 
00812     try {
00813       switch(signature.charAt(0)) {
00814       case 'B' : return "byte";
00815       case 'C' : return "char";
00816       case 'D' : return "double";
00817       case 'F' : return "float";
00818       case 'I' : return "int";
00819       case 'J' : return "long";
00820 
00821       case 'L' : { // Full class name
00822     int    index = signature.indexOf(';'); // Look for closing `;'
00823 
00824     if(index < 0)
00825       throw new ClassFormatError("Invalid signature: " + signature);
00826     
00827     consumed_chars = index + 1; // "Lblabla;" `L' and `;' are removed
00828 
00829     return compactClassName(signature.substring(1, index), chopit);
00830       }
00831 
00832       case 'S' : return "short";
00833       case 'Z' : return "boolean";
00834 
00835       case '[' : { // Array declaration
00836     int          n;
00837     StringBuffer buf, brackets;
00838     String       type;
00839     char         ch;
00840     int          consumed_chars; // Shadows global var
00841 
00842     brackets = new StringBuffer(); // Accumulate []'s
00843 
00844     // Count opening brackets and look for optional size argument
00845     for(n=0; signature.charAt(n) == '['; n++)
00846       brackets.append("[]");
00847 
00848     consumed_chars = n; // Remember value
00849 
00850     // The rest of the string denotes a `<field_type>'
00851     type = signatureToString(signature.substring(n), chopit);
00852     
00853     Utility.consumed_chars += consumed_chars;
00854     return type + brackets.toString();
00855       }
00856 
00857       case 'V' : return "void";
00858 
00859       default  : throw new ClassFormatError("Invalid signature: `" +
00860                         signature + "'");
00861       }
00862     } catch(StringIndexOutOfBoundsException e) { // Should never occur
00863       throw new ClassFormatError("Invalid signature: " + e + ":" + signature);
00864     }
00865   }  
00866   /*
00867    * @return bytes as hexidecimal string, e.g. 00 FA 12 ...
00868    */
00869   public static final String toHexString(byte[] bytes) {
00870     StringBuffer buf = new StringBuffer();
00871 
00872     for(int i=0; i < bytes.length; i++) {
00873       short  b   = byteToShort(bytes[i]);
00874       String hex = Integer.toString(b, 0x10);
00875 
00876       if(b < 0x10) // just one digit, prepend '0'
00877     buf.append('0');
00878 
00879       buf.append(hex);
00880 
00881       if(i < bytes.length - 1)
00882     buf.append(' ');
00883     }
00884 
00885     return buf.toString();
00886   }  
00887   /**
00888    * Return type of method signature as a byte value as defined in <em>Constants</em>
00889    *
00890    * @param  signature in format described above
00891    * @return type of method signature
00892    * @see    Constants
00893    */
00894   public static final byte typeOfMethodSignature(String signature)
00895     throws ClassFormatError
00896   {
00897     int index;
00898 
00899     try {
00900       if(signature.charAt(0) != '(')
00901     throw new ClassFormatError("Invalid method signature: " + signature);
00902 
00903       index = signature.lastIndexOf(')') + 1;
00904       return typeOfSignature(signature.substring(index));
00905     } catch(StringIndexOutOfBoundsException e) {
00906       throw new ClassFormatError("Invalid method signature: " + signature);
00907     }
00908   }  
00909   /**
00910    * Return type of signature as a byte value as defined in <em>Constants</em>
00911    *
00912    * @param  signature in format described above
00913    * @return type of signature
00914    * @see    Constants
00915    */
00916   public static final byte typeOfSignature(String signature)
00917     throws ClassFormatError
00918   {
00919     try {
00920       switch(signature.charAt(0)) {
00921       case 'B' : return T_BYTE;
00922       case 'C' : return T_CHAR;
00923       case 'D' : return T_DOUBLE;
00924       case 'F' : return T_FLOAT;
00925       case 'I' : return T_INT;
00926       case 'J' : return T_LONG;
00927       case 'L' : return T_REFERENCE;
00928       case '[' : return T_ARRAY;
00929       case 'V' : return T_VOID;
00930       case 'Z' : return T_BOOLEAN;
00931       case 'S' : return T_SHORT;
00932       default:  
00933     throw new ClassFormatError("Invalid method signature: " + signature);
00934       }
00935     } catch(StringIndexOutOfBoundsException e) {
00936       throw new ClassFormatError("Invalid method signature: " + signature);
00937     }
00938   }  
00939   /**
00940    * Gets Java conformant type like `String[]' and returns a string containing
00941    * the type in byte code format, i.e. String[] becomes [Ljava/lang/String;
00942    *
00943    * @param  str Type string like int[][]
00944    * @return Byte code representation of type like [[I
00945    */
00946   public final static String typeToSignature(String str)
00947     throws ClassFormatError
00948   {
00949     int    index = str.indexOf('[');
00950     String type, array=null, code=null;
00951 
00952     try {
00953       if(index > -1) { // Is an array?
00954     type  = str.substring(0, index);
00955     array = str.substring(index);
00956       }
00957       else
00958     type = str;
00959 
00960       if(array == null) // Not an array
00961     array = "";
00962       else {
00963     StringBuffer buf = new StringBuffer();
00964     char ch, lastchar='X';
00965 
00966     for(int i=0; i < array.length(); i++) {
00967       ch = array.charAt(i);
00968 
00969       if(ch == '[') 
00970         buf.append('[');
00971       else if((ch == ']') || is_space(ch)) // Ignore
00972         ;
00973       else if(is_digit(ch)) {
00974         if((lastchar == '[') || is_digit(lastchar)) // Then it's OK
00975           buf.append(ch);
00976         else
00977           throw new ClassFormatError("Invalid type: " + str);
00978       }
00979       else
00980         throw new ClassFormatError("Invalid type: " + str);
00981 
00982       lastchar = ch;
00983     }
00984 
00985     array = buf.toString();
00986       }
00987     } catch(StringIndexOutOfBoundsException e) {
00988       throw new ClassFormatError("Invalid type: " + str);
00989     }
00990 
00991     int i;
00992     for(i=T_BOOLEAN; i <= T_VOID; i++) {
00993       if(type.equals(TYPE_NAMES[i])) {
00994     code = SHORT_TYPE_NAMES[i];
00995     break;
00996       }
00997     }
00998 
00999     if(i == T_VOID) {
01000       if(array.startsWith("[")) // Array of void !?
01001     throw new ClassFormatError("Invalid type: " + str);
01002     }
01003     else if(i > T_VOID) // Interpret as class name
01004       code = "L" + type.replace('.', '/') + ";";
01005 
01006     return array + code;
01007   }  
01008 }

Generated at Thu Feb 7 06:59:05 2002 for Bandera by doxygen1.2.10 written by Dimitri van Heesch, © 1997-2001