package rtt.annotation.editor.data.asm; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassReader; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.MethodNode; import rtt.annotation.editor.model.ClassElement; import rtt.annotation.editor.model.ClassElement.ClassType; import rtt.annotation.editor.model.ClassElementReference; import rtt.annotation.editor.model.ClassModel; import rtt.annotation.editor.model.ClassModelFactory; import rtt.annotation.editor.model.FieldElement; import rtt.annotation.editor.model.MethodElement; import rtt.annotation.editor.model.annotation.Annotatable; import rtt.annotation.editor.model.annotation.Annotation; import rtt.annotation.editor.model.annotation.InitAnnotation; import rtt.annotation.editor.model.annotation.ValueAnnotation; final class ImportModelFileWalker extends AbstractFileWalker { protected static final class RTTAnnotationVisitor extends AnnotationVisitor { private Annotation annotation; public RTTAnnotationVisitor(Annotation annotation) { super(Opcodes.ASM5); this.annotation = annotation; } @Override public void visit(String name, Object value) { annotation.setAttribute(name, value); } } public ImportModelFileWalker(ClassModel model) { super(model); } @SuppressWarnings("unchecked") @Override protected void processData(Path file) throws IOException { ClassReader reader = new ClassReader(Files.readAllBytes(file)); ClassNode node = new ClassNode(); reader.accept(node, ClassReader.SKIP_CODE); String completeName = node.name.replace("/", "."); String className = ASMClassModelManager.RESOLVER.computeClassName(completeName); String packageName = ASMClassModelManager.RESOLVER.computePackageName(completeName); ClassModelFactory factory = ClassModelFactory.getFactory(); ClassElement classElement = factory.createClassElement( model, className, packageName); if (checkAccess(node.access, Opcodes.ACC_ABSTRACT)) { classElement.setType(ClassType.ABSTRACT); } if (checkAccess(node.access, Opcodes.ACC_INTERFACE)) { classElement.setType(ClassType.INTERFACE); } if (checkAccess(node.access, Opcodes.ACC_ENUM)) { classElement.setType(ClassType.ENUMERATION); } if (node.superName != null && !node.superName.equals("java/lang/Object")) { String superName = node.superName.replace("/", "."); classElement.setSuperClass(ClassElementReference.create(superName)); } if (node.interfaces != null && !node.interfaces.isEmpty()) { List<ClassElementReference> interfaceReferences = new ArrayList<>(); String interfaceName = null; for (Object interfaceObject : node.interfaces) { interfaceName = ((String) interfaceObject).replace("/", "."); interfaceReferences.add(ClassElementReference.create(interfaceName)); } classElement.setInterfaces(interfaceReferences); } setAnnotation(node.visibleAnnotations, classElement); if (node.fields != null && !node.fields.isEmpty()) { addFields(node.fields, classElement); } if (node.methods != null && !node.methods.isEmpty()) { addMethods(node.methods, classElement); } model.addClassElement(classElement); } private boolean checkAccess(int access, int code) { return (access & code) == code; } private boolean isSynthetic(int access) { return checkAccess(access, Opcodes.ACC_SYNTHETIC); } private <A extends Annotation> void setAnnotation( List<AnnotationNode> annotations, Annotatable<A> annotatable) { if (annotations != null && !annotations.isEmpty()) { for (AnnotationNode annotationNode : annotations) { A annotation = ASMAnnotationConverter. getAnnotation(annotationNode.desc); if (annotation != null) { annotationNode.accept(new RTTAnnotationVisitor(annotation)); annotatable.setAnnotation(annotation); } } } } @SuppressWarnings("unchecked") private void addFields(List<FieldNode> fields, ClassElement classElement) { for (FieldNode fieldNode : fields) { if (!isSynthetic(fieldNode.access)) { FieldElement<ValueAnnotation> fieldElement = factory.createFieldElement(classElement, fieldNode.name); fieldElement.setType(Type.getType(fieldNode.desc).getClassName()); setAnnotation(fieldNode.visibleAnnotations, fieldElement); classElement.addValuableField(fieldElement); } } } @SuppressWarnings("unchecked") private void addMethods(List<MethodNode> methods, ClassElement classElement) { for (MethodNode methodNode : methods) { if (!isSynthetic(methodNode.access)) { Type methodType = Type.getType(methodNode.desc); if (isValuableMethod(methodType)) { MethodElement<ValueAnnotation> methodElement = factory.createMethodElement(classElement, methodNode.name); methodElement.setType(methodType.getReturnType().getClassName()); setAnnotation(methodNode.visibleAnnotations, methodElement); classElement.addValuableMethod(methodElement); } else if (isInitializableMethod(methodType)) { MethodElement<InitAnnotation> methodElement = factory.createMethodElement(classElement, methodNode.name); methodElement.setType(methodType.getReturnType().getClassName()); setAnnotation(methodNode.visibleAnnotations, methodElement); Type[] arguments = Type.getArgumentTypes(methodNode.desc); for (Type argument : arguments) { methodElement.getParameters().add(argument.getClassName()); } classElement.addInitializableMethod(methodElement); } } } } private boolean isValuableMethod(Type methodType) { return hasNonVoidReturnType(methodType) && methodType.getArgumentTypes().length == 0; } private boolean isInitializableMethod(Type methodType) { if (hasNonVoidReturnType(methodType)) { return false; } int argumentCount = methodType.getArgumentTypes().length; if (argumentCount < 1 || argumentCount > 2) { return false; } Type firstArgument = methodType.getArgumentTypes()[0]; if (!firstArgument.equals(Type.getType(InputStream.class))) { return false; } if (argumentCount == 2) { Type secondArgument = methodType.getArgumentTypes()[1]; if (!secondArgument.equals(Type.getType(String[].class))) { return false; } } return true; } private boolean hasNonVoidReturnType(Type methodType) { return !methodType.getReturnType().equals(Type.VOID_TYPE); } }