All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jaxx.compiler.reflect.resolvers.ClassDescriptorResolverFromJavaFile Maven / Gradle / Ivy

There is a newer version: 3.0-alpha-6
Show newest version
/*
 * #%L
 * JAXX :: Compiler
 * %%
 * Copyright (C) 2008 - 2014 Code Lutin, Tony Chemit
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as 
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program 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 Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public 
 * License along with this program.  If not, see
 * .
 * #L%
 */
package jaxx.compiler.reflect.resolvers;

import jaxx.compiler.CompilerException;
import jaxx.compiler.JAXXCompiler;
import jaxx.compiler.JAXXFactory;
import jaxx.compiler.java.parser.JavaParser;
import jaxx.compiler.java.parser.JavaParserTreeConstants;
import jaxx.compiler.java.parser.ParseException;
import jaxx.compiler.java.parser.SimpleNode;
import jaxx.compiler.reflect.ClassDescriptor;
import jaxx.compiler.reflect.ClassDescriptorHelper;
import jaxx.compiler.reflect.ClassDescriptorResolver;
import jaxx.compiler.reflect.FieldDescriptor;
import jaxx.compiler.reflect.MethodDescriptor;
import jaxx.compiler.tags.TagManager;
import jaxx.runtime.JAXXUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * To obtain a class descriptor from a java source file.
 *
 * @author Tony Chemit - [email protected]
 * @since 2.0.2
 */
public class ClassDescriptorResolverFromJavaFile extends ClassDescriptorResolver {

    /** Logger */
    private static final Log log =
            LogFactory.getLog(ClassDescriptorResolverFromJavaFile.class);

    private final boolean parseMethodBody;

    public ClassDescriptorResolverFromJavaFile() {
        this(false);
    }
    public ClassDescriptorResolverFromJavaFile(boolean parseMethodBody) {
        super(ClassDescriptorHelper.ResolverType.JAVA_FILE);
        this.parseMethodBody = parseMethodBody;
    }

    @Override
    public ClassDescriptor resolvDescriptor(String className,
                                            URL source) throws ClassNotFoundException {

        ClassLoader classLoader = getClassLoader();

        try {
            Reader reader = new InputStreamReader(source.openStream(), "utf-8");
            try {

                String displayName = source.toString();
                if (log.isDebugEnabled()) {
                    log.debug("for source " + displayName);
                }
                JavaFileParser parser = new JavaFileParser(classLoader, parseMethodBody);
                if (log.isDebugEnabled()) {
                    log.debug("starting parsing : " + displayName);
                }
                try {
                    parser.doParse(displayName, reader);
                } catch (Exception e) {
//                    log.error(e.getMessage());
                    throw new RuntimeException(e);
                }
                ClassDescriptor result =
                        new JavaFileClassDescriptor(parser, classLoader);
                return result;

            } finally {

                reader.close();
            }
        } catch (IOException e) {
            throw new ClassNotFoundException(
                    "Could not resolv descriptor from source " + source,
                    e);
        }
    }

    private class JavaFileClassDescriptor extends ClassDescriptor {

        public JavaFileClassDescriptor(JavaFileParser parser,
                                       ClassLoader classLoader) {
            super(
                    ClassDescriptorResolverFromJavaFile.this.getResolverType(),
                    parser.className,
                    parser.packageName,
                    parser.superclass,
                    parser.interfaces.toArray(new String[parser.interfaces.size()]),
                    parser.isInterface,
                    false,
                    null,
                    parser.jaxxObjectDescriptorValue == null ? null : JAXXUtil.decodeCompressedJAXXObjectDescriptor(parser.jaxxObjectDescriptorValue),
                    classLoader,
                    parser.constructors.toArray(new MethodDescriptor[parser.constructors.size()]),
                    parser.methods.toArray(new MethodDescriptor[parser.methods.size()]),
                    parser.fields.toArray(new FieldDescriptor[parser.fields.size()]),
                    parser.declaredFields.toArray(new FieldDescriptor[parser.declaredFields.size()])
            );

        }

//        @Override
//        public FieldDescriptor getDeclaredFieldDescriptor(String name) throws NoSuchFieldException {
//            for (FieldDescriptor descriptor : declaredFieldDescriptors) {
//                if (name.equals(descriptor.getName())) {
//                    if (log.isDebugEnabled()) {
//                        log.debug("Using a declared field descriptor [" + name + "] for " + getName());
//                    }
//                    return descriptor;
//                }
//            }
//            throw new NoSuchFieldException(name);
//        }

