alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (ClassReader.java)

This example Java source code file (ClassReader.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

annotation, classfile, classreader, constantpoolexception, element, field, file, integer, ioexception, jar, list, override, runtimeexception, string, util, void

The ClassReader.java Java example source code

/*
 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package xmlkit; // -*- mode: java; indent-tabs-mode: nil -*-

import com.sun.tools.classfile.AccessFlags;
import com.sun.tools.classfile.Annotation;
import com.sun.tools.classfile.Annotation.*;
import com.sun.tools.classfile.AnnotationDefault_attribute;
import com.sun.tools.classfile.Attribute;
import com.sun.tools.classfile.Attributes;
import com.sun.tools.classfile.BootstrapMethods_attribute;
import com.sun.tools.classfile.CharacterRangeTable_attribute;
import com.sun.tools.classfile.ClassFile;
import com.sun.tools.classfile.Code_attribute;
import com.sun.tools.classfile.CompilationID_attribute;
import com.sun.tools.classfile.ConstantPool;
import com.sun.tools.classfile.ConstantPool.*;
import com.sun.tools.classfile.ConstantPoolException;
import com.sun.tools.classfile.ConstantValue_attribute;
import com.sun.tools.classfile.DefaultAttribute;
import com.sun.tools.classfile.Deprecated_attribute;
import com.sun.tools.classfile.Descriptor.InvalidDescriptor;
import com.sun.tools.classfile.EnclosingMethod_attribute;
import com.sun.tools.classfile.Exceptions_attribute;
import com.sun.tools.classfile.Field;
import com.sun.tools.classfile.InnerClasses_attribute;
import com.sun.tools.classfile.InnerClasses_attribute.Info;
import com.sun.tools.classfile.Instruction;
import com.sun.tools.classfile.Instruction.TypeKind;
import com.sun.tools.classfile.LineNumberTable_attribute;
import com.sun.tools.classfile.LocalVariableTable_attribute;
import com.sun.tools.classfile.LocalVariableTypeTable_attribute;
import com.sun.tools.classfile.Method;
import com.sun.tools.classfile.MethodParameters_attribute;
import com.sun.tools.classfile.Opcode;
import com.sun.tools.classfile.RuntimeInvisibleAnnotations_attribute;
import com.sun.tools.classfile.RuntimeInvisibleParameterAnnotations_attribute;
import com.sun.tools.classfile.RuntimeInvisibleTypeAnnotations_attribute;
import com.sun.tools.classfile.RuntimeVisibleAnnotations_attribute;
import com.sun.tools.classfile.RuntimeVisibleParameterAnnotations_attribute;
import com.sun.tools.classfile.RuntimeVisibleTypeAnnotations_attribute;
import com.sun.tools.classfile.Signature_attribute;
import com.sun.tools.classfile.SourceDebugExtension_attribute;
import com.sun.tools.classfile.SourceFile_attribute;
import com.sun.tools.classfile.SourceID_attribute;
import com.sun.tools.classfile.StackMapTable_attribute;
import com.sun.tools.classfile.StackMapTable_attribute.*;
import com.sun.tools.classfile.StackMap_attribute;
import com.sun.tools.classfile.Synthetic_attribute;
import com.sun.tools.classfile.TypeAnnotation;
import com.sun.tools.classfile.TypeAnnotation.Position;
import static com.sun.tools.classfile.TypeAnnotation.TargetType.THROWS;
import java.util.*;
import java.io.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import xmlkit.XMLKit.Element;

/*
 * @author jrose, ksrini
 */
public class ClassReader {

    private static final CommandLineParser CLP = new CommandLineParser(""
            + "-source:     +> = \n"
            + "-dest:       +> = \n"
            + "-encoding:   +> = \n"
            + "-jcov           $ \n   -nojcov         !-jcov        \n"
            + "-verbose        $ \n   -noverbose      !-verbose     \n"
            + "-keepPath       $ \n   -nokeepPath     !-keepPath    \n"
            + "-keepCP         $ \n   -nokeepCP       !-keepCP      \n"
            + "-keepOrder      $ \n   -nokeepOrder    !-keepOrder   \n"
            + "-continue       $ \n   -nocontinue     !-continue    \n"
            + "-@         >-@  . \n"
            + "-              +? \n"
            + "\n");


    // Protected state for representing the class file.
    protected Element cfile;          // <ClassFile ...>
    protected Element cpool;          // <ConstantPool ...>
    protected Element klass;          // <Class ...>
    protected List<String> thePool;    // stringified flattened Constant Pool

    public static void main(String[] ava) throws IOException {
        ArrayList<String> av = new ArrayList<>(Arrays.asList(ava));
        HashMap<String, String> props = new HashMap<>();
        props.put("-encoding:", "UTF8");  // default
        props.put("-keepOrder", null);    // CLI default
        props.put("-pretty", "1");     // CLI default
        props.put("-continue", "1");     // CLI default
        CLP.parse(av, props);
        //System.out.println(props+" ++ "+av);
        File source = asFile(props.get("-source:"));
        File dest = asFile(props.get("-dest:"));
        String encoding = props.get("-encoding:");
        boolean contError = props.containsKey("-continue");
        ClassReader options = new ClassReader();
        options.copyOptionsFrom(props);
        /*
        if (dest == null && av.size() > 1) {
        dest = File.createTempFile("TestOut", ".dir", new File("."));
        dest.delete();
        if (!dest.mkdir())
        throw new RuntimeException("Cannot create "+dest);
        System.out.println("Writing results to "+dest);
        }
         */
        if (av.isEmpty()) {
            av.add("");  //to enter this loop
        }
        boolean readList = false;
        for (String a : av) {
            if (readList) {
                readList = false;
                InputStream fin;
                if (a.equals("-")) {
                    fin = System.in;
                } else {
                    fin = new FileInputStream(a);
                }

                BufferedReader files = makeReader(fin, encoding);
                for (String file; (file = files.readLine()) != null;) {
                    doFile(file, source, dest, options, encoding, contError);
                }
                if (fin != System.in) {
                    fin.close();
                }
            } else if (a.equals("-@")) {
                readList = true;
            } else if (a.startsWith("-")) {
                throw new RuntimeException("Bad flag argument: " + a);
            } else if (source.getName().endsWith(".jar")) {
                doJar(a, source, dest, options, encoding, contError);
            } else {
                doFile(a, source, dest, options, encoding, contError);
            }
        }
    }

    private static File asFile(String str) {
        return (str == null) ? null : new File(str);
    }

    private static void doFile(String a,
            File source, File dest,
            ClassReader options, String encoding,
            boolean contError) throws IOException  {
        if (!contError) {
            doFile(a, source, dest, options, encoding);
        } else {
            try {
                doFile(a, source, dest, options, encoding);
            } catch (Exception ee) {
                System.out.println("Error processing " + source + ": " + ee);
                ee.printStackTrace();
            }
        }
    }

    private static void doJar(String a, File source, File dest,
                              ClassReader options, String encoding,
                              Boolean contError) throws IOException {
        try {
            JarFile jf = new JarFile(source);
            for (JarEntry je : Collections.list(jf.entries())) {
                String name = je.getName();
                if (!name.endsWith(".class")) {
                    continue;
                }
                try {
                    doStream(name, jf.getInputStream(je), dest, options, encoding);
                } catch (Exception e) {
                    if (contError) {
                        System.out.println("Error processing " + source + ": " + e);
                        e.printStackTrace();
                        continue;
                    }
                }
            }
        } catch (IOException ioe) {
            throw ioe;
        }
    }

    private static void doStream(String a, InputStream in, File dest,
                                 ClassReader options, String encoding) throws IOException {

        File f = new File(a);
        ClassReader cr = new ClassReader(options);
        Element e;
        if (options.verbose) {
            System.out.println("Reading " + f);
        }
        e = cr.readFrom(in);

        OutputStream out;
        if (dest == null) {
            out = System.out;
        } else {
            File outf = new File(dest, f.isAbsolute() ? f.getName() : f.getPath());
            String outName = outf.getName();
            File outSubdir = outf.getParentFile();
            outSubdir.mkdirs();
            int extPos = outName.lastIndexOf('.');
            if (extPos > 0) {
                outf = new File(outSubdir, outName.substring(0, extPos) + ".xml");
            }
            out = new FileOutputStream(outf);
        }

        Writer outw = makeWriter(out, encoding);
        if (options.pretty || !options.keepOrder) {
            e.writePrettyTo(outw);
        } else {
            e.writeTo(outw);
        }
        if (out == System.out) {
            outw.write("\n");
            outw.flush();
        } else {
            outw.close();
        }
    }

    private static void doFile(String a,
            File source, File dest,
            ClassReader options, String encoding) throws IOException {
        File inf = new File(source, a);
        if (dest != null && options.verbose) {
            System.out.println("Reading " + inf);
        }

        BufferedInputStream in = new BufferedInputStream(new FileInputStream(inf));

        doStream(a, in, dest, options, encoding);

    }

    public static BufferedReader makeReader(InputStream in,
                                            String encoding) throws IOException {
        Reader inw;
        in = new BufferedInputStream(in);  // add buffering
        if (encoding == null) {
            inw = new InputStreamReader(in);
        } else {
            inw = new InputStreamReader(in, encoding);
        }
        return new BufferedReader(inw);  // add buffering
    }

    public static Writer makeWriter(OutputStream out,
                                    String encoding) throws IOException {
        Writer outw;
        if (encoding == null) {
            outw = new OutputStreamWriter(out);
        } else {
            outw = new OutputStreamWriter(out, encoding);
        }
        return new BufferedWriter(outw);  // add buffering
    }

    public Element result() {
        return cfile;
    }

    protected InputStream in;
    protected ByteArrayOutputStream buf = new ByteArrayOutputStream(1024);
    // input options
    public boolean pretty = false;
    public boolean verbose = false;
    public boolean keepPath = false;
    public boolean keepCP = false;
    public boolean keepBytes = false;
    public boolean parseBytes = true;
    public boolean resolveRefs = true;
    public boolean keepOrder = true;
    public boolean keepSizes = false;

    public ClassReader() {
        cfile = new Element("ClassFile");
    }

    public ClassReader(ClassReader options) {
        this();
        copyOptionsFrom(options);
    }

    public void copyOptionsFrom(ClassReader options) {
        pretty = options.pretty;
        verbose = options.verbose;
        keepPath = options.keepPath;
        keepCP = options.keepCP;
        keepOrder = options.keepOrder;
    }

    public void copyOptionsFrom(Map<String, String> options) {
        if (options.containsKey("-pretty")) {
            pretty = (options.get("-pretty") != null);
        }
        if (options.containsKey("-verbose")) {
            verbose = (options.get("-verbose") != null);
        }
        if (options.containsKey("-keepPath")) {
            keepPath = (options.get("-keepPath") != null);
        }
        if (options.containsKey("-keepCP")) {
            keepCP = (options.get("-keepCP") != null);
        }
        if (options.containsKey("-keepOrder")) {
            keepOrder = (options.get("-keepOrder") != null);
        }
    }

    protected String getCpString(int i) {
        return thePool.get(i);
    }

    public Element readFrom(InputStream in) throws IOException {
        try {
            this.in = in;
            ClassFile c = ClassFile.read(in);
            // read the file header
            if (c.magic != 0xCAFEBABE) {
                throw new RuntimeException("bad magic number " +
                        Integer.toHexString(c.magic));
            }
            cfile.setAttr("magic", "" + c.magic);
            int minver = c.minor_version;
            int majver = c.major_version;
            cfile.setAttr("minver", "" + minver);
            cfile.setAttr("majver", "" + majver);
            readCP(c);
            readClass(c);
            return result();
        } catch (InvalidDescriptor | ConstantPoolException ex) {
            throw new IOException("Fatal error", ex);
        }
    }

    public Element readFrom(File file) throws IOException {
        try (InputStream strm = new FileInputStream(file)) {
            Element e = readFrom(new BufferedInputStream(strm));
            if (keepPath) {
                e.setAttr("path", file.toString());
            }
            return e;
        }
    }

    private void readClass(ClassFile c) throws IOException,
                                               ConstantPoolException,
                                               InvalidDescriptor {
        klass = new Element("Class");
        cfile.add(klass);
        String thisk = c.getName();

        klass.setAttr("name", thisk);

        AccessFlags af = new AccessFlags(c.access_flags.flags);
        klass.setAttr("flags", flagString(af, klass));
        if (!"java/lang/Object".equals(thisk)) {
            klass.setAttr("super", c.getSuperclassName());
        }
        for (int i : c.interfaces) {
            klass.add(new Element("Interface", "name", getCpString(i)));
        }
        readFields(c, klass);
        readMethods(c, klass);
        readAttributesFor(c, c.attributes, klass);
        klass.trimToSize();
    }

    private void readFields(ClassFile c, Element klass) throws IOException {
        int len = c.fields.length;
        Element fields = new Element(len);
        for (Field f : c.fields) {
            Element field = new Element("Field");
            field.setAttr("name", getCpString(f.name_index));
            field.setAttr("type", getCpString(f.descriptor.index));
            field.setAttr("flags", flagString(f.access_flags.flags, field));
            readAttributesFor(c, f.attributes, field);

            field.trimToSize();
            fields.add(field);
        }
        if (!keepOrder) {
            fields.sort();
        }
        klass.addAll(fields);
    }


    private void readMethods(ClassFile c, Element klass) throws IOException {
        int len = c.methods.length;
        Element methods = new Element(len);
        for (Method m : c.methods) {
            Element member = new Element("Method");
            member.setAttr("name", getCpString(m.name_index));
            member.setAttr("type", getCpString(m.descriptor.index));
            member.setAttr("flags", flagString(m.access_flags.flags, member));
            readAttributesFor(c, m.attributes, member);

            member.trimToSize();
            methods.add(member);
        }
        if (!keepOrder) {
            methods.sort();
        }
        klass.addAll(methods);
    }

    private AccessFlags.Kind getKind(Element e) {
        switch(e.getName()) {
            case "Class":
                return AccessFlags.Kind.Class;
            case "InnerClass":
                return AccessFlags.Kind.InnerClass;
            case "Field":
                return AccessFlags.Kind.Field ;
            case "Method":
                return AccessFlags.Kind.Method;
            default: throw new RuntimeException("should not reach here");
        }
    }

    protected String flagString(int flags, Element holder) {
        return flagString(new AccessFlags(flags), holder);
    }
    protected String flagString(AccessFlags af, Element holder) {
        return flagString(af, holder.getName());
    }
    protected String flagString(int flags, String kind) {
        return flagString(new AccessFlags(flags), kind);
    }
    protected String flagString(AccessFlags af, String kind) {
        Set<String> mods = null;
        switch (kind) {
            case "Class":
                mods = af.getClassFlags();
                break;
            case "InnerClass":
                mods = af.getInnerClassFlags();
                break;
            case "Field":
                mods = af.getFieldFlags();
                break;
            case "Method":
                mods = af.getMethodFlags();
                break;
            default:
                throw new RuntimeException("should not reach here");
        }
        StringBuilder sb = new StringBuilder();
        for (String x : mods) {
            sb.append(x.substring(x.indexOf('_') + 1).toLowerCase()).append(" ");
        }
        return sb.toString().trim();
    }


    protected  void readAttributesFor(ClassFile c, Attributes attrs, Element x) {
        Element container = new Element();
        AttributeVisitor av = new AttributeVisitor(this, c);
        for (Attribute a : attrs) {
            av.visit(a, container);
        }
        if (!keepOrder) {
            container.sort();
        }
        x.addAll(container);
    }

    private int fileSize = 0;
    private HashMap<String, int[]> attrSizes = new HashMap<>();

    private void attachTo(Element x, Object aval0) {
        if (aval0 == null) {
            return;
        }
        if (!(aval0 instanceof Element)) {
            x.add(aval0);
            return;
        }
        Element aval = (Element) aval0;
        if (!aval.isAnonymous()) {
            x.add(aval);
            return;
        }
        for (int imax = aval.attrSize(), i = 0; i < imax; i++) {
            //%%
            attachAttrTo(x, aval.getAttrName(i), aval.getAttr(i));
        }
        x.addAll(aval);
    }

    private void attachAttrTo(Element x, String aname, String aval) {
        String aval0 = x.getAttr(aname);
        if (aval0 != null) {
            aval = aval0 + " " + aval;
        }
        x.setAttr(aname, aval);
    }

    private void readCP(ClassFile c) throws IOException {
        cpool = new Element("ConstantPool", c.constant_pool.size());
        ConstantPoolVisitor cpv = new ConstantPoolVisitor(cpool, c,
                c.constant_pool.size());
        for (int i = 1 ; i < c.constant_pool.size() ; i++) {
            try {
                cpv.visit(c.constant_pool.get(i), i);
            } catch (InvalidIndex ex) {
                // can happen periodically when accessing doubles etc. ignore it
                // ex.printStackTrace();
            }
        }
        thePool = cpv.getPoolList();
        if (verbose) {
            for (int i = 0; i < thePool.size(); i++) {
                System.out.println("[" + i + "]: " + thePool.get(i));
            }
        }
        if (keepCP) {
            cfile.add(cpool);
        }
    }
}

class ConstantPoolVisitor implements ConstantPool.Visitor<String, Integer> {
    final List<String> slist;
    final Element xpool;
    final ClassFile cf;
    final ConstantPool cfpool;
    final List<String> bsmlist;


    public ConstantPoolVisitor(Element xpool, ClassFile cf, int size) {
        slist = new ArrayList<>(size);
        for (int i = 0 ; i < size; i++) {
            slist.add(null);
        }
        this.xpool = xpool;
        this.cf = cf;
        this.cfpool = cf.constant_pool;
        bsmlist = readBSM();
    }

    public List<String> getPoolList() {
        return Collections.unmodifiableList(slist);
    }

    public List<String> getBSMList() {
        return Collections.unmodifiableList(bsmlist);
    }

    public String visit(CPInfo c, int index) {
        return c.accept(this, index);
    }

    private List<String> readBSM() {
        BootstrapMethods_attribute bsmAttr =
                (BootstrapMethods_attribute) cf.getAttribute(Attribute.BootstrapMethods);
        if (bsmAttr != null) {
            List<String> out =
                    new ArrayList<>(bsmAttr.bootstrap_method_specifiers.length);
            for (BootstrapMethods_attribute.BootstrapMethodSpecifier bsms :
                    bsmAttr.bootstrap_method_specifiers) {
                int index = bsms.bootstrap_method_ref;
                try {
                    String value = slist.get(index);
                    String bsmStr = value;
                    if (value == null) {
                        value = visit(cfpool.get(index), index);
                        slist.set(index, value);
                    }
                    bsmStr = value;
                    for (int idx : bsms.bootstrap_arguments) {
                        value = slist.get(idx);
                        if (value == null) {
                            value = visit(cfpool.get(idx), idx);
                            slist.set(idx, value);
                        }
                        bsmStr = bsmStr.concat("," + value);
                    }
                    out.add(bsmStr);
                } catch (InvalidIndex ex) {
                    ex.printStackTrace();
                }
            }
            return out;
        }
        return new ArrayList<>(0);
    }

    @Override
    public String visitClass(CONSTANT_Class_info c, Integer p) {
        String value = slist.get(p);
        if (value == null) {
            try {
                value = visit(cfpool.get(c.name_index), c.name_index);
                slist.set(p, value);
                xpool.add(new Element("CONSTANT_Class",
                        new String[]{"id", p.toString()},
                        value));
            } catch (ConstantPoolException ex) {
                ex.printStackTrace();
            }
        }
        return value;
    }

    @Override
    public String visitDouble(CONSTANT_Double_info c, Integer p) {
        String value = slist.get(p);
        if (value == null) {
            value = Double.toString(c.value);
            slist.set(p, value);
            xpool.add(new Element("CONSTANT_Double",
                      new String[]{"id", p.toString()},
                      value));
        }
        return value;
    }

    @Override
    public String visitFieldref(CONSTANT_Fieldref_info c, Integer p) {
    String value = slist.get(p);
        if (value == null) {
            try {
                value = visit(cfpool.get(c.class_index), c.class_index);
                value = value.concat(" " + visit(cfpool.get(c.name_and_type_index),
                                     c.name_and_type_index));
                slist.set(p, value);
                xpool.add(new Element("CONSTANT_Fieldref",
                          new String[]{"id", p.toString()},
                          value));
            } catch (ConstantPoolException ex) {
                ex.printStackTrace();
            }
        }
        return value;
    }

    @Override
    public String visitFloat(CONSTANT_Float_info c, Integer p) {
        String value = slist.get(p);
        if (value == null) {
            value = Float.toString(c.value);
            slist.set(p, value);
            xpool.add(new Element("CONSTANT_Float",
                      new String[]{"id", p.toString()},
                      value));
        }
        return value;
    }

    @Override
    public String visitInteger(CONSTANT_Integer_info cnstnt, Integer p) {
        String value = slist.get(p);
        if (value == null) {
            value = Integer.toString(cnstnt.value);
            slist.set(p, value);
            xpool.add(new Element("CONSTANT_Integer",
                      new String[]{"id", p.toString()},
                      value));
        }
        return value;
    }

    @Override
    public String visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info c,
                                          Integer p) {
        String value = slist.get(p);
        if (value == null) {
            try {
                value = visit(cfpool.get(c.class_index), c.class_index);
                value = value.concat(" " +
                                     visit(cfpool.get(c.name_and_type_index),
                                     c.name_and_type_index));
                slist.set(p, value);
                xpool.add(new Element("CONSTANT_InterfaceMethodref",
                          new String[]{"id", p.toString()},
                          value));

            } catch (ConstantPoolException ex) {
                ex.printStackTrace();
            }
        }
        return value;
    }

    @Override
    public String visitInvokeDynamic(CONSTANT_InvokeDynamic_info c, Integer p) {
        String value = slist.get(p);
        if (value == null) {
            try {
                value = bsmlist.get(c.bootstrap_method_attr_index) + " "
                        + visit(cfpool.get(c.name_and_type_index), c.name_and_type_index);
                slist.set(p, value);
                xpool.add(new Element("CONSTANT_InvokeDynamic",
                          new String[]{"id", p.toString()},
                          value));

            } catch (ConstantPoolException ex) {
                ex.printStackTrace();
            }
        }
        return value;
    }

    @Override
    public String visitLong(CONSTANT_Long_info c, Integer p) {
        String value = slist.get(p);
        if (value == null) {
            value = Long.toString(c.value);
            slist.set(p, value);
            xpool.add(new Element("CONSTANT_Long",
                      new String[]{"id", p.toString()},
                      value));
        }
        return value;
    }

    @Override
    public String visitNameAndType(CONSTANT_NameAndType_info c, Integer p) {
        String value = slist.get(p);
        if (value == null) {
            try {
                value = visit(cfpool.get(c.name_index), c.name_index);
                value = value.concat(" " +
                        visit(cfpool.get(c.type_index), c.type_index));
                slist.set(p, value);
                xpool.add(new Element("CONSTANT_NameAndType",
                          new String[]{"id", p.toString()},
                          value));
            } catch (InvalidIndex ex) {
                ex.printStackTrace();
            }
        }
        return value;
    }

    @Override
    public String visitMethodref(CONSTANT_Methodref_info c, Integer p) {
        String value = slist.get(p);
        if (value == null) {
            try {
                value = visit(cfpool.get(c.class_index), c.class_index);
                value = value.concat(" " +
                                     visit(cfpool.get(c.name_and_type_index),
                                     c.name_and_type_index));
                slist.set(p, value);
                xpool.add(new Element("CONSTANT_Methodref",
                          new String[]{"id", p.toString()},
                          value));

            } catch (ConstantPoolException ex) {
                ex.printStackTrace();
            }
        }
        return value;
    }

    @Override
    public String visitMethodHandle(CONSTANT_MethodHandle_info c, Integer p) {
    String value = slist.get(p);
        if (value == null) {
            try {
                value = c.reference_kind.name();
                value = value.concat(" "
                        + visit(cfpool.get(c.reference_index), c.reference_index));
                slist.set(p, value);
                xpool.add(new Element("CONSTANT_MethodHandle",
                          new String[]{"id", p.toString()},
                          value));

            } catch (ConstantPoolException ex) {
                ex.printStackTrace();
            }
        }
        return value;
    }

    @Override
    public String visitMethodType(CONSTANT_MethodType_info c, Integer p) {
        String value = slist.get(p);
        if (value == null) {
            try {
                value = visit(cfpool.get(c.descriptor_index), c.descriptor_index);
                slist.set(p, value);
                xpool.add(new Element("CONSTANT_MethodType",
                          new String[]{"id", p.toString()},
                          value));
            } catch (ConstantPoolException ex) {
                ex.printStackTrace();
            }
        }
        return value;
    }

    @Override
    public String visitString(CONSTANT_String_info c, Integer p) {
        try {

            String value = slist.get(p);
            if (value == null) {
                value = c.getString();
                slist.set(p, value);
                xpool.add(new Element("CONSTANT_String",
                          new String[]{"id", p.toString()},
                          value));
            }
            return value;
        } catch (ConstantPoolException ex) {
            throw new RuntimeException("Fatal error", ex);
        }
    }

    @Override
    public String  visitUtf8(CONSTANT_Utf8_info cnstnt, Integer p) {
        String value = slist.get(p);
        if (value == null) {
            value = cnstnt.value;
            slist.set(p, value);
            xpool.add(new Element("CONSTANT_Utf8",
                      new String[]{"id", p.toString()},
                      value));
        }
        return value;

    }
}


