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      * &lt;return_signature&gt; ::= &lt;field_type&gt; | 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      * &lt;argument_signature&gt; ::= &lt;field_type&gt;
661      * </pre>
662      *
663      * A method signature represents the arguments that the method expects, and
664      * the value that it returns.
665      * <pre>
666      * &lt;method_signature&gt; ::= (&lt;arguments_signature&gt;) &lt;return_signature&gt;
667      * &lt;arguments_signature&gt;::= &lt;argument_signature&gt;*
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      * &lt;field_signature&gt; ::= &lt;field_type&gt;
783      * &lt;field_type&gt;      ::= &lt;base_type&gt;|&lt;object_type&gt;|&lt;array_type&gt;
784      * &lt;base_type&gt;       ::= B|C|D|F|I|J|S|Z
785      * &lt;object_type&gt;     ::= L&lt;fullclassname&gt;;
786      * &lt;array_type&gt;      ::= [&lt;field_type&gt;
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&lt;fullclassname&gt;; ... an object of the given class
796      * S short signed short
797      * Z boolean true or false
798      * [&lt;field sig&gt; ... 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