de.micromata.tpsb.doc.parser.japa.TestCaseVisitor.java Source code

Java tutorial

Introduction

Here is the source code for de.micromata.tpsb.doc.parser.japa.TestCaseVisitor.java

Source

//
// Copyright (C) 2010-2016 Micromata GmbH
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package de.micromata.tpsb.doc.parser.japa;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.comments.JavadocComment;
import com.github.javaparser.ast.expr.AnnotationExpr;
import com.github.javaparser.ast.expr.BooleanLiteralExpr;
import com.github.javaparser.ast.expr.ClassExpr;
import com.github.javaparser.ast.expr.DoubleLiteralExpr;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.expr.IntegerLiteralExpr;
import com.github.javaparser.ast.expr.LiteralExpr;
import com.github.javaparser.ast.expr.LongLiteralExpr;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.NameExpr;
import com.github.javaparser.ast.expr.ObjectCreationExpr;
import com.github.javaparser.ast.expr.StringLiteralExpr;
import com.github.javaparser.ast.expr.UnaryExpr;
import com.github.javaparser.ast.expr.VariableDeclarationExpr;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.ast.type.Type;

import de.micromata.tpsb.doc.ParserContext;
import de.micromata.tpsb.doc.TpsbEnvironment;
import de.micromata.tpsb.doc.parser.FileInfo;
import de.micromata.tpsb.doc.parser.JavaDocInfo;
import de.micromata.tpsb.doc.parser.JavaDocUtil;
import de.micromata.tpsb.doc.parser.MethodInfo;
import de.micromata.tpsb.doc.parser.ParameterInfo;
import de.micromata.tpsb.doc.parser.TestStepInfo;
import de.micromata.tpsb.doc.parser.japa.handler.INodeHandler;

/**
 * AST Visitor welche fuer Testcases interessante Informationen besucht
 * 
 * @author Stefan Sttzer (s.stuetzer@micromata.com)
 * @author Roger Kommer (roger.kommer.extern@micromata.de)
 */
public class TestCaseVisitor extends TestBuilderVisitor {

    private final static Logger log = Logger.getLogger(TestCaseVisitor.class);

    private int methodDepth = 0;

    @Override
    public void visit(ClassOrInterfaceDeclaration n, ParserContext ctx) {
        super.visitClassOrInterfaceDeclaration(n, ctx, false);
    }

    @Override
    public void visit(FieldDeclaration n, ParserContext ctx) {
        if (n.getVariables() == null) {
            super.visit(n, ctx);
            return;
        }
        for (VariableDeclarator var : n.getVariables()) {
            if (var.getInit() != null && var.getInit() instanceof ObjectCreationExpr) {
                ClassOrInterfaceType type = ((ObjectCreationExpr) var.getInit()).getType();
                ctx.addField(var.getId().getName(), type.toString());
                log.debug(IND_2 + "Felddeklaration: " + type + " " + var.getId());
            }
        }
        super.visit(n, ctx);
    }