class AttributeVisitor implements Attribute.Visitor<Element, Element> {
    final ClassFile cf;
    final ClassReader x;
    final AnnotationsElementVisitor aev;
    final InstructionVisitor iv;

    public AttributeVisitor(ClassReader x, ClassFile cf) {
        this.x = x;
        this.cf = cf;
        iv =  new InstructionVisitor(x, cf);
        aev = new AnnotationsElementVisitor(x, cf);
    }

    public void visit(Attribute a, Element parent) {
        a.accept(this, parent);
    }

    @Override
    public Element visitBootstrapMethods(BootstrapMethods_attribute bm, Element p) {
        Element e = new Element(x.getCpString(bm.attribute_name_index));
        for (BootstrapMethods_attribute.BootstrapMethodSpecifier bsm : bm.bootstrap_method_specifiers) {
            Element be = new Element("BootstrapMethodSpecifier");
            be.setAttr("ref", x.getCpString(bsm.bootstrap_method_ref));
            if (bsm.bootstrap_arguments.length > 0) {
                Element bme = new Element("MethodArguments");
                for (int index : bsm.bootstrap_arguments) {
                    bme.add(x.getCpString(index));
                }
                bme.trimToSize();
                be.add(bme);
            }
            be.trimToSize();
            e.add(be);
        }
        e.trimToSize();
        if (!x.keepOrder) {
            e.sort();
        }
        p.add(e);
        return null;
    }

