1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 /*
   5  * Licensed to the Apache Software Foundation (ASF) under one or more
   6  * contributor license agreements.  See the NOTICE file distributed with
   7  * this work for additional information regarding copyright ownership.
   8  * The ASF licenses this file to You under the Apache License, Version 2.0
   9  * (the "License"); you may not use this file except in compliance with
  10  * the License.  You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 package com.sun.org.apache.bcel.internal.classfile;
  21 
  22 import java.io.ByteArrayInputStream;
  23 import java.io.ByteArrayOutputStream;
  24 import java.io.CharArrayReader;
  25 import java.io.CharArrayWriter;
  26 import java.io.FilterReader;
  27 import java.io.FilterWriter;
  28 import java.io.IOException;
  29 import java.io.PrintStream;
  30 import java.io.PrintWriter;
  31 import java.io.Reader;
  32 import java.io.Writer;
  33 import java.util.ArrayList;
  34 import java.util.List;
  35 import java.util.Locale;
  36 import java.util.zip.GZIPInputStream;
  37 import java.util.zip.GZIPOutputStream;
  38 
  39 import com.sun.org.apache.bcel.internal.Const;
  40 import com.sun.org.apache.bcel.internal.util.ByteSequence;
  41 
  42 /**
  43  * Utility functions that do not really belong to any class in particular.
  44  *
  45  * @version $Id: Utility.java 1751107 2016-07-03 02:41:18Z dbrosius $
  46  * @LastModified: Oct 2017
  47  */
  48 // @since 6.0 methods are no longer final
  49 public abstract class Utility {
  50 
  51     private static int unwrap(final ThreadLocal<Integer> tl) {
  52         return tl.get().intValue();
  53     }
  54 
  55     private static void wrap(final ThreadLocal<Integer> tl, final int value) {
  56         tl.set(Integer.valueOf(value));
  57     }
  58 
  59     private static ThreadLocal<Integer> consumed_chars = new ThreadLocal<Integer>() {
  60 
  61         @Override
  62         protected Integer initialValue() {
  63             return Integer.valueOf(0);
  64         }
  65     };/* How many chars have been consumed
  66      * during parsing in signatureToString().
  67      * Read by methodSignatureToString().
  68      * Set by side effect,but only internally.
  69      */
  70 
  71     private static boolean wide = false; /* The `WIDE' instruction is used in the
  72      * byte code to allow 16-bit wide indices
  73      * for local variables. This opcode
  74      * precedes an `ILOAD', e.g.. The opcode
  75      * immediately following takes an extra
  76      * byte which is combined with the
  77      * following byte to form a
  78      * 16-bit value.
  79      */
  80 
  81 
  82     /**
  83      * Convert bit field of flags into string such as `static final'.
  84      *
  85      * @param access_flags Access flags
  86      * @return String representation of flags
  87      */
  88     public static String accessToString(final int access_flags) {
  89         return accessToString(access_flags, false);
  90     }
  91 
  92     /**
  93      * Convert bit field of flags into string such as `static final'.
  94      *
  95      * Special case: Classes compiled with new compilers and with the
  96      * `ACC_SUPER' flag would be said to be "synchronized". This is because SUN
  97      * used the same value for the flags `ACC_SUPER' and `ACC_SYNCHRONIZED'.
  98      *
  99      * @param access_flags Access flags
 100      * @param for_class access flags are for class qualifiers ?
 101      * @return String representation of flags
 102      */
 103     public static String accessToString(final int access_flags, final boolean for_class) {
 104         final StringBuilder buf = new StringBuilder();
 105         int p = 0;
 106         for (int i = 0; p < Const.MAX_ACC_FLAG; i++) { // Loop through known flags
 107             p = pow2(i);
 108             if ((access_flags & p) != 0) {
 109                 /* Special case: Classes compiled with new compilers and with the
 110                  * `ACC_SUPER' flag would be said to be "synchronized". This is
 111                  * because SUN used the same value for the flags `ACC_SUPER' and
 112                  * `ACC_SYNCHRONIZED'.
 113                  */
 114                 if (for_class && ((p == Const.ACC_SUPER) || (p == Const.ACC_INTERFACE))) {
 115                     continue;
 116                 }
 117                 buf.append(Const.getAccessName(i)).append(" ");
 118             }
 119         }
 120         return buf.toString().trim();
 121     }
 122 
 123     /**
 124      * @param access_flags the class flags
 125      *
 126      * @return "class" or "interface", depending on the ACC_INTERFACE flag
 127      */
 128     public static String classOrInterface(final int access_flags) {
 129         return ((access_flags & Const.ACC_INTERFACE) != 0) ? "interface" : "class";
 130     }
 131 
 132     /**
 133      * Disassemble a byte array of JVM byte codes starting from code line
 134      * `index' and return the disassembled string representation. Decode only
 135      * `num' opcodes (including their operands), use -1 if you want to decompile
 136      * everything.
 137      *
 138      * @param code byte code array
 139      * @param constant_pool Array of constants
 140      * @param index offset in `code' array
 141      * <EM>(number of opcodes, not bytes!)</EM>
 142      * @param length number of opcodes to decompile, -1 for all
 143      * @param verbose be verbose, e.g. print constant pool index
 144      * @return String representation of byte codes
 145      */
 146     public static String codeToString(final byte[] code, final ConstantPool constant_pool,
 147             final int index, final int length, final boolean verbose) {
 148         // Should be sufficient // CHECKSTYLE IGNORE MagicNumber
 149         final StringBuilder buf = new StringBuilder(code.length * 20);
 150         try (ByteSequence stream = new ByteSequence(code)) {
 151             for (int i = 0; i < index; i++) {
 152                 codeToString(stream, constant_pool, verbose);
 153             }
 154             for (int i = 0; stream.available() > 0; i++) {
 155                 if ((length < 0) || (i < length)) {
 156                     final String indices = fillup(stream.getIndex() + ":", 6, true, ' ');
 157                     buf.append(indices)
 158                             .append(codeToString(stream, constant_pool, verbose))
 159                             .append('\n');
 160                 }
 161             }
 162         } catch (final IOException e) {
 163             throw new ClassFormatException("Byte code error: " + buf.toString(), e);
 164         }
 165         return buf.toString();
 166     }
 167 
 168     public static String codeToString(final byte[] code, final ConstantPool constant_pool,
 169             final int index, final int length) {
 170         return codeToString(code, constant_pool, index, length, true);
 171     }
 172 
 173     /**
 174      * Disassemble a stream of byte codes and return the string representation.
 175      *
 176      * @param bytes stream of bytes
 177      * @param constant_pool Array of constants
 178      * @param verbose be verbose, e.g. print constant pool index
 179      * @return String representation of byte code
 180      *
 181      * @throws IOException if a failure from reading from the bytes argument
 182      * occurs
 183      */
 184     @SuppressWarnings("fallthrough") // by design for case Const.INSTANCEOF
 185     public static String codeToString(final ByteSequence bytes, final ConstantPool constant_pool,
 186             final boolean verbose) throws IOException {
 187         final short opcode = (short) bytes.readUnsignedByte();
 188         int default_offset = 0;
 189         int low;
 190         int high;
 191         int npairs;
 192         int index;
 193         int vindex;
 194         int constant;
 195         int[] match;
 196         int[] jump_table;
 197         int no_pad_bytes = 0;
 198         int offset;
 199         final StringBuilder buf = new StringBuilder(Const.getOpcodeName(opcode));
 200         /* Special case: Skip (0-3) padding bytes, i.e., the
 201          * following bytes are 4-byte-aligned
 202          */
 203         if ((opcode == Const.TABLESWITCH) || (opcode == Const.LOOKUPSWITCH)) {
 204             final int remainder = bytes.getIndex() % 4;
 205             no_pad_bytes = (remainder == 0) ? 0 : 4 - remainder;
 206             for (int i = 0; i < no_pad_bytes; i++) {
 207                 byte b;
 208                 if ((b = bytes.readByte()) != 0) {
 209                     System.err.println("Warning: Padding byte != 0 in "
 210                             + Const.getOpcodeName(opcode) + ":" + b);
 211                 }
 212             }
 213             // Both cases have a field default_offset in common
 214             default_offset = bytes.readInt();
 215         }
 216         switch (opcode) {
 217             /* Table switch has variable length arguments.
 218              */
 219             case Const.TABLESWITCH:
 220                 low = bytes.readInt();
 221                 high = bytes.readInt();
 222                 offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
 223                 default_offset += offset;
 224                 buf.append("\tdefault = ").append(default_offset).append(", low = ").append(low)
 225                         .append(", high = ").append(high).append("(");
 226                 jump_table = new int[high - low + 1];
 227                 for (int i = 0; i < jump_table.length; i++) {
 228                     jump_table[i] = offset + bytes.readInt();
 229                     buf.append(jump_table[i]);
 230                     if (i < jump_table.length - 1) {
 231                         buf.append(", ");
 232                     }
 233                 }
 234                 buf.append(")");
 235                 break;
 236             /* Lookup switch has variable length arguments.
 237              */
 238             case Const.LOOKUPSWITCH: {
 239                 npairs = bytes.readInt();
 240                 offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
 241                 match = new int[npairs];
 242                 jump_table = new int[npairs];
 243                 default_offset += offset;
 244                 buf.append("\tdefault = ").append(default_offset).append(", npairs = ").append(
 245                         npairs).append(" (");
 246                 for (int i = 0; i < npairs; i++) {
 247                     match[i] = bytes.readInt();
 248                     jump_table[i] = offset + bytes.readInt();
 249                     buf.append("(").append(match[i]).append(", ").append(jump_table[i]).append(")");
 250                     if (i < npairs - 1) {
 251                         buf.append(", ");
 252                     }
 253                 }
 254                 buf.append(")");
 255             }
 256             break;
 257             /* Two address bytes + offset from start of byte stream form the
 258              * jump target
 259              */
 260             case Const.GOTO:
 261             case Const.IFEQ:
 262             case Const.IFGE:
 263             case Const.IFGT:
 264             case Const.IFLE:
 265             case Const.IFLT:
 266             case Const.JSR:
 267             case Const.IFNE:
 268             case Const.IFNONNULL:
 269             case Const.IFNULL:
 270             case Const.IF_ACMPEQ:
 271             case Const.IF_ACMPNE:
 272             case Const.IF_ICMPEQ:
 273             case Const.IF_ICMPGE:
 274             case Const.IF_ICMPGT:
 275             case Const.IF_ICMPLE:
 276             case Const.IF_ICMPLT:
 277             case Const.IF_ICMPNE:
 278                 buf.append("\t\t#").append((bytes.getIndex() - 1) + bytes.readShort());
 279                 break;
 280             /* 32-bit wide jumps
 281              */
 282             case Const.GOTO_W:
 283             case Const.JSR_W:
 284                 buf.append("\t\t#").append((bytes.getIndex() - 1) + bytes.readInt());
 285                 break;
 286             /* Index byte references local variable (register)
 287              */
 288             case Const.ALOAD:
 289             case Const.ASTORE:
 290             case Const.DLOAD:
 291             case Const.DSTORE:
 292             case Const.FLOAD:
 293             case Const.FSTORE:
 294             case Const.ILOAD:
 295             case Const.ISTORE:
 296             case Const.LLOAD:
 297             case Const.LSTORE:
 298             case Const.RET:
 299                 if (wide) {
 300                     vindex = bytes.readUnsignedShort();
 301                     wide = false; // Clear flag
 302                 } else {
 303                     vindex = bytes.readUnsignedByte();
 304                 }
 305                 buf.append("\t\t%").append(vindex);
 306                 break;
 307             /*
 308              * Remember wide byte which is used to form a 16-bit address in the
 309              * following instruction. Relies on that the method is called again with
 310              * the following opcode.
 311              */
 312             case Const.WIDE:
 313                 wide = true;
 314                 buf.append("\t(wide)");
 315                 break;
 316             /* Array of basic type.
 317              */
 318             case Const.NEWARRAY:
 319                 buf.append("\t\t<").append(Const.getTypeName(bytes.readByte())).append(">");
 320                 break;
 321             /* Access object/class fields.
 322              */
 323             case Const.GETFIELD:
 324             case Const.GETSTATIC:
 325             case Const.PUTFIELD:
 326             case Const.PUTSTATIC:
 327                 index = bytes.readUnsignedShort();
 328                 buf.append("\t\t").append(
 329                         constant_pool.constantToString(index, Const.CONSTANT_Fieldref)).append(
 330                                 verbose ? " (" + index + ")" : "");
 331                 break;
 332             /* Operands are references to classes in constant pool
 333              */
 334             case Const.NEW:
 335             case Const.CHECKCAST:
 336                 buf.append("\t");
 337             //$FALL-THROUGH$
 338             case Const.INSTANCEOF:
 339                 index = bytes.readUnsignedShort();
 340                 buf.append("\t<").append(
 341                         constant_pool.constantToString(index, Const.CONSTANT_Class))
 342                         .append(">").append(verbose ? " (" + index + ")" : "");
 343                 break;
 344             /* Operands are references to methods in constant pool
 345              */
 346             case Const.INVOKESPECIAL:
 347             case Const.INVOKESTATIC:
 348                 index = bytes.readUnsignedShort();
 349                 final Constant c = constant_pool.getConstant(index);
 350                 // With Java8 operand may be either a CONSTANT_Methodref
 351                 // or a CONSTANT_InterfaceMethodref.   (markro)
 352                 buf.append("\t").append(
 353                         constant_pool.constantToString(index, c.getTag()))
 354                         .append(verbose ? " (" + index + ")" : "");
 355                 break;
 356             case Const.INVOKEVIRTUAL:
 357                 index = bytes.readUnsignedShort();
 358                 buf.append("\t").append(
 359                         constant_pool.constantToString(index, Const.CONSTANT_Methodref))
 360                         .append(verbose ? " (" + index + ")" : "");
 361                 break;
 362             case Const.INVOKEINTERFACE:
 363                 index = bytes.readUnsignedShort();
 364                 final int nargs = bytes.readUnsignedByte(); // historical, redundant
 365                 buf.append("\t").append(
 366                         constant_pool
 367                         .constantToString(index, Const.CONSTANT_InterfaceMethodref))
 368                         .append(verbose ? " (" + index + ")\t" : "").append(nargs).append("\t")
 369                         .append(bytes.readUnsignedByte()); // Last byte is a reserved space
 370                 break;
 371             case Const.INVOKEDYNAMIC:
 372                 index = bytes.readUnsignedShort();
 373                 buf.append("\t").append(
 374                         constant_pool
 375                         .constantToString(index, Const.CONSTANT_InvokeDynamic))
 376                         .append(verbose ? " (" + index + ")\t" : "")
 377                         .append(bytes.readUnsignedByte()) // Thrid byte is a reserved space
 378                         .append(bytes.readUnsignedByte()); // Last byte is a reserved space
 379                 break;
 380             /* Operands are references to items in constant pool
 381              */
 382             case Const.LDC_W:
 383             case Const.LDC2_W:
 384                 index = bytes.readUnsignedShort();
 385                 buf.append("\t\t").append(
 386                         constant_pool.constantToString(index, constant_pool.getConstant(index)
 387                                 .getTag())).append(verbose ? " (" + index + ")" : "");
 388                 break;
 389             case Const.LDC:
 390                 index = bytes.readUnsignedByte();
 391                 buf.append("\t\t").append(
 392                         constant_pool.constantToString(index, constant_pool.getConstant(index)
 393                                 .getTag())).append(verbose ? " (" + index + ")" : "");
 394                 break;
 395             /* Array of references.
 396              */
 397             case Const.ANEWARRAY:
 398                 index = bytes.readUnsignedShort();
 399                 buf.append("\t\t<").append(
 400                         compactClassName(constant_pool.getConstantString(index,
 401                                         Const.CONSTANT_Class), false)).append(">").append(
 402                                 verbose ? " (" + index + ")" : "");
 403                 break;
 404             /* Multidimensional array of references.
 405              */
 406             case Const.MULTIANEWARRAY: {
 407                 index = bytes.readUnsignedShort();
 408                 final int dimensions = bytes.readUnsignedByte();
 409                 buf.append("\t<").append(
 410                         compactClassName(constant_pool.getConstantString(index,
 411                                         Const.CONSTANT_Class), false)).append(">\t").append(dimensions)
 412                         .append(verbose ? " (" + index + ")" : "");
 413             }
 414             break;
 415             /* Increment local variable.
 416              */
 417             case Const.IINC:
 418                 if (wide) {
 419                     vindex = bytes.readUnsignedShort();
 420                     constant = bytes.readShort();
 421                     wide = false;
 422                 } else {
 423                     vindex = bytes.readUnsignedByte();
 424                     constant = bytes.readByte();
 425                 }
 426                 buf.append("\t\t%").append(vindex).append("\t").append(constant);
 427                 break;
 428             default:
 429                 if (Const.getNoOfOperands(opcode) > 0) {
 430                     for (int i = 0; i < Const.getOperandTypeCount(opcode); i++) {
 431                         buf.append("\t\t");
 432                         switch (Const.getOperandType(opcode, i)) {
 433                             case Const.T_BYTE:
 434                                 buf.append(bytes.readByte());
 435                                 break;
 436                             case Const.T_SHORT:
 437                                 buf.append(bytes.readShort());
 438                                 break;
 439                             case Const.T_INT:
 440                                 buf.append(bytes.readInt());
 441                                 break;
 442                             default: // Never reached
 443                                 throw new IllegalStateException("Unreachable default case reached!");
 444                         }
 445                     }
 446                 }
 447         }
 448         return buf.toString();
 449     }
 450 
 451     public static String codeToString(final ByteSequence bytes, final ConstantPool constant_pool)
 452             throws IOException {
 453         return codeToString(bytes, constant_pool, true);
 454     }
 455 
 456     /**
 457      * Shorten long class names, <em>java/lang/String</em> becomes
 458      * <em>String</em>.
 459      *
 460      * @param str The long class name
 461      * @return Compacted class name
 462      */
 463     public static String compactClassName(final String str) {
 464         return compactClassName(str, true);
 465     }
 466 
 467     /**
 468      * Shorten long class name <em>str</em>, i.e., chop off the <em>prefix</em>,
 469      * if the class name starts with this string and the flag <em>chopit</em> is
 470      * true. Slashes <em>/</em> are converted to dots <em>.</em>.
 471      *
 472      * @param str The long class name
 473      * @param prefix The prefix the get rid off
 474      * @param chopit Flag that determines whether chopping is executed or not
 475      * @return Compacted class name
 476      */
 477     public static String compactClassName(String str, final String prefix, final boolean chopit) {
 478         final int len = prefix.length();
 479         str = str.replace('/', '.'); // Is `/' on all systems, even DOS
 480         if (chopit) {
 481             // If string starts with `prefix' and contains no further dots
 482             if (str.startsWith(prefix) && (str.substring(len).indexOf('.') == -1)) {
 483                 str = str.substring(len);
 484             }
 485         }
 486         return str;
 487     }
 488 
 489     /**
 490      * Shorten long class names, <em>java/lang/String</em> becomes
 491      * <em>java.lang.String</em>, e.g.. If <em>chopit</em> is <em>true</em> the
 492      * prefix <em>java.lang</em>
 493      * is also removed.
 494      *
 495      * @param str The long class name
 496      * @param chopit Flag that determines whether chopping is executed or not
 497      * @return Compacted class name
 498      */
 499     public static String compactClassName(final String str, final boolean chopit) {
 500         return compactClassName(str, "java.lang.", chopit);
 501     }
 502 
 503     /**
 504      * @return `flag' with bit `i' set to 1
 505      */
 506     public static int setBit(final int flag, final int i) {
 507         return flag | pow2(i);
 508     }
 509 
 510     /**
 511      * @return `flag' with bit `i' set to 0
 512      */
 513     public static int clearBit(final int flag, final int i) {
 514         final int bit = pow2(i);
 515         return (flag & bit) == 0 ? flag : flag ^ bit;
 516     }
 517 
 518     /**
 519      * @return true, if bit `i' in `flag' is set
 520      */
 521     public static boolean isSet(final int flag, final int i) {
 522         return (flag & pow2(i)) != 0;
 523     }
 524 
 525     /**
 526      * Converts string containing the method return and argument types to a byte
 527      * code method signature.
 528      *
 529      * @param ret Return type of method
 530      * @param argv Types of method arguments
 531      * @return Byte code representation of method signature
 532      *
 533      * @throws ClassFormatException if the signature is for Void
 534      */
 535     public static String methodTypeToSignature(final String ret, final String[] argv)
 536             throws ClassFormatException {
 537         final StringBuilder buf = new StringBuilder("(");
 538         String str;
 539         if (argv != null) {
 540             for (final String element : argv) {
 541                 str = getSignature(element);
 542                 if (str.endsWith("V")) {
 543                     throw new ClassFormatException("Invalid type: " + element);
 544                 }
 545                 buf.append(str);
 546             }
 547         }
 548         str = getSignature(ret);
 549         buf.append(")").append(str);
 550         return buf.toString();
 551     }
 552 
 553     /**
 554      * @param signature Method signature
 555      * @return Array of argument types
 556      * @throws ClassFormatException
 557      */
 558     public static String[] methodSignatureArgumentTypes(final String signature)
 559             throws ClassFormatException {
 560         return methodSignatureArgumentTypes(signature, true);
 561     }
 562 
 563     /**
 564      * @param signature Method signature
 565      * @param chopit Shorten class names ?
 566      * @return Array of argument types
 567      * @throws ClassFormatException
 568      */
 569     public static String[] methodSignatureArgumentTypes(final String signature, final boolean chopit)
 570             throws ClassFormatException {
 571         final List<String> vec = new ArrayList<>();
 572         int index;
 573         try { // Read all declarations between for `(' and `)'
 574             if (signature.charAt(0) != '(') {
 575                 throw new ClassFormatException("Invalid method signature: " + signature);
 576             }
 577             index = 1; // current string position
 578             while (signature.charAt(index) != ')') {
 579                 vec.add(signatureToString(signature.substring(index), chopit));
 580                 //corrected concurrent private static field acess
 581                 index += unwrap(consumed_chars); // update position
 582             }
 583         } catch (final StringIndexOutOfBoundsException e) { // Should never occur
 584             throw new ClassFormatException("Invalid method signature: " + signature, e);
 585         }
 586         return vec.toArray(new String[vec.size()]);
 587     }
 588 
 589     /**
 590      * @param signature Method signature
 591      * @return return type of method
 592      * @throws ClassFormatException
 593      */
 594     public static String methodSignatureReturnType(final String signature)
 595             throws ClassFormatException {
 596         return methodSignatureReturnType(signature, true);
 597     }
 598 
 599     /**
 600      * @param signature Method signature
 601      * @param chopit Shorten class names ?
 602      * @return return type of method
 603      * @throws ClassFormatException
 604      */
 605     public static String methodSignatureReturnType(final String signature,
 606             final boolean chopit) throws ClassFormatException {
 607         int index;
 608         String type;
 609         try {
 610             // Read return type after `)'
 611             index = signature.lastIndexOf(')') + 1;
 612             type = signatureToString(signature.substring(index), chopit);
 613         } catch (final StringIndexOutOfBoundsException e) { // Should never occur
 614             throw new ClassFormatException("Invalid method signature: " + signature, e);
 615         }
 616         return type;
 617     }
 618 
 619     /**
 620      * Converts method signature to string with all class names compacted.
 621      *
 622      * @param signature to convert
 623      * @param name of method
 624      * @param access flags of method
 625      * @return Human readable signature
 626      */
 627     public static String methodSignatureToString(final String signature,
 628             final String name, final String access) {
 629         return methodSignatureToString(signature, name, access, true);
 630     }
 631 
 632     public static String methodSignatureToString(final String signature,
 633             final String name, final String access, final boolean chopit) {
 634         return methodSignatureToString(signature, name, access, chopit, null);
 635     }
 636 
 637     /**
 638      * A returntype signature represents the return value from a method. It is a
 639      * series of bytes in the following grammar:
 640      *
 641      * <pre>
 642      * &lt;return_signature&gt; ::= &lt;field_type&gt; | V
 643      * </pre>
 644      *
 645      * The character V indicates that the method returns no value. Otherwise,
 646      * the signature indicates the type of the return value. An argument
 647      * signature represents an argument passed to a method:
 648      *
 649      * <pre>
 650      * &lt;argument_signature&gt; ::= &lt;field_type&gt;
 651      * </pre>
 652      *
 653      * A method signature represents the arguments that the method expects, and
 654      * the value that it returns.
 655      * <pre>
 656      * &lt;method_signature&gt; ::= (&lt;arguments_signature&gt;) &lt;return_signature&gt;
 657      * &lt;arguments_signature&gt;::= &lt;argument_signature&gt;*
 658      * </pre>
 659      *
 660      * This method converts such a string into a Java type declaration like
 661      * `void main(String[])' and throws a `ClassFormatException' when the parsed
 662      * type is invalid.
 663      *
 664      * @param signature Method signature
 665      * @param name Method name
 666      * @param access Method access rights
 667      * @param chopit
 668      * @param vars
 669      * @return Java type declaration
 670      * @throws ClassFormatException
 671      */
 672     public static String methodSignatureToString(final String signature, final String name,
 673             final String access, final boolean chopit, final LocalVariableTable vars)
 674             throws ClassFormatException {
 675         final StringBuilder buf = new StringBuilder("(");
 676         String type;
 677         int index;
 678         int var_index = access.contains("static") ? 0 : 1;
 679         try { // Read all declarations between for `(' and `)'
 680             if (signature.charAt(0) != '(') {
 681                 throw new ClassFormatException("Invalid method signature: " + signature);
 682             }
 683             index = 1; // current string position
 684             while (signature.charAt(index) != ')') {
 685                 final String param_type = signatureToString(signature.substring(index), chopit);
 686                 buf.append(param_type);
 687                 if (vars != null) {
 688                     final LocalVariable l = vars.getLocalVariable(var_index, 0);
 689                     if (l != null) {
 690                         buf.append(" ").append(l.getName());
 691                     }
 692                 } else {
 693                     buf.append(" arg").append(var_index);
 694                 }
 695                 if ("double".equals(param_type) || "long".equals(param_type)) {
 696                     var_index += 2;
 697                 } else {
 698                     var_index++;
 699                 }
 700                 buf.append(", ");
 701                 //corrected concurrent private static field acess
 702                 index += unwrap(consumed_chars); // update position
 703             }
 704             index++; // update position
 705             // Read return type after `)'
 706             type = signatureToString(signature.substring(index), chopit);
 707         } catch (final StringIndexOutOfBoundsException e) { // Should never occur
 708             throw new ClassFormatException("Invalid method signature: " + signature, e);
 709         }
 710         if (buf.length() > 1) {
 711             buf.setLength(buf.length() - 2);
 712         }
 713         buf.append(")");
 714         return access + ((access.length() > 0) ? " " : "") + // May be an empty string
 715                 type + " " + name + buf.toString();
 716     }
 717 
 718     // Guess what this does
 719     private static int pow2(final int n) {
 720         return 1 << n;
 721     }
 722 
 723     /**
 724      * Replace all occurrences of <em>old</em> in <em>str</em> with
 725      * <em>new</em>.
 726      *
 727      * @param str String to permute
 728      * @param old String to be replaced
 729      * @param new_ Replacement string
 730      * @return new String object
 731      */
 732     public static String replace(String str, final String old, final String new_) {
 733         int index;
 734         int old_index;
 735         try {
 736             if (str.contains(old)) { // `old' found in str
 737                 final StringBuilder buf = new StringBuilder();
 738                 old_index = 0; // String start offset
 739                 // While we have something to replace
 740                 while ((index = str.indexOf(old, old_index)) != -1) {
 741                     buf.append(str.substring(old_index, index)); // append prefix
 742                     buf.append(new_); // append replacement
 743                     old_index = index + old.length(); // Skip `old'.length chars
 744                 }
 745                 buf.append(str.substring(old_index)); // append rest of string
 746                 str = buf.toString();
 747             }
 748         } catch (final StringIndexOutOfBoundsException e) { // Should not occur
 749             System.err.println(e);
 750         }
 751         return str;
 752     }
 753 
 754     /**
 755      * Converts signature to string with all class names compacted.
 756      *
 757      * @param signature to convert
 758      * @return Human readable signature
 759      */
 760     public static String signatureToString(final String signature) {
 761         return signatureToString(signature, true);
 762     }
 763 
 764     /**
 765      * The field signature represents the value of an argument to a function or
 766      * the value of a variable. It is a series of bytes generated by the
 767      * following grammar:
 768      *
 769      * <PRE>
 770      * &lt;field_signature&gt; ::= &lt;field_type&gt;
 771      * &lt;field_type&gt;      ::= &lt;base_type&gt;|&lt;object_type&gt;|&lt;array_type&gt;
 772      * &lt;base_type&gt;       ::= B|C|D|F|I|J|S|Z
 773      * &lt;object_type&gt;     ::= L&lt;fullclassname&gt;;
 774      * &lt;array_type&gt;      ::= [&lt;field_type&gt;
 775      *
 776      * The meaning of the base types is as follows:
 777      * B byte signed byte
 778      * C char character
 779      * D double double precision IEEE float
 780      * F float single precision IEEE float
 781      * I int integer
 782      * J long long integer
 783      * L&lt;fullclassname&gt;; ... an object of the given class
 784      * S short signed short
 785      * Z boolean true or false
 786      * [&lt;field sig&gt; ... array
 787      * </PRE>
 788      *
 789      * This method converts this string into a Java type declaration such as
 790      * `String[]' and throws a `ClassFormatException' when the parsed type is
 791      * invalid.
 792      *
 793      * @param signature Class signature
 794      * @param chopit Flag that determines whether chopping is executed or not
 795      * @return Java type declaration
 796      * @throws ClassFormatException
 797      */
 798     public static String signatureToString(final String signature, final boolean chopit) {
 799         //corrected concurrent private static field acess
 800         wrap(consumed_chars, 1); // This is the default, read just one char like `B'
 801         try {
 802             switch (signature.charAt(0)) {
 803                 case 'B':
 804                     return "byte";
 805                 case 'C':
 806                     return "char";
 807                 case 'D':
 808                     return "double";
 809                 case 'F':
 810                     return "float";
 811                 case 'I':
 812                     return "int";
 813                 case 'J':
 814                     return "long";
 815                 case 'T': { // TypeVariableSignature
 816                     final int index = signature.indexOf(';'); // Look for closing `;'
 817                     if (index < 0) {
 818                         throw new ClassFormatException("Invalid signature: " + signature);
 819                     }
 820                     //corrected concurrent private static field acess
 821                     wrap(consumed_chars, index + 1); // "Tblabla;" `T' and `;' are removed
 822                     return compactClassName(signature.substring(1, index), chopit);
 823                 }
 824                 case 'L': { // Full class name
 825                     // should this be a while loop? can there be more than
 826                     // one generic clause?  (markro)
 827                     int fromIndex = signature.indexOf('<'); // generic type?
 828                     if (fromIndex < 0) {
 829                         fromIndex = 0;
 830                     } else {
 831                         fromIndex = signature.indexOf('>', fromIndex);
 832                         if (fromIndex < 0) {
 833                             throw new ClassFormatException("Invalid signature: " + signature);
 834                         }
 835                     }
 836                     final int index = signature.indexOf(';', fromIndex); // Look for closing `;'
 837                     if (index < 0) {
 838                         throw new ClassFormatException("Invalid signature: " + signature);
 839                     }
 840                     // check to see if there are any TypeArguments
 841                     final int bracketIndex = signature.substring(0, index).indexOf('<');
 842                     if (bracketIndex < 0) {
 843                         // just a class identifier
 844                         wrap(consumed_chars, index + 1); // "Lblabla;" `L' and `;' are removed
 845                         return compactClassName(signature.substring(1, index), chopit);
 846                     }
 847 
 848                     // we have TypeArguments; build up partial result
 849                     // as we recurse for each TypeArgument
 850                     final StringBuilder type = new StringBuilder(
 851                             compactClassName(signature.substring(1, bracketIndex), chopit))
 852                             .append("<");
 853                     int consumed_chars = bracketIndex + 1; // Shadows global var
 854 
 855                     // check for wildcards
 856                     if (signature.charAt(consumed_chars) == '+') {
 857                         type.append("? extends ");
 858                         consumed_chars++;
 859                     } else if (signature.charAt(consumed_chars) == '-') {
 860                         type.append("? super ");
 861                         consumed_chars++;
 862                     } else if (signature.charAt(consumed_chars) == '*') {
 863                         // must be at end of signature
 864                         if (signature.charAt(consumed_chars + 1) != '>') {
 865                             throw new ClassFormatException("Invalid signature: " + signature);
 866                         }
 867                         if (signature.charAt(consumed_chars + 2) != ';') {
 868                             throw new ClassFormatException("Invalid signature: " + signature);
 869                         }
 870                         wrap(Utility.consumed_chars, consumed_chars + 3); // remove final "*>;"
 871                         return type + "?>...";
 872                     }
 873 
 874                     // get the first TypeArgument
 875                     type.append(signatureToString(signature.substring(consumed_chars), chopit));
 876                     // update our consumed count by the number of characters the for type argument
 877                     consumed_chars = unwrap(Utility.consumed_chars) + consumed_chars;
 878                     wrap(Utility.consumed_chars, consumed_chars);
 879 
 880                     // are there more TypeArguments?
 881                     while (signature.charAt(consumed_chars) != '>') {
 882                         type.append(", ").append(signatureToString(signature.substring(consumed_chars), chopit));
 883                         // update our consumed count by the number of characters the for type argument
 884                         consumed_chars = unwrap(Utility.consumed_chars) + consumed_chars;
 885                         wrap(Utility.consumed_chars, consumed_chars);
 886                     }
 887 
 888                     if (signature.charAt(consumed_chars + 1) != ';') {
 889                         throw new ClassFormatException("Invalid signature: " + signature);
 890                     }
 891                     wrap(Utility.consumed_chars, consumed_chars + 2); // remove final ">;"
 892                     return type.append(">").toString();
 893                 }
 894                 case 'S':
 895                     return "short";
 896                 case 'Z':
 897                     return "boolean";
 898                 case '[': { // Array declaration
 899                     int n;
 900                     StringBuilder brackets;
 901                     String type;
 902                     int consumed_chars; // Shadows global var
 903                     brackets = new StringBuilder(); // Accumulate []'s
 904                     // Count opening brackets and look for optional size argument
 905                     for (n = 0; signature.charAt(n) == '['; n++) {
 906                         brackets.append("[]");
 907                     }
 908                     consumed_chars = n; // Remember value
 909                     // The rest of the string denotes a `<field_type>'
 910                     type = signatureToString(signature.substring(n), chopit);
 911                     //corrected concurrent private static field acess
 912                     //Utility.consumed_chars += consumed_chars; is replaced by:
 913                     final int _temp = unwrap(Utility.consumed_chars) + consumed_chars;
 914                     wrap(Utility.consumed_chars, _temp);
 915                     return type + brackets.toString();
 916                 }
 917                 case 'V':
 918                     return "void";
 919                 default:
 920                     throw new ClassFormatException("Invalid signature: `" + signature + "'");
 921             }
 922         } catch (final StringIndexOutOfBoundsException e) { // Should never occur
 923             throw new ClassFormatException("Invalid signature: " + signature, e);
 924         }
 925     }
 926 
 927     /**
 928      * Parse Java type such as "char", or "java.lang.String[]" and return the
 929      * signature in byte code format, e.g. "C" or "[Ljava/lang/String;"
 930      * respectively.
 931      *
 932      * @param type Java type
 933      * @return byte code signature
 934      */
 935     public static String getSignature(String type) {
 936         final StringBuilder buf = new StringBuilder();
 937         final char[] chars = type.toCharArray();
 938         boolean char_found = false;
 939         boolean delim = false;
 940         int index = -1;
 941         loop:
 942         for (int i = 0; i < chars.length; i++) {
 943             switch (chars[i]) {
 944                 case ' ':
 945                 case '\t':
 946                 case '\n':
 947                 case '\r':
 948                 case '\f':
 949                     if (char_found) {
 950                         delim = true;
 951                     }
 952                     break;
 953                 case '[':
 954                     if (!char_found) {
 955                         throw new RuntimeException("Illegal type: " + type);
 956                     }
 957                     index = i;
 958                     break loop;
 959                 default:
 960                     char_found = true;
 961                     if (!delim) {
 962                         buf.append(chars[i]);
 963                     }
 964             }
 965         }
 966         int brackets = 0;
 967         if (index > 0) {
 968             brackets = countBrackets(type.substring(index));
 969         }
 970         type = buf.toString();
 971         buf.setLength(0);
 972         for (int i = 0; i < brackets; i++) {
 973             buf.append('[');
 974         }
 975         boolean found = false;
 976         for (int i = Const.T_BOOLEAN; (i <= Const.T_VOID) && !found; i++) {
 977             if (Const.getTypeName(i).equals(type)) {
 978                 found = true;
 979                 buf.append(Const.getShortTypeName(i));
 980             }
 981         }
 982         if (!found) {
 983             buf.append('L').append(type.replace('.', '/')).append(';');
 984         }
 985         return buf.toString();
 986     }
 987 
 988     private static int countBrackets(final String brackets) {
 989         final char[] chars = brackets.toCharArray();
 990         int count = 0;
 991         boolean open = false;
 992         for (final char c : chars) {
 993             switch (c) {
 994                 case '[':
 995                     if (open) {
 996                         throw new RuntimeException("Illegally nested brackets:" + brackets);
 997                     }
 998                     open = true;
 999                     break;
1000                 case ']':
1001                     if (!open) {
1002                         throw new RuntimeException("Illegally nested brackets:" + brackets);
1003                     }
1004                     open = false;
1005                     count++;
1006                     break;
1007                 default:
1008                     // Don't care
1009                     break;
1010             }
1011         }
1012         if (open) {
1013             throw new RuntimeException("Illegally nested brackets:" + brackets);
1014         }
1015         return count;
1016     }
1017 
1018     /**
1019      * Return type of method signature as a byte value as defined in
1020      * <em>Constants</em>
1021      *
1022      * @param signature in format described above
1023      * @return type of method signature
1024      * @see Const
1025      *
1026      * @throws ClassFormatException if signature is not a method signature
1027      */
1028     public static byte typeOfMethodSignature(final String signature) throws ClassFormatException {
1029         int index;
1030         try {
1031             if (signature.charAt(0) != '(') {
1032                 throw new ClassFormatException("Invalid method signature: " + signature);
1033             }
1034             index = signature.lastIndexOf(')') + 1;
1035             return typeOfSignature(signature.substring(index));
1036         } catch (final StringIndexOutOfBoundsException e) {
1037             throw new ClassFormatException("Invalid method signature: " + signature, e);
1038         }
1039     }
1040 
1041     /**
1042      * Return type of signature as a byte value as defined in <em>Constants</em>
1043      *
1044      * @param signature in format described above
1045      * @return type of signature
1046      * @see Const
1047      *
1048      * @throws ClassFormatException if signature isn't a known type
1049      */
1050     public static byte typeOfSignature(final String signature) throws ClassFormatException {
1051         try {
1052             switch (signature.charAt(0)) {
1053                 case 'B':
1054                     return Const.T_BYTE;
1055                 case 'C':
1056                     return Const.T_CHAR;
1057                 case 'D':
1058                     return Const.T_DOUBLE;
1059                 case 'F':
1060                     return Const.T_FLOAT;
1061                 case 'I':
1062                     return Const.T_INT;
1063                 case 'J':
1064                     return Const.T_LONG;
1065                 case 'L':
1066                 case 'T':
1067                     return Const.T_REFERENCE;
1068                 case '[':
1069                     return Const.T_ARRAY;
1070                 case 'V':
1071                     return Const.T_VOID;
1072                 case 'Z':
1073                     return Const.T_BOOLEAN;
1074                 case 'S':
1075                     return Const.T_SHORT;
1076                 case '!':
1077                 case '+':
1078                 case '*':
1079                     return typeOfSignature(signature.substring(1));
1080                 default:
1081                     throw new ClassFormatException("Invalid method signature: " + signature);
1082             }
1083         } catch (final StringIndexOutOfBoundsException e) {
1084             throw new ClassFormatException("Invalid method signature: " + signature, e);
1085         }
1086     }
1087 
1088     /**
1089      * Map opcode names to opcode numbers. E.g., return Constants.ALOAD for
1090      * "aload"
1091      */
1092     public static short searchOpcode(String name) {
1093         name = name.toLowerCase(Locale.ENGLISH);
1094         for (short i = 0; i < Const.OPCODE_NAMES_LENGTH; i++) {
1095             if (Const.getOpcodeName(i).equals(name)) {
1096                 return i;
1097             }
1098         }
1099         return -1;
1100     }
1101 
1102     /**
1103      * Convert (signed) byte to (unsigned) short value, i.e., all negative
1104      * values become positive.
1105      */
1106     private static short byteToShort(final byte b) {
1107         return (b < 0) ? (short) (256 + b) : (short) b;
1108     }
1109 
1110     /**
1111      * Convert bytes into hexadecimal string
1112      *
1113      * @param bytes an array of bytes to convert to hexadecimal
1114      *
1115      * @return bytes as hexadecimal string, e.g. 00 fa 12 ...
1116      */
1117     public static String toHexString(final byte[] bytes) {
1118         final StringBuilder buf = new StringBuilder();
1119         for (int i = 0; i < bytes.length; i++) {
1120             final short b = byteToShort(bytes[i]);
1121             final String hex = Integer.toHexString(b);
1122             if (b < 0x10) {
1123                 buf.append('0');
1124             }
1125             buf.append(hex);
1126             if (i < bytes.length - 1) {
1127                 buf.append(' ');
1128             }
1129         }
1130         return buf.toString();
1131     }
1132 
1133     /**
1134      * Return a string for an integer justified left or right and filled up with
1135      * `fill' characters if necessary.
1136      *
1137      * @param i integer to format
1138      * @param length length of desired string
1139      * @param left_justify format left or right
1140      * @param fill fill character
1141      * @return formatted int
1142      */
1143     public static String format(final int i, final int length,
1144             final boolean left_justify, final char fill) {
1145         return fillup(Integer.toString(i), length, left_justify, fill);
1146     }
1147 
1148     /**
1149      * Fillup char with up to length characters with char `fill' and justify it
1150      * left or right.
1151      *
1152      * @param str string to format
1153      * @param length length of desired string
1154      * @param left_justify format left or right
1155      * @param fill fill character
1156      * @return formatted string
1157      */
1158     public static String fillup(final String str, final int length,
1159             final boolean left_justify, final char fill) {
1160         final int len = length - str.length();
1161         final char[] buf = new char[(len < 0) ? 0 : len];
1162         for (int j = 0; j < buf.length; j++) {
1163             buf[j] = fill;
1164         }
1165         if (left_justify) {
1166             return str + new String(buf);
1167         }
1168         return new String(buf) + str;
1169     }
1170 
1171     static boolean equals(final byte[] a, final byte[] b) {
1172         int size;
1173         if ((size = a.length) != b.length) {
1174             return false;
1175         }
1176         for (int i = 0; i < size; i++) {
1177             if (a[i] != b[i]) {
1178                 return false;
1179             }
1180         }
1181         return true;
1182     }
1183 
1184     public static void printArray(final PrintStream out, final Object[] obj) {
1185         out.println(printArray(obj, true));
1186     }
1187 
1188     public static void printArray(final PrintWriter out, final Object[] obj) {
1189         out.println(printArray(obj, true));
1190     }
1191 
1192     public static String printArray(final Object[] obj) {
1193         return printArray(obj, true);
1194     }
1195 
1196     public static String printArray(final Object[] obj, final boolean braces) {
1197         return printArray(obj, braces, false);
1198     }
1199 
1200     public static String printArray(final Object[] obj, final boolean braces, final boolean quote) {
1201         if (obj == null) {
1202             return null;
1203         }
1204         final StringBuilder buf = new StringBuilder();
1205         if (braces) {
1206             buf.append('{');
1207         }
1208         for (int i = 0; i < obj.length; i++) {
1209             if (obj[i] != null) {
1210                 buf.append(quote ? "\"" : "").append(obj[i]).append(quote ? "\"" : "");
1211             } else {
1212                 buf.append("null");
1213             }
1214             if (i < obj.length - 1) {
1215                 buf.append(", ");
1216             }
1217         }
1218         if (braces) {
1219             buf.append('}');
1220         }
1221         return buf.toString();
1222     }
1223 
1224     /**
1225      * @param ch the character to test if it's part of an identifier
1226      *
1227      * @return true, if character is one of (a, ... z, A, ... Z, 0, ... 9, _)
1228      */
1229     public static boolean isJavaIdentifierPart(final char ch) {
1230         return ((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z'))
1231                 || ((ch >= '0') && (ch <= '9')) || (ch == '_');
1232     }
1233 
1234     /**
1235      * Encode byte array it into Java identifier string, i.e., a string that
1236      * only contains the following characters: (a, ... z, A, ... Z, 0, ... 9, _,
1237      * $). The encoding algorithm itself is not too clever: if the current
1238      * byte's ASCII value already is a valid Java identifier part, leave it as
1239      * it is. Otherwise it writes the escape character($) followed by:
1240      *
1241      * <ul>
1242      * <li> the ASCII value as a hexadecimal string, if the value is not in the
1243      * range 200..247</li>
1244      * <li>a Java identifier char not used in a lowercase hexadecimal string, if
1245      * the value is in the range 200..247</li>
1246      * </ul>
1247      *
1248      * <p>
1249      * This operation inflates the original byte array by roughly 40-50%</p>
1250      *
1251      * @param bytes the byte array to convert
1252      * @param compress use gzip to minimize string
1253      *
1254      * @throws IOException if there's a gzip exception
1255      */
1256     public static String encode(byte[] bytes, final boolean compress) throws IOException {
1257         if (compress) {
1258             try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
1259                     GZIPOutputStream gos = new GZIPOutputStream(baos)) {
1260                 gos.write(bytes, 0, bytes.length);
1261                 bytes = baos.toByteArray();
1262             }
1263         }
1264         final CharArrayWriter caw = new CharArrayWriter();
1265         try (JavaWriter jw = new JavaWriter(caw)) {
1266             for (final byte b : bytes) {
1267                 final int in = b & 0x000000ff; // Normalize to unsigned
1268                 jw.write(in);
1269             }
1270         }
1271         return caw.toString();
1272     }
1273 
1274     /**
1275      * Decode a string back to a byte array.
1276      *
1277      * @param s the string to convert
1278      * @param uncompress use gzip to uncompress the stream of bytes
1279      *
1280      * @throws IOException if there's a gzip exception
1281      */
1282     public static byte[] decode(final String s, final boolean uncompress) throws IOException {
1283         byte[] bytes;
1284         try (JavaReader jr = new JavaReader(new CharArrayReader(s.toCharArray()));
1285                 ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
1286             int ch;
1287             while ((ch = jr.read()) >= 0) {
1288                 bos.write(ch);
1289             }
1290             bytes = bos.toByteArray();
1291         }
1292         if (uncompress) {
1293             final GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(bytes));
1294             final byte[] tmp = new byte[bytes.length * 3]; // Rough estimate
1295             int count = 0;
1296             int b;
1297             while ((b = gis.read()) >= 0) {
1298                 tmp[count++] = (byte) b;
1299             }
1300             bytes = new byte[count];
1301             System.arraycopy(tmp, 0, bytes, 0, count);
1302         }
1303         return bytes;
1304     }
1305 
1306     // A-Z, g-z, _, $
1307     private static final int FREE_CHARS = 48;
1308     private static int[] CHAR_MAP = new int[FREE_CHARS];
1309     private static int[] MAP_CHAR = new int[256]; // Reverse map
1310     private static final char ESCAPE_CHAR = '$';
1311 
1312     static {
1313         int j = 0;
1314         for (int i = 'A'; i <= 'Z'; i++) {
1315             CHAR_MAP[j] = i;
1316             MAP_CHAR[i] = j;
1317             j++;
1318         }
1319         for (int i = 'g'; i <= 'z'; i++) {
1320             CHAR_MAP[j] = i;
1321             MAP_CHAR[i] = j;
1322             j++;
1323         }
1324         CHAR_MAP[j] = '$';
1325         MAP_CHAR['$'] = j;
1326         j++;
1327         CHAR_MAP[j] = '_';
1328         MAP_CHAR['_'] = j;
1329     }
1330 
1331     /**
1332      * Decode characters into bytes. Used by <a
1333      * href="Utility.html#decode(java.lang.String, boolean)">decode()</a>
1334      */
1335     private static class JavaReader extends FilterReader {
1336 
1337         public JavaReader(final Reader in) {
1338             super(in);
1339         }
1340 
1341         @Override
1342         public int read() throws IOException {
1343             final int b = in.read();
1344             if (b != ESCAPE_CHAR) {
1345                 return b;
1346             }
1347             final int i = in.read();
1348             if (i < 0) {
1349                 return -1;
1350             }
1351             if (((i >= '0') && (i <= '9')) || ((i >= 'a') && (i <= 'f'))) { // Normal escape
1352                 final int j = in.read();
1353                 if (j < 0) {
1354                     return -1;
1355                 }
1356                 final char[] tmp = {
1357                     (char) i, (char) j
1358                 };
1359                 final int s = Integer.parseInt(new String(tmp), 16);
1360                 return s;
1361             }
1362             return MAP_CHAR[i];
1363         }
1364 
1365         @Override
1366         public int read(final char[] cbuf, final int off, final int len) throws IOException {
1367             for (int i = 0; i < len; i++) {
1368                 cbuf[off + i] = (char) read();
1369             }
1370             return len;
1371         }
1372     }
1373 
1374     /**
1375      * Encode bytes into valid java identifier characters. Used by <a
1376      * href="Utility.html#encode(byte[], boolean)">encode()</a>
1377      */
1378     private static class JavaWriter extends FilterWriter {
1379 
1380         public JavaWriter(final Writer out) {
1381             super(out);
1382         }
1383 
1384         @Override
1385         public void write(final int b) throws IOException {
1386             if (isJavaIdentifierPart((char) b) && (b != ESCAPE_CHAR)) {
1387                 out.write(b);
1388             } else {
1389                 out.write(ESCAPE_CHAR); // Escape character
1390                 // Special escape
1391                 if (b >= 0 && b < FREE_CHARS) {
1392                     out.write(CHAR_MAP[b]);
1393                 } else { // Normal escape
1394                     final char[] tmp = Integer.toHexString(b).toCharArray();
1395                     if (tmp.length == 1) {
1396                         out.write('0');
1397                         out.write(tmp[0]);
1398                     } else {
1399                         out.write(tmp[0]);
1400                         out.write(tmp[1]);
1401                     }
1402                 }
1403             }
1404         }
1405 
1406         @Override
1407         public void write(final char[] cbuf, final int off, final int len) throws IOException {
1408             for (int i = 0; i < len; i++) {
1409                 write(cbuf[off + i]);
1410             }
1411         }
1412 
1413         @Override
1414         public void write(final String str, final int off, final int len) throws IOException {
1415             write(str.toCharArray(), off, len);
1416         }
1417     }
1418 
1419     /**
1420      * Escape all occurences of newline chars '\n', quotes \", etc.
1421      */
1422     public static String convertString(final String label) {
1423         final char[] ch = label.toCharArray();
1424         final StringBuilder buf = new StringBuilder();
1425         for (final char element : ch) {
1426             switch (element) {
1427                 case '\n':
1428                     buf.append("\\n");
1429                     break;
1430                 case '\r':
1431                     buf.append("\\r");
1432                     break;
1433                 case '\"':
1434                     buf.append("\\\"");
1435                     break;
1436                 case '\'':
1437                     buf.append("\\'");
1438                     break;
1439                 case '\\':
1440                     buf.append("\\\\");
1441                     break;
1442                 default:
1443                     buf.append(element);
1444                     break;
1445             }
1446         }
1447         return buf.toString();
1448     }
1449 }