    @Override
    public void visit(VariableDeclarationExpr n, ParserContext ctx) {
        if (n.getVars() == null) {
            super.visit(n, ctx);
            return;
        }

        for (VariableDeclarator var : n.getVars()) {
            if (var.getInit() == null) {
                super.visit(n, ctx);
                return;
            }

            // 1. untersttze Fall: Direkte Instanziierung (z.B. TestBuilder a =
            // new TestBuilder();)
            if (var.getInit() instanceof ObjectCreationExpr) {
                ClassOrInterfaceType type = ((ObjectCreationExpr) var.getInit()).getType();
                ctx.addLocalVar(var.getId().getName(), type.toString());
                log.debug(IND_2 + "Variablen Deklaration: " + type + " " + var.getId());
            }

            // 2. untersttze Fall: Instanziierung in Methodenargument (z.B.
            // TestBuilder a = Proxy.wrap(new TestBuilder());)
            else if (var.getInit() instanceof MethodCallExpr) {
                MethodCallExpr methodCall = (MethodCallExpr) var.getInit();
                if (methodCall.getArgs() != null && methodCall.getArgs().size() != 0) {
                    Expression nv = methodCall.getArgs().iterator().next();
                    if (nv instanceof ObjectCreationExpr) {
                        ClassOrInterfaceType type = ((ObjectCreationExpr) methodCall.getArgs().iterator().next())
                                .getType();
                        ctx.addLocalVar(var.getId().getName(), type.toString());
                        log.debug(IND_2 + "Variablen Deklaration: " + type + " " + var.getId());
                    } else if (nv instanceof ClassExpr) {
                        ClassExpr clazzExpr = (ClassExpr) nv;
                        Type type = clazzExpr.getType();
                        ctx.addLocalVar(var.getId().getName(), type.toString());
                        log.debug(IND_2 + "Variablen Deklaration: " + type + " " + var.getId());
                    } else {
                        log.warn(IND_2 + "Unknown VariableDeclarator init type: " + var.getInit());
                    }
                }
            } else if (var.getInit() instanceof LiteralExpr) {

            } else {
                log.warn(IND_2 + "Unknown VariableDeclarator type: " + var);
            }
        }
        super.visit(n, ctx);
    }

    /**
     * Checks if the methods is a test method ==> Either starts with test or has an annotation @Test
     * 
     * @param n the methoddeclaration which is to test if it is a test
     * @return true if it is a test method TODO consider check if the test method is void, etc and with no args?
     */
    protected boolean isTest(MethodDeclaration n) {
        if ((n.getModifiers() & Modifier.PUBLIC) != Modifier.PUBLIC) {
            return false;
        }
        if ((n.getModifiers() & Modifier.STATIC) == Modifier.STATIC) {
            return false;
        }
        if (n.getName().startsWith("test")) {
            return true;
        }
        if (n.getAnnotations() == null) {
            return false;
        }

        for (AnnotationExpr ae : n.getAnnotations()) {
            if (ae.getName().getName().equals("Test")) {
                return true;
            }
            if (ae.getName().getName().equals("TpsbMetaMethod") == true) {
                return true;
            }
        }

        return false;
    }

    @Override
    public void visit(MethodDeclaration n, ParserContext ctx) {
        if (isTest(n)) {
            super.visit(n, ctx);
        }
    }

