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

io.vertx.codetrans.ModelBuilder Maven / Gradle / Ivy

There is a newer version: 4.1.0.Beta1
Show newest version
package io.vertx.codetrans;

import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.BinaryTree;
import com.sun.source.tree.BlockTree;
import com.sun.source.tree.ConditionalExpressionTree;
import com.sun.source.tree.EnhancedForLoopTree;
import com.sun.source.tree.ExpressionStatementTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.ForLoopTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.IfTree;
import com.sun.source.tree.LambdaExpressionTree;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.ParenthesizedTree;
import com.sun.source.tree.StatementTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.UnaryTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.tree.JCTree;
import io.vertx.codegen.ClassKind;
import io.vertx.codegen.TypeInfo;

import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import java.util.List;
import java.util.stream.Collectors;

/**
 * The model builder is a tree scanner for building a code model from the Java program AST.
 *
 * @author Julien Viet
 */
public class ModelBuilder extends TreePathScanner {

  private final DeclaredType SystemType;
  private final TypeInfo.Factory factory;
  private final Lang lang;

  public ModelBuilder(DeclaredType systemType, TypeInfo.Factory factory, Lang lang) {
    SystemType = systemType;
    this.factory = factory;
    this.lang = lang;
  }

  public CodeModel build(TreePath path) {
    return scan(path, new VisitContext());
  }

  public StatementModel scan(StatementTree tree, VisitContext visitContext) {
    return (StatementModel) scan((Tree) tree, visitContext);
  }

  public ExpressionModel scan(ExpressionTree tree, VisitContext visitContext) {
    return (ExpressionModel) scan((Tree) tree, visitContext);
  }

  @Override
  public CodeModel visitForLoop(ForLoopTree node, VisitContext p) {
    if (node.getInitializer().size() != 1) {
      throw new UnsupportedOperationException();
    }
    if (node.getUpdate().size() != 1) {
      throw new UnsupportedOperationException();
    }
    StatementModel initializer = scan(node.getInitializer().get(0), p);
    ExpressionModel update = scan(node.getUpdate().get(0).getExpression(), p);
    StatementModel body = scan(node.getStatement(), p);
    ExpressionModel condition = scan(node.getCondition(), p);
    return lang.forLoop(initializer, condition, update, body);
  }

  @Override
  public CodeModel visitEnhancedForLoop(EnhancedForLoopTree node, VisitContext p) {
    ExpressionModel expression = scan(node.getExpression(), p);
    StatementModel body = scan(node.getStatement(), p);
    return lang.enhancedForLoop(node.getVariable().getName().toString(), expression, body);
  }

  @Override
  public CodeModel visitAssignment(AssignmentTree node, VisitContext context) {
    ExpressionModel variable = scan(node.getVariable(), context);
    ExpressionModel expression = scan(node.getExpression(), context);
    return ExpressionModel.forAssign(variable, expression);
  }

  @Override
  public StatementModel visitVariable(VariableTree node, VisitContext p) {
    JCTree.JCVariableDecl decl = (JCTree.JCVariableDecl) node;
    ExpressionModel initializer;
    if (node.getInitializer() != null) {
      initializer = scan(node.getInitializer(), p);
    } else {
      initializer = null;
    }
    TypeInfo type = factory.create(decl.type);
    return lang.variable(
        type,
        decl.name.toString(),
        initializer
    );
  }

  @Override
  public StatementModel visitIf(IfTree node, VisitContext visitContext) {
    ExpressionModel condition = scan(node.getCondition(), visitContext);
    StatementModel thenBody = scan(node.getThenStatement(), visitContext);
    StatementModel elseBody = node.getElseStatement() != null ? scan(node.getElseStatement(), visitContext) : null;
    return StatementModel.ifThenElse(condition, thenBody, elseBody);
  }

  @Override
  public CodeModel visitConditionalExpression(ConditionalExpressionTree node, VisitContext visitContext) {
    ExpressionModel condition = scan(node.getCondition(), visitContext);
    ExpressionModel trueExpression = scan(node.getTrueExpression(), visitContext);
    ExpressionModel falseExpression = scan(node.getFalseExpression(), visitContext);
    return ExpressionModel.forConditionalExpression(condition, trueExpression, falseExpression);
  }

