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