    @Override
    public Element visitDefault(DefaultAttribute da, Element p) {
        Element e = new Element(x.getCpString(da.attribute_name_index));
        StringBuilder sb = new StringBuilder();
        for (byte x : da.info) {
            sb.append("0x").append(Integer.toHexString(x)).append(" ");
        }
        e.setAttr("bytes", sb.toString().trim());
        e.trimToSize();
        p.add(e);
        return null;
    }

    @Override
    public Element visitAnnotationDefault(AnnotationDefault_attribute ad, Element p) {
        Element e = new Element(x.getCpString(ad.attribute_name_index));
        e.setAttr("tag", "" + ad.default_value.tag);
        Element child = aev.visit(ad.default_value, e);
        if (child != null) {
            e.add(child);
        }
        e.trimToSize();
        p.add(e);
        return null;
    }

    @Override
    public Element visitCharacterRangeTable(CharacterRangeTable_attribute crt,
                                            Element p) {
        Element e = new Element(x.getCpString(crt.attribute_name_index));
        for (CharacterRangeTable_attribute.Entry ce : crt.character_range_table) {
            e.setAttr("start_pc", "" + ce.start_pc);
            e.setAttr("end_pc", "" + ce.end_pc);
            e.setAttr("range_start", "" + ce.character_range_start);
            e.setAttr("range_end", "" + ce.character_range_end);
            e.setAttr("flags", x.flagString(ce.flags, "Method"));
        }
        e.trimToSize();
        p.add(e);
        return null;
    }