        @Override
        public MethodDescriptor getDeclaredMethodDescriptor(String name, ClassDescriptor... parameterTypes) throws NoSuchMethodException {
            throw new NoSuchMethodException(name);
        }

    }

    public static class JavaFileParser {

        /** Logger */
        static private final Log log = LogFactory.getLog(JavaFileParser.class);

        /**
         * the compiler used (this is a dummy compiler with no link with any file).
         * 

* FIXME-TC20100504 We should remove this link : should not need of a * compiler to parse a java files. */ private JAXXCompiler compiler; /** fully qualified name of the class */ private String className; /** package of the class */ private String packageName; /** fully qualified name of the super class or {@code null} if no super class. */ private String superclass; /** * flag to known if we deal with an enum (the state is setted in the * {@link #doParse(String, Reader)} method). */ private boolean isEnum; /** * flag to known if we deal with an interface(the state is setted in the * {@link #doParse(String, Reader)} method). */ private boolean isInterface; /** set of fully qualified names of interfaces of the class. */ private Set interfaces; /** public methods of the class */ private List methods; private List constructors; /** none public methods of the class */ // private List declaredMethods; /** public fields of the class */ private List fields; /** none public fields of the class */ private List declaredFields; /** * If sets, compressed value of the $jaxxObjectDescriptor field, this means * the class if a JAXXObject implementation. */ private String jaxxObjectDescriptorValue; public static final String[] EMPTY_STRING_ARRAY = new String[0]; private final boolean parseMethodBody; /** * To test if a compilation unit was already parsed. If so, then stop * parsing the java file : one parsing can only give one class * descriptor. * * @since 2.4.2 */ private boolean firstTypeScanned; protected JavaFileParser(ClassLoader classLoader,boolean parseMethodBody) { this.parseMethodBody = parseMethodBody; //FIXME-TC-20100504 : shoudl remove this to make the parser free of jaxx :) // We could imagine just to offers to the parser a list of namespaces // (for class resolving)... compiler = JAXXFactory.newDummyCompiler(classLoader); methods = new ArrayList(); constructors = new ArrayList(); // declaredMethods = new ArrayList(); interfaces = new HashSet(); fields = new ArrayList(); declaredFields = new ArrayList(); superclass = Object.class.getName(); } public void doParse(String className, Reader src) throws ClassNotFoundException { // reset this internal state firstTypeScanned = false; try { JavaParser p = new JavaParser(src, parseMethodBody); p.CompilationUnit(); SimpleNode node = p.popNode(); if (node != null) { scanCompilationUnit(node); if (isInterface) { // remove super class superclass = null; // load all super classes if (!interfaces.isEmpty()) { for (String anInterface : interfaces) { ClassDescriptor superclassDescriptor = ClassDescriptorHelper.getClassDescriptor(anInterface, compiler.getClassLoader()); methods.addAll(Arrays.asList(superclassDescriptor.getMethodDescriptors())); fields.addAll(Arrays.asList(superclassDescriptor.getFieldDescriptors())); } } } if (isEnum) { // super class is always Enum superclass = Enum.class.getName(); } if (superclass != null) { //FIXME-TC20100504 This is not good, should add nothing here // and modify the algorithm of ClassDescriptor to go and seek // in super classes on interfaces if required. ClassDescriptor superclassDescriptor = ClassDescriptorHelper.getClassDescriptor(superclass, compiler.getClassLoader()); methods.addAll(Arrays.asList(superclassDescriptor.getMethodDescriptors())); fields.addAll(Arrays.asList(superclassDescriptor.getFieldDescriptors())); declaredFields.addAll(Arrays.asList(superclassDescriptor.getDeclaredFieldDescriptors())); } return; } throw new CompilerException("Internal error: null node parsing Java file from " + src); } catch (ParseException e) { throw new CompilerException("Error parsing Java source code " + className + ": " + e.getMessage()); } } private void scanCompilationUnit(SimpleNode node) { for (int i = 0; i < node.jjtGetNumChildren(); i++) { SimpleNode child = node.getChild(i); scanCompilationUnitChild(child); } } private void scanCompilationUnitChild(SimpleNode child) { if (firstTypeScanned) { // already scan the first class of the java file // for the moment, can not do anything else... if (log.isWarnEnabled()) { log.warn("There is more than one type in current file, skip next type..."); } return; } int nodeType = child.getId(); switch (nodeType) { case JavaParserTreeConstants.JJTPACKAGEDECLARATION: packageName = child.getChild(1).getText().trim(); compiler.addImport(packageName + ".*"); // add implicit java.lnag namespace available compiler.addImport("java.lang.*"); break; case JavaParserTreeConstants.JJTIMPORTDECLARATION: String text = child.getText().trim(); int importIndex = text.indexOf("import"); if (importIndex > -1) { text = text.substring(importIndex + "import".length()).trim(); } if (text.endsWith(";")) { text = text.substring(0, text.length() - 1); } if (log.isDebugEnabled()) { log.debug("import " + text); } compiler.addImport(text); break; case JavaParserTreeConstants.JJTTYPEDECLARATION: scanCompilationUnit(child); break; case JavaParserTreeConstants.JJTCLASSORINTERFACEDECLARATION: isInterface = child.firstToken.image.equals("interface"); scanClass(child); break; case JavaParserTreeConstants.JJTENUMDECLARATION: isEnum = child.firstToken.image.equals("enum"); scanClass(child); break; } } // scans the main ClassOrInterfaceDeclaration private void scanClass(SimpleNode node) { firstTypeScanned = true; // boolean isInterface = node.firstToken.image.equals("interface"); className = node.firstToken.next.image; if (packageName != null) { className = packageName + "." + className; } for (int i = 0; i < node.jjtGetNumChildren(); i++) { SimpleNode child = node.getChild(i); int nodeType = child.getId(); if (nodeType == JavaParserTreeConstants.JJTIMPLEMENTSLIST) { if (log.isDebugEnabled()) { log.debug("[" + className + "] Found a implements list " + child + " :: " + child.jjtGetNumChildren()); } // obtain interfaces for (int j = 0; j < child.jjtGetNumChildren(); j++) { String rawName = child.getChild(j).getText().trim(); addInterface(rawName); } continue; } if (nodeType == JavaParserTreeConstants.JJTEXTENDSLIST) { if (isInterface) { // obtain interfaces for (int j = 0; j < child.jjtGetNumChildren(); j++) { String rawName = child.getChild(j).getText().trim(); addInterface(rawName); } continue; } // this is an extends assert child.jjtGetNumChildren() == 1 : "expected ExtendsList to have exactly one child for a non-interface class"; String rawName = child.getChild(0).getText().trim(); superclass = TagManager.resolveClassName(rawName, compiler); if (superclass == null) { throw new CompilerException("Could not find class: " + rawName); } if (log.isDebugEnabled()) { log.debug("Set superClass = " + superclass); } } else if (nodeType == JavaParserTreeConstants.JJTCLASSORINTERFACEBODY) { scanClassNode(child); } } } // scans class body nodes private void scanClassNode(SimpleNode node) { int nodeType = node.getId(); switch (nodeType) { case JavaParserTreeConstants.JJTCLASSORINTERFACEDECLARATION: // TODO: handle inner classes break; case JavaParserTreeConstants.JJTCONSTRUCTORDECLARATION: scanConstructorDeclaration(node); break; case JavaParserTreeConstants.JJTMETHODDECLARATION: scanMethodDeclaration(node); break; case JavaParserTreeConstants.JJTFIELDDECLARATION: scanFieldDeclaration(node); break; default: // go throught it for (int i = 0; i < node.jjtGetNumChildren(); i++) { SimpleNode child = node.getChild(i); scanClassNode(child); } } } protected void scanFieldDeclaration(SimpleNode node) { String text = node.getText(); String declaration = text; String value = null; int equals = text.indexOf("="); if (equals != -1) { value = declaration.substring(equals + 1).trim(); declaration = declaration.substring(0, equals); } declaration = declaration.trim(); // get modifiers of the field int modifiers; if (isInterface) { modifiers = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL; } else { modifiers = getModifiers(node); } if (log.isDebugEnabled()) { log.debug("field [" + declaration + "] modifiers == " + Modifier.toString(modifiers)); } String[] declarationTokens = declaration.split("\\s"); String name = declarationTokens[declarationTokens.length - 1]; if (name.endsWith(";")) { name = name.substring(0, name.length() - 1).trim(); } String cName = declarationTokens[declarationTokens.length - 2]; String type = TagManager.resolveClassName(cName, compiler); FieldDescriptor descriptor = new FieldDescriptor( name, modifiers, type, compiler.getClassLoader() ); if ("$jaxxObjectDescriptor".equals(name) && value != null) { // we are in a jaxx object, save the value of the field // value have this form : = "XXX";, we just want the XXX part int firstIndex = value.indexOf("\""); int lastIndex = value.lastIndexOf("\""); jaxxObjectDescriptorValue = value.substring(firstIndex + 1, lastIndex); if (log.isDebugEnabled()) { log.debug("detected a $jaxxObjectDescriptor = " + jaxxObjectDescriptorValue); } } if (Modifier.isPublic(modifiers)) { fields.add(descriptor); } else { declaredFields.add(descriptor); } } protected void scanConstructorDeclaration(SimpleNode node) { String name = null; List parameterTypes = new ArrayList(); //List parameterNames = new ArrayList(); for (int i = 0; i < node.jjtGetNumChildren(); i++) { SimpleNode child = node.getChild(i); int type = child.getId(); if (type == JavaParserTreeConstants.JJTMETHODDECLARATOR) { name = child.firstToken.image.trim(); SimpleNode formalParameters = child.getChild(0); assert formalParameters.getId() == JavaParserTreeConstants.JJTFORMALPARAMETERS; for (int j = 0; j < formalParameters.jjtGetNumChildren(); j++) { SimpleNode parameter = formalParameters.getChild(j); String rawParameterType = parameter.getChild(1).getText().trim().replaceAll("\\.\\.\\.", "[]"); String parameterType = TagManager.resolveClassName(rawParameterType, compiler); if (parameterType == null && JAXXCompiler.STRICT_CHECKS) { throw new CompilerException("could not find class '" + rawParameterType + "'"); } parameterTypes.add(parameterType); //parameterNames.add(parameter.getChild(2).getText().trim()); } } } // determine the actual modifiers int modifiers = getModifiers(node); if (isInterface) { modifiers = Modifier.PUBLIC; } if (log.isDebugEnabled()) { log.debug("method [" + name + "] modifiers == " + Modifier.toString(modifiers)); } MethodDescriptor methodDescriptor = new MethodDescriptor( name, modifiers, null, parameterTypes.toArray(new String[parameterTypes.size()]), compiler.getClassLoader() ); constructors.add(methodDescriptor); } protected void scanMethodDeclaration(SimpleNode node) { String returnType = null; String name = null; List parameterTypes = new ArrayList(); //List parameterNames = new ArrayList(); for (int i = 0; i < node.jjtGetNumChildren(); i++) { SimpleNode child = node.getChild(i); int type = child.getId(); if (type == JavaParserTreeConstants.JJTRESULTTYPE) { // returnType = TagManager.resolveClassName(child.getText().trim(), compiler); //tchemit 2011-04-21 Remove anything before return type (like javadoc and other comments) returnType = TagManager.resolveClassName(child.firstToken.image.trim(), compiler); } else if (type == JavaParserTreeConstants.JJTMETHODDECLARATOR) { name = child.firstToken.image.trim(); SimpleNode formalParameters = child.getChild(0); assert formalParameters.getId() == JavaParserTreeConstants.JJTFORMALPARAMETERS; for (int j = 0; j < formalParameters.jjtGetNumChildren(); j++) { SimpleNode parameter = formalParameters.getChild(j); String rawParameterType = parameter.getChild(1).getText().trim().replaceAll("\\.\\.\\.", "[]"); String parameterType = TagManager.resolveClassName(rawParameterType, compiler); if (parameterType == null && JAXXCompiler.STRICT_CHECKS) { throw new CompilerException("could not find class '" + rawParameterType + "'"); } parameterTypes.add(parameterType); //parameterNames.add(parameter.getChild(2).getText().trim()); } } } // determine the actual modifiers int modifiers = getModifiers(node); if (isInterface) { modifiers = Modifier.PUBLIC; } if (log.isDebugEnabled()) { log.debug("method [" + name + "] modifiers == " + Modifier.toString(modifiers)); } MethodDescriptor methodDescriptor = new MethodDescriptor( name, modifiers, returnType, parameterTypes.toArray(new String[parameterTypes.size()]), compiler.getClassLoader() ); if (Modifier.isPublic(modifiers)) { methods.add(methodDescriptor); } else { //TODO At the moment, do not deal with it... // declaredMethods.add(methodDescriptor); } } protected void addInterface(String rawName) { if (rawName.contains("<")) { // generic type rawName = rawName.substring(0, rawName.indexOf("<")); } if (log.isDebugEnabled()) { log.debug("[" + className + "] try to obtain type of interface " + rawName); } String myInterface = resolveFullyQualifiedName(rawName); if (myInterface == null) { throw new CompilerException("Could not find interface: " + myInterface); } if (!interfaces.contains(myInterface)) { if (log.isDebugEnabled()) { log.debug("[" + className + "] add interface " + myInterface); } interfaces.add(myInterface); } } protected String resolveFullyQualifiedName(String rawName) { String result; String realRawName = null; String realParentRawName = null; if (rawName.contains(".")) { // this is a inner class int index = rawName.lastIndexOf("."); realParentRawName = rawName.substring(0, index); realRawName = rawName.substring(index + 1); if (log.isDebugEnabled()) { log.debug("inner class detected ? " + realParentRawName + "//" + realRawName); } } if (log.isDebugEnabled()) { log.debug("try fqn = " + rawName); } result = TagManager.resolveClassName(rawName, compiler); if (result != null) { // interface is detected fine (fqn was used or in good package ?) return result; } String suffix = "." + rawName; if (realParentRawName != null) { suffix = "." + realParentRawName; } for (String aClass : compiler.getImportedClasses()) { if (aClass.endsWith(suffix)) { // found the class as an already knwon class if (realRawName != null) { aClass += "." + realRawName; } return aClass; } } // try on packages Set importedPackages = compiler.getImportedPackages(); for (String aClass : importedPackages) { String fqn = aClass + rawName; if (log.isDebugEnabled()) { log.debug("try fqn = " + fqn); } result = TagManager.resolveClassName(fqn, compiler); if (result != null) { return result; } } // nothing was found return null; } protected int getModifiers(SimpleNode node) { SimpleNode parentNode = node.getParent(); for (int i = 0; i < parentNode.jjtGetNumChildren(); i++) { SimpleNode child = parentNode.getChild(i); if (child.getId() == JavaParserTreeConstants.JJTMODIFIERS) { String modifiersStr = child.getText().trim(); int modifiers = scanModifiers(modifiersStr); return modifiers; } } return 0; } protected int scanModifiers(String modifiersStr) { int modifiers = 0; if (modifiersStr.contains("public")) { modifiers |= Modifier.PUBLIC; } if (modifiersStr.contains("protected")) { modifiers |= Modifier.PROTECTED; } if (modifiersStr.contains("private")) { modifiers |= Modifier.PRIVATE; } if (modifiersStr.contains("static")) { modifiers |= Modifier.STATIC; } if (modifiersStr.contains("final")) { modifiers |= Modifier.FINAL; } if (modifiersStr.contains("volatile")) { modifiers |= Modifier.VOLATILE; } if (modifiersStr.contains("transient")) { modifiers |= Modifier.TRANSIENT; } if (modifiersStr.contains("synchronized")) { modifiers |= Modifier.SYNCHRONIZED; } if (modifiersStr.contains("abstract")) { modifiers |= Modifier.ABSTRACT; } return modifiers; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy