40 import com.sun.org.apache.bcel.internal.util.ByteSequence; |
40 import com.sun.org.apache.bcel.internal.util.ByteSequence; |
41 |
41 |
42 /** |
42 /** |
43 * Utility functions that do not really belong to any class in particular. |
43 * Utility functions that do not really belong to any class in particular. |
44 * |
44 * |
45 * @version $Id: Utility.java 1751107 2016-07-03 02:41:18Z dbrosius $ |
45 * @version $Id$ |
46 * @LastModified: Oct 2017 |
46 * @LastModified: Jun 2019 |
47 */ |
47 */ |
48 // @since 6.0 methods are no longer final |
48 // @since 6.0 methods are no longer final |
49 public abstract class Utility { |
49 public abstract class Utility { |
50 |
50 |
51 private static int unwrap(final ThreadLocal<Integer> tl) { |
51 private static int unwrap( final ThreadLocal<Integer> tl ) { |
52 return tl.get().intValue(); |
52 return tl.get(); |
53 } |
53 } |
54 |
54 |
55 private static void wrap(final ThreadLocal<Integer> tl, final int value) { |
55 |
56 tl.set(Integer.valueOf(value)); |
56 private static void wrap( final ThreadLocal<Integer> tl, final int value ) { |
|
57 tl.set(value); |
57 } |
58 } |
58 |
59 |
59 private static ThreadLocal<Integer> consumed_chars = new ThreadLocal<Integer>() { |
60 private static ThreadLocal<Integer> consumed_chars = new ThreadLocal<Integer>() { |
60 |
61 |
61 @Override |
62 @Override |
62 protected Integer initialValue() { |
63 protected Integer initialValue() { |
63 return Integer.valueOf(0); |
64 return 0; |
64 } |
65 } |
65 };/* How many chars have been consumed |
66 };/* How many chars have been consumed |
66 * during parsing in signatureToString(). |
67 * during parsing in signatureToString(). |
67 * Read by methodSignatureToString(). |
68 * Read by methodSignatureToString(). |
68 * Set by side effect,but only internally. |
69 * Set by side effect,but only internally. |
69 */ |
70 */ |
70 |
|
71 private static boolean wide = false; /* The `WIDE' instruction is used in the |
71 private static boolean wide = false; /* The `WIDE' instruction is used in the |
72 * byte code to allow 16-bit wide indices |
72 * byte code to allow 16-bit wide indices |
73 * for local variables. This opcode |
73 * for local variables. This opcode |
74 * precedes an `ILOAD', e.g.. The opcode |
74 * precedes an `ILOAD', e.g.. The opcode |
75 * immediately following takes an extra |
75 * immediately following takes an extra |
80 |
80 |
81 |
81 |
82 /** |
82 /** |
83 * Convert bit field of flags into string such as `static final'. |
83 * Convert bit field of flags into string such as `static final'. |
84 * |
84 * |
85 * @param access_flags Access flags |
85 * @param access_flags Access flags |
86 * @return String representation of flags |
86 * @return String representation of flags |
87 */ |
87 */ |
88 public static String accessToString(final int access_flags) { |
88 public static String accessToString( final int access_flags ) { |
89 return accessToString(access_flags, false); |
89 return accessToString(access_flags, false); |
90 } |
90 } |
91 |
91 |
|
92 |
92 /** |
93 /** |
93 * Convert bit field of flags into string such as `static final'. |
94 * Convert bit field of flags into string such as `static final'. |
94 * |
95 * |
95 * Special case: Classes compiled with new compilers and with the |
96 * 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 * `ACC_SUPER' flag would be said to be "synchronized". This is |
97 * used the same value for the flags `ACC_SUPER' and `ACC_SYNCHRONIZED'. |
98 * because SUN used the same value for the flags `ACC_SUPER' and |
98 * |
99 * `ACC_SYNCHRONIZED'. |
99 * @param access_flags Access flags |
100 * |
100 * @param for_class access flags are for class qualifiers ? |
101 * @param access_flags Access flags |
|
102 * @param for_class access flags are for class qualifiers ? |
101 * @return String representation of flags |
103 * @return String representation of flags |
102 */ |
104 */ |
103 public static String accessToString(final int access_flags, final boolean for_class) { |
105 public static String accessToString( final int access_flags, final boolean for_class ) { |
104 final StringBuilder buf = new StringBuilder(); |
106 final StringBuilder buf = new StringBuilder(); |
105 int p = 0; |
107 int p = 0; |
106 for (int i = 0; p < Const.MAX_ACC_FLAG; i++) { // Loop through known flags |
108 for (int i = 0; p < Const.MAX_ACC_FLAG; i++) { // Loop through known flags |
107 p = pow2(i); |
109 p = pow2(i); |
108 if ((access_flags & p) != 0) { |
110 if ((access_flags & p) != 0) { |
118 } |
120 } |
119 } |
121 } |
120 return buf.toString().trim(); |
122 return buf.toString().trim(); |
121 } |
123 } |
122 |
124 |
|
125 |
123 /** |
126 /** |
124 * @param access_flags the class flags |
127 * @param access_flags the class flags |
125 * |
128 * |
126 * @return "class" or "interface", depending on the ACC_INTERFACE flag |
129 * @return "class" or "interface", depending on the ACC_INTERFACE flag |
127 */ |
130 */ |
128 public static String classOrInterface(final int access_flags) { |
131 public static String classOrInterface( final int access_flags ) { |
129 return ((access_flags & Const.ACC_INTERFACE) != 0) ? "interface" : "class"; |
132 return ((access_flags & Const.ACC_INTERFACE) != 0) ? "interface" : "class"; |
130 } |
133 } |
|
134 |
131 |
135 |
132 /** |
136 /** |
133 * Disassemble a byte array of JVM byte codes starting from code line |
137 * Disassemble a byte array of JVM byte codes starting from code line |
134 * `index' and return the disassembled string representation. Decode only |
138 * `index' and return the disassembled string representation. Decode only |
135 * `num' opcodes (including their operands), use -1 if you want to decompile |
139 * `num' opcodes (including their operands), use -1 if you want to |
136 * everything. |
140 * decompile everything. |
137 * |
141 * |
138 * @param code byte code array |
142 * @param code byte code array |
139 * @param constant_pool Array of constants |
143 * @param constant_pool Array of constants |
140 * @param index offset in `code' array |
144 * @param index offset in `code' array |
141 * <EM>(number of opcodes, not bytes!)</EM> |
145 * <EM>(number of opcodes, not bytes!)</EM> |
142 * @param length number of opcodes to decompile, -1 for all |
146 * @param length number of opcodes to decompile, -1 for all |
143 * @param verbose be verbose, e.g. print constant pool index |
147 * @param verbose be verbose, e.g. print constant pool index |
144 * @return String representation of byte codes |
148 * @return String representation of byte codes |
145 */ |
149 */ |
146 public static String codeToString(final byte[] code, final ConstantPool constant_pool, |
150 public static String codeToString( final byte[] code, final ConstantPool constant_pool, final int index, |
147 final int index, final int length, final boolean verbose) { |
151 final int length, final boolean verbose ) { |
148 // Should be sufficient // CHECKSTYLE IGNORE MagicNumber |
152 final StringBuilder buf = new StringBuilder(code.length * 20); // Should be sufficient // CHECKSTYLE IGNORE MagicNumber |
149 final StringBuilder buf = new StringBuilder(code.length * 20); |
|
150 try (ByteSequence stream = new ByteSequence(code)) { |
153 try (ByteSequence stream = new ByteSequence(code)) { |
151 for (int i = 0; i < index; i++) { |
154 for (int i = 0; i < index; i++) { |
152 codeToString(stream, constant_pool, verbose); |
155 codeToString(stream, constant_pool, verbose); |
153 } |
156 } |
154 for (int i = 0; stream.available() > 0; i++) { |
157 for (int i = 0; stream.available() > 0; i++) { |
155 if ((length < 0) || (i < length)) { |
158 if ((length < 0) || (i < length)) { |
156 final String indices = fillup(stream.getIndex() + ":", 6, true, ' '); |
159 final String indices = fillup(stream.getIndex() + ":", 6, true, ' '); |
157 buf.append(indices) |
160 buf.append(indices).append(codeToString(stream, constant_pool, verbose)).append('\n'); |
158 .append(codeToString(stream, constant_pool, verbose)) |
|
159 .append('\n'); |
|
160 } |
161 } |
161 } |
162 } |
162 } catch (final IOException e) { |
163 } catch (final IOException e) { |
163 throw new ClassFormatException("Byte code error: " + buf.toString(), e); |
164 throw new ClassFormatException("Byte code error: " + buf.toString(), e); |
164 } |
165 } |
165 return buf.toString(); |
166 return buf.toString(); |
166 } |
167 } |
167 |
168 |
168 public static String codeToString(final byte[] code, final ConstantPool constant_pool, |
169 |
169 final int index, final int length) { |
170 public static String codeToString( final byte[] code, final ConstantPool constant_pool, final int index, final int length ) { |
170 return codeToString(code, constant_pool, index, length, true); |
171 return codeToString(code, constant_pool, index, length, true); |
171 } |
172 } |
172 |
173 |
173 /** |
174 |
174 * Disassemble a stream of byte codes and return the string representation. |
175 /** |
175 * |
176 * Disassemble a stream of byte codes and return the |
176 * @param bytes stream of bytes |
177 * string representation. |
177 * @param constant_pool Array of constants |
178 * |
178 * @param verbose be verbose, e.g. print constant pool index |
179 * @param bytes stream of bytes |
|
180 * @param constant_pool Array of constants |
|
181 * @param verbose be verbose, e.g. print constant pool index |
179 * @return String representation of byte code |
182 * @return String representation of byte code |
180 * |
183 * |
181 * @throws IOException if a failure from reading from the bytes argument |
184 * @throws IOException if a failure from reading from the bytes argument occurs |
182 * occurs |
|
183 */ |
185 */ |
184 @SuppressWarnings("fallthrough") // by design for case Const.INSTANCEOF |
186 @SuppressWarnings("fallthrough") // by design for case Const.INSTANCEOF |
185 public static String codeToString(final ByteSequence bytes, final ConstantPool constant_pool, |
187 public static String codeToString(final ByteSequence bytes, final ConstantPool constant_pool, |
186 final boolean verbose) throws IOException { |
188 final boolean verbose) throws IOException { |
187 final short opcode = (short) bytes.readUnsignedByte(); |
189 final short opcode = (short) bytes.readUnsignedByte(); |
325 case Const.PUTFIELD: |
327 case Const.PUTFIELD: |
326 case Const.PUTSTATIC: |
328 case Const.PUTSTATIC: |
327 index = bytes.readUnsignedShort(); |
329 index = bytes.readUnsignedShort(); |
328 buf.append("\t\t").append( |
330 buf.append("\t\t").append( |
329 constant_pool.constantToString(index, Const.CONSTANT_Fieldref)).append( |
331 constant_pool.constantToString(index, Const.CONSTANT_Fieldref)).append( |
330 verbose ? " (" + index + ")" : ""); |
332 verbose ? " (" + index + ")" : ""); |
331 break; |
333 break; |
332 /* Operands are references to classes in constant pool |
334 /* Operands are references to classes in constant pool |
333 */ |
335 */ |
334 case Const.NEW: |
336 case Const.NEW: |
335 case Const.CHECKCAST: |
337 case Const.CHECKCAST: |
336 buf.append("\t"); |
338 buf.append("\t"); |
337 //$FALL-THROUGH$ |
339 //$FALL-THROUGH$ |
338 case Const.INSTANCEOF: |
340 case Const.INSTANCEOF: |
339 index = bytes.readUnsignedShort(); |
341 index = bytes.readUnsignedShort(); |
340 buf.append("\t<").append( |
342 buf.append("\t<").append( |
341 constant_pool.constantToString(index, Const.CONSTANT_Class)) |
343 constant_pool.constantToString(index, Const.CONSTANT_Class)) |
342 .append(">").append(verbose ? " (" + index + ")" : ""); |
344 .append(">").append(verbose ? " (" + index + ")" : ""); |
362 case Const.INVOKEINTERFACE: |
364 case Const.INVOKEINTERFACE: |
363 index = bytes.readUnsignedShort(); |
365 index = bytes.readUnsignedShort(); |
364 final int nargs = bytes.readUnsignedByte(); // historical, redundant |
366 final int nargs = bytes.readUnsignedByte(); // historical, redundant |
365 buf.append("\t").append( |
367 buf.append("\t").append( |
366 constant_pool |
368 constant_pool |
367 .constantToString(index, Const.CONSTANT_InterfaceMethodref)) |
369 .constantToString(index, Const.CONSTANT_InterfaceMethodref)) |
368 .append(verbose ? " (" + index + ")\t" : "").append(nargs).append("\t") |
370 .append(verbose ? " (" + index + ")\t" : "").append(nargs).append("\t") |
369 .append(bytes.readUnsignedByte()); // Last byte is a reserved space |
371 .append(bytes.readUnsignedByte()); // Last byte is a reserved space |
370 break; |
372 break; |
371 case Const.INVOKEDYNAMIC: |
373 case Const.INVOKEDYNAMIC: |
372 index = bytes.readUnsignedShort(); |
374 index = bytes.readUnsignedShort(); |
373 buf.append("\t").append( |
375 buf.append("\t").append( |
374 constant_pool |
376 constant_pool |
375 .constantToString(index, Const.CONSTANT_InvokeDynamic)) |
377 .constantToString(index, Const.CONSTANT_InvokeDynamic)) |
376 .append(verbose ? " (" + index + ")\t" : "") |
378 .append(verbose ? " (" + index + ")\t" : "") |
377 .append(bytes.readUnsignedByte()) // Thrid byte is a reserved space |
379 .append(bytes.readUnsignedByte()) // Thrid byte is a reserved space |
378 .append(bytes.readUnsignedByte()); // Last byte is a reserved space |
380 .append(bytes.readUnsignedByte()); // Last byte is a reserved space |
379 break; |
381 break; |
380 /* Operands are references to items in constant pool |
382 /* Operands are references to items in constant pool |
381 */ |
383 */ |
382 case Const.LDC_W: |
384 case Const.LDC_W: |
396 */ |
398 */ |
397 case Const.ANEWARRAY: |
399 case Const.ANEWARRAY: |
398 index = bytes.readUnsignedShort(); |
400 index = bytes.readUnsignedShort(); |
399 buf.append("\t\t<").append( |
401 buf.append("\t\t<").append( |
400 compactClassName(constant_pool.getConstantString(index, |
402 compactClassName(constant_pool.getConstantString(index, |
401 Const.CONSTANT_Class), false)).append(">").append( |
403 Const.CONSTANT_Class), false)).append(">").append( |
402 verbose ? " (" + index + ")" : ""); |
404 verbose ? " (" + index + ")" : ""); |
403 break; |
405 break; |
404 /* Multidimensional array of references. |
406 /* Multidimensional array of references. |
405 */ |
407 */ |
406 case Const.MULTIANEWARRAY: { |
408 case Const.MULTIANEWARRAY: { |
407 index = bytes.readUnsignedShort(); |
409 index = bytes.readUnsignedShort(); |
408 final int dimensions = bytes.readUnsignedByte(); |
410 final int dimensions = bytes.readUnsignedByte(); |
409 buf.append("\t<").append( |
411 buf.append("\t<").append( |
410 compactClassName(constant_pool.getConstantString(index, |
412 compactClassName(constant_pool.getConstantString(index, |
411 Const.CONSTANT_Class), false)).append(">\t").append(dimensions) |
413 Const.CONSTANT_Class), false)).append(">\t").append(dimensions) |
412 .append(verbose ? " (" + index + ")" : ""); |
414 .append(verbose ? " (" + index + ")" : ""); |
413 } |
415 } |
414 break; |
416 break; |
415 /* Increment local variable. |
417 /* Increment local variable. |
416 */ |
418 */ |
417 case Const.IINC: |
419 case Const.IINC: |
418 if (wide) { |
420 if (wide) { |
419 vindex = bytes.readUnsignedShort(); |
421 vindex = bytes.readUnsignedShort(); |
446 } |
448 } |
447 } |
449 } |
448 return buf.toString(); |
450 return buf.toString(); |
449 } |
451 } |
450 |
452 |
451 public static String codeToString(final ByteSequence bytes, final ConstantPool constant_pool) |
453 |
|
454 public static String codeToString( final ByteSequence bytes, final ConstantPool constant_pool ) |
452 throws IOException { |
455 throws IOException { |
453 return codeToString(bytes, constant_pool, true); |
456 return codeToString(bytes, constant_pool, true); |
454 } |
457 } |
455 |
458 |
|
459 |
456 /** |
460 /** |
457 * Shorten long class names, <em>java/lang/String</em> becomes |
461 * Shorten long class names, <em>java/lang/String</em> becomes |
458 * <em>String</em>. |
462 * <em>String</em>. |
459 * |
463 * |
460 * @param str The long class name |
464 * @param str The long class name |
461 * @return Compacted class name |
465 * @return Compacted class name |
462 */ |
466 */ |
463 public static String compactClassName(final String str) { |
467 public static String compactClassName( final String str ) { |
464 return compactClassName(str, true); |
468 return compactClassName(str, true); |
465 } |
469 } |
466 |
470 |
|
471 |
467 /** |
472 /** |
468 * Shorten long class name <em>str</em>, i.e., chop off the <em>prefix</em>, |
473 * 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 |
474 * if the |
470 * true. Slashes <em>/</em> are converted to dots <em>.</em>. |
475 * class name starts with this string and the flag <em>chopit</em> is true. |
|
476 * Slashes <em>/</em> are converted to dots <em>.</em>. |
471 * |
477 * |
472 * @param str The long class name |
478 * @param str The long class name |
473 * @param prefix The prefix the get rid off |
479 * @param prefix The prefix the get rid off |
474 * @param chopit Flag that determines whether chopping is executed or not |
480 * @param chopit Flag that determines whether chopping is executed or not |
475 * @return Compacted class name |
481 * @return Compacted class name |
476 */ |
482 */ |
477 public static String compactClassName(String str, final String prefix, final boolean chopit) { |
483 public static String compactClassName( String str, final String prefix, final boolean chopit ) { |
478 final int len = prefix.length(); |
484 final int len = prefix.length(); |
479 str = str.replace('/', '.'); // Is `/' on all systems, even DOS |
485 str = str.replace('/', '.'); // Is `/' on all systems, even DOS |
480 if (chopit) { |
486 if (chopit) { |
481 // If string starts with `prefix' and contains no further dots |
487 // If string starts with `prefix' and contains no further dots |
482 if (str.startsWith(prefix) && (str.substring(len).indexOf('.') == -1)) { |
488 if (str.startsWith(prefix) && (str.substring(len).indexOf('.') == -1)) { |
484 } |
490 } |
485 } |
491 } |
486 return str; |
492 return str; |
487 } |
493 } |
488 |
494 |
|
495 |
489 /** |
496 /** |
490 * Shorten long class names, <em>java/lang/String</em> becomes |
497 * 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 |
498 * <em>java.lang.String</em>, |
492 * prefix <em>java.lang</em> |
499 * e.g.. If <em>chopit</em> is <em>true</em> the prefix <em>java.lang</em> |
493 * is also removed. |
500 * is also removed. |
494 * |
501 * |
495 * @param str The long class name |
502 * @param str The long class name |
496 * @param chopit Flag that determines whether chopping is executed or not |
503 * @param chopit Flag that determines whether chopping is executed or not |
497 * @return Compacted class name |
504 * @return Compacted class name |
498 */ |
505 */ |
499 public static String compactClassName(final String str, final boolean chopit) { |
506 public static String compactClassName( final String str, final boolean chopit ) { |
500 return compactClassName(str, "java.lang.", chopit); |
507 return compactClassName(str, "java.lang.", chopit); |
501 } |
508 } |
502 |
509 |
|
510 |
503 /** |
511 /** |
504 * @return `flag' with bit `i' set to 1 |
512 * @return `flag' with bit `i' set to 1 |
505 */ |
513 */ |
506 public static int setBit(final int flag, final int i) { |
514 public static int setBit( final int flag, final int i ) { |
507 return flag | pow2(i); |
515 return flag | pow2(i); |
508 } |
516 } |
509 |
517 |
|
518 |
510 /** |
519 /** |
511 * @return `flag' with bit `i' set to 0 |
520 * @return `flag' with bit `i' set to 0 |
512 */ |
521 */ |
513 public static int clearBit(final int flag, final int i) { |
522 public static int clearBit( final int flag, final int i ) { |
514 final int bit = pow2(i); |
523 final int bit = pow2(i); |
515 return (flag & bit) == 0 ? flag : flag ^ bit; |
524 return (flag & bit) == 0 ? flag : flag ^ bit; |
516 } |
525 } |
517 |
526 |
|
527 |
518 /** |
528 /** |
519 * @return true, if bit `i' in `flag' is set |
529 * @return true, if bit `i' in `flag' is set |
520 */ |
530 */ |
521 public static boolean isSet(final int flag, final int i) { |
531 public static boolean isSet( final int flag, final int i ) { |
522 return (flag & pow2(i)) != 0; |
532 return (flag & pow2(i)) != 0; |
523 } |
533 } |
524 |
534 |
525 /** |
535 |
526 * Converts string containing the method return and argument types to a byte |
536 /** |
527 * code method signature. |
537 * Converts string containing the method return and argument types |
528 * |
538 * to a byte code method signature. |
529 * @param ret Return type of method |
539 * |
530 * @param argv Types of method arguments |
540 * @param ret Return type of method |
|
541 * @param argv Types of method arguments |
531 * @return Byte code representation of method signature |
542 * @return Byte code representation of method signature |
532 * |
543 * |
533 * @throws ClassFormatException if the signature is for Void |
544 * @throws ClassFormatException if the signature is for Void |
534 */ |
545 */ |
535 public static String methodTypeToSignature(final String ret, final String[] argv) |
546 public static String methodTypeToSignature( final String ret, final String[] argv ) |
536 throws ClassFormatException { |
547 throws ClassFormatException { |
537 final StringBuilder buf = new StringBuilder("("); |
548 final StringBuilder buf = new StringBuilder("("); |
538 String str; |
549 String str; |
539 if (argv != null) { |
550 if (argv != null) { |
540 for (final String element : argv) { |
551 for (final String element : argv) { |
548 str = getSignature(ret); |
559 str = getSignature(ret); |
549 buf.append(")").append(str); |
560 buf.append(")").append(str); |
550 return buf.toString(); |
561 return buf.toString(); |
551 } |
562 } |
552 |
563 |
553 /** |
564 |
554 * @param signature Method signature |
565 /** |
|
566 * @param signature Method signature |
555 * @return Array of argument types |
567 * @return Array of argument types |
556 * @throws ClassFormatException |
568 * @throws ClassFormatException |
557 */ |
569 */ |
558 public static String[] methodSignatureArgumentTypes(final String signature) |
570 public static String[] methodSignatureArgumentTypes( final String signature ) |
559 throws ClassFormatException { |
571 throws ClassFormatException { |
560 return methodSignatureArgumentTypes(signature, true); |
572 return methodSignatureArgumentTypes(signature, true); |
561 } |
573 } |
562 |
574 |
563 /** |
575 |
564 * @param signature Method signature |
576 /** |
|
577 * @param signature Method signature |
565 * @param chopit Shorten class names ? |
578 * @param chopit Shorten class names ? |
566 * @return Array of argument types |
579 * @return Array of argument types |
567 * @throws ClassFormatException |
580 * @throws ClassFormatException |
568 */ |
581 */ |
569 public static String[] methodSignatureArgumentTypes(final String signature, final boolean chopit) |
582 public static String[] methodSignatureArgumentTypes( final String signature, final boolean chopit ) |
570 throws ClassFormatException { |
583 throws ClassFormatException { |
571 final List<String> vec = new ArrayList<>(); |
584 final List<String> vec = new ArrayList<>(); |
572 int index; |
585 int index; |
573 try { // Read all declarations between for `(' and `)' |
586 try { // Read all declarations between for `(' and `)' |
574 if (signature.charAt(0) != '(') { |
587 if (signature.charAt(0) != '(') { |
584 throw new ClassFormatException("Invalid method signature: " + signature, e); |
597 throw new ClassFormatException("Invalid method signature: " + signature, e); |
585 } |
598 } |
586 return vec.toArray(new String[vec.size()]); |
599 return vec.toArray(new String[vec.size()]); |
587 } |
600 } |
588 |
601 |
589 /** |
602 |
590 * @param signature Method signature |
603 /** |
|
604 * @param signature Method signature |
591 * @return return type of method |
605 * @return return type of method |
592 * @throws ClassFormatException |
606 * @throws ClassFormatException |
593 */ |
607 */ |
594 public static String methodSignatureReturnType(final String signature) |
608 public static String methodSignatureReturnType( final String signature ) throws ClassFormatException { |
595 throws ClassFormatException { |
|
596 return methodSignatureReturnType(signature, true); |
609 return methodSignatureReturnType(signature, true); |
597 } |
610 } |
598 |
611 |
599 /** |
612 |
600 * @param signature Method signature |
613 /** |
|
614 * @param signature Method signature |
601 * @param chopit Shorten class names ? |
615 * @param chopit Shorten class names ? |
602 * @return return type of method |
616 * @return return type of method |
603 * @throws ClassFormatException |
617 * @throws ClassFormatException |
604 */ |
618 */ |
605 public static String methodSignatureReturnType(final String signature, |
619 public static String methodSignatureReturnType( final String signature, final boolean chopit ) throws ClassFormatException { |
606 final boolean chopit) throws ClassFormatException { |
|
607 int index; |
620 int index; |
608 String type; |
621 String type; |
609 try { |
622 try { |
610 // Read return type after `)' |
623 // Read return type after `)' |
611 index = signature.lastIndexOf(')') + 1; |
624 index = signature.lastIndexOf(')') + 1; |
614 throw new ClassFormatException("Invalid method signature: " + signature, e); |
627 throw new ClassFormatException("Invalid method signature: " + signature, e); |
615 } |
628 } |
616 return type; |
629 return type; |
617 } |
630 } |
618 |
631 |
|
632 |
619 /** |
633 /** |
620 * Converts method signature to string with all class names compacted. |
634 * Converts method signature to string with all class names compacted. |
621 * |
635 * |
622 * @param signature to convert |
636 * @param signature to convert |
623 * @param name of method |
637 * @param name of method |
624 * @param access flags of method |
638 * @param access flags of method |
625 * @return Human readable signature |
639 * @return Human readable signature |
626 */ |
640 */ |
627 public static String methodSignatureToString(final String signature, |
641 public static String methodSignatureToString( final String signature, final String name, final String access ) { |
628 final String name, final String access) { |
|
629 return methodSignatureToString(signature, name, access, true); |
642 return methodSignatureToString(signature, name, access, true); |
630 } |
643 } |
631 |
644 |
632 public static String methodSignatureToString(final String signature, |
645 |
633 final String name, final String access, final boolean chopit) { |
646 public static String methodSignatureToString( final String signature, final String name, final String access, final boolean chopit ) { |
634 return methodSignatureToString(signature, name, access, chopit, null); |
647 return methodSignatureToString(signature, name, access, chopit, null); |
635 } |
648 } |
636 |
649 |
637 /** |
650 |
638 * A returntype signature represents the return value from a method. It is a |
651 /** |
639 * series of bytes in the following grammar: |
652 * A returntype signature represents the return value from a method. |
|
653 * It is a series of bytes in the following grammar: |
640 * |
654 * |
641 * <pre> |
655 * <pre> |
642 * <return_signature> ::= <field_type> | V |
656 * <return_signature> ::= <field_type> | V |
643 * </pre> |
657 * </pre> |
644 * |
658 * |
645 * The character V indicates that the method returns no value. Otherwise, |
659 * The character V indicates that the method returns no value. Otherwise, the |
646 * the signature indicates the type of the return value. An argument |
660 * signature indicates the type of the return value. |
647 * signature represents an argument passed to a method: |
661 * An argument signature represents an argument passed to a method: |
648 * |
662 * |
649 * <pre> |
663 * <pre> |
650 * <argument_signature> ::= <field_type> |
664 * <argument_signature> ::= <field_type> |
651 * </pre> |
665 * </pre> |
652 * |
666 * |
659 * |
673 * |
660 * This method converts such a string into a Java type declaration like |
674 * This method converts such a string into a Java type declaration like |
661 * `void main(String[])' and throws a `ClassFormatException' when the parsed |
675 * `void main(String[])' and throws a `ClassFormatException' when the parsed |
662 * type is invalid. |
676 * type is invalid. |
663 * |
677 * |
664 * @param signature Method signature |
678 * @param signature Method signature |
665 * @param name Method name |
679 * @param name Method name |
666 * @param access Method access rights |
680 * @param access Method access rights |
667 * @param chopit |
681 * @param chopit |
668 * @param vars |
682 * @param vars |
669 * @return Java type declaration |
683 * @return Java type declaration |
670 * @throws ClassFormatException |
684 * @throws ClassFormatException |
671 */ |
685 */ |
672 public static String methodSignatureToString(final String signature, final String name, |
686 public static String methodSignatureToString( final String signature, final String name, |
673 final String access, final boolean chopit, final LocalVariableTable vars) |
687 final String access, final boolean chopit, final LocalVariableTable vars ) throws ClassFormatException { |
674 throws ClassFormatException { |
|
675 final StringBuilder buf = new StringBuilder("("); |
688 final StringBuilder buf = new StringBuilder("("); |
676 String type; |
689 String type; |
677 int index; |
690 int index; |
678 int var_index = access.contains("static") ? 0 : 1; |
691 int var_index = access.contains("static") ? 0 : 1; |
679 try { // Read all declarations between for `(' and `)' |
692 try { // Read all declarations between for `(' and `)' |
713 buf.append(")"); |
726 buf.append(")"); |
714 return access + ((access.length() > 0) ? " " : "") + // May be an empty string |
727 return access + ((access.length() > 0) ? " " : "") + // May be an empty string |
715 type + " " + name + buf.toString(); |
728 type + " " + name + buf.toString(); |
716 } |
729 } |
717 |
730 |
|
731 |
718 // Guess what this does |
732 // Guess what this does |
719 private static int pow2(final int n) { |
733 private static int pow2( final int n ) { |
720 return 1 << n; |
734 return 1 << n; |
721 } |
735 } |
722 |
736 |
723 /** |
737 |
724 * Replace all occurrences of <em>old</em> in <em>str</em> with |
738 /** |
725 * <em>new</em>. |
739 * Replace all occurrences of <em>old</em> in <em>str</em> with <em>new</em>. |
726 * |
740 * |
727 * @param str String to permute |
741 * @param str String to permute |
728 * @param old String to be replaced |
742 * @param old String to be replaced |
729 * @param new_ Replacement string |
743 * @param new_ Replacement string |
730 * @return new String object |
744 * @return new String object |
731 */ |
745 */ |
732 public static String replace(String str, final String old, final String new_) { |
746 public static String replace( String str, final String old, final String new_ ) { |
733 int index; |
747 int index; |
734 int old_index; |
748 int old_index; |
735 try { |
749 try { |
736 if (str.contains(old)) { // `old' found in str |
750 if (str.contains(old)) { // `old' found in str |
737 final StringBuilder buf = new StringBuilder(); |
751 final StringBuilder buf = new StringBuilder(); |
788 * |
804 * |
789 * This method converts this string into a Java type declaration such as |
805 * This method converts this string into a Java type declaration such as |
790 * `String[]' and throws a `ClassFormatException' when the parsed type is |
806 * `String[]' and throws a `ClassFormatException' when the parsed type is |
791 * invalid. |
807 * invalid. |
792 * |
808 * |
793 * @param signature Class signature |
809 * @param signature Class signature |
794 * @param chopit Flag that determines whether chopping is executed or not |
810 * @param chopit Flag that determines whether chopping is executed or not |
795 * @return Java type declaration |
811 * @return Java type declaration |
796 * @throws ClassFormatException |
812 * @throws ClassFormatException |
797 */ |
813 */ |
798 public static String signatureToString(final String signature, final boolean chopit) { |
814 public static String signatureToString( final String signature, final boolean chopit ) { |
799 //corrected concurrent private static field acess |
815 //corrected concurrent private static field acess |
800 wrap(consumed_chars, 1); // This is the default, read just one char like `B' |
816 wrap(consumed_chars, 1); // This is the default, read just one char like `B' |
801 try { |
817 try { |
802 switch (signature.charAt(0)) { |
818 switch (signature.charAt(0)) { |
803 case 'B': |
819 case 'B': |
835 } |
851 } |
836 final int index = signature.indexOf(';', fromIndex); // Look for closing `;' |
852 final int index = signature.indexOf(';', fromIndex); // Look for closing `;' |
837 if (index < 0) { |
853 if (index < 0) { |
838 throw new ClassFormatException("Invalid signature: " + signature); |
854 throw new ClassFormatException("Invalid signature: " + signature); |
839 } |
855 } |
|
856 |
840 // check to see if there are any TypeArguments |
857 // check to see if there are any TypeArguments |
841 final int bracketIndex = signature.substring(0, index).indexOf('<'); |
858 final int bracketIndex = signature.substring(0, index).indexOf('<'); |
842 if (bracketIndex < 0) { |
859 if (bracketIndex < 0) { |
843 // just a class identifier |
860 // just a class identifier |
844 wrap(consumed_chars, index + 1); // "Lblabla;" `L' and `;' are removed |
861 wrap(consumed_chars, index + 1); // "Lblabla;" `L' and `;' are removed |
845 return compactClassName(signature.substring(1, index), chopit); |
862 return compactClassName(signature.substring(1, index), chopit); |
846 } |
863 } |
|
864 // but make sure we are not looking past the end of the current item |
|
865 fromIndex = signature.indexOf(';'); |
|
866 if (fromIndex < 0) { |
|
867 throw new ClassFormatException("Invalid signature: " + signature); |
|
868 } |
|
869 if (fromIndex < bracketIndex) { |
|
870 // just a class identifier |
|
871 wrap(consumed_chars, fromIndex + 1); // "Lblabla;" `L' and `;' are removed |
|
872 return compactClassName(signature.substring(1, fromIndex), chopit); |
|
873 } |
847 |
874 |
848 // we have TypeArguments; build up partial result |
875 // we have TypeArguments; build up partial result |
849 // as we recurse for each TypeArgument |
876 // as we recurse for each TypeArgument |
850 final StringBuilder type = new StringBuilder( |
877 final StringBuilder type = new StringBuilder(compactClassName(signature.substring(1, bracketIndex), chopit)).append("<"); |
851 compactClassName(signature.substring(1, bracketIndex), chopit)) |
|
852 .append("<"); |
|
853 int consumed_chars = bracketIndex + 1; // Shadows global var |
878 int consumed_chars = bracketIndex + 1; // Shadows global var |
854 |
879 |
855 // check for wildcards |
880 // check for wildcards |
856 if (signature.charAt(consumed_chars) == '+') { |
881 if (signature.charAt(consumed_chars) == '+') { |
857 type.append("? extends "); |
882 type.append("? extends "); |
858 consumed_chars++; |
883 consumed_chars++; |
859 } else if (signature.charAt(consumed_chars) == '-') { |
884 } else if (signature.charAt(consumed_chars) == '-') { |
860 type.append("? super "); |
885 type.append("? super "); |
861 consumed_chars++; |
886 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 } |
887 } |
873 |
888 |
874 // get the first TypeArgument |
889 // get the first TypeArgument |
875 type.append(signatureToString(signature.substring(consumed_chars), chopit)); |
890 if (signature.charAt(consumed_chars) == '*') { |
876 // update our consumed count by the number of characters the for type argument |
891 type.append("?"); |
877 consumed_chars = unwrap(Utility.consumed_chars) + consumed_chars; |
892 consumed_chars++; |
878 wrap(Utility.consumed_chars, consumed_chars); |
893 } else { |
879 |
894 type.append(signatureToString(signature.substring(consumed_chars), chopit)); |
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 |
895 // update our consumed count by the number of characters the for type argument |
884 consumed_chars = unwrap(Utility.consumed_chars) + consumed_chars; |
896 consumed_chars = unwrap(Utility.consumed_chars) + consumed_chars; |
885 wrap(Utility.consumed_chars, consumed_chars); |
897 wrap(Utility.consumed_chars, consumed_chars); |
886 } |
898 } |
887 |
899 |
888 if (signature.charAt(consumed_chars + 1) != ';') { |
900 // are there more TypeArguments? |
|
901 while (signature.charAt(consumed_chars) != '>') { |
|
902 type.append(", "); |
|
903 // check for wildcards |
|
904 if (signature.charAt(consumed_chars) == '+') { |
|
905 type.append("? extends "); |
|
906 consumed_chars++; |
|
907 } else if (signature.charAt(consumed_chars) == '-') { |
|
908 type.append("? super "); |
|
909 consumed_chars++; |
|
910 } |
|
911 if (signature.charAt(consumed_chars) == '*') { |
|
912 type.append("?"); |
|
913 consumed_chars++; |
|
914 } else { |
|
915 type.append(signatureToString(signature.substring(consumed_chars), chopit)); |
|
916 // update our consumed count by the number of characters the for type argument |
|
917 consumed_chars = unwrap(Utility.consumed_chars) + consumed_chars; |
|
918 wrap(Utility.consumed_chars, consumed_chars); |
|
919 } |
|
920 } |
|
921 |
|
922 // process the closing ">" |
|
923 consumed_chars++; |
|
924 type.append(">"); |
|
925 |
|
926 if (signature.charAt(consumed_chars) == '.') { |
|
927 // we have a ClassTypeSignatureSuffix |
|
928 type.append("."); |
|
929 // convert SimpleClassTypeSignature to fake ClassTypeSignature |
|
930 // and then recurse to parse it |
|
931 type.append(signatureToString("L" + signature.substring(consumed_chars+1), chopit)); |
|
932 // update our consumed count by the number of characters the for type argument |
|
933 // note that this count includes the "L" we added, but that is ok |
|
934 // as it accounts for the "." we didn't consume |
|
935 consumed_chars = unwrap(Utility.consumed_chars) + consumed_chars; |
|
936 wrap(Utility.consumed_chars, consumed_chars); |
|
937 return type.toString(); |
|
938 } |
|
939 if (signature.charAt(consumed_chars) != ';') { |
889 throw new ClassFormatException("Invalid signature: " + signature); |
940 throw new ClassFormatException("Invalid signature: " + signature); |
890 } |
941 } |
891 wrap(Utility.consumed_chars, consumed_chars + 2); // remove final ">;" |
942 wrap(Utility.consumed_chars, consumed_chars + 1); // remove final ";" |
892 return type.append(">").toString(); |
943 return type.toString(); |
893 } |
944 } |
894 case 'S': |
945 case 'S': |
895 return "short"; |
946 return "short"; |
896 case 'Z': |
947 case 'Z': |
897 return "boolean"; |
948 return "boolean"; |
922 } catch (final StringIndexOutOfBoundsException e) { // Should never occur |
973 } catch (final StringIndexOutOfBoundsException e) { // Should never occur |
923 throw new ClassFormatException("Invalid signature: " + signature, e); |
974 throw new ClassFormatException("Invalid signature: " + signature, e); |
924 } |
975 } |
925 } |
976 } |
926 |
977 |
927 /** |
978 |
928 * Parse Java type such as "char", or "java.lang.String[]" and return the |
979 /** 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;" |
980 * signature in byte code format, e.g. "C" or "[Ljava/lang/String;" respectively. |
930 * respectively. |
981 * |
931 * |
982 * @param type Java type |
932 * @param type Java type |
|
933 * @return byte code signature |
983 * @return byte code signature |
934 */ |
984 */ |
935 public static String getSignature(String type) { |
985 public static String getSignature( String type ) { |
936 final StringBuilder buf = new StringBuilder(); |
986 final StringBuilder buf = new StringBuilder(); |
937 final char[] chars = type.toCharArray(); |
987 final char[] chars = type.toCharArray(); |
938 boolean char_found = false; |
988 boolean char_found = false; |
939 boolean delim = false; |
989 boolean delim = false; |
940 int index = -1; |
990 int index = -1; |
941 loop: |
991 loop: for (int i = 0; i < chars.length; i++) { |
942 for (int i = 0; i < chars.length; i++) { |
|
943 switch (chars[i]) { |
992 switch (chars[i]) { |
944 case ' ': |
993 case ' ': |
945 case '\t': |
994 case '\t': |
946 case '\n': |
995 case '\n': |
947 case '\r': |
996 case '\r': |
1083 } catch (final StringIndexOutOfBoundsException e) { |
1134 } catch (final StringIndexOutOfBoundsException e) { |
1084 throw new ClassFormatException("Invalid method signature: " + signature, e); |
1135 throw new ClassFormatException("Invalid method signature: " + signature, e); |
1085 } |
1136 } |
1086 } |
1137 } |
1087 |
1138 |
1088 /** |
1139 |
1089 * Map opcode names to opcode numbers. E.g., return Constants.ALOAD for |
1140 /** Map opcode names to opcode numbers. E.g., return Constants.ALOAD for "aload" |
1090 * "aload" |
1141 */ |
1091 */ |
1142 public static short searchOpcode( String name ) { |
1092 public static short searchOpcode(String name) { |
|
1093 name = name.toLowerCase(Locale.ENGLISH); |
1143 name = name.toLowerCase(Locale.ENGLISH); |
1094 for (short i = 0; i < Const.OPCODE_NAMES_LENGTH; i++) { |
1144 for (short i = 0; i < Const.OPCODE_NAMES_LENGTH; i++) { |
1095 if (Const.getOpcodeName(i).equals(name)) { |
1145 if (Const.getOpcodeName(i).equals(name)) { |
1096 return i; |
1146 return i; |
1097 } |
1147 } |
1098 } |
1148 } |
1099 return -1; |
1149 return -1; |
1100 } |
1150 } |
1101 |
1151 |
|
1152 |
1102 /** |
1153 /** |
1103 * Convert (signed) byte to (unsigned) short value, i.e., all negative |
1154 * Convert (signed) byte to (unsigned) short value, i.e., all negative |
1104 * values become positive. |
1155 * values become positive. |
1105 */ |
1156 */ |
1106 private static short byteToShort(final byte b) { |
1157 private static short byteToShort( final byte b ) { |
1107 return (b < 0) ? (short) (256 + b) : (short) b; |
1158 return (b < 0) ? (short) (256 + b) : (short) b; |
1108 } |
1159 } |
1109 |
1160 |
1110 /** |
1161 |
1111 * Convert bytes into hexadecimal string |
1162 /** Convert bytes into hexadecimal string |
1112 * |
1163 * |
1113 * @param bytes an array of bytes to convert to hexadecimal |
1164 * @param bytes an array of bytes to convert to hexadecimal |
1114 * |
1165 * |
1115 * @return bytes as hexadecimal string, e.g. 00 fa 12 ... |
1166 * @return bytes as hexadecimal string, e.g. 00 fa 12 ... |
1116 */ |
1167 */ |
1117 public static String toHexString(final byte[] bytes) { |
1168 public static String toHexString( final byte[] bytes ) { |
1118 final StringBuilder buf = new StringBuilder(); |
1169 final StringBuilder buf = new StringBuilder(); |
1119 for (int i = 0; i < bytes.length; i++) { |
1170 for (int i = 0; i < bytes.length; i++) { |
1120 final short b = byteToShort(bytes[i]); |
1171 final short b = byteToShort(bytes[i]); |
1121 final String hex = Integer.toHexString(b); |
1172 final String hex = Integer.toHexString(b); |
1122 if (b < 0x10) { |
1173 if (b < 0x10) { |
1128 } |
1179 } |
1129 } |
1180 } |
1130 return buf.toString(); |
1181 return buf.toString(); |
1131 } |
1182 } |
1132 |
1183 |
|
1184 |
1133 /** |
1185 /** |
1134 * Return a string for an integer justified left or right and filled up with |
1186 * Return a string for an integer justified left or right and filled up with |
1135 * `fill' characters if necessary. |
1187 * `fill' characters if necessary. |
1136 * |
1188 * |
1137 * @param i integer to format |
1189 * @param i integer to format |
1138 * @param length length of desired string |
1190 * @param length length of desired string |
1139 * @param left_justify format left or right |
1191 * @param left_justify format left or right |
1140 * @param fill fill character |
1192 * @param fill fill character |
1141 * @return formatted int |
1193 * @return formatted int |
1142 */ |
1194 */ |
1143 public static String format(final int i, final int length, |
1195 public static String format( final int i, final int length, final boolean left_justify, final char fill ) { |
1144 final boolean left_justify, final char fill) { |
|
1145 return fillup(Integer.toString(i), length, left_justify, fill); |
1196 return fillup(Integer.toString(i), length, left_justify, fill); |
1146 } |
1197 } |
1147 |
1198 |
1148 /** |
1199 |
1149 * Fillup char with up to length characters with char `fill' and justify it |
1200 /** |
1150 * left or right. |
1201 * Fillup char with up to length characters with char `fill' and justify it left or right. |
1151 * |
1202 * |
1152 * @param str string to format |
1203 * @param str string to format |
1153 * @param length length of desired string |
1204 * @param length length of desired string |
1154 * @param left_justify format left or right |
1205 * @param left_justify format left or right |
1155 * @param fill fill character |
1206 * @param fill fill character |
1156 * @return formatted string |
1207 * @return formatted string |
1157 */ |
1208 */ |
1158 public static String fillup(final String str, final int length, |
1209 public static String fillup( final String str, final int length, final boolean left_justify, final char fill ) { |
1159 final boolean left_justify, final char fill) { |
|
1160 final int len = length - str.length(); |
1210 final int len = length - str.length(); |
1161 final char[] buf = new char[(len < 0) ? 0 : len]; |
1211 final char[] buf = new char[(len < 0) ? 0 : len]; |
1162 for (int j = 0; j < buf.length; j++) { |
1212 for (int j = 0; j < buf.length; j++) { |
1163 buf[j] = fill; |
1213 buf[j] = fill; |
1164 } |
1214 } |
1179 } |
1230 } |
1180 } |
1231 } |
1181 return true; |
1232 return true; |
1182 } |
1233 } |
1183 |
1234 |
1184 public static void printArray(final PrintStream out, final Object[] obj) { |
1235 |
|
1236 public static void printArray( final PrintStream out, final Object[] obj ) { |
1185 out.println(printArray(obj, true)); |
1237 out.println(printArray(obj, true)); |
1186 } |
1238 } |
1187 |
1239 |
1188 public static void printArray(final PrintWriter out, final Object[] obj) { |
1240 |
|
1241 public static void printArray( final PrintWriter out, final Object[] obj ) { |
1189 out.println(printArray(obj, true)); |
1242 out.println(printArray(obj, true)); |
1190 } |
1243 } |
1191 |
1244 |
1192 public static String printArray(final Object[] obj) { |
1245 |
|
1246 public static String printArray( final Object[] obj ) { |
1193 return printArray(obj, true); |
1247 return printArray(obj, true); |
1194 } |
1248 } |
1195 |
1249 |
1196 public static String printArray(final Object[] obj, final boolean braces) { |
1250 |
|
1251 public static String printArray( final Object[] obj, final boolean braces ) { |
1197 return printArray(obj, braces, false); |
1252 return printArray(obj, braces, false); |
1198 } |
1253 } |
1199 |
1254 |
1200 public static String printArray(final Object[] obj, final boolean braces, final boolean quote) { |
1255 |
|
1256 public static String printArray( final Object[] obj, final boolean braces, final boolean quote ) { |
1201 if (obj == null) { |
1257 if (obj == null) { |
1202 return null; |
1258 return null; |
1203 } |
1259 } |
1204 final StringBuilder buf = new StringBuilder(); |
1260 final StringBuilder buf = new StringBuilder(); |
1205 if (braces) { |
1261 if (braces) { |
1219 buf.append('}'); |
1275 buf.append('}'); |
1220 } |
1276 } |
1221 return buf.toString(); |
1277 return buf.toString(); |
1222 } |
1278 } |
1223 |
1279 |
|
1280 |
1224 /** |
1281 /** |
1225 * @param ch the character to test if it's part of an identifier |
1282 * @param ch the character to test if it's part of an identifier |
1226 * |
1283 * |
1227 * @return true, if character is one of (a, ... z, A, ... Z, 0, ... 9, _) |
1284 * @return true, if character is one of (a, ... z, A, ... Z, 0, ... 9, _) |
1228 */ |
1285 */ |
1229 public static boolean isJavaIdentifierPart(final char ch) { |
1286 public static boolean isJavaIdentifierPart( final char ch ) { |
1230 return ((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) |
1287 return ((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) |
1231 || ((ch >= '0') && (ch <= '9')) || (ch == '_'); |
1288 || ((ch >= '0') && (ch <= '9')) || (ch == '_'); |
1232 } |
1289 } |
1233 |
1290 |
1234 /** |
1291 |
1235 * Encode byte array it into Java identifier string, i.e., a string that |
1292 /** |
1236 * only contains the following characters: (a, ... z, A, ... Z, 0, ... 9, _, |
1293 * Encode byte array it into Java identifier string, i.e., a string |
1237 * $). The encoding algorithm itself is not too clever: if the current |
1294 * that only contains the following characters: (a, ... z, A, ... Z, |
1238 * byte's ASCII value already is a valid Java identifier part, leave it as |
1295 * 0, ... 9, _, $). The encoding algorithm itself is not too |
1239 * it is. Otherwise it writes the escape character($) followed by: |
1296 * clever: if the current byte's ASCII value already is a valid Java |
|
1297 * identifier part, leave it as it is. Otherwise it writes the |
|
1298 * escape character($) followed by: |
1240 * |
1299 * |
1241 * <ul> |
1300 * <ul> |
1242 * <li> the ASCII value as a hexadecimal string, if the value is not in the |
1301 * <li> the ASCII value as a hexadecimal string, if the value is not in the range 200..247</li> |
1243 * range 200..247</li> |
1302 * <li>a Java identifier char not used in a lowercase hexadecimal string, if the value is in the 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> |
1303 * </ul> |
1247 * |
1304 * |
1248 * <p> |
1305 * <p>This operation inflates the original byte array by roughly 40-50%</p> |
1249 * This operation inflates the original byte array by roughly 40-50%</p> |
|
1250 * |
1306 * |
1251 * @param bytes the byte array to convert |
1307 * @param bytes the byte array to convert |
1252 * @param compress use gzip to minimize string |
1308 * @param compress use gzip to minimize string |
1253 * |
1309 * |
1254 * @throws IOException if there's a gzip exception |
1310 * @throws IOException if there's a gzip exception |
1352 final int j = in.read(); |
1409 final int j = in.read(); |
1353 if (j < 0) { |
1410 if (j < 0) { |
1354 return -1; |
1411 return -1; |
1355 } |
1412 } |
1356 final char[] tmp = { |
1413 final char[] tmp = { |
1357 (char) i, (char) j |
1414 (char) i, (char) j |
1358 }; |
1415 }; |
1359 final int s = Integer.parseInt(new String(tmp), 16); |
1416 final int s = Integer.parseInt(new String(tmp), 16); |
1360 return s; |
1417 return s; |
1361 } |
1418 } |
1362 return MAP_CHAR[i]; |
1419 return MAP_CHAR[i]; |
1363 } |
1420 } |
1364 |
1421 |
|
1422 |
1365 @Override |
1423 @Override |
1366 public int read(final char[] cbuf, final int off, final int len) throws IOException { |
1424 public int read( final char[] cbuf, final int off, final int len ) throws IOException { |
1367 for (int i = 0; i < len; i++) { |
1425 for (int i = 0; i < len; i++) { |
1368 cbuf[off + i] = (char) read(); |
1426 cbuf[off + i] = (char) read(); |
1369 } |
1427 } |
1370 return len; |
1428 return len; |
1371 } |
1429 } |
1372 } |
1430 } |
1373 |
1431 |
1374 /** |
1432 /** |
1375 * Encode bytes into valid java identifier characters. Used by <a |
1433 * Encode bytes into valid java identifier characters. |
1376 * href="Utility.html#encode(byte[], boolean)">encode()</a> |
1434 * Used by <a href="Utility.html#encode(byte[], boolean)">encode()</a> |
1377 */ |
1435 */ |
1378 private static class JavaWriter extends FilterWriter { |
1436 private static class JavaWriter extends FilterWriter { |
1379 |
1437 |
1380 public JavaWriter(final Writer out) { |
1438 public JavaWriter(final Writer out) { |
1381 super(out); |
1439 super(out); |
1382 } |
1440 } |
1383 |
1441 |
|
1442 |
1384 @Override |
1443 @Override |
1385 public void write(final int b) throws IOException { |
1444 public void write( final int b ) throws IOException { |
1386 if (isJavaIdentifierPart((char) b) && (b != ESCAPE_CHAR)) { |
1445 if (isJavaIdentifierPart((char) b) && (b != ESCAPE_CHAR)) { |
1387 out.write(b); |
1446 out.write(b); |
1388 } else { |
1447 } else { |
1389 out.write(ESCAPE_CHAR); // Escape character |
1448 out.write(ESCAPE_CHAR); // Escape character |
1390 // Special escape |
1449 // Special escape |
1401 } |
1460 } |
1402 } |
1461 } |
1403 } |
1462 } |
1404 } |
1463 } |
1405 |
1464 |
|
1465 |
1406 @Override |
1466 @Override |
1407 public void write(final char[] cbuf, final int off, final int len) throws IOException { |
1467 public void write( final char[] cbuf, final int off, final int len ) throws IOException { |
1408 for (int i = 0; i < len; i++) { |
1468 for (int i = 0; i < len; i++) { |
1409 write(cbuf[off + i]); |
1469 write(cbuf[off + i]); |
1410 } |
1470 } |
1411 } |
1471 } |
1412 |
1472 |
|
1473 |
1413 @Override |
1474 @Override |
1414 public void write(final String str, final int off, final int len) throws IOException { |
1475 public void write( final String str, final int off, final int len ) throws IOException { |
1415 write(str.toCharArray(), off, len); |
1476 write(str.toCharArray(), off, len); |
1416 } |
1477 } |
1417 } |
1478 } |
1418 |
1479 |
|
1480 |
1419 /** |
1481 /** |
1420 * Escape all occurences of newline chars '\n', quotes \", etc. |
1482 * Escape all occurences of newline chars '\n', quotes \", etc. |
1421 */ |
1483 */ |
1422 public static String convertString(final String label) { |
1484 public static String convertString( final String label ) { |
1423 final char[] ch = label.toCharArray(); |
1485 final char[] ch = label.toCharArray(); |
1424 final StringBuilder buf = new StringBuilder(); |
1486 final StringBuilder buf = new StringBuilder(); |
1425 for (final char element : ch) { |
1487 for (final char element : ch) { |
1426 switch (element) { |
1488 switch (element) { |
1427 case '\n': |
1489 case '\n': |