org.eclipse.ocl.examples.codegen.asm5.ASM5JavaAnnotationReader.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.ocl.examples.codegen.asm5.ASM5JavaAnnotationReader.java

Source

/*******************************************************************************
 * Copyright (c) 2013 CEA LIST and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *   E.D.Willink(CEA LIST) - Initial API and implementation
 *******************************************************************************/
package org.eclipse.ocl.examples.codegen.asm5;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.TypePath;
import org.objectweb.asm.TypeReference;

/**
 * JavaAnnotationReader supports determination of the declared @NonNull, @Nullable return annotation of a method.
 * 
 * (The internal processing also determines the parameter type annotations, but as yet there is no API to exploit this.) 
 */
public class ASM5JavaAnnotationReader {
    private static final Logger logger = Logger.getLogger(ASM5JavaAnnotationReader.class);

    private final @NonNull Map<@NonNull String, Map<@NonNull Integer, @Nullable Boolean>> desc2typerefValue2state = new HashMap<@NonNull String, Map<@NonNull Integer, @Nullable Boolean>>();
    private final @NonNull Set<String> readClasses = new HashSet<String>();
    @SuppressWarnings("null")
    private final @NonNull String nonNullDesc = Type.getDescriptor(NonNull.class);
    @SuppressWarnings("null")
    private final @NonNull String nullableDesc = Type.getDescriptor(Nullable.class);

    /**
     * Return true for an @NonNull annotation, false for an @Nullable annotation, null otherwise.
     */
    public ASM5JavaAnnotationReader() {
    }

    public @Nullable Boolean getIsNonNull(@NonNull Method method) {
        final String className = method.getDeclaringClass().getName();
        final String requiredDesc = getMethodKey(className, method.getName(), Type.getMethodDescriptor(method));
        Map<@NonNull Integer, @Nullable Boolean> typeref2state = desc2typerefValue2state.get(requiredDesc);
        Integer returnTypeReference = TypeReference.newTypeReference(TypeReference.METHOD_RETURN).getValue();
        if (typeref2state != null) {
            return typeref2state.get(returnTypeReference);
        }
        if (!readClasses.add(className)) {
            return null;
        }
        //      System.out.println("getIsNonNull: " + requiredDesc + " " + Integer.toHexString(returnTypeReference));
        InputStream classStream = null;
        try {
            final int flags = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES | ClassReader.SKIP_CODE;
            ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
            String classFileName = className.replace('.', '/') + ".class";
            classStream = contextClassLoader.getResourceAsStream(classFileName);
            final ClassReader cr = new ClassReader(classStream) {

                @Override
                public void accept(ClassVisitor classVisitor, int flags) {
                    super.accept(classVisitor, flags);
                }

                @Override
                public void accept(ClassVisitor classVisitor, Attribute[] attrs, int flags) {
                    super.accept(classVisitor, attrs, flags);
                }

            };
            ClassVisitor cv = new ClassVisitor(Opcodes.ASM5) {
                @Override
                public void visit(int version, int access, String name, String signature, String superName,
                        String[] interfaces) {
                }

                @Override
                public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
                    return null;
                }

                @Override
                public void visitAttribute(Attribute attr) {
                }

                @Override
                public void visitEnd() {
                }

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

                @Override
                public void visitInnerClass(String name, String outerName, String innerName, int access) {
                }

                @Override
                public MethodVisitor visitMethod(int access, String name, String desc, String signature,
                        String[] exceptions) {
                    final String methodDesc = getMethodKey(className, name, desc);// + " " + signature;
                    //               System.out.println("  ClassVisitor.visitMethod: " + methodDesc);
                    final HashMap<@NonNull Integer, @Nullable Boolean> typerefValue2state = new HashMap<@NonNull Integer, @Nullable Boolean>();
                    desc2typerefValue2state.put(methodDesc, typerefValue2state);
                    return new MethodVisitor(Opcodes.ASM5) {
                        @Override
                        public AnnotationVisitor visitAnnotation(String annotationDesc, boolean visible) {
                            return null;
                        }

                        @Override
                        public AnnotationVisitor visitAnnotationDefault() {
                            return null;
                        }

                        @Override
                        public void visitAttribute(Attribute attr) {
                        }

                        @Override
                        public void visitCode() {
                        }

                        @Override
                        public void visitEnd() {
                        }

                        @Override
                        public void visitFieldInsn(int opcode, String owner, String name, String desc) {
                        }

                        @Override
                        public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
                        }

                        @Override
                        public void visitIincInsn(int var, int increment) {
                        }

                        @Override
                        public void visitInsn(int opcode) {
                        }

                        @Override
                        public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String desc,
                                boolean visible) {
                            return null;
                        }

                        @Override
                        public void visitIntInsn(int opcode, int operand) {
                        }

                        @Override
                        public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
                                Object... bsmArgs) {
                        }

                        @Override
                        public void visitJumpInsn(int opcode, Label label) {
                        }

                        @Override
                        public void visitLabel(Label label) {
                        }

                        @Override
                        public void visitLdcInsn(Object cst) {
                        }

                        @Override
                        public void visitLineNumber(int line, Label start) {
                        }

                        @Override
                        public void visitLocalVariable(String name, String desc, String signature, Label start,
                                Label end, int index) {
                        }

                        @Override
                        public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath,
                                Label[] start, Label[] end, int[] index, String desc, boolean visible) {
                            return null;
                        }