    private Element instructions(Element code, Code_attribute c) {
        Element ielement = new Element("Instructions");
        for (Instruction ins : c.getInstructions()) {
            ielement.add(iv.visit(ins));
        }
        ielement.trimToSize();
        return ielement;
    }

    @Override
    public Element visitCode(Code_attribute c, Element p) {
        Element e = null;

        e = new Element(x.getCpString(c.attribute_name_index),
                "stack", "" + c.max_stack,
                "local", "" + c.max_locals);

        e.add(instructions(e, c));

        for (Code_attribute.Exception_data edata : c.exception_table) {
            e.add(new Element("Handler",
                    "start", "" + edata.start_pc,
                    "end", "" + edata.end_pc,
                    "catch", "" + edata.handler_pc,
                    "class", x.getCpString(edata.catch_type)));

        }
        this.x.readAttributesFor(cf, c.attributes, e);
        e.trimToSize();
        p.add(e);
        return null;
    }

    @Override
    public Element visitCompilationID(CompilationID_attribute cid, Element p) {
        Element e = new Element(x.getCpString(cid.attribute_name_index),
                x.getCpString(cid.compilationID_index));
        p.add(e);
        return null;
    }

    @Override
    public Element visitConstantValue(ConstantValue_attribute cv, Element p) {
        Element e = new Element(x.getCpString(cv.attribute_name_index));
        e.add(x.getCpString(cv.constantvalue_index));
        p.add(e);
        return null;
    }

