ca.weblite.mirah.ant.mirrors.ClassReader.java Source code

Java tutorial

Introduction

Here is the source code for ca.weblite.mirah.ant.mirrors.ClassReader.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package ca.weblite.mirah.ant.mirrors;

import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.ImportTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.JavacTask;
import com.sun.source.util.TreePathScanner;
import com.sun.tools.javac.api.JavacTool;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Set;
import java.util.Stack;
import javax.lang.model.element.Modifier;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InnerClassNode;
import org.objectweb.asm.tree.MethodNode;

/**
 *
 * @author shannah
 */
public class ClassReader {

    private ClassNode node;
    private File sourceFile;
    LinkedList<Scope> scopeStack = new LinkedList<>();
    LinkedList<ClassNode> classNodeStack = new LinkedList<>();
    PackageScope packageScope;

    public ClassReader(ClassNode node, File sourceFile, ResourceLoader loader) {
        Scope baseScope = new Scope(null);
        baseScope.setLoader(loader);
        scopeStack.add(baseScope);
        this.node = node;
        this.sourceFile = sourceFile;

    }

    /**
     * Wrapper for java file object so that it can be read by the TreeScanner
     * 
     */
    private static class MyFileObject extends SimpleJavaFileObject {

        private String src;

        public MyFileObject(File file) throws IOException {
            super(file.toURI(), JavaFileObject.Kind.SOURCE);
            src = readFile(file.getPath());
        }

        private String readFile(String file) throws IOException {
            BufferedReader reader = new BufferedReader(new FileReader(file));
            String line = null;
            StringBuilder stringBuilder = new StringBuilder();
            String ls = System.getProperty("line.separator");

            while ((line = reader.readLine()) != null) {
                stringBuilder.append(line);
                stringBuilder.append(ls);
            }

            return stringBuilder.toString();
        }