  @Override
  public ExpressionModel visitUnary(UnaryTree node, VisitContext p) {
    ExpressionModel expression = scan(node.getExpression(), p);
    switch (node.getKind()) {
      case POSTFIX_INCREMENT:
        // Note we don't handle the case (3++) that is not legal in JavaScript
        return expression.onPostFixIncrement();
      case POSTFIX_DECREMENT:
        // Note we don't handle the case (3--) that is not legal in JavaScript
        return expression.onPostFixDecrement();
      case PREFIX_INCREMENT:
        // Note we don't handle the case (++3) that is not legal in JavaScript
        return expression.onPrefixIncrement();
      case PREFIX_DECREMENT:
        // Note we don't handle the case (--3) that is not legal in JavaScript
        return expression.onPrefixDecrement();
      case LOGICAL_COMPLEMENT:
        return expression.onLogicalComplement();
      case UNARY_MINUS:
        return expression.unaryMinus();
      case UNARY_PLUS:
        return expression.unaryPlus();
      default:
        throw new UnsupportedOperationException("Unary operator " + node.getKind().name() + " not yet implemented");
    }
  }

  @Override
  public CodeModel visitExpressionStatement(ExpressionStatementTree node, VisitContext context) {
    ExpressionModel expression = scan(node.getExpression(), context);
    return StatementModel.render(expression::render);
  }

  @Override
  public ExpressionModel visitBinary(BinaryTree node, VisitContext p) {
    ExpressionModel left = scan(node.getLeftOperand(), p);
    ExpressionModel right = scan(node.getRightOperand(), p);
    String op;
    switch (node.getKind()) {
      case CONDITIONAL_AND:
        op = "&&";
        break;
      case CONDITIONAL_OR:
        op = "||";
        break;
      case EQUAL_TO:
        op = "==";
        break;
      case NOT_EQUAL_TO:
        op = "!=";
        break;
      case PLUS:
        op = "+";
        break;
      case LESS_THAN:
        op = "<";
        break;
      case LESS_THAN_EQUAL:
        op = "<=";
        break;
      case GREATER_THAN:
        op = ">";
        break;
      case GREATER_THAN_EQUAL:
        op = ">=";
        break;
      case MULTIPLY:
        op = "*";
        break;
      case DIVIDE:
        op = "/";
        break;
      case AND:
        op = "&";
        break;
      case OR:
        op = "|";
        break;
      case XOR:
        op = "^";
        break;
      case MINUS:
        op = "-";
        break;
      case REMAINDER:
        op = "%";
        break;
      default:
        throw new UnsupportedOperationException("Binary operator " + node.getKind().name() + " not yet implemented");
    }
    return lang.combine(left, op, right);
  }

  @Override
  public ExpressionModel visitLiteral(LiteralTree node, VisitContext p) {
    switch (node.getKind()) {
      case NULL_LITERAL:
        return lang.nullLiteral();
      case STRING_LITERAL:
        return lang.stringLiteral(node.getValue().toString());
      case BOOLEAN_LITERAL:
        return ExpressionModel.render(renderer -> renderer.getLang().renderBooleanLiteral(node.getValue().toString(), renderer));
      case INT_LITERAL:
        return ExpressionModel.render(renderer -> renderer.getLang().renderIntegerLiteral(node.getValue().toString(), renderer));
      case CHAR_LITERAL:
        return ExpressionModel.render(renderer -> renderer.getLang().renderCharLiteral(node.getValue().toString().charAt(0), renderer));
      default:
        throw new UnsupportedOperationException("Literal " + node.getKind().name() + " not yet implemented");
    }
  }