    @Override
    public Element visitDeprecated(Deprecated_attribute d, Element p) {
        Element e = new Element(x.getCpString(d.attribute_name_index));
        p.add(e);
        return null;
    }

    @Override
    public Element visitEnclosingMethod(EnclosingMethod_attribute em, Element p) {
        Element e = new Element(x.getCpString(em.attribute_name_index));
        e.setAttr("class", x.getCpString(em.class_index));
        e.setAttr("desc", x.getCpString(em.method_index));
        e.trimToSize();
        p.add(e);
        return null;
    }

    @Override
    public Element visitExceptions(Exceptions_attribute e, Element p) {
        Element ee = new Element(x.getCpString(e.attribute_name_index));
        for (int idx : e.exception_index_table) {
            Element n = new Element("Item");
            n.setAttr("class", x.getCpString(idx));
            ee.add(n);
        }
        ee.trimToSize();
        p.add(ee);
        return null;
    }

    @Override
    public Element visitInnerClasses(InnerClasses_attribute ic, Element p) {
        for (Info info : ic.classes) {
            Element e = new Element(x.getCpString(ic.attribute_name_index));
            e.setAttr("class", x.getCpString(info.inner_class_info_index));
            e.setAttr("outer", x.getCpString(info.outer_class_info_index));
            e.setAttr("name", x.getCpString(info.inner_name_index));
            e.setAttr("flags", x.flagString(info.inner_class_access_flags,
                    "InnerClass"));
            e.trimToSize();
            p.add(e);
        }
        return null;
    }

    @Override
    public Element visitLineNumberTable(LineNumberTable_attribute lnt, Element p) {
        String name = x.getCpString(lnt.attribute_name_index);
        for (LineNumberTable_attribute.Entry e : lnt.line_number_table) {
            Element l = new Element(name);
            l.setAttr("bci", "" + e.start_pc);
            l.setAttr("line", "" + e.line_number);
            l.trimToSize();
            p.add(l);
        }
        return null; // already added to parent
    }

    @Override
    public Element visitLocalVariableTable(LocalVariableTable_attribute lvt,
                                                Element p) {
        String name = x.getCpString(lvt.attribute_name_index);
        for (LocalVariableTable_attribute.Entry e : lvt.local_variable_table) {
            Element l = new Element(name);
            l.setAttr("bci", "" + e.start_pc);
            l.setAttr("span", "" + e.length);
            l.setAttr("name", x.getCpString(e.name_index));
            l.setAttr("type", x.getCpString(e.descriptor_index));
            l.setAttr("slot", "" + e.index);
            l.trimToSize();
            p.add(l);
        }
        return null; // already added to parent
    }

    @Override
    public Element visitLocalVariableTypeTable(LocalVariableTypeTable_attribute lvtt,
                                                    Element p) {
        String name = x.getCpString(lvtt.attribute_name_index);
        for (LocalVariableTypeTable_attribute.Entry e : lvtt.local_variable_table) {
            Element l = new Element(name);
            l.setAttr("bci", "" + e.start_pc);
            l.setAttr("span", "" + e.length);
            l.setAttr("name", x.getCpString(e.name_index));
            l.setAttr("type", x.getCpString(e.signature_index));
            l.setAttr("slot", "" + e.index);
            l.trimToSize();
            p.add(l);
        }
        return null; // already added to parent
    }

