1 /*
   2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   3  *
   4  * This code is free software; you can redistribute it and/or modify it
   5  * under the terms of the GNU General Public License version 2 only, as
   6  * published by the Free Software Foundation.  Oracle designates this
   7  * particular file as subject to the "Classpath" exception as provided
   8  * by Oracle in the LICENSE file that accompanied this code.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  */
  24 
  25 /*
  26  * This file is available under and governed by the GNU General Public
  27  * License version 2 only, as published by the Free Software Foundation.
  28  * However, the following notice accompanied the original version of this
  29  * file:
  30  *
  31  * ASM: a very small and fast Java bytecode manipulation framework
  32  * Copyright (c) 2000-2011 INRIA, France Telecom
  33  * All rights reserved.
  34  *
  35  * Redistribution and use in source and binary forms, with or without
  36  * modification, are permitted provided that the following conditions
  37  * are met:
  38  * 1. Redistributions of source code must retain the above copyright
  39  *    notice, this list of conditions and the following disclaimer.
  40  * 2. Redistributions in binary form must reproduce the above copyright
  41  *    notice, this list of conditions and the following disclaimer in the
  42  *    documentation and/or other materials provided with the distribution.
  43  * 3. Neither the name of the copyright holders nor the names of its
  44  *    contributors may be used to endorse or promote products derived from
  45  *    this software without specific prior written permission.
  46  *
  47  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  48  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  49  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  50  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  51  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  52  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  53  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  54  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  55  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  56  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  57  * THE POSSIBILITY OF SUCH DAMAGE.
  58  */
  59 package jdk.internal.org.objectweb.asm.util;
  60 
  61 import java.io.PrintWriter;
  62 import java.util.ArrayList;
  63 import java.util.List;
  64 
  65 import jdk.internal.org.objectweb.asm.Attribute;
  66 import jdk.internal.org.objectweb.asm.Handle;
  67 import jdk.internal.org.objectweb.asm.Label;
  68 import jdk.internal.org.objectweb.asm.Opcodes;
  69 import jdk.internal.org.objectweb.asm.TypePath;
  70 
  71 /**
  72  * An abstract converter from visit events to text.
  73  *
  74  * @author Eric Bruneton
  75  */
  76 public abstract class Printer {
  77 
  78     /**
  79      * The names of the Java Virtual Machine opcodes.
  80      */
  81     public static final String[] OPCODES;
  82 
  83     /**
  84      * The names of the for <code>operand</code> parameter values of the
  85      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitIntInsn} method when
  86      * <code>opcode</code> is <code>NEWARRAY</code>.
  87      */
  88     public static final String[] TYPES;
  89 
  90     /**
  91      * The names of the <code>tag</code> field values for
  92      * {@link jdk.internal.org.objectweb.asm.Handle}.
  93      */
  94     public static final String[] HANDLE_TAG;
  95 
  96     static {
  97         String s = "NOP,ACONST_NULL,ICONST_M1,ICONST_0,ICONST_1,ICONST_2,"
  98                 + "ICONST_3,ICONST_4,ICONST_5,LCONST_0,LCONST_1,FCONST_0,"
  99                 + "FCONST_1,FCONST_2,DCONST_0,DCONST_1,BIPUSH,SIPUSH,LDC,,,"
 100                 + "ILOAD,LLOAD,FLOAD,DLOAD,ALOAD,,,,,,,,,,,,,,,,,,,,,IALOAD,"
 101                 + "LALOAD,FALOAD,DALOAD,AALOAD,BALOAD,CALOAD,SALOAD,ISTORE,"
 102                 + "LSTORE,FSTORE,DSTORE,ASTORE,,,,,,,,,,,,,,,,,,,,,IASTORE,"
 103                 + "LASTORE,FASTORE,DASTORE,AASTORE,BASTORE,CASTORE,SASTORE,POP,"
 104                 + "POP2,DUP,DUP_X1,DUP_X2,DUP2,DUP2_X1,DUP2_X2,SWAP,IADD,LADD,"
 105                 + "FADD,DADD,ISUB,LSUB,FSUB,DSUB,IMUL,LMUL,FMUL,DMUL,IDIV,LDIV,"
 106                 + "FDIV,DDIV,IREM,LREM,FREM,DREM,INEG,LNEG,FNEG,DNEG,ISHL,LSHL,"
 107                 + "ISHR,LSHR,IUSHR,LUSHR,IAND,LAND,IOR,LOR,IXOR,LXOR,IINC,I2L,"
 108                 + "I2F,I2D,L2I,L2F,L2D,F2I,F2L,F2D,D2I,D2L,D2F,I2B,I2C,I2S,LCMP,"
 109                 + "FCMPL,FCMPG,DCMPL,DCMPG,IFEQ,IFNE,IFLT,IFGE,IFGT,IFLE,"
 110                 + "IF_ICMPEQ,IF_ICMPNE,IF_ICMPLT,IF_ICMPGE,IF_ICMPGT,IF_ICMPLE,"
 111                 + "IF_ACMPEQ,IF_ACMPNE,GOTO,JSR,RET,TABLESWITCH,LOOKUPSWITCH,"
 112                 + "IRETURN,LRETURN,FRETURN,DRETURN,ARETURN,RETURN,GETSTATIC,"
 113                 + "PUTSTATIC,GETFIELD,PUTFIELD,INVOKEVIRTUAL,INVOKESPECIAL,"
 114                 + "INVOKESTATIC,INVOKEINTERFACE,INVOKEDYNAMIC,NEW,NEWARRAY,"
 115                 + "ANEWARRAY,ARRAYLENGTH,ATHROW,CHECKCAST,INSTANCEOF,"
 116                 + "MONITORENTER,MONITOREXIT,,MULTIANEWARRAY,IFNULL,IFNONNULL,";
 117         OPCODES = new String[200];
 118         int i = 0;
 119         int j = 0;
 120         int l;
 121         while ((l = s.indexOf(',', j)) > 0) {
 122             OPCODES[i++] = j + 1 == l ? null : s.substring(j, l);
 123             j = l + 1;
 124         }
 125 
 126         s = "T_BOOLEAN,T_CHAR,T_FLOAT,T_DOUBLE,T_BYTE,T_SHORT,T_INT,T_LONG,";
 127         TYPES = new String[12];
 128         j = 0;
 129         i = 4;
 130         while ((l = s.indexOf(',', j)) > 0) {
 131             TYPES[i++] = s.substring(j, l);
 132             j = l + 1;
 133         }
 134 
 135         s = "H_GETFIELD,H_GETSTATIC,H_PUTFIELD,H_PUTSTATIC,"
 136                 + "H_INVOKEVIRTUAL,H_INVOKESTATIC,H_INVOKESPECIAL,"
 137                 + "H_NEWINVOKESPECIAL,H_INVOKEINTERFACE,";
 138         HANDLE_TAG = new String[10];
 139         j = 0;
 140         i = 1;
 141         while ((l = s.indexOf(',', j)) > 0) {
 142             HANDLE_TAG[i++] = s.substring(j, l);
 143             j = l + 1;
 144         }
 145     }
 146 
 147     /**
 148      * The ASM API version implemented by this class. The value of this field
 149      * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
 150      */
 151     protected final int api;
 152 
 153     /**
 154      * A buffer that can be used to create strings.
 155      */
 156     protected final StringBuffer buf;
 157 
 158     /**
 159      * The text to be printed. Since the code of methods is not necessarily
 160      * visited in sequential order, one method after the other, but can be
 161      * interlaced (some instructions from method one, then some instructions
 162      * from method two, then some instructions from method one again...), it is
 163      * not possible to print the visited instructions directly to a sequential
 164      * stream. A class is therefore printed in a two steps process: a string
 165      * tree is constructed during the visit, and printed to a sequential stream
 166      * at the end of the visit. This string tree is stored in this field, as a
 167      * string list that can contain other string lists, which can themselves
 168      * contain other string lists, and so on.
 169      */
 170     public final List<Object> text;
 171 
 172     /**
 173      * Constructs a new {@link Printer}.
 174      */
 175     protected Printer(final int api) {
 176         this.api = api;
 177         this.buf = new StringBuffer();
 178         this.text = new ArrayList<Object>();
 179     }
 180 
 181     /**
 182      * Class header. See {@link jdk.internal.org.objectweb.asm.ClassVisitor#visit}.
 183      */
 184     public abstract void visit(final int version, final int access,
 185             final String name, final String signature, final String superName,
 186             final String[] interfaces);
 187 
 188     /**
 189      * Class source. See {@link jdk.internal.org.objectweb.asm.ClassVisitor#visitSource}.
 190      */
 191     public abstract void visitSource(final String file, final String debug);
 192 
 193     /**
 194      * Class outer class. See
 195      * {@link jdk.internal.org.objectweb.asm.ClassVisitor#visitOuterClass}.
 196      */
 197     public abstract void visitOuterClass(final String owner, final String name,
 198             final String desc);
 199 
 200     /**
 201      * Class annotation. See
 202      * {@link jdk.internal.org.objectweb.asm.ClassVisitor#visitAnnotation}.
 203      */
 204     public abstract Printer visitClassAnnotation(final String desc,
 205             final boolean visible);
 206 
 207     /**
 208      * Class type annotation. See
 209      * {@link jdk.internal.org.objectweb.asm.ClassVisitor#visitTypeAnnotation}.
 210      */
 211     public Printer visitClassTypeAnnotation(final int typeRef,
 212             final TypePath typePath, final String desc, final boolean visible) {
 213         throw new RuntimeException("Must be overriden");
 214     }
 215 
 216     /**
 217      * Class attribute. See
 218      * {@link jdk.internal.org.objectweb.asm.ClassVisitor#visitAttribute}.
 219      */
 220     public abstract void visitClassAttribute(final Attribute attr);
 221 
 222     /**
 223      * Class inner name. See
 224      * {@link jdk.internal.org.objectweb.asm.ClassVisitor#visitInnerClass}.
 225      */
 226     public abstract void visitInnerClass(final String name,
 227             final String outerName, final String innerName, final int access);
 228 
 229     /**
 230      * Class field. See {@link jdk.internal.org.objectweb.asm.ClassVisitor#visitField}.
 231      */
 232     public abstract Printer visitField(final int access, final String name,
 233             final String desc, final String signature, final Object value);
 234 
 235     /**
 236      * Class method. See {@link jdk.internal.org.objectweb.asm.ClassVisitor#visitMethod}.
 237      */
 238     public abstract Printer visitMethod(final int access, final String name,
 239             final String desc, final String signature, final String[] exceptions);
 240 
 241     /**
 242      * Class end. See {@link jdk.internal.org.objectweb.asm.ClassVisitor#visitEnd}.
 243      */
 244     public abstract void visitClassEnd();
 245 
 246     // ------------------------------------------------------------------------
 247     // Annotations
 248     // ------------------------------------------------------------------------
 249 
 250     /**
 251      * Annotation value. See {@link jdk.internal.org.objectweb.asm.AnnotationVisitor#visit}.
 252      */
 253     public abstract void visit(final String name, final Object value);
 254 
 255     /**
 256      * Annotation enum value. See
 257      * {@link jdk.internal.org.objectweb.asm.AnnotationVisitor#visitEnum}.
 258      */
 259     public abstract void visitEnum(final String name, final String desc,
 260             final String value);
 261 
 262     /**
 263      * Nested annotation value. See
 264      * {@link jdk.internal.org.objectweb.asm.AnnotationVisitor#visitAnnotation}.
 265      */
 266     public abstract Printer visitAnnotation(final String name, final String desc);
 267 
 268     /**
 269      * Annotation array value. See
 270      * {@link jdk.internal.org.objectweb.asm.AnnotationVisitor#visitArray}.
 271      */
 272     public abstract Printer visitArray(final String name);
 273 
 274     /**
 275      * Annotation end. See {@link jdk.internal.org.objectweb.asm.AnnotationVisitor#visitEnd}.
 276      */
 277     public abstract void visitAnnotationEnd();
 278 
 279     // ------------------------------------------------------------------------
 280     // Fields
 281     // ------------------------------------------------------------------------
 282 
 283     /**
 284      * Field annotation. See
 285      * {@link jdk.internal.org.objectweb.asm.FieldVisitor#visitAnnotation}.
 286      */
 287     public abstract Printer visitFieldAnnotation(final String desc,
 288             final boolean visible);
 289 
 290     /**
 291      * Field type annotation. See
 292      * {@link jdk.internal.org.objectweb.asm.FieldVisitor#visitTypeAnnotation}.
 293      */
 294     public Printer visitFieldTypeAnnotation(final int typeRef,
 295             final TypePath typePath, final String desc, final boolean visible) {
 296         throw new RuntimeException("Must be overriden");
 297     }
 298 
 299     /**
 300      * Field attribute. See
 301      * {@link jdk.internal.org.objectweb.asm.FieldVisitor#visitAttribute}.
 302      */
 303     public abstract void visitFieldAttribute(final Attribute attr);
 304 
 305     /**
 306      * Field end. See {@link jdk.internal.org.objectweb.asm.FieldVisitor#visitEnd}.
 307      */
 308     public abstract void visitFieldEnd();
 309 
 310     // ------------------------------------------------------------------------
 311     // Methods
 312     // ------------------------------------------------------------------------
 313 
 314     /**
 315      * Method parameter. See
 316      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitParameter(String, int)}.
 317      */
 318     public void visitParameter(String name, int access) {
 319         throw new RuntimeException("Must be overriden");
 320     }
 321 
 322     /**
 323      * Method default annotation. See
 324      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitAnnotationDefault}.
 325      */
 326     public abstract Printer visitAnnotationDefault();
 327 
 328     /**
 329      * Method annotation. See
 330      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitAnnotation}.
 331      */
 332     public abstract Printer visitMethodAnnotation(final String desc,
 333             final boolean visible);
 334 
 335     /**
 336      * Method type annotation. See
 337      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitTypeAnnotation}.
 338      */
 339     public Printer visitMethodTypeAnnotation(final int typeRef,
 340             final TypePath typePath, final String desc, final boolean visible) {
 341         throw new RuntimeException("Must be overriden");
 342     }
 343 
 344     /**
 345      * Method parameter annotation. See
 346      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitParameterAnnotation}.
 347      */
 348     public abstract Printer visitParameterAnnotation(final int parameter,
 349             final String desc, final boolean visible);
 350 
 351     /**
 352      * Method attribute. See
 353      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitAttribute}.
 354      */
 355     public abstract void visitMethodAttribute(final Attribute attr);
 356 
 357     /**
 358      * Method start. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitCode}.
 359      */
 360     public abstract void visitCode();
 361 
 362     /**
 363      * Method stack frame. See
 364      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitFrame}.
 365      */
 366     public abstract void visitFrame(final int type, final int nLocal,
 367             final Object[] local, final int nStack, final Object[] stack);
 368 
 369     /**
 370      * Method instruction. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitInsn}
 371      * .
 372      */
 373     public abstract void visitInsn(final int opcode);
 374 
 375     /**
 376      * Method instruction. See
 377      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitIntInsn}.
 378      */
 379     public abstract void visitIntInsn(final int opcode, final int operand);
 380 
 381     /**
 382      * Method instruction. See
 383      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitVarInsn}.
 384      */
 385     public abstract void visitVarInsn(final int opcode, final int var);
 386 
 387     /**
 388      * Method instruction. See
 389      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitTypeInsn}.
 390      */
 391     public abstract void visitTypeInsn(final int opcode, final String type);
 392 
 393     /**
 394      * Method instruction. See
 395      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitFieldInsn}.
 396      */
 397     public abstract void visitFieldInsn(final int opcode, final String owner,
 398             final String name, final String desc);
 399 
 400     /**
 401      * Method instruction. See
 402      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitMethodInsn}.
 403      */
 404     @Deprecated
 405     public void visitMethodInsn(final int opcode, final String owner,
 406             final String name, final String desc) {
 407         if (api >= Opcodes.ASM5) {
 408             boolean itf = opcode == Opcodes.INVOKEINTERFACE;
 409             visitMethodInsn(opcode, owner, name, desc, itf);
 410             return;
 411         }
 412         throw new RuntimeException("Must be overriden");
 413     }
 414 
 415     /**
 416      * Method instruction. See
 417      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitMethodInsn}.
 418      */
 419     public void visitMethodInsn(final int opcode, final String owner,
 420             final String name, final String desc, final boolean itf) {
 421         if (api < Opcodes.ASM5) {
 422             if (itf != (opcode == Opcodes.INVOKEINTERFACE)) {
 423                 throw new IllegalArgumentException(
 424                         "INVOKESPECIAL/STATIC on interfaces require ASM 5");
 425             }
 426             visitMethodInsn(opcode, owner, name, desc);
 427             return;
 428         }
 429         throw new RuntimeException("Must be overriden");
 430     }
 431 
 432     /**
 433      * Method instruction. See
 434      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitInvokeDynamicInsn}.
 435      */
 436     public abstract void visitInvokeDynamicInsn(String name, String desc,
 437             Handle bsm, Object... bsmArgs);
 438 
 439     /**
 440      * Method instruction. See
 441      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitJumpInsn}.
 442      */
 443     public abstract void visitJumpInsn(final int opcode, final Label label);
 444 
 445     /**
 446      * Method label. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitLabel}.
 447      */
 448     public abstract void visitLabel(final Label label);
 449 
 450     /**
 451      * Method instruction. See
 452      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitLdcInsn}.
 453      */
 454     public abstract void visitLdcInsn(final Object cst);
 455 
 456     /**
 457      * Method instruction. See
 458      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitIincInsn}.
 459      */
 460     public abstract void visitIincInsn(final int var, final int increment);
 461 
 462     /**
 463      * Method instruction. See
 464      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitTableSwitchInsn}.
 465      */
 466     public abstract void visitTableSwitchInsn(final int min, final int max,
 467             final Label dflt, final Label... labels);
 468 
 469     /**
 470      * Method instruction. See
 471      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitLookupSwitchInsn}.
 472      */
 473     public abstract void visitLookupSwitchInsn(final Label dflt,
 474             final int[] keys, final Label[] labels);
 475 
 476     /**
 477      * Method instruction. See
 478      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitMultiANewArrayInsn}.
 479      */
 480     public abstract void visitMultiANewArrayInsn(final String desc,
 481             final int dims);
 482 
 483     /**
 484      * Instruction type annotation. See
 485      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitInsnAnnotation}.
 486      */
 487     public Printer visitInsnAnnotation(final int typeRef,
 488             final TypePath typePath, final String desc, final boolean visible) {
 489         throw new RuntimeException("Must be overriden");
 490     }
 491 
 492     /**
 493      * Method exception handler. See
 494      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitTryCatchBlock}.
 495      */
 496     public abstract void visitTryCatchBlock(final Label start, final Label end,
 497             final Label handler, final String type);
 498 
 499     /**
 500      * Try catch block type annotation. See
 501      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitTryCatchAnnotation}.
 502      */
 503     public Printer visitTryCatchAnnotation(final int typeRef,
 504             final TypePath typePath, final String desc, final boolean visible) {
 505         throw new RuntimeException("Must be overriden");
 506     }
 507 
 508     /**
 509      * Method debug info. See
 510      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitLocalVariable}.
 511      */
 512     public abstract void visitLocalVariable(final String name,
 513             final String desc, final String signature, final Label start,
 514             final Label end, final int index);
 515 
 516     /**
 517      * Local variable type annotation. See
 518      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitTryCatchAnnotation}.
 519      */
 520     public Printer visitLocalVariableAnnotation(final int typeRef,
 521             final TypePath typePath, final Label[] start, final Label[] end,
 522             final int[] index, final String desc, final boolean visible) {
 523         throw new RuntimeException("Must be overriden");
 524     }
 525 
 526     /**
 527      * Method debug info. See
 528      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitLineNumber}.
 529      */
 530     public abstract void visitLineNumber(final int line, final Label start);
 531 
 532     /**
 533      * Method max stack and max locals. See
 534      * {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitMaxs}.
 535      */
 536     public abstract void visitMaxs(final int maxStack, final int maxLocals);
 537 
 538     /**
 539      * Method end. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitEnd}.
 540      */
 541     public abstract void visitMethodEnd();
 542 
 543     /**
 544      * Returns the text constructed by this visitor.
 545      *
 546      * @return the text constructed by this visitor.
 547      */
 548     public List<Object> getText() {
 549         return text;
 550     }
 551 
 552     /**
 553      * Prints the text constructed by this visitor.
 554      *
 555      * @param pw
 556      *            the print writer to be used.
 557      */
 558     public void print(final PrintWriter pw) {
 559         printList(pw, text);
 560     }
 561 
 562     /**
 563      * Appends a quoted string to a given buffer.
 564      *
 565      * @param buf
 566      *            the buffer where the string must be added.
 567      * @param s
 568      *            the string to be added.
 569      */
 570     public static void appendString(final StringBuffer buf, final String s) {
 571         buf.append('\"');
 572         for (int i = 0; i < s.length(); ++i) {
 573             char c = s.charAt(i);
 574             if (c == '\n') {
 575                 buf.append("\\n");
 576             } else if (c == '\r') {
 577                 buf.append("\\r");
 578             } else if (c == '\\') {
 579                 buf.append("\\\\");
 580             } else if (c == '"') {
 581                 buf.append("\\\"");
 582             } else if (c < 0x20 || c > 0x7f) {
 583                 buf.append("\\u");
 584                 if (c < 0x10) {
 585                     buf.append("000");
 586                 } else if (c < 0x100) {
 587                     buf.append("00");
 588                 } else if (c < 0x1000) {
 589                     buf.append('0');
 590                 }
 591                 buf.append(Integer.toString(c, 16));
 592             } else {
 593                 buf.append(c);
 594             }
 595         }
 596         buf.append('\"');
 597     }
 598 
 599     /**
 600      * Prints the given string tree.
 601      *
 602      * @param pw
 603      *            the writer to be used to print the tree.
 604      * @param l
 605      *            a string tree, i.e., a string list that can contain other
 606      *            string lists, and so on recursively.
 607      */
 608     static void printList(final PrintWriter pw, final List<?> l) {
 609         for (int i = 0; i < l.size(); ++i) {
 610             Object o = l.get(i);
 611             if (o instanceof List) {
 612                 printList(pw, (List<?>) o);
 613             } else {
 614                 pw.print(o.toString());
 615             }
 616         }
 617     }
 618 }