    /**
     * Besuche einen Methodenaufruf auf einem Testbuilder
     */
    @Override
    public void visit(MethodCallExpr n, ParserContext ctx) {

        //    super.visit(n, ctx);
        if (n.getScope() != null) {
            n.getScope().accept(this, ctx);
        }

        if (reservedMethods.contains(n.getName()) == true) {
            @SuppressWarnings("unchecked")
            INodeHandler<Node> handler = (INodeHandler) handlers.get(n.getName());
            handler.handle(n, ctx);
            return;
        }
        // leider ist das immer begin der gesamten Expression.
        int line = n.getBeginLine();

        String inlineComment = null;//ParserUtil.lookupInlineCode(ctx.getSourceText(), line);
        log.debug(IND_3 + "Methodenaufruf: " + n.getName());
        if (n.getScope() instanceof NameExpr) {
            String type = ctx.getTypeOfVariable(n.getScope().toString());
            ctx.setCurrentScope(type);
        } else if (n.getScope() instanceof ObjectCreationExpr) {
            ObjectCreationExpr oce = (ObjectCreationExpr) n.getScope();
            ctx.setCurrentScope(oce.getType().toString());
        } else if (n.getScope() instanceof MethodCallExpr) {

            ; // nothing
        } else {
            log.warn(IND_4 + "unknown expression in method call: " + n.getScope());
        }
        String scopeAsString = ctx.getCurrentScope() == null ? "" : ctx.getCurrentScope().toString();
        log.debug(IND_4 + "auf Objekt: " + scopeAsString);
        if (StringUtils.isEmpty(scopeAsString)) {
            log.warn(IND_4 + "No scope for Method: " + n.getScope());
            return;
        }
        // In Testbuilderdaten den Methodenaufruf suchen
        FileInfo tbFileInfo = ctx.getTestBuilderInfoFromCurrentScope(ctx.getCurrentScope());
        if (tbFileInfo == null) {
            log.warn(IND_4 + "Testbuilder nicht gefunden: " + n.getName() + //
                    ", scope=" + ctx.getCurrentScope());
            return;
        }
        List<String> argTypes = parseArgs(n.getArgs());
        String scope = extractScope(ctx, n.getArgs(), n.getName());

        ctx.setCurrentScope(scope);
        log.debug(IND_3 + "New Scope: " + scope);
        MethodInfo calledTestbuilderMethodInfo = tbFileInfo.findMethodInfo(n.getName(), argTypes);
        if (calledTestbuilderMethodInfo == null) {
            log.error("Canot find method " + n.getName() + ", scope=" + ctx.getCurrentScope());
            // only for debugging
            calledTestbuilderMethodInfo = tbFileInfo.findMethodInfo(n.getName(), argTypes);
        }
        // TestStep dokumentieren
        TestStepInfo stepInfo = new TestStepInfo();
        stepInfo.setLineNo(n.getBeginLine());
        stepInfo.setTbClassName(tbFileInfo.getClassName());
        if (calledTestbuilderMethodInfo != null) {
            stepInfo.setTbMethodName(calledTestbuilderMethodInfo.getMethodName());
            stepInfo.setTbJavaDocInfo(calledTestbuilderMethodInfo.getJavaDocInfo());
        }
        if (StringUtils.isNotBlank(ctx.getCurrentInlineComment()) == true) {
            JavaDocInfo inlineJavaDocInfo = JavaDocUtil
                    .parseJavaDoc(new JavadocComment(ctx.getCurrentInlineComment()));
            stepInfo.setInlineJavaDocInfo(inlineJavaDocInfo);
            ctx.resetCurrentInlineComment();
        } else if (inlineComment != null) {
            stepInfo.setInlineJavaDocInfo(new JavaDocInfo(inlineComment));
        }
        if (calledTestbuilderMethodInfo != null) {
            if (n.getArgs() != null) {
                for (int i = 0; i < n.getArgs().size(); i++) {
                    Expression expr = n.getArgs().get(i);
                    ParameterInfo pInfo = calledTestbuilderMethodInfo.getParameterAtPos(i);
                    if (pInfo == null) {
                        continue;
                    }

                    ParameterInfo callArgument = new ParameterInfo(pInfo);
                    callArgument.setParamValue(expr.toString());
                    stepInfo.getParameters().add(callArgument);
                    log.debug(IND_4 + "Argument: " + expr.toString());
                }
            }
        }
        MethodInfo currentMethodInfo = ctx.findMethodInfo(ctx.getCurrentClassName(), ctx.getCurrentMethodName());
        if (currentMethodInfo != null) {
            currentMethodInfo.addTestStepInfo(stepInfo);
        }

    }

    private String resolveTypeFromGenericSupers(ParserContext ctx, MethodInfo methodInfo) {
        String retName = methodInfo.getReturnType();
        FileInfo thisTb = TpsbEnvironment.get().findTestBuilder(ctx.getCurrentScope());
        if (thisTb == null || thisTb.getSuperTemplateArgs() == null
                || thisTb.getSuperTemplateArgs().isEmpty() == true) {
            return null;
        }
        if (thisTb.getSuperClassFileInfo() == null) {
            return null;
        }
        FileInfo superF = thisTb.getSuperClassFileInfo();
        if (superF.getTypeArgNames() == null) {
            return null;
        }
        // TODO RK das hier ist nicht richtig, man muss positional die Generic-Namen aufloesen.
        for (int i = 0; i < superF.getTypeArgNames().size(); ++i) {
            String typeArgName = superF.getTypeArgNames().get(i);
            if (retName.equals(typeArgName) == true) {
                if (thisTb.getSuperTemplateArgs().size() > i) {
                    FileInfo retTypeFi = TpsbEnvironment.get()
                            .findTestBuilder(thisTb.getSuperTemplateArgs().get(i));
                    if (retTypeFi != null) {
                        return retTypeFi.getClassName();
                    }
                }
            }
        }
        return null;
    }