    @Override
    public Element visitMethodParameters(MethodParameters_attribute mp, Element p) {
        String name = x.getCpString(mp.attribute_name_index);
        for (MethodParameters_attribute.Entry e : mp.method_parameter_table) {
            Element l = new Element(name);
            l.setAttr("name", x.getCpString(e.name_index));
            l.setAttr("flag", "" + e.flags);
            l.trimToSize();
            p.add(l);
        }
        return null; // already added to parent
    }
    private void parseAnnotation(Annotation anno, Element p) {
        Element ea = new Element("Annotation");
        ea.setAttr("name", "" + x.getCpString(anno.type_index));
        for (Annotation.element_value_pair evp : anno.element_value_pairs) {
            Element evpe = new Element("Element");
            evpe.setAttr("tag", "" + evp.value.tag);
            evpe.setAttr("value", x.getCpString(evp.element_name_index));
            Element child = aev.visit(evp.value, evpe);
            if (child != null) {
                evpe.add(child);
            }
            ea.add(evpe);
        }
        ea.trimToSize();
        p.add(ea);
    }

    private void parseAnnotations(Annotation[] ra, Element p) {
        for (Annotation anno : ra) {
            parseAnnotation(anno, p);
        }
    }

    @Override
    public Element visitRuntimeVisibleAnnotations(RuntimeVisibleAnnotations_attribute rva,
                                                  Element p) {
        Element e = new Element(x.getCpString(rva.attribute_name_index));
        parseAnnotations(rva.annotations, e);
        e.trimToSize();
        p.add(e);
        return null;
    }

    @Override
    public Element visitRuntimeInvisibleAnnotations(RuntimeInvisibleAnnotations_attribute ria,
                                                    Element p) {
        Element e = new Element(x.getCpString(ria.attribute_name_index));
        parseAnnotations(ria.annotations, e);
        e.trimToSize();
        p.add(e);
        return null;
    }

    @Override
    public Element visitRuntimeVisibleParameterAnnotations(RuntimeVisibleParameterAnnotations_attribute rvpa,
                                                           Element p) {
        Element e = new Element(x.getCpString(rvpa.attribute_name_index));
        for (Annotation[] pa : rvpa.parameter_annotations) {
           parseAnnotations(pa, e);
        }
        p.add(e);
        return null;
    }

    @Override
    public Element visitRuntimeInvisibleParameterAnnotations(RuntimeInvisibleParameterAnnotations_attribute ripa,
                                                             Element p) {
        Element e = new Element(x.getCpString(ripa.attribute_name_index));
        for (Annotation[] pa : ripa.parameter_annotations) {
            parseAnnotations(pa, e);
        }
        p.add(e);
        return null;
    }

    private void parsePosition(Position ap, Element p) {
        Element te = new Element();
        switch (ap.type) {
            case CLASS_TYPE_PARAMETER: // 0x00
                te.setName("CLASS_TYPE_PARAMETER");
                te.setAttr("idx", "" + ap.parameter_index);
                break;
            case METHOD_TYPE_PARAMETER: // 0x01
                te.setName("METHOD_TYPE_PARAMETER");
                te.setAttr("idx", "" + ap.parameter_index);
                break;
            case CLASS_EXTENDS: // 0x10
                te.setName("CLASS_EXTENDS");
                te.setAttr("idx", "" + ap.type_index);
                break;
            case CLASS_TYPE_PARAMETER_BOUND: // 0x11
                te.setName("CLASS_TYPE_PARAMETER_BOUND");
                te.setAttr("idx1", "" + ap.parameter_index);
                te.setAttr("idx2", "" + ap.bound_index);
                break;
            case METHOD_TYPE_PARAMETER_BOUND: // 0x12
                te.setName("METHOD_TYPE_PARAMETER_BOUND");
                te.setAttr("idx1", "" + ap.parameter_index);
                te.setAttr("idx2", "" + ap.bound_index);
                break;
            case FIELD: // 0x13
                te.setName("FIELD");
                break;
            case METHOD_RETURN: // 0x14
                te.setName("METHOD_RETURN");
                break;
            case METHOD_RECEIVER: // 0x15
                te.setName("METHOD_RECEIVER");
                break;
            case METHOD_FORMAL_PARAMETER: // 0x16
                te.setName("METHOD_FORMAL_PARAMETER");
                te.setAttr("idx", "" + ap.parameter_index);
                break;
            case THROWS: // 0x17
                te.setName("THROWS");
                te.setAttr("idx", "" + ap.type_index);
                break;
            case LOCAL_VARIABLE: // 0x40
                te.setName("LOCAL_VARIABLE");
                for (int i = 0; i < ap.lvarIndex.length; i++) {
                    te.setAttr("lvar_idx_" + i, "" + ap.lvarIndex[i]);
                    te.setAttr("lvar_len_" + i, "" + ap.lvarLength[i]);
                    te.setAttr("lvar_off_" + i, "" + ap.lvarOffset[i]);
                }
                break;
            case RESOURCE_VARIABLE: // 0x41
                te.setName("RESOURCE_VARIABLE");
                for (int i = 0; i < ap.lvarIndex.length ; i++) {
                    te.setAttr("lvar_idx_" + i, "" + ap.lvarIndex[i]);
                    te.setAttr("lvar_len_" + i, "" + ap.lvarLength[i]);
                    te.setAttr("lvar_off_" + i, "" + ap.lvarOffset[i]);
                }
                break;
            case EXCEPTION_PARAMETER: // 0x42
                te.setName("EXCEPTION_PARAMETER");
                te.setAttr("idx", "" + ap.exception_index);
                break;
            case INSTANCEOF: // 0x43
                te.setName("INSTANCE_OF");
                te.setAttr("off", "" + ap.offset);
                break;
            case NEW: // 0x44
                te.setName("NEW");
                te.setAttr("off", "" + ap.offset);
                break;
            case CONSTRUCTOR_REFERENCE: // 0x45
                te.setName("CONSTRUCTOR_REFERENCE_RECEIVER");
                te.setAttr("off", "" + ap.offset);
                break;
            case METHOD_REFERENCE: // 0x46
                te.setName("METHOD_REFERENCE_RECEIVER");
                te.setAttr("off", "" + ap.offset);
                break;
            case CAST: // 0x47
                te.setName("CAST");
                te.setAttr("off", "" + ap.offset);
                te.setAttr("idx", "" + ap.type_index);
                break;
            case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: // 0x48
                te.setName("CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT");
                te.setAttr("off", "" + ap.offset);
                te.setAttr("idx", "" + ap.type_index);
                break;
            case METHOD_INVOCATION_TYPE_ARGUMENT: // 0x49
                te.setName("METHOD_INVOCATION_TYPE_ARGUMENT");
                te.setAttr("off", "" + ap.offset);
                te.setAttr("idx", "" + ap.type_index);
                break;
            case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: // 0x4A
                te.setName("CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT");
                te.setAttr("off", "" + ap.offset);
                te.setAttr("idx", "" + ap.type_index);
                break;
            case METHOD_REFERENCE_TYPE_ARGUMENT: // 0x4B
                te.setName("METHOD_REFERENCE_TYPE_ARGUMENT");
                te.setAttr("off", "" + ap.offset);
                te.setAttr("idx", "" + ap.type_index);
                break;
            default:
                throw new RuntimeException("not implemented");
        }
        te.trimToSize();
        p.add(te);
    }
    private void parseTypeAnnotations(TypeAnnotation pa, Element p) {
        Element pta = new Element("RuntimeVisibleTypeAnnotation");
        p.add(pta);
        Position pos = pa.position;
        parsePosition(pos, pta);
        parseAnnotation(pa.annotation, pta);
    }

