XMLPrinter.java

  1. /*
  2.  *  Copyright (c) 2001-2024, Jean Tessier
  3.  *  All rights reserved.
  4.  *  
  5.  *  Redistribution and use in source and binary forms, with or without
  6.  *  modification, are permitted provided that the following conditions
  7.  *  are met:
  8.  *  
  9.  *      * Redistributions of source code must retain the above copyright
  10.  *        notice, this list of conditions and the following disclaimer.
  11.  *  
  12.  *      * Redistributions in binary form must reproduce the above copyright
  13.  *        notice, this list of conditions and the following disclaimer in the
  14.  *        documentation and/or other materials provided with the distribution.
  15.  *  
  16.  *      * Neither the name of Jean Tessier nor the names of his contributors
  17.  *        may be used to endorse or promote products derived from this software
  18.  *        without specific prior written permission.
  19.  *  
  20.  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21.  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22.  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23.  *  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
  24.  *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  25.  *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  26.  *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  27.  *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  28.  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  29.  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  30.  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31.  */

  32. package com.jeantessier.classreader;

  33. import com.jeantessier.text.Hex;

  34. import java.io.*;
  35. import java.util.*;

  36. public class XMLPrinter extends Printer {
  37.     public static final String DEFAULT_ENCODING   = "utf-8";
  38.     public static final String DEFAULT_DTD_PREFIX = "https://jeantessier.github.io/dependency-finder/dtd";

  39.     private static final BitFormat format = new BitFormat(16);

  40.     private boolean top = true;

  41.     public XMLPrinter(PrintWriter out) {
  42.         this(out, DEFAULT_ENCODING, DEFAULT_DTD_PREFIX);
  43.     }
  44.    
  45.     public XMLPrinter(PrintWriter out, String encoding, String dtdPrefix) {
  46.         super(out);
  47.        
  48.         appendHeader(encoding, dtdPrefix);
  49.     }

  50.     private Printer appendHeader(String encoding, String dtdPrefix) {
  51.         append("<?xml version=\"1.0\" encoding=\"").append(encoding).append("\" ?>").eol();
  52.         eol();
  53.         append("<!DOCTYPE classfiles SYSTEM \"").append(dtdPrefix).append("/classfile.dtd\">").eol();
  54.         eol();

  55.         return this;
  56.     }

  57.     public void visitClassfiles(Collection<Classfile> classfiles) {
  58.         indent().append("<classfiles>").eol();
  59.         raiseIndent();

  60.         super.visitClassfiles(classfiles);

  61.         lowerIndent();
  62.         indent().append("</classfiles>").eol();
  63.     }
  64.    
  65.     public void visitClassfile(Classfile classfile) {
  66.         indent().append("<!-- ").append(classfile.getClassName()).append(" -->").eol();
  67.         indent().append("<classfile magic-number=\"0x").append(Integer.toHexString(classfile.getMagicNumber()).toUpperCase()).append("\" minor-version=\"").append(classfile.getMinorVersion()).append("\" major-version=\"").append(classfile.getMajorVersion()).append("\" access-flags=\"").append(format.format(classfile.getAccessFlags())).append("\">").eol();
  68.         raiseIndent();

  69.         top = true;
  70.         classfile.getConstantPool().accept(this);
  71.         top = false;
  72.        
  73.         if (classfile.isPublic())     indent().append("<public/>").eol();
  74.         if (classfile.isFinal())      indent().append("<final/>").eol();
  75.         if (classfile.isSuper())      indent().append("<super/>").eol();
  76.         if (classfile.isInterface())  indent().append("<is-interface/>").eol();
  77.         if (classfile.isAbstract())   indent().append("<abstract/>").eol();
  78.         if (classfile.isSynthetic())  indent().append("<synthetic/>").eol();
  79.         if (classfile.isAnnotation()) indent().append("<is-annotation/>").eol();
  80.         if (classfile.isEnum())       indent().append("<enum/>").eol();
  81.         if (classfile.isModule())     indent().append("<is-module/>").eol();

  82.         indent();
  83.         append("<this-class>");
  84.         classfile.getRawClass().accept(this);
  85.         append("</this-class>").eol();

  86.         indent();
  87.         append("<superclass>");
  88.         if (classfile.hasSuperclass()) {
  89.             classfile.getRawSuperclass().accept(this);
  90.         }
  91.         append("</superclass>").eol();

  92.         if (!classfile.getAllInterfaces().isEmpty()) {
  93.             indent().append("<interfaces>").eol();
  94.             raiseIndent();
  95.             for (Class_info class_info : classfile.getAllInterfaces()) {
  96.                 indent();
  97.                 append("<interface>");
  98.                 class_info.accept(this);
  99.                 append("</interface>").eol();
  100.             }
  101.             lowerIndent();
  102.             indent().append("</interfaces>").eol();
  103.         }
  104.        
  105.         if (!classfile.getAllFields().isEmpty()) {
  106.             indent().append("<fields>").eol();
  107.             raiseIndent();

  108.             visitClassfileFields(classfile);

  109.             lowerIndent();
  110.             indent().append("</fields>").eol();
  111.         }

  112.         if (!classfile.getAllMethods().isEmpty()) {
  113.             indent().append("<methods>").eol();
  114.             raiseIndent();

  115.             visitClassfileMethods(classfile);

  116.             lowerIndent();
  117.             indent().append("</methods>").eol();
  118.         }

  119.         if (!classfile.getAttributes().isEmpty()) {
  120.             indent().append("<attributes>").eol();
  121.             raiseIndent();

  122.             visitClassfileAttributes(classfile);

  123.             lowerIndent();
  124.             indent().append("</attributes>").eol();
  125.         }

  126.         lowerIndent();
  127.         indent().append("</classfile>").eol();
  128.     }

  129.     public void visitConstantPool(ConstantPool constantPool) {
  130.         indent().append("<constant-pool>").eol();
  131.         raiseIndent();

  132.         super.visitConstantPool(constantPool);

  133.         lowerIndent();
  134.         indent().append("</constant-pool>").eol();
  135.     }

  136.     public void visitClass_info(Class_info entry) {
  137.         if (top) {
  138.             top = false;
  139.             appendClassInfo(currentIndex(), entry);
  140.             top = true;
  141.         } else {
  142.             // entry.getRawName().accept(this);
  143.             append(entry.getName());
  144.         }
  145.     }

  146.     public void visitFieldRef_info(FieldRef_info entry) {
  147.         Class_info       c   = entry.getRawClass();
  148.         NameAndType_info nat = entry.getRawNameAndType();

  149.         if (top) {
  150.             top = false;
  151.             indent();
  152.             append("<field-ref-info index=\"").append(currentIndex()).append("\">");
  153.             append("<class>");
  154.             c.accept(this);
  155.             append("</class>");
  156.             append("<type>");
  157.             nat.getRawType().accept(this);
  158.             append("</type>");
  159.             append("<name>");
  160.             nat.getRawName().accept(this);
  161.             append("</name>");
  162.             append("</field-ref-info>").eol();
  163.             top = true;
  164.         } else {
  165.             append(DescriptorHelper.getType(nat.getType()));
  166.             append(" ");
  167.             append(entry.getFullSignature());
  168.         }
  169.     }

  170.     public void visitMethodRef_info(MethodRef_info entry) {
  171.         Class_info       c   = entry.getRawClass();
  172.         NameAndType_info nat = entry.getRawNameAndType();

  173.         if (top) {
  174.             top = false;
  175.             indent();
  176.             append("<method-ref-info index=\"").append(currentIndex()).append("\">");
  177.             append("<class>");
  178.             c.accept(this);
  179.             append("</class>");
  180.             append("<name>");
  181.             nat.getRawName().accept(this);
  182.             append("</name>");
  183.             append("<type>");
  184.             nat.getRawType().accept(this);
  185.             append("</type>");
  186.             append("</method-ref-info>").eol();
  187.             top = true;
  188.         } else {
  189.             if (!entry.isConstructor() && !entry.isStaticInitializer()) {
  190.                 append(DescriptorHelper.getReturnType(nat.getType())).append(" ");
  191.             }
  192.             append(entry.getFullSignature());
  193.         }
  194.     }

  195.     public void visitInterfaceMethodRef_info(InterfaceMethodRef_info entry) {
  196.         Class_info       c   = entry.getRawClass();
  197.         NameAndType_info nat = entry.getRawNameAndType();

  198.         if (top) {
  199.             top = false;
  200.             indent();
  201.             append("<interface-method-ref-info index=\"").append(currentIndex()).append("\">");
  202.             append("<class>");
  203.             c.accept(this);
  204.             append("</class>");
  205.             append("<name>");
  206.             nat.getRawName().accept(this);
  207.             append("</name>");
  208.             append("<type>");
  209.             nat.getRawType().accept(this);
  210.             append("</type>");
  211.             append("</interface-method-ref-info>").eol();
  212.             top = true;
  213.         } else {
  214.             append(DescriptorHelper.getReturnType(nat.getType()));
  215.             append(" ");
  216.             append(entry.getFullSignature());
  217.         }
  218.     }

  219.     public void visitString_info(String_info entry) {
  220.         if (top) {
  221.             top = false;
  222.             indent();
  223.             append("<string-info index=\"").append(currentIndex()).append("\">");
  224.             entry.getRawValue().accept(this);
  225.             append("</string-info>").eol();
  226.             top = true;
  227.         } else {
  228.             entry.getRawValue().accept(this);
  229.         }
  230.     }

  231.     public void visitInteger_info(Integer_info entry) {
  232.         if (top) {
  233.             top = false;
  234.             indent();
  235.             append("<integer-info index=\"").append(currentIndex()).append("\">");
  236.             append(entry.getValue());
  237.             append("</integer-info>").eol();
  238.             top = true;
  239.         } else {
  240.             append(entry.getValue());
  241.         }
  242.     }

  243.     public void visitFloat_info(Float_info entry) {
  244.         if (top) {
  245.             top = false;
  246.             indent();
  247.             append("<float-info index=\"").append(currentIndex()).append("\">");
  248.             append(entry.getValue());
  249.             append("</float-info>").eol();
  250.             top = true;
  251.         } else {
  252.             append(entry.getValue());
  253.         }
  254.     }

  255.     public void visitLong_info(Long_info entry) {
  256.         if (top) {
  257.             top = false;
  258.             indent();
  259.             append("<long-info index=\"").append(currentIndex()).append("\">");
  260.             append(entry.getValue());
  261.             append("</long-info>").eol();
  262.             top = true;
  263.         } else {
  264.             append(entry.getValue());
  265.         }
  266.     }

  267.     public void visitDouble_info(Double_info entry) {
  268.         if (top) {
  269.             top = false;
  270.             indent();
  271.             append("<double-info index=\"").append(currentIndex()).append("\">");
  272.             append(entry.getValue());
  273.             append("</double-info>").eol();
  274.             top = true;
  275.         } else {
  276.             append(entry.getValue());
  277.         }
  278.     }

  279.     public void visitNameAndType_info(NameAndType_info entry) {
  280.         if (top) {
  281.             top = false;
  282.             indent();
  283.             append("<name-and-type-info index=\"").append(currentIndex()).append("\">");
  284.             append("<name>");
  285.             entry.getRawName().accept(this);
  286.             append("</name>");
  287.             append("<type>");
  288.             entry.getRawType().accept(this);
  289.             append("</type>");
  290.             append("</name-and-type-info>").eol();
  291.             top = true;
  292.         } else {
  293.             entry.getRawName().accept(this);
  294.             append(" ");
  295.             entry.getRawType().accept(this);
  296.         }
  297.     }

  298.     public void visitUTF8_info(UTF8_info entry) {
  299.         if (top) {
  300.             top = false;
  301.             indent().append("<utf8-info index=\"").append(currentIndex()).append("\">");
  302.             append(escapeXMLCharacters(entry.getValue()));
  303.             append("</utf8-info>").eol();
  304.             top = true;
  305.         } else {
  306.             append(escapeXMLCharacters(entry.getValue()));
  307.         }
  308.     }

  309.     public void visitMethodHandle_info(MethodHandle_info entry) {
  310.         if (top) {
  311.             top = false;
  312.             indent();
  313.             append("<method-handle-info index=\"").append(currentIndex()).append("\">");
  314.             append("<reference-kind kind=\"").append(entry.getRawReferenceKind()).append("\">");
  315.             append(entry.getReferenceKind().getDescription());
  316.             append("</reference-kind>");
  317.             append("<reference index=\"").append(entry.getReferenceIndex()).append("\">");
  318.             entry.getReference().accept(this);
  319.             append("</reference>");
  320.             append("</method-handle-info>").eol();
  321.             top = true;
  322.         } else {
  323.             append(entry.getReferenceKind().getDescription());
  324.             append(" ");
  325.             entry.getReference().accept(this);
  326.         }
  327.     }

  328.     public void visitMethodType_info(MethodType_info entry) {
  329.         if (top) {
  330.             top = false;
  331.             indent();
  332.             append("<method-type-info index=\"").append(currentIndex()).append("\">");
  333.             entry.getRawDescriptor().accept(this);
  334.             append("</method-type-info>").eol();
  335.             top = true;
  336.         } else {
  337.             entry.getRawDescriptor().accept(this);
  338.         }
  339.     }

  340.     public void visitDynamic_info(Dynamic_info entry) {
  341.         NameAndType_info nat = entry.getRawNameAndType();

  342.         if (top) {
  343.             top = false;
  344.             indent();
  345.             append("<dynamic-info index=\"").append(currentIndex()).append("\">");
  346.             append("<bootstrap-method-attr index=\"").append(entry.getBootstrapMethodAttrIndex()).append("\"/>");
  347.             append("<name>");
  348.             nat.getRawName().accept(this);
  349.             append("</name>");
  350.             append("<type>");
  351.             nat.getRawType().accept(this);
  352.             append("</type>");
  353.             append("</dynamic-info>").eol();
  354.             top = true;
  355.         } else {
  356.             if (!entry.isStaticInitializer()) {
  357.                 append(DescriptorHelper.getReturnType(nat.getType())).append(" ");
  358.             }
  359.             append(entry.getSignature());
  360.         }
  361.     }

  362.     public void visitInvokeDynamic_info(InvokeDynamic_info entry) {
  363.         NameAndType_info nat = entry.getRawNameAndType();

  364.         if (top) {
  365.             top = false;
  366.             indent();
  367.             append("<invoke-dynamic-info index=\"").append(currentIndex()).append("\">");
  368.             append("<bootstrap-method-attr index=\"").append(entry.getBootstrapMethodAttrIndex()).append("\"/>");
  369.             append("<name>");
  370.             nat.getRawName().accept(this);
  371.             append("</name>");
  372.             append("<type>");
  373.             nat.getRawType().accept(this);
  374.             append("</type>");
  375.             append("</invoke-dynamic-info>").eol();
  376.             top = true;
  377.         } else {
  378.             if (!entry.isStaticInitializer()) {
  379.                 append(DescriptorHelper.getReturnType(nat.getType())).append(" ");
  380.             }
  381.             append(entry.getSignature());
  382.         }
  383.     }

  384.     public void visitModule_info(Module_info entry) {
  385.         if (top) {
  386.             top = false;
  387.             indent();
  388.             append("<module index=\"").append(currentIndex()).append("\">");
  389.             // entry.getRawName().accept(this);
  390.             append(entry.getName());
  391.             append("</module>").eol();
  392.             top = true;
  393.         } else {
  394.             // entry.getRawName().accept(this);
  395.             append(entry.getName());
  396.         }
  397.     }

  398.     public void visitPackage_info(Package_info entry) {
  399.         if (top) {
  400.             top = false;
  401.             indent();
  402.             append("<package index=\"").append(currentIndex()).append("\">");
  403.             // entry.getRawName().accept(this);
  404.             append(entry.getName());
  405.             append("</package>").eol();
  406.             top = true;
  407.         } else {
  408.             // entry.getRawName().accept(this);
  409.             append(entry.getName());
  410.         }
  411.     }

  412.     public void visitUnusableEntry(UnusableEntry entry) {
  413.         if (top) {
  414.             top = false;
  415.             indent().append("<unusable index=\"").append(currentIndex()).append("\">").append(entry.getReason()).append("</unusable>").eol();
  416.             top = true;
  417.         } else {
  418.             append(entry);
  419.         }
  420.     }

  421.     public void visitField_info(Field_info entry) {
  422.         indent().append("<field-info access-flags=\"").append(format.format(entry.getAccessFlags())).append("\">").eol();
  423.         raiseIndent();

  424.         if (entry.isPublic())    indent().append("<public/>").eol();
  425.         if (entry.isProtected()) indent().append("<protected/>").eol();
  426.         if (entry.isPrivate())   indent().append("<private/>").eol();
  427.         if (entry.isStatic())    indent().append("<static/>").eol();
  428.         if (entry.isFinal())     indent().append("<final/>").eol();
  429.         if (entry.isVolatile())  indent().append("<volatile/>").eol();
  430.         if (entry.isTransient()) indent().append("<transient/>").eol();
  431.         if (entry.isSynthetic()) indent().append("<synthetic/>").eol();
  432.         if (entry.isEnum())      indent().append("<enum/>").eol();

  433.         indent();
  434.         append("<name>");
  435.         entry.getRawName().accept(this);
  436.         append("</name>").eol();
  437.        
  438.         indent().append("<type>").append(entry.getType()).append("</type>").eol();

  439.         if (!entry.getAttributes().isEmpty()) {
  440.             indent().append("<attributes>").eol();
  441.             raiseIndent();
  442.             super.visitField_info(entry);
  443.             lowerIndent();
  444.             indent().append("</attributes>").eol();
  445.         }

  446.         lowerIndent();
  447.         indent().append("</field-info>").eol();
  448.     }

  449.     public void visitMethod_info(Method_info entry) {
  450.         indent().append("<method-info access-flags=\"").append(format.format(entry.getAccessFlags())).append("\">").eol();
  451.         raiseIndent();

  452.         if (entry.isPublic())       indent().append("<public/>").eol();
  453.         if (entry.isProtected())    indent().append("<protected/>").eol();
  454.         if (entry.isPrivate())      indent().append("<private/>").eol();
  455.         if (entry.isStatic())       indent().append("<static/>").eol();
  456.         if (entry.isFinal())        indent().append("<final/>").eol();
  457.         if (entry.isSynchronized()) indent().append("<synchronized/>").eol();
  458.         if (entry.isBridge())       indent().append("<bridge/>").eol();
  459.         if (entry.isVarargs())      indent().append("<varargs/>").eol();
  460.         if (entry.isNative())       indent().append("<native/>").eol();
  461.         if (entry.isAbstract())     indent().append("<abstract/>").eol();
  462.         if (entry.isStrict())       indent().append("<strict/>").eol();
  463.         if (entry.isSynthetic())    indent().append("<synthetic/>").eol();

  464.         indent();
  465.         append("<name>");
  466.         entry.getRawName().accept(this);
  467.         append("</name>").eol();
  468.        
  469.         if (!entry.getName().equals("<init>") && !entry.getName().equals("<clinit>")) {
  470.             indent().append("<return-type>").append((entry.getReturnType() != null) ? entry.getReturnType() : "void").append("</return-type>").eol();
  471.         }
  472.         indent().append("<signature>").append(entry.getSignature()).append("</signature>").eol();

  473.         if (!entry.getAttributes().isEmpty()) {
  474.             indent().append("<attributes>").eol();
  475.             raiseIndent();
  476.             super.visitMethod_info(entry);
  477.             lowerIndent();
  478.             indent().append("</attributes>").eol();
  479.         }

  480.         lowerIndent();
  481.         indent().append("</method-info>").eol();
  482.     }

  483.     public void visitConstantValue_attribute(ConstantValue_attribute attribute) {
  484.         indent().append("<constant-value-attribute>");

  485.         attribute.getRawValue().accept(this);

  486.         append("</constant-value-attribute>").eol();
  487.     }

  488.     public void visitCode_attribute(Code_attribute attribute) {
  489.         indent().append("<code-attribute>").eol();
  490.         raiseIndent();

  491.         indent().append("<length>").append(attribute.getCode().length).append("</length>").eol();

  492.         indent().append("<instructions>").eol();
  493.         raiseIndent();
  494.         for (Instruction instruction : attribute) {
  495.             instruction.accept(this);
  496.         }
  497.         lowerIndent();
  498.         indent().append("</instructions>").eol();

  499.         if (!attribute.getExceptionHandlers().isEmpty()) {
  500.             indent().append("<exception-handlers>").eol();
  501.             raiseIndent();
  502.             for (ExceptionHandler exceptionHandler : attribute.getExceptionHandlers()) {
  503.                 exceptionHandler.accept(this);
  504.             }
  505.             lowerIndent();
  506.             indent().append("</exception-handlers>").eol();
  507.         }
  508.        
  509.         if (!attribute.getAttributes().isEmpty()) {
  510.             indent().append("<attributes>").eol();
  511.             raiseIndent();
  512.             for (Attribute_info attribute_info : attribute.getAttributes()) {
  513.                 attribute_info.accept(this);
  514.             }
  515.             lowerIndent();
  516.             indent().append("</attributes>").eol();
  517.         }
  518.        
  519.         lowerIndent();
  520.         indent().append("</code-attribute>").eol();
  521.     }

  522.     public void visitStackMapTable_attribute(StackMapTable_attribute attribute) {
  523.         indent().append("<stack-map-table-attribute>").eol();
  524.         raiseIndent();

  525.         super.visitStackMapTable_attribute(attribute);

  526.         lowerIndent();
  527.         indent().append("</stack-map-table-attribute>").eol();
  528.     }

  529.     public void visitExceptions_attribute(Exceptions_attribute attribute) {
  530.         indent().append("<exceptions-attribute>").eol();
  531.         raiseIndent();

  532.         attribute.getExceptions().forEach(exception -> {
  533.             indent();
  534.             append("<exception>");
  535.             exception.accept(this);
  536.             append("</exception>").eol();
  537.         });

  538.         lowerIndent();
  539.         indent().append("</exceptions-attribute>").eol();
  540.     }

  541.     public void visitInnerClasses_attribute(InnerClasses_attribute attribute) {
  542.         indent().append("<inner-classes-attribute>").eol();
  543.         raiseIndent();

  544.         super.visitInnerClasses_attribute(attribute);

  545.         lowerIndent();
  546.         indent().append("</inner-classes-attribute>").eol();
  547.     }

  548.     public void visitEnclosingMethod_attribute(EnclosingMethod_attribute attribute) {
  549.         indent().append("<enclosing-method-attribute>").eol();
  550.         raiseIndent();

  551.         appendClassInfo(attribute.getClassIndex(), attribute.getRawClassInfo());

  552.         indent().append("<method>");
  553.         if (attribute.hasMethod()) {
  554.             NameAndType_info nat = attribute.getRawMethod();
  555.             if (nat.getName().equals("<init>")) {
  556.                 String className = attribute.getClassInfo();
  557.                 className = className.substring(className.lastIndexOf(".") + 1);
  558.                 append(className).append(DescriptorHelper.getSignature(nat.getType()));
  559.             } else {
  560.                 append(DescriptorHelper.getReturnType(nat.getType())).append(" ").append(nat.getName()).append(DescriptorHelper.getSignature(nat.getType()));
  561.             }
  562.         }
  563.         append("</method>").eol();

  564.         lowerIndent();
  565.         indent().append("</enclosing-method-attribute>").eol();
  566.     }

  567.     public void visitSynthetic_attribute(Synthetic_attribute attribute) {
  568.         indent().append("<synthetic-attribute/>").eol();
  569.     }

  570.     public void visitSignature_attribute(Signature_attribute attribute) {
  571.         indent().append("<signature-attribute>");
  572.         attribute.getRawSignature().accept(this);
  573.         append("</signature-attribute>").eol();
  574.     }

  575.     public void visitSourceFile_attribute(SourceFile_attribute attribute) {
  576.         indent().append("<source-file-attribute>").append(attribute.getSourceFile()).append("</source-file-attribute>").eol();
  577.     }

  578.     public void visitSourceDebugExtension_attribute(SourceDebugExtension_attribute attribute) {
  579.         indent().append("<source-debug-extension>").append(attribute.getDebugExtension()).append("</source-debug-extension>").eol();
  580.     }

  581.     public void visitLineNumberTable_attribute(LineNumberTable_attribute attribute) {
  582.         indent().append("<line-number-table-attribute>").eol();
  583.         raiseIndent();

  584.         super.visitLineNumberTable_attribute(attribute);

  585.         lowerIndent();
  586.         indent().append("</line-number-table-attribute>").eol();
  587.     }

  588.     public void visitLocalVariableTable_attribute(LocalVariableTable_attribute attribute) {
  589.         indent().append("<local-variable-table-attribute>").eol();
  590.         raiseIndent();

  591.         super.visitLocalVariableTable_attribute(attribute);

  592.         lowerIndent();
  593.         indent().append("</local-variable-table-attribute>").eol();
  594.     }

  595.     public void visitLocalVariableTypeTable_attribute(LocalVariableTypeTable_attribute attribute) {
  596.         indent().append("<local-variable-type-table-attribute>").eol();
  597.         raiseIndent();

  598.         super.visitLocalVariableTypeTable_attribute(attribute);

  599.         lowerIndent();
  600.         indent().append("</local-variable-type-table-attribute>").eol();
  601.     }

  602.     public void visitDeprecated_attribute(Deprecated_attribute attribute) {
  603.         indent().append("<deprecated-attribute/>").eol();
  604.     }

  605.     public void visitRuntimeVisibleAnnotations_attribute(RuntimeVisibleAnnotations_attribute attribute) {
  606.         indent().append("<runtime-visible-annotations-attribute>").eol();
  607.         raiseIndent();

  608.         super.visitRuntimeVisibleAnnotations_attribute(attribute);

  609.         lowerIndent();
  610.         indent().append("</runtime-visible-annotations-attribute>").eol();
  611.     }

  612.     public void visitRuntimeInvisibleAnnotations_attribute(RuntimeInvisibleAnnotations_attribute attribute) {
  613.         indent().append("<runtime-invisible-annotations-attribute>").eol();
  614.         raiseIndent();

  615.         super.visitRuntimeInvisibleAnnotations_attribute(attribute);

  616.         lowerIndent();
  617.         indent().append("</runtime-invisible-annotations-attribute>").eol();
  618.     }

  619.     protected void visitRuntimeAnnotations_attribute(RuntimeAnnotations_attribute attribute) {
  620.         indent().append("<annotations>").eol();
  621.         raiseIndent();

  622.         super.visitRuntimeAnnotations_attribute(attribute);

  623.         lowerIndent();
  624.         indent().append("</annotations>").eol();
  625.     }

  626.     public void visitRuntimeVisibleParameterAnnotations_attribute(RuntimeVisibleParameterAnnotations_attribute attribute) {
  627.         indent().append("<runtime-visible-parameter-annotations-attribute>").eol();
  628.         raiseIndent();

  629.         super.visitRuntimeVisibleParameterAnnotations_attribute(attribute);

  630.         lowerIndent();
  631.         indent().append("</runtime-visible-parameter-annotations-attribute>").eol();
  632.     }

  633.     public void visitRuntimeInvisibleParameterAnnotations_attribute(RuntimeInvisibleParameterAnnotations_attribute attribute) {
  634.         indent().append("<runtime-invisible-parameter-annotations-attribute>").eol();
  635.         raiseIndent();

  636.         super.visitRuntimeInvisibleParameterAnnotations_attribute(attribute);

  637.         lowerIndent();
  638.         indent().append("</runtime-invisible-parameter-annotations-attribute>").eol();
  639.     }

  640.     protected void visitRuntimeParameterAnnotations_attribute(RuntimeParameterAnnotations_attribute attribute) {
  641.         indent().append("<parameter-annotations>").eol();
  642.         raiseIndent();

  643.         super.visitRuntimeParameterAnnotations_attribute(attribute);

  644.         lowerIndent();
  645.         indent().append("</parameter-annotations>").eol();
  646.     }

  647.     public void visitRuntimeVisibleTypeAnnotations_attribute(RuntimeVisibleTypeAnnotations_attribute attribute) {
  648.         indent().append("<runtime-visible-type-annotations-attribute>").eol();
  649.         raiseIndent();

  650.         super.visitRuntimeVisibleTypeAnnotations_attribute(attribute);

  651.         lowerIndent();
  652.         indent().append("</runtime-visible-type-annotations-attribute>").eol();
  653.     }

  654.     public void visitRuntimeInvisibleTypeAnnotations_attribute(RuntimeInvisibleTypeAnnotations_attribute attribute) {
  655.         indent().append("<runtime-invisible-type-annotations-attribute>").eol();
  656.         raiseIndent();

  657.         super.visitRuntimeInvisibleTypeAnnotations_attribute(attribute);

  658.         lowerIndent();
  659.         indent().append("</runtime-invisible-type-annotations-attribute>").eol();
  660.     }

  661.     protected void visitRuntimeTypeAnnotations_attribute(RuntimeTypeAnnotations_attribute attribute) {
  662.         indent().append("<type-annotations>").eol();
  663.         raiseIndent();

  664.         super.visitRuntimeTypeAnnotations_attribute(attribute);

  665.         lowerIndent();
  666.         indent().append("</type-annotations>").eol();
  667.     }

  668.     public void visitAnnotationDefault_attribute(AnnotationDefault_attribute attribute) {
  669.         indent().append("<annotation-default-attribute>").eol();
  670.         raiseIndent();

  671.         super.visitAnnotationDefault_attribute(attribute);

  672.         lowerIndent();
  673.         indent().append("</annotation-default-attribute>").eol();
  674.     }

  675.     public void visitBootstrapMethods_attribute(BootstrapMethods_attribute attribute) {
  676.         indent().append("<bootstrap-methods-attribute>").eol();
  677.         raiseIndent();

  678.         super.visitBootstrapMethods_attribute(attribute);

  679.         lowerIndent();
  680.         indent().append("</bootstrap-methods-attribute>").eol();
  681.     }

  682.     public void visitMethodParameters_attribute(MethodParameters_attribute attribute) {
  683.         indent().append("<method-parameters-attribute>").eol();
  684.         raiseIndent();

  685.         super.visitMethodParameters_attribute(attribute);

  686.         lowerIndent();
  687.         indent().append("</method-parameters-attribute>").eol();
  688.     }

  689.     public void visitModule_attribute(Module_attribute attribute) {
  690.         indent().append("<module-attribute module-flags=\"").append(format.format(attribute.getModuleFlags())).append("\">").eol();
  691.         raiseIndent();

  692.         indent();
  693.         append("<name index=\"").append(attribute.getModuleNameIndex()).append("\">");
  694.         attribute.getRawModuleName().accept(this);
  695.         append("</name>").eol();

  696.         if (attribute.isOpen())      indent().append("<open/>").eol();
  697.         if (attribute.isSynthetic()) indent().append("<synthetic/>").eol();
  698.         if (attribute.isMandated())  indent().append("<mandated/>").eol();

  699.         if (attribute.hasModuleVersion()) {
  700.             indent();
  701.             append("<version index=\"").append(attribute.getModuleVersionIndex()).append("\">");
  702.             attribute.getRawModuleVersion().accept(this);
  703.             append("</version>").eol();
  704.         }

  705.         super.visitModule_attribute(attribute);

  706.         lowerIndent();
  707.         indent().append("</module-attribute>").eol();
  708.     }

  709.     public void visitModulePackages_attribute(ModulePackages_attribute attribute) {
  710.         indent().append("<module-packages-attribute>").eol();
  711.         raiseIndent();

  712.         super.visitModulePackages_attribute(attribute);

  713.         lowerIndent();
  714.         indent().append("</module-packages-attribute>").eol();
  715.     }

  716.     public void visitModuleMainClass_attribute(ModuleMainClass_attribute attribute) {
  717.         indent();
  718.         append("<module-main-class-attribute index=\"").append(attribute.getMainClassIndex()).append("\">");
  719.         attribute.getRawMainClass().accept(this);
  720.         append("</module-main-class-attribute>").eol();
  721.     }

  722.     public void visitNestHost_attribute(NestHost_attribute attribute) {
  723.         indent();
  724.         append("<nest-host-attribute index=\"").append(attribute.getHostClassIndex()).append("\">");
  725.         attribute.getRawHostClass().accept(this);
  726.         append("</nest-host-attribute>").eol();
  727.     }

  728.     public void visitNestMembers_attribute(NestMembers_attribute attribute) {
  729.         indent().append("<nest-members-attribute>").eol();
  730.         raiseIndent();

  731.         super.visitNestMembers_attribute(attribute);

  732.         lowerIndent();
  733.         indent().append("</nest-members-attribute>").eol();
  734.     }

  735.     public void visitRecord_attribute(Record_attribute attribute) {
  736.         indent().append("<record-attribute>").eol();
  737.         raiseIndent();

  738.         super.visitRecord_attribute(attribute);

  739.         lowerIndent();
  740.         indent().append("</record-attribute>").eol();
  741.     }

  742.     public void visitPermittedSubclasses_attribute(PermittedSubclasses_attribute attribute) {
  743.         indent().append("<permitted-subclasses-attribute>").eol();
  744.         raiseIndent();

  745.         super.visitPermittedSubclasses_attribute(attribute);

  746.         lowerIndent();
  747.         indent().append("</permitted-subclasses-attribute>").eol();
  748.     }

  749.     public void visitCustom_attribute(Custom_attribute attribute) {
  750.         indent().append("<custom-attribute name=\"").append(escapeXMLCharacters(attribute.getName())).append("\">").append(Hex.toString(attribute.getInfo())).append("</custom-attribute>").eol();
  751.     }

  752.     public void visitInstruction(Instruction instruction) {
  753.         indent();
  754.         append("<instruction pc=\"").append(instruction.getStart()).append("\" length=\"").append(instruction.getLength()).append("\" op-code=\"0x").append(String.format("%02X", instruction.getOpcode())).append("\"");
  755.         switch (instruction.getOpcode()) {
  756.             case 0x02: // iconst_m1
  757.             case 0x03: // iconst_0
  758.             case 0x04: // iconst_1
  759.             case 0x05: // iconst_2
  760.             case 0x06: // iconst_3
  761.             case 0x07: // iconst_4
  762.             case 0x08: // iconst_5
  763.             case 0x09: // lconst_0
  764.             case 0x0a: // lconst_1
  765.             case 0x0b: // fconst_0
  766.             case 0x0c: // fconst_1
  767.             case 0x0d: // fconst_2
  768.             case 0x0e: // dconst_0
  769.             case 0x0f: // dconst_1
  770.                 append(" value=\"").append(instruction.getValue()).append("\">");
  771.                 append(instruction);
  772.                 break;
  773.             case 0x10: // bipush
  774.             case 0x11: // sipush
  775.                 append(" value=\"").append(instruction.getValue()).append("\">");
  776.                 append(instruction).append(" ").append(instruction.getValue());
  777.                 break;
  778.             case 0x12: // ldc
  779.             case 0x13: // ldc_w
  780.             case 0x14: // ldc2_w
  781.             case 0xb2: // getstatic
  782.             case 0xb3: // putstatic
  783.             case 0xb4: // getfield
  784.             case 0xb5: // putfield
  785.             case 0xb6: // invokevirtual
  786.             case 0xb7: // invokespecial
  787.             case 0xb8: // invokestatic
  788.             case 0xb9: // invokeinterface
  789.             case 0xbb: // new
  790.             case 0xbd: // anewarray
  791.             case 0xc0: // checkcast
  792.             case 0xc1: // instanceof
  793.             case 0xc5: // multianewarray
  794.                 append(" index=\"").append(instruction.getIndex()).append("\">");
  795.                 append(instruction);
  796.                 append(" ");
  797.                 instruction.getIndexedConstantPoolEntry().accept(this);
  798.                 break;
  799.             case 0x1a: // iload_0
  800.             case 0x1e: // lload_0
  801.             case 0x22: // fload_0
  802.             case 0x26: // dload_0
  803.             case 0x2a: // aload_0
  804.             case 0x3b: // istore_0
  805.             case 0x3f: // lstore_0
  806.             case 0x43: // fstore_0
  807.             case 0x47: // dstore_0
  808.             case 0x4b: // astore_0
  809.             case 0x1b: // iload_1
  810.             case 0x1f: // lload_1
  811.             case 0x23: // fload_1
  812.             case 0x27: // dload_1
  813.             case 0x2b: // aload_1
  814.             case 0x3c: // istore_1
  815.             case 0x40: // lstore_1
  816.             case 0x44: // fstore_1
  817.             case 0x48: // dstore_1
  818.             case 0x4c: // astore_1
  819.             case 0x1c: // iload_2
  820.             case 0x20: // lload_2
  821.             case 0x24: // fload_2
  822.             case 0x28: // dload_2
  823.             case 0x2c: // aload_2
  824.             case 0x3d: // istore_2
  825.             case 0x41: // lstore_2
  826.             case 0x45: // fstore_2
  827.             case 0x49: // dstore_2
  828.             case 0x4d: // astore_2
  829.             case 0x1d: // iload_3
  830.             case 0x21: // lload_3
  831.             case 0x25: // fload_3
  832.             case 0x29: // dload_3
  833.             case 0x2d: // aload_3
  834.             case 0x3e: // istore_3
  835.             case 0x42: // lstore_3
  836.             case 0x46: // fstore_3
  837.             case 0x4a: // dstore_3
  838.             case 0x4e: // astore_3
  839.             case 0x15: // iload
  840.             case 0x16: // llload
  841.             case 0x17: // fload
  842.             case 0x18: // dload
  843.             case 0x19: // aload
  844.             case 0x36: // istore
  845.             case 0x37: // lstore
  846.             case 0x38: // fstore
  847.             case 0x39: // dstore
  848.             case 0x3a: // astore
  849.             case 0xa9: // ret
  850.                 append(" index=\"").append(instruction.getIndex()).append("\">");
  851.                 append(instruction);
  852.                 appendLocalVariable(instruction.getIndexedLocalVariable());
  853.                 break;
  854.             case 0x99: // ifeq
  855.             case 0x9a: // ifne
  856.             case 0x9b: // iflt
  857.             case 0x9c: // ifge
  858.             case 0x9d: // ifgt
  859.             case 0x9e: // ifle
  860.             case 0x9f: // if_icmpeq
  861.             case 0xa0: // if_icmpne
  862.             case 0xa1: // if_icmplt
  863.             case 0xa2: // if_icmpge
  864.             case 0xa3: // if_icmpgt
  865.             case 0xa4: // if_icmple
  866.             case 0xa5: // if_acmpeq
  867.             case 0xa6: // if_acmpne
  868.             case 0xa7: // goto
  869.             case 0xa8: // jsr
  870.             case 0xc6: // ifnull
  871.             case 0xc7: // ifnonnull
  872.             case 0xc8: // goto_w
  873.             case 0xc9: // jsr_w
  874.                 append(" offset=\"").append(instruction.getOffset()).append("\">");
  875.                 append(instruction).append(" ").append(instruction.getStart() + instruction.getOffset());
  876.                 break;
  877.             case 0x84: // iinc
  878.                 append(" index=\"").append(instruction.getIndex()).append("\" value=\"").append(instruction.getValue()).append("\">");
  879.                 append(instruction);
  880.                 appendLocalVariable(instruction.getIndexedLocalVariable());
  881.                 break;
  882.             case 0xaa: // tableswitch
  883.                 append(" padding=\"").append(instruction.getPadding()).append("\" default=\"").appendSwitchDefault(instruction).append("\" low=\"").append(instruction.getLow()).append("\" high=\"").append(instruction.getHigh()).append("\">");
  884.                 append(instruction).append(" ").appendTableSwitch(instruction, " | ");
  885.                 break;
  886.             case 0xab: // lookupswitch
  887.                 append(" padding=\"").append(instruction.getPadding()).append("\" default=\"").appendSwitchDefault(instruction).append("\" npairs=\"").append(instruction.getNPairs()).append("\">");
  888.                 append(instruction).append(" ").appendLookupSwitch(instruction, " | ");
  889.                 break;
  890.             case 0xba: // invokedynamic
  891.                 append(" index=\"").append(instruction.getIndex()).append("\">");
  892.                 append(instruction);
  893.                 var indexedEntry = instruction.getIndexedConstantPoolEntry();
  894.                 if (indexedEntry instanceof Dynamic_info dynamic_info) {
  895.                     append(" ").append(dynamic_info.getName());
  896.                 } else if (indexedEntry instanceof InvokeDynamic_info invokeDynamic_info) {
  897.                     append(" ").append(invokeDynamic_info.getName());
  898.                 }
  899.                 // TODO: Replace with type pattern matching in switch expression in Java 21
  900.                 // switch (instruction.getIndexedConstantPoolEntry()) {
  901.                 //     case Dynamic_info entry -> append(" ").append(entry.getName());
  902.                 //     case InvokeDynamic_info entry -> append(" ").append(entry.getName());
  903.                 //     default -> append("");
  904.                 // }
  905.                 instruction.getDynamicConstantPoolEntries().forEach(entry -> {
  906.                     append(" ");
  907.                     entry.accept(this);
  908.                 });
  909.                 break;
  910.             case 0xc4: // wide
  911.                 if (instruction.getByte(1) == 0x84 /* iinc */) {
  912.                     append(" index=\"").append(instruction.getIndex()).append("\" value=\"").append(instruction.getValue()).append("\">");
  913.                 } else {
  914.                     append(" index=\"").append(instruction.getIndex()).append("\">");
  915.                 }
  916.                 append(instruction);
  917.                 appendLocalVariable(instruction.getIndexedLocalVariable());
  918.                 break;
  919.             default:
  920.                 append(">");
  921.                 append(instruction);
  922.                 break;
  923.         }
  924.         append("</instruction>").eol();
  925.     }

  926.     public void visitExceptionHandler(ExceptionHandler helper) {
  927.         indent();
  928.         append("<exception-handler>");
  929.         append("<start-pc>").append(helper.getStartPC()).append("</start-pc>");
  930.         append("<end-pc>").append(helper.getEndPC()).append("</end-pc>");
  931.         append("<handler-pc>").append(helper.getHandlerPC()).append("</handler-pc>");

  932.         append("<catch-type>");
  933.         if (helper.hasCatchType()) {
  934.             helper.getRawCatchType().accept(this);
  935.         }
  936.         append("</catch-type>");

  937.         append("</exception-handler>").eol();
  938.     }

  939.     public void visitInnerClass(InnerClass helper) {
  940.         indent().append("<inner-class access-flags=\"").append(format.format(helper.getAccessFlags())).append("\">").eol();
  941.         raiseIndent();

  942.         if (helper.isPublic())     indent().append("<public/>").eol();
  943.         if (helper.isProtected())  indent().append("<protected/>").eol();
  944.         if (helper.isPrivate())    indent().append("<private/>").eol();
  945.         if (helper.isStatic())     indent().append("<static/>").eol();
  946.         if (helper.isFinal())      indent().append("<final/>").eol();
  947.         if (helper.isInterface())  indent().append("<is-interface/>").eol();
  948.         if (helper.isAbstract())   indent().append("<abstract/>").eol();
  949.         if (helper.isSynthetic())  indent().append("<synthetic/>").eol();
  950.         if (helper.isAnnotation()) indent().append("<is-annotation/>").eol();
  951.         if (helper.isEnum())       indent().append("<enum/>").eol();

  952.         indent();
  953.         append("<inner-class-info>");
  954.         helper.getRawInnerClassInfo().accept(this);
  955.         append("</inner-class-info>").eol();

  956.         indent();
  957.         append("<outer-class-info>");
  958.         if (helper.hasOuterClassInfo()) {
  959.             helper.getRawOuterClassInfo().accept(this);
  960.         }
  961.         append("</outer-class-info>").eol();

  962.         indent();
  963.         append("<inner-name>");
  964.         if (helper.hasInnerName()) {
  965.             helper.getRawInnerName().accept(this);
  966.         }
  967.         append("</inner-name>").eol();

  968.         lowerIndent();
  969.         indent().append("</inner-class>").eol();
  970.     }

  971.     public void visitLineNumber(LineNumber helper) {
  972.         indent();
  973.         append("<line-number>");
  974.         append("<start-pc>").append(helper.getStartPC()).append("</start-pc>");
  975.         append("<line>").append(helper.getLineNumber()).append("</line>");
  976.         append("</line-number>").eol();
  977.     }

  978.     public void visitLocalVariable(LocalVariable helper) {
  979.         indent();
  980.         append("<local-variable pc=\"").append(helper.getStartPC()).append("\" length=\"").append(helper.getLength()).append("\" index=\"").append(helper.getIndex()).append("\">");
  981.         append("<name>");
  982.         helper.getRawName().accept(this);
  983.         append("</name>");

  984.         append("<type>").append(DescriptorHelper.getType(helper.getDescriptor())).append("</type>");
  985.         append("</local-variable>").eol();
  986.     }

  987.     public void visitLocalVariableType(LocalVariableType helper) {
  988.         indent();
  989.         append("<local-variable-type pc=\"").append(helper.getStartPC()).append("\" length=\"").append(helper.getLength()).append("\" index=\"").append(helper.getIndex()).append("\">");
  990.         append("<name>");
  991.         helper.getRawName().accept(this);
  992.         append("</name>");

  993.         append("<signature>");
  994.         helper.getRawSignature().accept(this);
  995.         append("</signature>");
  996.         append("</local-variable-type>").eol();
  997.     }

  998.     public void visitBootstrapMethod(BootstrapMethod helper) {
  999.         indent().append("<bootstrap-method>").eol();
  1000.         raiseIndent();

  1001.         indent();
  1002.         append("<bootstrap-method-ref index=\"").append(helper.getBootstrapMethodRef()).append("\">");
  1003.         helper.getBootstrapMethod().accept(this);
  1004.         append("</bootstrap-method-ref>").eol();

  1005.         indent().append("<arguments>").eol();
  1006.         raiseIndent();

  1007.         helper.getArgumentIndices()
  1008.                 .forEach(index -> {
  1009.                     indent();
  1010.                     append("<argument index=\"").append(index).append("\">");
  1011.                     helper.getArgument(index).accept(this);
  1012.                     append("</argument>").eol();
  1013.                 });

  1014.         lowerIndent();
  1015.         indent().append("</arguments>").eol();

  1016.         lowerIndent();
  1017.         indent().append("</bootstrap-method>").eol();
  1018.     }

  1019.     public void visitMethodParameter(MethodParameter helper) {
  1020.         indent().append("<method-parameter access-flags=\"").append(format.format(helper.getAccessFlags())).append("\">").eol();
  1021.         raiseIndent();

  1022.         if (helper.hasName()) {
  1023.             indent();
  1024.             append("<name>");
  1025.             helper.getRawName().accept(this);
  1026.             append("</name>").eol();
  1027.         }

  1028.         if (helper.isFinal())     indent().append("<final/>").eol();
  1029.         if (helper.isSynthetic()) indent().append("<synthetic/>").eol();
  1030.         if (helper.isMandated())  indent().append("<mandated/>").eol();

  1031.         lowerIndent();
  1032.         indent().append("</method-parameter>").eol();
  1033.     }

  1034.     public void visitModuleRequires(ModuleRequires helper) {
  1035.         indent().append("<module-requires requires-flags=\"").append(format.format(helper.getRequiresFlags())).append("\">").eol();
  1036.         raiseIndent();

  1037.         indent();
  1038.         append("<module index=\"").append(helper.getRequiresIndex()).append("\">");
  1039.         helper.getRawRequires().accept(this);
  1040.         append("</module>").eol();

  1041.         if (helper.isTransitive())  indent().append("<transitive/>").eol();
  1042.         if (helper.isStaticPhase()) indent().append("<static-phase/>").eol();
  1043.         if (helper.isSynthetic())   indent().append("<synthetic/>").eol();
  1044.         if (helper.isMandated())    indent().append("<mandated/>").eol();

  1045.         if (helper.hasRequiresVersion()) {
  1046.             indent();
  1047.             append("<version index=\"").append(helper.getRequiresVersionIndex()).append("\">");
  1048.             helper.getRawRequiresVersion().accept(this);
  1049.             append("</version>").eol();
  1050.         }

  1051.         lowerIndent();
  1052.         indent().append("</module-requires>").eol();
  1053.     }

  1054.     public void visitModuleExports(ModuleExports helper) {
  1055.         indent().append("<module-exports exports-flags=\"").append(format.format(helper.getExportsFlags())).append("\">").eol();
  1056.         raiseIndent();

  1057.         indent();
  1058.         append("<package index=\"").append(helper.getExportsIndex()).append("\">");
  1059.         helper.getRawExports().accept(this);
  1060.         append("</package>").eol();

  1061.         if (helper.isSynthetic())   indent().append("<synthetic/>").eol();
  1062.         if (helper.isMandated())    indent().append("<mandated/>").eol();

  1063.         helper.getExportsTos().forEach(moduleExportsTo -> moduleExportsTo.accept(this));

  1064.         lowerIndent();
  1065.         indent().append("</module-exports>").eol();
  1066.     }

  1067.     public void visitModuleExportsTo(ModuleExportsTo helper) {
  1068.         indent().append("<module-exports-to>").eol();
  1069.         raiseIndent();

  1070.         indent();
  1071.         append("<module index=\"").append(helper.getExportsToIndex()).append("\">");
  1072.         helper.getRawExportsTo().accept(this);
  1073.         append("</module>").eol();

  1074.         lowerIndent();
  1075.         indent().append("</module-exports-to>").eol();
  1076.     }

  1077.     public void visitModuleOpens(ModuleOpens helper) {
  1078.         indent().append("<module-opens opens-flags=\"").append(format.format(helper.getOpensFlags())).append("\">").eol();
  1079.         raiseIndent();

  1080.         indent();
  1081.         append("<package index=\"").append(helper.getOpensIndex()).append("\">");
  1082.         helper.getRawOpens().accept(this);
  1083.         append("</package>").eol();

  1084.         if (helper.isSynthetic())   indent().append("<synthetic/>").eol();
  1085.         if (helper.isMandated())    indent().append("<mandated/>").eol();

  1086.         helper.getOpensTos().forEach(moduleOpensTo -> moduleOpensTo.accept(this));

  1087.         lowerIndent();
  1088.         indent().append("</module-opens>").eol();
  1089.     }

  1090.     public void visitModuleOpensTo(ModuleOpensTo helper) {
  1091.         indent().append("<module-opens-to>").eol();
  1092.         raiseIndent();

  1093.         indent();
  1094.         append("<module index=\"").append(helper.getOpensToIndex()).append("\">");
  1095.         helper.getRawOpensTo().accept(this);
  1096.         append("</module>").eol();

  1097.         lowerIndent();
  1098.         indent().append("</module-opens-to>").eol();
  1099.     }

  1100.     public void visitModuleUses(ModuleUses helper) {
  1101.         indent().append("<module-uses>").eol();
  1102.         raiseIndent();

  1103.         appendClassInfo(helper.getUsesIndex(), helper.getRawUses());

  1104.         lowerIndent();
  1105.         indent().append("</module-uses>").eol();
  1106.     }

  1107.     public void visitModuleProvides(ModuleProvides helper) {
  1108.         indent().append("<module-provides>").eol();
  1109.         raiseIndent();

  1110.         appendClassInfo(helper.getProvidesIndex(), helper.getRawProvides());

  1111.         helper.getProvidesWiths().forEach(moduleProvidesWith -> moduleProvidesWith.accept(this));

  1112.         lowerIndent();
  1113.         indent().append("</module-provides>").eol();
  1114.     }

  1115.     public void visitModuleProvidesWith(ModuleProvidesWith helper) {
  1116.         indent().append("<module-provides-with>").eol();
  1117.         raiseIndent();

  1118.         appendClassInfo(helper.getProvidesWithIndex(), helper.getRawProvidesWith());

  1119.         lowerIndent();
  1120.         indent().append("</module-provides-with>").eol();
  1121.     }

  1122.     public void visitModulePackage(ModulePackage helper) {
  1123.         indent();
  1124.         append("<package index=\"").append(helper.getPackageIndex()).append("\">");
  1125.         helper.getRawPackage().accept(this);
  1126.         append("</package>").eol();
  1127.     }

  1128.     public void visitNestMember(NestMember helper) {
  1129.         appendClassInfo(helper.getMemberClassIndex(), helper.getRawMemberClass());
  1130.     }

  1131.     public void visitRecordComponent_info(RecordComponent_info helper) {
  1132.         indent().append("<record-component>").eol();
  1133.         raiseIndent();

  1134.         indent();
  1135.         append("<name index=\"").append(helper.getNameIndex()).append("\">");
  1136.         helper.getRawName().accept(this);
  1137.         append("</name>").eol();

  1138.         indent().append("<type index=\"").append(helper.getDescriptorIndex()).append("\">").append(helper.getType()).append("</type>").eol();

  1139.         indent().append("<attributes>").eol();
  1140.         raiseIndent();

  1141.         super.visitRecordComponent_info(helper);

  1142.         lowerIndent();
  1143.         indent().append("</attributes>").eol();

  1144.         lowerIndent();
  1145.         indent().append("</record-component>").eol();
  1146.     }


  1147.     public void visitPermittedSubclass(PermittedSubclass helper) {
  1148.         appendClassInfo(helper.getSubclassIndex(), helper.getRawSubclass());
  1149.     }

  1150.     public void visitAnnotation(Annotation helper) {
  1151.         indent().append("<annotation>").eol();
  1152.         raiseIndent();

  1153.         indent().append("<type>").append(helper.getType()).append("</type>").eol();

  1154.         indent().append("<element-value-pairs>").eol();
  1155.         raiseIndent();

  1156.         super.visitAnnotation(helper);

  1157.         lowerIndent();
  1158.         indent().append("</element-value-pairs>").eol();

  1159.         lowerIndent();
  1160.         indent().append("</annotation>").eol();
  1161.     }

  1162.     public void visitParameterAnnotation(ParameterAnnotation helper) {
  1163.         indent().append("<parameter-annotation>").eol();
  1164.         raiseIndent();

  1165.         indent().append("<annotations>").eol();
  1166.         raiseIndent();

  1167.         super.visitParameterAnnotation(helper);

  1168.         lowerIndent();
  1169.         indent().append("</annotations>").eol();

  1170.         lowerIndent();
  1171.         indent().append("</parameter-annotation>").eol();
  1172.     }

  1173.     public void visitTypeAnnotation(TypeAnnotation helper) {
  1174.         indent().append("<type-annotation>").eol();
  1175.         raiseIndent();

  1176.         helper.getTarget().accept(this);

  1177.         indent().append("<target-path>").eol();
  1178.         raiseIndent();

  1179.         helper.getTargetPath().accept(this);

  1180.         lowerIndent();
  1181.         indent().append("</target-path>").eol();

  1182.         indent().append("<element-value-pairs>").eol();
  1183.         raiseIndent();

  1184.         helper.getElementValuePairs().forEach(elementValuePair -> elementValuePair.accept(this));

  1185.         lowerIndent();
  1186.         indent().append("</element-value-pairs>").eol();

  1187.         lowerIndent();
  1188.         indent().append("</type-annotation>").eol();
  1189.     }

  1190.     public void visitTypeParameterTarget(TypeParameterTarget helper) {
  1191.         indent().append("<type-parameter-target target-type=\"").append(helper.getHexTargetType()).append("\">").eol();
  1192.         raiseIndent();

  1193.         indent().append("<type-parameter-index>").append(helper.getTypeParameterIndex()).append("</type-parameter-index>").eol();

  1194.         lowerIndent();
  1195.         indent().append("</type-parameter-target>").eol();
  1196.     }

  1197.     public void visitSupertypeTarget(SupertypeTarget helper) {
  1198.         indent().append("<supertype-target target-type=\"").append(helper.getHexTargetType()).append("\">").eol();
  1199.         raiseIndent();

  1200.         indent().append("<supertype-index>").append(helper.getSupertypeIndex()).append("</supertype-index>").eol();

  1201.         lowerIndent();
  1202.         indent().append("</supertype-target>").eol();
  1203.     }

  1204.     public void visitTypeParameterBoundTarget(TypeParameterBoundTarget helper) {
  1205.         indent().append("<type-parameter-bound-target target-type=\"").append(helper.getHexTargetType()).append("\">").eol();
  1206.         raiseIndent();

  1207.         indent().append("<type-parameter-index>").append(helper.getTypeParameterIndex()).append("</type-parameter-index>").eol();
  1208.         indent().append("<bound-index>").append(helper.getBoundIndex()).append("</bound-index>").eol();

  1209.         lowerIndent();
  1210.         indent().append("</type-parameter-bound-target>").eol();
  1211.     }

  1212.     public void visitEmptyTarget(EmptyTarget helper) {
  1213.         indent().append("<empty-target target-type=\"").append(helper.getHexTargetType()).append("\"/>").eol();
  1214.     }

  1215.     public void visitFormalParameterTarget(FormalParameterTarget helper) {
  1216.         indent().append("<formal-parameter-target target-type=\"").append(helper.getHexTargetType()).append("\">").eol();
  1217.         raiseIndent();

  1218.         indent().append("<formal-parameter-index>").append(helper.getFormalParameterIndex()).append("</formal-parameter-index>").eol();

  1219.         lowerIndent();
  1220.         indent().append("</formal-parameter-target>").eol();
  1221.     }

  1222.     public void visitThrowsTarget(ThrowsTarget helper) {
  1223.         indent().append("<throws-target target-type=\"").append(helper.getHexTargetType()).append("\">").eol();
  1224.         raiseIndent();

  1225.         indent().append("<throws-type-index>").append(helper.getThrowsTypeIndex()).append("</throws-type-index>").eol();

  1226.         lowerIndent();
  1227.         indent().append("</throws-target>").eol();
  1228.     }

  1229.     public void visitLocalvarTarget(LocalvarTarget helper) {
  1230.         indent().append("<localvar-target target-type=\"").append(helper.getHexTargetType()).append("\">").eol();
  1231.         raiseIndent();

  1232.         super.visitLocalvarTarget(helper);

  1233.         lowerIndent();
  1234.         indent().append("</localvar-target>").eol();
  1235.     }

  1236.     public void visitCatchTarget(CatchTarget helper) {
  1237.         indent().append("<catch-target target-type=\"").append(helper.getHexTargetType()).append("\">").eol();
  1238.         raiseIndent();

  1239.         indent().append("<exception-table-index>").append(helper.getExceptionTableIndex()).append("</exception-table-index>").eol();

  1240.         lowerIndent();
  1241.         indent().append("</catch-target>").eol();
  1242.     }

  1243.     public void visitOffsetTarget(OffsetTarget helper) {
  1244.         indent().append("<offset-target target-type=\"").append(helper.getHexTargetType()).append("\">").eol();
  1245.         raiseIndent();

  1246.         indent().append("<offset>").append(helper.getOffset()).append("</offset>").eol();

  1247.         lowerIndent();
  1248.         indent().append("</offset-target>").eol();
  1249.     }

  1250.     public void visitTypeArgumentTarget(TypeArgumentTarget helper) {
  1251.         indent().append("<type-argument-target target-type=\"").append(helper.getHexTargetType()).append("\">").eol();
  1252.         raiseIndent();

  1253.         indent().append("<offset>").append(helper.getOffset()).append("</offset>").eol();
  1254.         indent().append("<type-argument-index>").append(helper.getTypeArgumentIndex()).append("</type-argument-index>").eol();

  1255.         lowerIndent();
  1256.         indent().append("</type-argument-target>").eol();
  1257.     }

  1258.     public void visitTypePathEntry(TypePathEntry helper) {
  1259.         indent().append("<type-path>").eol();
  1260.         raiseIndent();

  1261.         indent().append("<type-path-kind>").append(helper.getTypePathKind().getTypePathKind()).append("</type-path-kind>").eol();
  1262.         indent().append("<type-argument-index>").append(helper.getTypeArgumentIndex()).append("</type-argument-index>").eol();

  1263.         lowerIndent();
  1264.         indent().append("</type-path>").eol();
  1265.     }

  1266.     public void visitElementValuePair(ElementValuePair helper) {
  1267.         indent().append("<element-value-pair>").eol();
  1268.         raiseIndent();

  1269.         indent().append("<element-name>").append(helper.getElementName()).append("</element-name>").eol();

  1270.         super.visitElementValuePair(helper);

  1271.         lowerIndent();
  1272.         indent().append("</element-value-pair>").eol();
  1273.     }

  1274.     public void visitByteConstantElementValue(ByteConstantElementValue helper) {
  1275.         visitConstantElementValue(helper, "byte");
  1276.     }

  1277.     public void visitCharConstantElementValue(CharConstantElementValue helper) {
  1278.         visitConstantElementValue(helper, "char");
  1279.     }

  1280.     public void visitDoubleConstantElementValue(DoubleConstantElementValue helper) {
  1281.         visitConstantElementValue(helper, "double");
  1282.     }

  1283.     public void visitFloatConstantElementValue(FloatConstantElementValue helper) {
  1284.         visitConstantElementValue(helper, "float");
  1285.     }

  1286.     public void visitIntegerConstantElementValue(IntegerConstantElementValue helper) {
  1287.         visitConstantElementValue(helper, "integer");
  1288.     }

  1289.     public void visitLongConstantElementValue(LongConstantElementValue helper) {
  1290.         visitConstantElementValue(helper, "long");
  1291.     }

  1292.     public void visitShortConstantElementValue(ShortConstantElementValue helper) {
  1293.         visitConstantElementValue(helper, "short");
  1294.     }

  1295.     public void visitBooleanConstantElementValue(BooleanConstantElementValue helper) {
  1296.         visitConstantElementValue(helper, "boolean");
  1297.     }

  1298.     public void visitStringConstantElementValue(StringConstantElementValue helper) {
  1299.         visitConstantElementValue(helper, "string");
  1300.     }

  1301.     private void visitConstantElementValue(ConstantElementValue helper, String type) {
  1302.         indent();
  1303.         append("<").append(type).append("-element-value tag=\"").append(helper.getTag()).append("\">");
  1304.         helper.getRawConstValue().accept(this);
  1305.         append("</").append(type).append("-element-value>").eol();
  1306.     }

  1307.     public void visitEnumElementValue(EnumElementValue helper) {
  1308.         indent();
  1309.         append("<enum-element-value tag=\"").append(helper.getTag()).append("\">");
  1310.         append(helper.getTypeName()).append(".").append(helper.getConstName());
  1311.         append("</enum-element-value>").eol();
  1312.     }

  1313.     public void visitClassElementValue(ClassElementValue helper) {
  1314.         indent();
  1315.         append("<class-element-value tag=\"").append(helper.getTag()).append("\">");
  1316.         append(helper.getClassInfo());
  1317.         append("</class-element-value>").eol();
  1318.     }

  1319.     public void visitAnnotationElementValue(AnnotationElementValue helper) {
  1320.         indent().append("<annotation-element-value tag=\"").append(helper.getTag()).append("\">").eol();
  1321.         raiseIndent();

  1322.         super.visitAnnotationElementValue(helper);

  1323.         lowerIndent();
  1324.         indent().append("</annotation-element-value>").eol();
  1325.     }

  1326.     public void visitArrayElementValue(ArrayElementValue helper) {
  1327.         indent().append("<array-element-value tag=\"").append(helper.getTag()).append("\">").eol();
  1328.         raiseIndent();

  1329.         super.visitArrayElementValue(helper);

  1330.         lowerIndent();
  1331.         indent().append("</array-element-value>").eol();
  1332.     }

  1333.     public void visitLocalvarTableEntry(LocalvarTableEntry helper) {
  1334.         indent().append("<localvar start-pc=\"").append(helper.getStartPc()).append("\" length=\"").append(helper.getLength()).append("\" index=\"").append(helper.getIndex()).append("\"/>").eol();
  1335.     }

  1336.     public void visitSameFrame(SameFrame helper) {
  1337.         indent().append("<same-frame frame-type=\"").append(helper.getFrameType()).append("\"/>").eol();
  1338.     }

  1339.     public void visitSameLocals1StackItemFrame(SameLocals1StackItemFrame helper) {
  1340.         indent().append("<same-locals-1-stack-item-frame frame-type=\"").append(helper.getFrameType()).append("\">").eol();
  1341.         raiseIndent();

  1342.         indent().append("<stack>").eol();
  1343.         raiseIndent();
  1344.         super.visitSameLocals1StackItemFrame(helper);
  1345.         lowerIndent();
  1346.         indent().append("</stack>").eol();

  1347.         lowerIndent();
  1348.         indent().append("</same-locals-1-stack-item-frame>").eol();
  1349.     }

  1350.     public void visitSameLocals1StackItemFrameExtended(SameLocals1StackItemFrameExtended helper) {
  1351.         indent().append("<same-locals-1-stack-item-frame-extended frame-type=\"").append(helper.getFrameType()).append("\" offset-delta=\"").append(helper.getOffsetDelta()).append("\">").eol();
  1352.         raiseIndent();

  1353.         indent().append("<stack>").eol();
  1354.         raiseIndent();
  1355.         super.visitSameLocals1StackItemFrameExtended(helper);
  1356.         lowerIndent();
  1357.         indent().append("</stack>").eol();

  1358.         lowerIndent();
  1359.         indent().append("</same-locals-1-stack-item-frame-extended>").eol();
  1360.     }

  1361.     public void visitChopFrame(ChopFrame helper) {
  1362.         indent().append("<chop-frame frame-type=\"").append(helper.getFrameType()).append("\" offset-delta=\"").append(helper.getOffsetDelta()).append("\"/>").eol();
  1363.     }

  1364.     public void visitSameFrameExtended(SameFrameExtended helper) {
  1365.         indent().append("<same-frame-extended frame-type=\"").append(helper.getFrameType()).append("\" offset-delta=\"").append(helper.getOffsetDelta()).append("\"/>").eol();
  1366.     }

  1367.     public void visitAppendFrame(AppendFrame helper) {
  1368.         indent().append("<append-frame frame-type=\"").append(helper.getFrameType()).append("\" offset-delta=\"").append(helper.getOffsetDelta()).append("\">").eol();
  1369.         raiseIndent();

  1370.         indent().append("<locals>").eol();
  1371.         raiseIndent();
  1372.         super.visitAppendFrame(helper);
  1373.         lowerIndent();
  1374.         indent().append("</locals>").eol();

  1375.         lowerIndent();
  1376.         indent().append("</append-frame>").eol();
  1377.     }

  1378.     public void visitFullFrame(FullFrame helper) {
  1379.         indent().append("<full-frame frame-type=\"").append(helper.getFrameType()).append("\" offset-delta=\"").append(helper.getOffsetDelta()).append("\">").eol();
  1380.         raiseIndent();

  1381.         indent().append("<locals>").eol();
  1382.         raiseIndent();
  1383.         helper.getLocals().forEach(local -> local.accept(this));
  1384.         lowerIndent();
  1385.         indent().append("</locals>").eol();

  1386.         indent().append("<stack>").eol();
  1387.         raiseIndent();
  1388.         helper.getStack().forEach(local -> local.accept(this));
  1389.         lowerIndent();
  1390.         indent().append("</stack>").eol();

  1391.         lowerIndent();
  1392.         indent().append("</full-frame>").eol();
  1393.     }

  1394.     public void visitTopVariableInfo(TopVariableInfo helper) {
  1395.         indent().append("<top-variable-info tag=\"").append(helper.getTag()).append("\"/>").eol();
  1396.     }

  1397.     public void visitIntegerVariableInfo(IntegerVariableInfo helper) {
  1398.         indent().append("<integer-variable-info tag=\"").append(helper.getTag()).append("\"/>").eol();
  1399.     }

  1400.     public void visitFloatVariableInfo(FloatVariableInfo helper) {
  1401.         indent().append("<float-variable-info tag=\"").append(helper.getTag()).append("\"/>").eol();
  1402.     }

  1403.     public void visitLongVariableInfo(LongVariableInfo helper) {
  1404.         indent().append("<long-variable-info tag=\"").append(helper.getTag()).append("\"/>").eol();
  1405.     }

  1406.     public void visitDoubleVariableInfo(DoubleVariableInfo helper) {
  1407.         indent().append("<double-variable-info tag=\"").append(helper.getTag()).append("\"/>").eol();
  1408.     }

  1409.     public void visitNullVariableInfo(NullVariableInfo helper) {
  1410.         indent().append("<null-variable-info tag=\"").append(helper.getTag()).append("\"/>").eol();
  1411.     }

  1412.     public void visitUninitializedThisVariableInfo(UninitializedThisVariableInfo helper) {
  1413.         indent().append("<uninitialized-this-variable-info tag=\"").append(helper.getTag()).append("\"/>").eol();
  1414.     }

  1415.     public void visitObjectVariableInfo(ObjectVariableInfo helper) {
  1416.         indent().append("<object-variable-info tag=\"").append(helper.getTag()).append("\">").eol();
  1417.         raiseIndent();

  1418.         top = true;
  1419.         super.visitObjectVariableInfo(helper);
  1420.         top = false;

  1421.         lowerIndent();
  1422.         indent().append("</object-variable-info>").eol();
  1423.     }

  1424.     public void visitUninitializedVariableInfo(UninitializedVariableInfo helper) {
  1425.         indent().append("<uninitialized-variable-info tag=\"").append(helper.getTag()).append("\" offset=\"").append(helper.getOffset()).append("\"/>").eol();
  1426.     }

  1427.     private Printer appendLocalVariable(LocalVariable localVariable) {
  1428.         if (localVariable != null) {
  1429.             append(" ");
  1430.             append(DescriptorHelper.getType(localVariable.getDescriptor())).append(" ").append(localVariable.getName());
  1431.         }
  1432.         return this;
  1433.     }

  1434.     private Printer appendClassInfo(int index, Class_info class_info) {
  1435.         indent();
  1436.         append("<class index=\"").append(index).append("\">");
  1437.         class_info.accept(this);
  1438.         append("</class>").eol();
  1439.         return this;
  1440.     }

  1441.     // Visible for testing
  1442.     String escapeXMLCharacters(String text) {
  1443.         StringBuilder result = new StringBuilder();

  1444.         text.codePoints()
  1445.                 .forEach(c -> {
  1446.                     if (c == '&') {
  1447.                         result.append("&amp;");
  1448.                     } else if (c == '<') {
  1449.                         result.append("&lt;");
  1450.                     } else if (c == '>') {
  1451.                         result.append("&gt;");
  1452.                     } else if (Character.isISOControl(c) || c > 0x9F) {
  1453.                         result.append("&#x");
  1454.                         result.append(Integer.toString(c, 16).toUpperCase());
  1455.                         result.append(";");
  1456.                     } else {
  1457.                         result.append(Character.toChars(c));
  1458.                     }
  1459.                 });

  1460.         if (containsControlCharacters(text)) {
  1461.             return "<![CDATA[" + result + "]]>";
  1462.         } else {
  1463.             return result.toString();
  1464.         }
  1465.     }

  1466.     private boolean containsControlCharacters(String text) {
  1467.         return text.codePoints().anyMatch(c -> Character.isISOControl(c) || c > 0x9F);
  1468.     }
  1469. }