        @Override
        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
            return src;
        }

    }

    /**
     * Generates the the mirah mirrors for a specific java source file.  This 
     * file must be a .java file.  It cannot be a directory.
     * 
     * @param javaSourceFile
     * @throws IOException 
     */
    public void parse() throws IOException {

        File javaSourceFile = sourceFile;
        JavaCompiler compiler = JavacTool.create();
        MyFileObject[] fos = new MyFileObject[] { new MyFileObject(javaSourceFile) };

        JavacTask task = (JavacTask) compiler.getTask(null, null, null, null, null, Arrays.asList(fos));
        Iterable<? extends CompilationUnitTree> asts = task.parse();
        final Stack<Set<String>> typeParams = new Stack<Set<String>>();
        TreePathScanner scanner;
        scanner = new TreePathScanner() {

            private boolean isGenericType(String type) {
                Stack<Set> desk = new Stack<Set>();
                boolean found = false;
                while (!typeParams.empty()) {
                    Set params = typeParams.pop();
                    desk.push(params);
                    if (params.contains(type)) {
                        found = true;

                        break;
                    }

                }
                while (!desk.empty()) {
                    typeParams.push(desk.pop());
                }
                return found;
            }

            String formatType(String type) {
                int pos = type.indexOf("<");
                if (pos >= 0) {
                    type = type.substring(0, pos);
                }
                if (isGenericType(type)) {
                    return "Object";
                }
                return type;
            }

            @Override
            public Object visitCompilationUnit(CompilationUnitTree cut, Object p) {
                PackageScope scope = new PackageScope(scopeStack.peek(), cut.getPackageName().toString());
                scope.addImport(scope.getPackageName() + ".*");
                scope.addImport("java.lang.*");
                scope.addImport("*");
                packageScope = scope;
                scopeStack.push(scope);
                return super.visitCompilationUnit(cut, p);

            }

            @Override
            public Object visitImport(ImportTree it, Object p) {

                String path = it.getQualifiedIdentifier().toString();
                packageScope.addImport(path);

                return super.visitImport(it, p);
            }

            private void decorateClassNode(ClassTree ct, ClassNode classNode) {
                int flags = getFlags(ct.getModifiers().getFlags());
                switch (ct.getKind()) {
                case INTERFACE:
                    flags |= Opcodes.ACC_INTERFACE;
                    break;

                case ENUM:
                    flags |= Opcodes.ACC_ENUM;
                    break;

                }
                classNode.access = flags;
                classNode.sourceFile = sourceFile.getPath();
                String extendsClause = "java.lang.Object";
                if (ct.getExtendsClause() != null) {
                    extendsClause = ct.getExtendsClause().toString();
                }

                String superPath = scopeStack.peek().resolveName(extendsClause, false);

                ClassIndex.Node superNode = scopeStack.peek().getLoader().find(superPath);

                if (superNode == null) {
                    throw new RuntimeException("Failed to find super class " + superPath);
                }

                classNode.superName = superNode.internalName;

                classNode.interfaces = new ArrayList<String>();
                String impl = ct.getImplementsClause().toString();
                if (!"".equals(impl)) {
                    String[] interfaces = impl.split(",");
                    for (String iface : interfaces) {
                        iface = iface.trim();
                        String ipath = scopeStack.peek().resolveName(iface, false);
                        ClassIndex.Node iNode = scopeStack.peek().getLoader().find(ipath);
                        if (iNode == null) {
                            throw new RuntimeException("Failed to load " + "interface " + ipath);
                        }

                        classNode.interfaces.add(iNode.internalName);
                    }
                }
            }

            @Override
            public Object visitClass(ClassTree ct, Object p) {
                ClassScope clsScope = null;
                ClassNode currClassNode = null;
                if (scopeStack.peek() == packageScope) {
                    String path = packageScope.getPackageName() + "." + ct.getSimpleName();
                    ClassIndex.Node n = packageScope.getLoader().find(path);
                    if (n == null) {
                        throw new RuntimeException("Failed to find class " + path);
                    }
                    node.name = n.internalName;
                    currClassNode = node;
                    decorateClassNode(ct, node);

                    clsScope = new ClassScope(scopeStack.peek(), n);

                } else {
                    // This must be an internal class
                    ClassNode parentClass = classNodeStack.peek();
                    if (parentClass.innerClasses == null) {
                        parentClass.innerClasses = new ArrayList<>();
                    }
                    String path = scopeStack.peek().resolveName(ct.getSimpleName().toString(), false);

                    ClassIndex.Node n = packageScope.getLoader().find(path);
                    if (n == null) {
                        throw new RuntimeException("Failed to find class " + path);
                    }
                    InnerClassNode innerNode = new InnerClassNode(n.internalName, parentClass.name, n.simpleName,
                            getFlags(ct.getModifiers().getFlags()));
                    parentClass.innerClasses.add(innerNode);

                    ClassNode cls = new ClassNode();
                    cls.name = n.internalName;
                    cls.outerClass = parentClass.name;

                    decorateClassNode(ct, cls);
                    clsScope = new ClassScope(scopeStack.peek(), n);
                    currClassNode = cls;
                }

                scopeStack.push(clsScope);
                classNodeStack.push(currClassNode);
                Object out = super.visitClass(ct, p);
                classNodeStack.pop();
                scopeStack.pop();

                return out;

            }

            /**
             * Converts modifier flags from Javac Tree into int flags usable in 
             * TypeMirror
             * @param mods
             * @return 
             */
            int getFlags(Set<Modifier> mods) {
                int flags = 0;
                for (Modifier m : mods) {
                    switch (m) {
                    case ABSTRACT:
                        flags |= Opcodes.ACC_ABSTRACT;
                        break;
                    case FINAL:
                        flags |= Opcodes.ACC_FINAL;
                        break;
                    case PRIVATE:
                        flags |= Opcodes.ACC_PRIVATE;
                        break;
                    case PROTECTED:
                        flags |= Opcodes.ACC_PROTECTED;
                        break;
                    case PUBLIC:

                        flags |= Opcodes.ACC_PUBLIC;
                        break;
                    case STATIC:
                        flags |= Opcodes.ACC_STATIC;
                        break;
                    }
                }

                return flags;
            }

            @Override
            public Object visitVariable(final VariableTree vt, Object p) {
                String typeName = vt.getType().toString();
                String typePath = scopeStack.peek().resolveName(vt.getType().toString(), false);
                String typeDescriptor = scopeStack.peek().resolveName(vt.getType().toString(), true);
                String name = vt.getName().toString();
                int flags = getFlags(vt.getModifiers().getFlags());

                FieldNode fieldNode = new FieldNode(flags, name, typeDescriptor, null, null);

                ClassNode cls = classNodeStack.peek();
                if (cls.fields == null) {
                    cls.fields = new ArrayList<FieldNode>();

                }
                cls.fields.add(fieldNode);

                return super.visitVariable(vt, p);
            }

            String generateMethodDescriptor(MethodTree mt) {
                StringBuilder sb = new StringBuilder();
                sb.append("(");
                for (VariableTree v : mt.getParameters()) {
                    String type = scopeStack.peek().resolveName(v.getType().toString(), true);
                    sb.append(type);

                }
                sb.append(")");
                String returnType = scopeStack.peek().resolveName(mt.getReturnType().toString(), true);
                sb.append(returnType);

                return sb.toString();
            }

            @Override
            public Object visitMethod(final MethodTree mt, Object p) {

                MethodNode m = new MethodNode();

                int flags = getFlags(mt.getModifiers().getFlags());
                m.access = flags;
                m.desc = generateMethodDescriptor(mt);
                m.name = mt.getName().toString();
                ClassNode cls = classNodeStack.peek();
                if (cls.methods == null) {
                    cls.methods = new ArrayList<MethodNode>();
                }
                cls.methods.add(m);

                return mt;
            }
        };
        scanner.scan(asts, null);

    }

}