    @Override
    public Element visitRuntimeVisibleTypeAnnotations(RuntimeVisibleTypeAnnotations_attribute rvta, Element p) {
        Element e = new Element(x.getCpString(rvta.attribute_name_index));
        for (TypeAnnotation pa : rvta.annotations) {
            parseTypeAnnotations(pa, e);
        }
        e.sort();
        p.add(e);
        return null;
    }

    @Override
    public Element visitRuntimeInvisibleTypeAnnotations(RuntimeInvisibleTypeAnnotations_attribute rita, Element p) {
        Element e = new Element(x.getCpString(rita.attribute_name_index));
        for (TypeAnnotation pa : rita.annotations) {
            parseTypeAnnotations(pa, e);
        }
        e.sort();
        p.add(e);
        return null;
    }

    @Override
    public Element visitSignature(Signature_attribute s, Element p) {
        String aname = x.getCpString(s.attribute_name_index);
        String sname = x.getCpString(s.signature_index);
        Element se = new Element(aname);
        se.add(sname);
        se.trimToSize();
        p.add(se);
        return null;
    }

    @Override
    public Element visitSourceDebugExtension(SourceDebugExtension_attribute sde,
                                                Element p) {
        String aname = x.getCpString(sde.attribute_name_index);
        Element se = new Element(aname);
        se.setAttr("val", sde.getValue());
        se.trimToSize();
        p.add(se);
        return null;
    }

    @Override
    public Element visitSourceFile(SourceFile_attribute sf, Element p) {
        String aname = x.getCpString(sf.attribute_name_index);
        String sname = x.getCpString(sf.sourcefile_index);
        Element se = new Element(aname);
        se.add(sname);
        se.trimToSize();
        p.add(se);
        return null;
    }

    @Override
    public Element visitSourceID(SourceID_attribute sid, Element p) {
        Element e = new Element(x.getCpString(sid.attribute_name_index));
        e.add(x.getCpString(sid.sourceID_index));
        e.trimToSize();
        p.add(e);
        return null;
    }

    @Override
    public Element visitStackMap(StackMap_attribute sm, Element p) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public Element visitStackMapTable(StackMapTable_attribute smt, Element p) {
        Element stackmap = new Element(x.getCpString(smt.attribute_name_index));
        for (StackMapTable_attribute.stack_map_frame f : smt.entries) {
           StackMapVisitor smv = new StackMapVisitor(x, cf, stackmap);
           stackmap.add(smv.visit(f));
        }
        stackmap.trimToSize();
        p.add(stackmap);
        return null;
    }

    @Override
    public Element visitSynthetic(Synthetic_attribute s, Element p) {
        Element e = new Element(x.getCpString(s.attribute_name_index));
        e.trimToSize();
        p.add(e);
        return null;
    }
}

class StackMapVisitor implements StackMapTable_attribute.stack_map_frame.Visitor<Element, Void> {

    final ClassFile cf;
    final ClassReader x;
    final Element parent;

    public StackMapVisitor(ClassReader x, ClassFile cf, Element parent) {
        this.x = x;
        this.cf = cf;
        this.parent = parent;
    }

    public Element visit(StackMapTable_attribute.stack_map_frame frame) {
        return frame.accept(this, null);
    }

    @Override
    public Element visit_same_frame(same_frame sm_frm, Void p) {
        Element e = new Element("SameFrame");
        e.setAttr("tag", "" + sm_frm.frame_type);
        return e;
    }

    @Override
    public Element visit_same_locals_1_stack_item_frame(same_locals_1_stack_item_frame s, Void p) {
        Element e = new Element("SameLocals1StackItemFrame");
        e.setAttr("tag", "" + s.frame_type);
        e.addAll(getVerificationTypeInfo("Stack", s.stack));
        e.trimToSize();
        return e;
    }

    @Override
    public Element visit_same_locals_1_stack_item_frame_extended(same_locals_1_stack_item_frame_extended s, Void p) {
        Element e = new Element("SameLocals1StackItemFrameExtended");
        e.setAttr("tag", "" + s.frame_type);
        e.addAll(getVerificationTypeInfo("Stack", s.stack));
        e.trimToSize();
        return e;
    }

    @Override
    public Element visit_chop_frame(chop_frame c, Void p) {
        Element e = new Element("Chop" + (251 - c.frame_type));
        e.setAttr("tag", "" + c.frame_type);
        e.setAttr("offset", "" + c.offset_delta);
        return e;
    }

    @Override
    public Element visit_same_frame_extended(same_frame_extended s, Void p) {
        Element e = new Element("SameFrameExtended");
        e.setAttr("tag", "" + s.frame_type);
        e.setAttr("offset", "" + s.offset_delta);
        return e;
    }

    @Override
    public Element visit_append_frame(append_frame a, Void p) {
       Element e = new Element("AppendFrame" + (a.frame_type - 251));
       e.setAttr("tag", "" + a.frame_type);
       e.addAll(getVerificationTypeInfo("Local", a.locals));
       e.trimToSize();
       return e;
    }

    @Override
    public Element visit_full_frame(full_frame fl_frm, Void p) {
         Element e = new Element("FullFrame");
         e.setAttr("tag", "" + fl_frm.frame_type);
         e.addAll(getVerificationTypeInfo("Local", fl_frm.locals));
         e.trimToSize();
         return e;
    }