  @Override
  public ExpressionModel visitIdentifier(IdentifierTree node, VisitContext context) {
    JCTree.JCIdent ident = (JCTree.JCIdent) node;
    if (ident.sym instanceof TypeElement) {
      if (ident.type.equals(SystemType)) {
        return ExpressionModel.forMemberSelect("out", () ->
            ExpressionModel.forMemberSelect("println", () ->
                ExpressionModel.forMethodInvocation(args -> lang.console(args.get(0)))));
      } else {
        TypeInfo.Class type = (TypeInfo.Class) factory.create(ident.type);
        if (type.getKind() == ClassKind.API) {
          return ExpressionModel.forMemberSelect((identifier) -> lang.staticFactory(type, identifier));
        } else if (type.getKind() == ClassKind.JSON_OBJECT) {
          return JsonObjectModel.CLASS_MODEL;
        } else if (type.getKind() == ClassKind.JSON_ARRAY) {
          return JsonArrayModel.CLASS_MODEL;
        } else if (type.getKind() == ClassKind.OPTIONS) {
          return OptionsModel.create(type);
        } else {
          return lang.classExpression(type);
        }
      }
    } else {
      ExpressionModel alias = context.getAlias(ident.sym);
      if (alias != null) {
        return alias;
      } else {
        return ExpressionModel.render(node.getName().toString());
      }
    }
  }

  @Override
  public CodeModel visitNewClass(NewClassTree node, VisitContext visitContext) {
    ExpressionModel identifier = scan(node.getIdentifier(), visitContext);
    List arguments = node.getArguments().stream().map(arg -> scan(arg, visitContext)).collect(Collectors.toList());
    return identifier.onNew(arguments);
  }

  @Override
  public CodeModel visitParenthesized(ParenthesizedTree node, VisitContext visitContext) {
    ExpressionModel expression = scan(node.getExpression(), visitContext);
    return ExpressionModel.forParenthesized(expression);
  }

  @Override
  public ExpressionModel visitMemberSelect(MemberSelectTree node, VisitContext p) {
    ExpressionModel expression = scan(node.getExpression(), p);
    return expression.onMemberSelect(node.getIdentifier().toString());
  }

  @Override
  public ExpressionModel visitMethodInvocation(MethodInvocationTree node, VisitContext p) {
    ExpressionModel methodSelect = scan(node.getMethodSelect(), p);
    List arguments = node.getArguments().stream().map(argument -> scan(argument, p)).collect(Collectors.toList());
    return methodSelect.onMethodInvocation(arguments);
  }

  @Override
  public StatementModel visitBlock(BlockTree node, VisitContext p) {
    List statements = node.getStatements().stream().map((statement) -> scan(statement, p)).collect(Collectors.toList());
    return StatementModel.block(statements);
  }

  @Override
  public ExpressionModel visitLambdaExpression(LambdaExpressionTree node, VisitContext p) {
    List parameterNames = node.getParameters().stream().map(parameter -> parameter.getName().toString()).collect(Collectors.toList());
    List parameterTypes = node.getParameters().stream().
        map(parameter -> factory.create(((JCTree.JCVariableDecl) parameter).type)).
        collect(Collectors.toList());
    int size = parameterNames.size();
    if (size > 0) {
      JCTree.JCVariableDecl last = (JCTree.JCVariableDecl) node.getParameters().get(size - 1);
      if (last.vartype instanceof JCTree.JCTypeApply) {
        JCTree.JCTypeApply typeApply = (JCTree.JCTypeApply) last.vartype;
        if (typeApply.clazz instanceof JCTree.JCFieldAccess) {
          JCTree.JCFieldAccess clazz = (JCTree.JCFieldAccess) typeApply.clazz;
          Symbol.ClassSymbol sym = (Symbol.ClassSymbol) clazz.sym;
          TypeInfo type = factory.create(sym.type);
          if (type.getKind() == ClassKind.ASYNC_RESULT) {
            ExpressionModel result = lang.asyncResult(last.name.toString());
            CodeModel body = scan(node.getBody(), p.putAlias(last.sym, result));
            return lang.asyncResultHandler(node.getBodyKind(), last.name.toString(), body);
          }
        }
      }
    }
    CodeModel body = scan(node.getBody(), p);
    return lang.lambda(node.getBodyKind(), parameterTypes, parameterNames, body);
  }

  @Override
  public CodeModel visitMethod(MethodTree node, VisitContext p) {
    return scan(node.getBody(), p);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy