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