    private Element getVerificationTypeInfo(String kind,
            StackMapTable_attribute.verification_type_info velems[]) {
        Element container = new Element(velems.length);
        for (StackMapTable_attribute.verification_type_info v : velems) {
            Element ve = null;
            int offset = 0;
            int index = 0;
            switch (v.tag) {
                case StackMapTable_attribute.verification_type_info.ITEM_Top:
                    ve = new Element("ITEM_Top");
                    break;
                case StackMapTable_attribute.verification_type_info.ITEM_Integer:
                    ve = new Element("ITEM_Integer");
                    break;
                case StackMapTable_attribute.verification_type_info.ITEM_Float:
                    ve = new Element("ITEM_Float");
                    break;
                case StackMapTable_attribute.verification_type_info.ITEM_Long:
                    ve = new Element("ITEM_Long");
                    break;
                case StackMapTable_attribute.verification_type_info.ITEM_Double:
                    ve = new Element("ITEM_Double");
                    break;
                case StackMapTable_attribute.verification_type_info.ITEM_Null:
                    ve = new Element("ITEM_Null");
                    break;
                case StackMapTable_attribute.verification_type_info.ITEM_Uninitialized:
                    ve = new Element("ITEM_Uninitialized");
                    offset = ((StackMapTable_attribute.Uninitialized_variable_info) v).offset;
                    ve.setAttr("offset", "" + offset);
                    break;
                case StackMapTable_attribute.verification_type_info.ITEM_UninitializedThis:
                    ve = new Element("ITEM_UnitializedtThis");
                    break;
                case StackMapTable_attribute.verification_type_info.ITEM_Object:
                    ve = new Element("ITEM_Object");
                    index = ((StackMapTable_attribute.Object_variable_info) v).cpool_index;
                    ve.setAttr("class", x.getCpString(index));
                    break;
                default:
                    ve = new Element("Unknown");
            }
            Element kindE = new Element(kind);
            kindE.setAttr("tag", "" + v.tag);
            container.add(kindE);
            kindE.add(ve);
        }
        container.trimToSize();
        return container;
    }
}

class InstructionVisitor implements Instruction.KindVisitor<Element, Void> {

    final ClassReader x;
    final ClassFile cf;

    public InstructionVisitor(ClassReader x, ClassFile cf) {
        this.x = x;
        this.cf = cf;
    }

    public Element visit(Instruction i) {
        Element ie =  i.accept(this, null);
        ie.trimToSize();
        return ie;
    }

    @Override
    public Element visitNoOperands(Instruction i, Void p) {
        Opcode o = i.getOpcode();
        Element e = new Element(i.getMnemonic());
        if (o.opcode > 0xab && o.opcode <= 0xb1) {
            e.setAttr("pc", "" + i.getPC());
        }
        return e;
    }

    @Override
    public Element visitArrayType(Instruction i, TypeKind tk, Void p) {
        Element ie = new Element(i.getMnemonic());
        ie.setAttr("num", "" + tk.value);
        ie.setAttr("val", tk.name);
        return ie;
    }

    @Override
    public Element visitBranch(Instruction i, int i1, Void p) {
        Element ie = new Element(i.getMnemonic());
        ie.setAttr("lab", "" + (i.getPC() + i1));
        return ie;
    }

    @Override
    public Element visitConstantPoolRef(Instruction i, int i1, Void p) {
        Element ie = new Element(i.getMnemonic());
        ie.setAttr("ref", x.getCpString(i1));
        return ie;
    }

    @Override
    public Element visitConstantPoolRefAndValue(Instruction i, int i1, int i2, Void p) {
        // workaround for a potential bug in classfile
        Element ie = new Element(i.getMnemonic());
        if (i.getOpcode().equals(Opcode.IINC_W)) {
            ie.setAttr("loc", "" + i1);
            ie.setAttr("num", "" + i2);
        } else {
            ie.setAttr("ref", x.getCpString(i1));
            ie.setAttr("val", "" + i2);
        }
        return ie;
    }

    @Override
    public Element visitLocal(Instruction i, int i1, Void p) {
        Element ie = new Element(i.getMnemonic());
        ie.setAttr("loc", "" + i1);
        return ie;
    }

    @Override
    public Element visitLocalAndValue(Instruction i, int i1, int i2, Void p) {
        Element ie = new Element(i.getMnemonic());
        ie.setAttr("loc", "" + i1);
        ie.setAttr("num", "" + i2);
        return ie;
    }

    @Override
    public Element visitLookupSwitch(Instruction i, int i1, int i2, int[] ints,
                                     int[] ints1, Void p) {
        Element ie = new Element(i.getMnemonic());
        int pc = i.getPC();
        ie.setAttr("lab", "" + (pc + i1));
        for (int k = 0 ; k < i2 ; k++) {
            Element c = new Element("Case");
            c.setAttr("num", "" + (ints[k]));
            c.setAttr("lab", "" + (pc + ints1[k]));
            c.trimToSize();
            ie.add(c);
        }
        return ie;
    }

    @Override
    public Element visitTableSwitch(Instruction i, int i1, int i2, int i3,
                                    int[] ints, Void p) {
        Element ie = new Element(i.getMnemonic());
        int pc = i.getPC();
        ie.setAttr("lab", "" + (pc + i1));
        for (int k : ints) {
            Element c = new Element("Case");
            c.setAttr("num", "" + (k + i2));
            c.setAttr("lab", "" + (pc + k));
            c.trimToSize();
            ie.add(c);
        }
        return ie;
    }

    @Override
    public Element visitValue(Instruction i, int i1, Void p) {
        Element ie = new Element(i.getMnemonic());
        ie.setAttr("num", "" + i1);
        return ie;
    }

    @Override
    public Element visitUnknown(Instruction i, Void p) {
        Element e = new Element(i.getMnemonic());
        e.setAttr("pc", "" + i.getPC());
        e.setAttr("opcode", "" + i.getOpcode().opcode);
        return e;
    }
}

class AnnotationsElementVisitor implements Annotation.element_value.Visitor<Element, Element> {
    final ClassReader x;
    final ClassFile cf;

    public AnnotationsElementVisitor(ClassReader x, ClassFile cf) {
        this.x = x;
        this.cf = cf;
    }

    public Element visit(Annotation.element_value v, Element p) {
        return v.accept(this, p);
    }

    @Override
    public Element visitPrimitive(Primitive_element_value e, Element p) {
        Element el = new Element("String");
        el.setAttr("val", x.getCpString(e.const_value_index));
        el.trimToSize();
        return el;
    }

    @Override
    public Element visitEnum(Enum_element_value e, Element p) {
        Element el = new Element("Enum");
        el.setAttr("name", x.getCpString(e.const_name_index));
        el.setAttr("type", x.getCpString(e.type_name_index));
        el.trimToSize();
        return el;
    }

    @Override
    public Element visitClass(Class_element_value c, Element p) {
        Element el = new Element("Class");
        el.setAttr("name", x.getCpString(c.class_info_index));
        el.trimToSize();
        return el;
    }

    @Override
    public Element visitAnnotation(Annotation_element_value a, Element p) {
        Element el = new Element("Annotation");
        Annotation anno = a.annotation_value;
        for (Annotation.element_value_pair evp : anno.element_value_pairs) {
            Element child = visit(evp.value, el);
            if (child != null) {
                el.add(child);
            }
        }
        el.trimToSize();
        return el;
    }

    @Override
    public Element visitArray(Array_element_value a, Element p) {
     Element el = new Element("Array");
        for (Annotation.element_value v : a.values) {
           Element child = visit(v, el);
           if (child != null) {
               el.add(child);
           }
        }
        el.trimToSize();
        return el;
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java ClassReader.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.