                        @Override
                        public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
                        }

                        @Override
                        public void visitMaxs(int maxStack, int maxLocals) {
                        }

                        @Override
                        public void visitMethodInsn(int opcode, String owner, String name, String desc) {
                        }

                        @Override
                        public void visitMethodInsn(int opcode, String owner, String name, String desc,
                                boolean itf) {
                        }

                        @Override
                        public void visitMultiANewArrayInsn(String desc, int dims) {
                        }

                        @Override
                        public void visitParameter(String name, int access) {
                        }

                        @Override
                        public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
                                boolean visible) {
                            return null;
                        }

                        @Override
                        public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
                        }

                        @Override
                        public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath,
                                String desc, boolean visible) {
                            return null;
                        }

                        @Override
                        public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
                        }

                        @Override
                        public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc,
                                boolean visible) {
                            //                     System.out.println("    MethodVisitor-TypeAnnotation:" + Integer.toHexString(typeRef) + " " + typePath + " " + desc + " " + visible);
                            //                     TypeReference typeReference = new TypeReference(typeRef);
                            //                     System.out.println("    : " + Integer.toHexString(typeReference.getValue()) + ":" + typeReference.getSort() + ":" + typeReference.getTypeParameterIndex());
                            if (desc.equals(nonNullDesc)) {
                                //                        System.out.println("    MethodVisitor-TypeAnnotation:" + Integer.toHexString(typeRef) + " " + typePath + " " + desc);
                                typerefValue2state.put(typeRef, true);
                            } else if (desc.equals(nullableDesc)) {
                                //                        System.out.println("    MethodVisitor-TypeAnnotation:" + Integer.toHexString(typeRef) + " " + typePath + " " + desc);
                                typerefValue2state.put(typeRef, false);
                            }
                            return null;
                        }

                        @Override
                        public void visitTypeInsn(int opcode, String type) {
                        }

                        @Override
                        public void visitVarInsn(int opcode, int var) {
                        }
                    };
                }

                @Override
                public void visitOuterClass(String owner, String name, String desc) {
                }

                @Override
                public void visitSource(String source, String debug) {
                }

                @Override
                public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc,
                        boolean visible) {
                    //               System.out.println("  ClassVisitor-TypeAnnotation:" + typeRef + " " + typePath + " " + desc + " " + visible);
                    return null;
                }
            };
            cr.accept(cv, flags);
        } catch (IOException e) {
            logger.error("Failed to read '" + className + "'", e);
        } finally {
            if (classStream != null) {
                try {
                    classStream.close();
                } catch (IOException e) {
                }
            }
        }
        typeref2state = desc2typerefValue2state.get(requiredDesc);
        if (typeref2state == null) {
            return null;
        }
        Boolean state = typeref2state.get(returnTypeReference);
        //      System.out.println("  => " + state);
        return state;
    }

    protected @NonNull String getMethodKey(/*@NonNull*/ String className, /*@NonNull*/ String name,
            /*@NonNull*/ String desc) {
        return className + " " + name + " " + desc;
    }
}