    private String extractScope(ParserContext ctx, List<Expression> list, String methodName) {
        FileInfo tbFileInfo = ctx.getTestBuilderInfoFromCurrentScope(ctx.getCurrentScope());
        MethodInfo methodInfo = tbFileInfo.findMethodInfo(methodName, parseArgs(list));
        if (methodInfo == null) {
            log.error("Cannot resolve return type for " + tbFileInfo.getClassName() + "." + methodName + "(" + list
                    + ")");
            return ctx.getCurrentScope();
        }
        String returnType = methodInfo.getReturnType();

        if (ctx.getParsedTestBuilders() != null) {
            if (ctx.getParsedTestBuilders().getFileInfoForClassName(returnType) != null) {
                return returnType;
            }

            String clazzName = extractArgs(ctx, list);
            if (ctx.getParsedTestBuilders().getFileInfoForClassName(clazzName) != null) {
                return clazzName;
            }
        }
        FileInfo fi = TpsbEnvironment.get().findTestBuilder(returnType);
        if (fi != null) {
            return fi.getClassName();
        }
        String resolvedTypeName = resolveTypeFromGenericSupers(ctx, methodInfo);
        if (resolvedTypeName != null) {
            return resolvedTypeName;
        }
        String clazzName = extractArgs(ctx, list);
        fi = TpsbEnvironment.get().findTestBuilder(clazzName);
        if (fi != null) {
            return fi.getClassName();
        }
        log.error("Cannot resolve testbuilder class: " + returnType + " " + methodInfo.getClassName() + "."
                + methodInfo.getMethodName());
        // TODO look for fqpath
        return ctx.getCurrentScope();
    }

    private String extractArgs(ParserContext ctx, List<Expression> list) {
        if (CollectionUtils.isEmpty(list)) {
            return StringUtils.EMPTY;
        }
        Expression expr = list.get(0);
        if (!(expr instanceof ClassExpr)) {
            if (list.size() > 1 && list.get(list.size() - 1) instanceof ClassExpr) {
                expr = list.get(list.size() - 1);
            } else {
                return StringUtils.EMPTY;
            }
        }
        ClassExpr clazzExpr = (ClassExpr) expr;
        Type type = clazzExpr.getType();
        String fullQualifiedClassName = ctx.getFullQualifiedNameFromImports(type.toString());
        if (StringUtils.isNotEmpty(fullQualifiedClassName)) {
            return type.toString();
        }
        return ctx.getCurrentClassName();

    }

    public String parseArg(Expression ex) {
        if (ex instanceof BooleanLiteralExpr) {
            return "Boolean";
        } else if (ex instanceof DoubleLiteralExpr) {
            return "Double";
        } else if (ex instanceof IntegerLiteralExpr) {
            return "Integer";
        } else if (ex instanceof LongLiteralExpr) {
            return "Long";
        } else if (ex instanceof StringLiteralExpr) {
            return "String";
        } else if (ex instanceof ObjectCreationExpr) {
            ObjectCreationExpr objEx = (ObjectCreationExpr) ex;
            return objEx.getType().getName();
        } else if (ex instanceof ClassExpr) {
            ClassExpr clsExpr = (ClassExpr) ex;
            Type type = clsExpr.getType();
            String shortClassName = type.toString();
            // TODO have to resolve
            return shortClassName;
        } else if (ex instanceof UnaryExpr) {
            UnaryExpr unary = (UnaryExpr) ex;
            return parseArg(unary.getExpr());
        } else {

            log.warn("Cannot understand type: " + ex);
            return "Unknown";
        }

    }

    public List<String> parseArgs(List<Expression> args) {
        if (args == null) {
            return new ArrayList<String>();
        }

        List<String> argTypes = new ArrayList<String>();
        for (Expression ex : args) {
            argTypes.add(parseArg(ex));
        }
        return argTypes;
    }
}