com.github.projectsandstone.asmmeta.asm.ASMMetaClassVisitor.java Source code

Java tutorial

Introduction

Here is the source code for com.github.projectsandstone.asmmeta.asm.ASMMetaClassVisitor.java

Source

/**
 *      ASMMeta - Index information about classes and elements.
 *
 *         The MIT License (MIT)
 *
 *      Copyright (c) 2016 Sandstone <https://github.com/ProjectSandstone/ASMMeta/>
 *      Copyright (c) contributors
 *
 *
 *      Permission is hereby granted, free of charge, to any person obtaining a copy
 *      of this software and associated documentation files (the "Software"), to deal
 *      in the Software without restriction, including without limitation the rights
 *      to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *      copies of the Software, and to permit persons to whom the Software is
 *      furnished to do so, subject to the following conditions:
 *
 *      The above copyright notice and this permission notice shall be included in
 *      all copies or substantial portions of the Software.
 *
 *      THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *      IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *      FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *      AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *      LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *      OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *      THE SOFTWARE.
 */
package com.github.projectsandstone.asmmeta.asm;

import com.github.projectsandstone.asmmeta.element.AnnotationElement;
import com.github.projectsandstone.asmmeta.element.ClassElement;
import com.github.projectsandstone.asmmeta.element.ConstructorElement;
import com.github.projectsandstone.asmmeta.element.FieldElement;
import com.github.projectsandstone.asmmeta.element.IMetaElement;
import com.github.projectsandstone.asmmeta.element.MetaElement;
import com.github.projectsandstone.asmmeta.element.MethodElement;
import com.github.projectsandstone.asmmeta.resolver.Resolvers;
import com.github.projectsandstone.asmmeta.value.KeyedValue;
import com.github.projectsandstone.asmmeta.value.Value;
import com.github.projectsandstone.asmmeta.data.MetaData;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import java.util.ArrayList;
import java.util.List;

public class ASMMetaClassVisitor extends ClassVisitor {
    private final Resolvers resolvers;
    private MetaElement<?> current;

    public ASMMetaClassVisitor(int api) {
        this(api, new Resolvers.Default());
    }

    public ASMMetaClassVisitor(int api, ClassVisitor cv) {
        this(api, cv, new Resolvers.Default());
    }

    public ASMMetaClassVisitor(int api, Resolvers resolvers) {
        super(api);
        this.resolvers = resolvers;
    }

    public ASMMetaClassVisitor(int api, ClassVisitor cv, Resolvers resolvers) {
        super(api, cv);
        this.resolvers = resolvers;
    }

    private AnnotationElement createAnnotationElement(MetaElement<?> metaElement, String desc) {
        return new AnnotationElement(metaElement, 0, desc, desc, new MetaData(),
                this.resolvers.getAnnotationResolver());
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName,
            String[] interfaces) {

        ClassElement classElement = new ClassElement(this.current, access, name, "", new MetaData(),
                this.resolvers.getClassResolver(), superName, interfaces);

        this.addToEnclosing(classElement);
    }

    @Override
    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
        AnnotationElement annotationElement = createAnnotationElement(this.current, desc);

        this.current.getMetaData().getAnnotationList().add(annotationElement);

        return new ASMMetaAnnotationVisitor(this.api, super.visitAnnotation(desc, visible), annotationElement,
                annotationElement.getValues());
    }

    @Override
    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {

        FieldElement fieldElement = new FieldElement(this.current, access, name, desc, new MetaData(),
                this.resolvers.getFieldResolver());

        this.addToEnclosing(fieldElement);

        return new FieldVisitor(Opcodes.ASM5, super.visitField(access, name, desc, signature, value)) {
            @Override
            public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
                AnnotationElement annotationElement = createAnnotationElement(fieldElement, desc);

                fieldElement.getMetaData().getAnnotationList().add(annotationElement);

                return new ASMMetaAnnotationVisitor(this.api, super.visitAnnotation(desc, visible),
                        annotationElement, annotationElement.getValues());
            }

            @Override
            public void visitEnd() {
                fieldElement.immutate();
                super.visitEnd();
            }
        };
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        MetaElement<?> mElement;

        if (name.equals("<init>") || name.equals("<clinit>")) {
            mElement = new ConstructorElement(this.current, access, name, signature, new MetaData(),
                    this.resolvers.getConstructorResolver());
        } else {
            mElement = new MethodElement(this.current, access, name, signature, new MetaData(),
                    this.resolvers.getMethodResolver());
        }

        this.addToEnclosing(mElement);

        return new MethodVisitor(Opcodes.ASM5, super.visitMethod(access, name, desc, signature, exceptions)) {
            @Override
            public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
                AnnotationElement annotationElement = createAnnotationElement(mElement, desc);

                mElement.getMetaData().getAnnotationList().add(annotationElement);

                return new ASMMetaAnnotationVisitor(this.api, super.visitAnnotation(desc, visible),
                        annotationElement, annotationElement.getValues());
            }

            @Override
            public void visitEnd() {
                mElement.immutate();
                super.visitEnd();
            }
        };
    }

    @Override
    public void visitEnd() {
        MetaElement<?> current = this.current;

        if (current != null) {
            current.immutate();
        }

        super.visitEnd();
    }

    public MetaElement<?> getCurrent() {
        return this.current;
    }

    public void addToEnclosing(MetaElement<?> iMetaElement) {
        IMetaElement<?> enclosing = this.current;

        if (enclosing == null) {
            current = iMetaElement;
        } else {
            enclosing.getChildElements().add(iMetaElement);
        }
    }

    private final class ASMMetaAnnotationVisitor extends AnnotationVisitor {
        private final MetaElement<?> element;
        private final List<Value<?>> values;

        public ASMMetaAnnotationVisitor(int api, AnnotationVisitor av, MetaElement<?> element,
                List<Value<?>> values) {
            super(api, av);
            this.element = element;
            this.values = values;
        }

        public Value<?> createValue(String name, Object value) {
            if (name == null)
                return new Value<>(value);
            else
                return new KeyedValue<>(name, value);
        }

        @Override
        public void visit(String name, Object value) {

            Value<?> createdValue;

            if (!(value instanceof Type)) {
                boolean array = value.getClass().isArray();

                if (!array) {
                    createdValue = this.createValue(name, value);
                } else {

                    AnnotationVisitor annotationVisitor = this.visitArray(name);

                    Object[] values = (Object[]) value;

                    for (Object o : values) {
                        annotationVisitor.visit(null, o);
                    }

                    annotationVisitor.visitEnd();

                    return;
                }

            } else {
                Type type = (Type) value;
                createdValue = this.createValue(name, new ClassElement(this.element, 0, type.getInternalName(), "",
                        new MetaData(), resolvers.getClassResolver(), null, new String[0]));
            }

            this.values.add(createdValue);

            super.visit(name, value);
        }

        @Override
        public AnnotationVisitor visitAnnotation(String name, String desc) {
            AnnotationElement annotationElement = createAnnotationElement(this.element, desc);

            Value<?> createdValue = this.createValue(name, annotationElement);

            this.values.add(createdValue);

            return new ASMMetaAnnotationVisitor(this.api, super.visitAnnotation(name, desc), annotationElement,
                    annotationElement.getValues());
        }

        @Override
        public AnnotationVisitor visitArray(String name) {

            List<Value<?>> list = new ArrayList<>();

            this.values.add(this.createValue(name, list));

            return new ASMMetaAnnotationVisitor(this.api, super.visitArray(name), this.element, list);
        }

        @Override
        public void visitEnum(String name, String desc, String value) {
            super.visitEnum(name, desc, value);
        }

        @Override
        public void visitEnd() {
            this.element.immutate();
            super.visitEnd();
        }
    }
}