com.google.dart.java2dart.processor.ObjectSemanticProcessor.java Source code

Java tutorial

Introduction

Here is the source code for com.google.dart.java2dart.processor.ObjectSemanticProcessor.java

Source

/*
 * Copyright (c) 2013, the Dart project authors.
 * 
 * Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html
 * 
 * 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 com.google.dart.java2dart.processor;

import com.google.common.collect.Lists;
import com.google.dart.engine.ast.ASTNode;
import com.google.dart.engine.ast.AssignmentExpression;
import com.google.dart.engine.ast.BinaryExpression;
import com.google.dart.engine.ast.CompilationUnit;
import com.google.dart.engine.ast.Expression;
import com.google.dart.engine.ast.InstanceCreationExpression;
import com.google.dart.engine.ast.IntegerLiteral;
import com.google.dart.engine.ast.InterpolationElement;
import com.google.dart.engine.ast.MethodDeclaration;
import com.google.dart.engine.ast.MethodInvocation;
import com.google.dart.engine.ast.NodeList;
import com.google.dart.engine.ast.PrefixExpression;
import com.google.dart.engine.ast.PropertyAccess;
import com.google.dart.engine.ast.SimpleIdentifier;
import com.google.dart.engine.ast.SimpleStringLiteral;
import com.google.dart.engine.ast.StringInterpolation;
import com.google.dart.engine.ast.SuperConstructorInvocation;
import com.google.dart.engine.ast.TypeName;
import com.google.dart.engine.ast.VariableDeclaration;
import com.google.dart.engine.ast.visitor.GeneralizingASTVisitor;
import com.google.dart.engine.scanner.Keyword;
import com.google.dart.engine.scanner.TokenType;
import com.google.dart.java2dart.Context;
import com.google.dart.java2dart.util.JavaUtils;

import static com.google.dart.java2dart.util.ASTFactory.assignmentExpression;
import static com.google.dart.java2dart.util.ASTFactory.binaryExpression;
import static com.google.dart.java2dart.util.ASTFactory.booleanLiteral;
import static com.google.dart.java2dart.util.ASTFactory.identifier;
import static com.google.dart.java2dart.util.ASTFactory.instanceCreationExpression;
import static com.google.dart.java2dart.util.ASTFactory.integer;
import static com.google.dart.java2dart.util.ASTFactory.interpolationExpression;
import static com.google.dart.java2dart.util.ASTFactory.interpolationString;
import static com.google.dart.java2dart.util.ASTFactory.methodInvocation;
import static com.google.dart.java2dart.util.ASTFactory.namedExpression;
import static com.google.dart.java2dart.util.ASTFactory.prefixExpression;
import static com.google.dart.java2dart.util.ASTFactory.propertyAccess;
import static com.google.dart.java2dart.util.ASTFactory.string;
import static com.google.dart.java2dart.util.ASTFactory.typeName;
import static com.google.dart.java2dart.util.TokenFactory.token;

import org.apache.commons.lang3.StringUtils;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;

import java.util.List;

/**
 * {@link SemanticProcessor} for Java <code>Object</code>.
 */
public class ObjectSemanticProcessor extends SemanticProcessor {
    private static List<Expression> gatherBinaryExpressions(BinaryExpression binary) {
        List<Expression> expressions = Lists.newArrayList();
        {
            Expression left = binary.getLeftOperand();
            if (left instanceof BinaryExpression) {
                expressions.addAll(gatherBinaryExpressions((BinaryExpression) left));
            } else {
                expressions.add(left);
            }
        }
        {
            Expression right = binary.getRightOperand();
            expressions.add(right);
        }
        return expressions;
    }

    private static boolean hasStringObjects(Context context, List<Expression> expressions) {
        for (Expression expression : expressions) {
            ITypeBinding binding = context.getNodeTypeBinding(expression);
            if (JavaUtils.isTypeNamed(binding, "java.lang.String")) {
                return true;
            }
        }
        return false;
    }

    public ObjectSemanticProcessor(Context context) {
        super(context);
    }

    @Override
    public void process(CompilationUnit unit) {
        unit.accept(new GeneralizingASTVisitor<Void>() {
            @Override
            public Void visitAssignmentExpression(AssignmentExpression node) {
                super.visitAssignmentExpression(node);
                if (node.getOperator().getType() == TokenType.EQ) {
                    Expression leftExpr = node.getLeftHandSide();
                    Expression rightExpr = node.getRightHandSide();
                    ITypeBinding leftBinding = context.getNodeTypeBinding(leftExpr);
                    ITypeBinding rightBinding = context.getNodeTypeBinding(rightExpr);
                    if (JavaUtils.isTypeNamed(leftBinding, "java.lang.CharSequence")
                            && JavaUtils.isTypeNamed(rightBinding, "java.lang.String")) {
                        node.setRightHandSide(
                                instanceCreationExpression(Keyword.NEW, typeName("CharSequence"), rightExpr));
                        return null;
                    }
                }
                return null;
            }

            @Override
            public Void visitBinaryExpression(BinaryExpression node) {
                if (node.getOperator().getType() == TokenType.PLUS) {
                    List<Expression> expressions = gatherBinaryExpressions(node);
                    if (hasStringObjects(context, expressions)) {
                        // try to rewrite each expression
                        for (Expression expression : expressions) {
                            expression.accept(this);
                        }
                        expressions = gatherBinaryExpressions(node);
                        // compose interpolation using expressions
                        List<InterpolationElement> elements = Lists.newArrayList();
                        elements.add(interpolationString("\"", ""));
                        for (Expression expression : expressions) {
                            if (expression instanceof SimpleStringLiteral) {
                                SimpleStringLiteral literal = (SimpleStringLiteral) expression;
                                String value = literal.getValue();
                                String lexeme = StringUtils.strip(literal.getLiteral().getLexeme(), "\"");
                                elements.add(interpolationString(lexeme, value));
                            } else if (expression instanceof IntegerLiteral
                                    && JavaUtils.isTypeNamed(context.getNodeTypeBinding(expression), "char")) {
                                String value = "" + (char) ((IntegerLiteral) expression).getValue().intValue();
                                elements.add(interpolationString(value, value));
                            } else {
                                elements.add(interpolationExpression(expression));
                            }
                        }
                        elements.add(interpolationString("\"", ""));
                        StringInterpolation interpolation = string(elements);
                        replaceNode(node, interpolation);
                        return null;
                    }
                }
                // super
                super.visitBinaryExpression(node);
                // in Java "true | false" will compute both operands
                if (node.getOperator().getType() == TokenType.BAR) {
                    Expression leftOperand = node.getLeftOperand();
                    ITypeBinding argTypeBinding = context.getNodeTypeBinding(leftOperand);
                    if (JavaUtils.isTypeNamed(argTypeBinding, "boolean")) {
                        replaceNode(node, methodInvocation("javaBooleanOr", leftOperand, node.getRightOperand()));
                        return null;
                    }
                }
                // done
                return null;
            }

            @Override
            public Void visitInstanceCreationExpression(InstanceCreationExpression node) {
                super.visitInstanceCreationExpression(node);
                ITypeBinding typeBinding = context.getNodeTypeBinding(node);
                List<Expression> args = node.getArgumentList().getArguments();
                String typeSimpleName = node.getConstructorName().getType().getName().getName();
                if (JavaUtils.isTypeNamed(typeBinding, "java.lang.StringBuilder")) {
                    args.clear();
                    return null;
                }
                if (typeSimpleName.equals("int")) {
                    if (args.size() == 1) {
                        replaceNode(node, methodInvocation(identifier("int"), "parse", args.get(0)));
                    } else {
                        replaceNode(node, methodInvocation(identifier("int"), "parse", args.get(0),
                                namedExpression("radix", args.get(1))));
                    }
                }
                return null;
            }

            @Override
            public Void visitMethodDeclaration(MethodDeclaration node) {
                if (node.getName() instanceof SimpleIdentifier) {
                    String name = node.getName().getName();
                    if (name.equals("hashCode")) {
                        node.setOperatorKeyword(token(Keyword.GET));
                        node.setParameters(null);
                    }
                    if (name.equals("equals") && node.getParameters().getParameters().size() == 1) {
                        node.setOperatorKeyword(token(Keyword.OPERATOR));
                        node.setName(identifier("=="));
                    }
                }
                return super.visitMethodDeclaration(node);
            }

            @Override
            public Void visitMethodInvocation(MethodInvocation node) {
                super.visitMethodInvocation(node);
                SimpleIdentifier nameNode = node.getMethodName();
                String name = nameNode.getName();
                NodeList<Expression> args = node.getArgumentList().getArguments();
                // System -> JavaSystem
                if (node.getTarget() instanceof SimpleIdentifier) {
                    SimpleIdentifier targetIdentifier = (SimpleIdentifier) node.getTarget();
                    if (targetIdentifier.getName().equals("System")) {
                        targetIdentifier.setToken(token("JavaSystem"));
                    }
                    if (isMethodInClass(node, "getProperty", "java.lang.System")
                            || isMethodInClass(node, "getenv", "java.lang.System")) {
                        targetIdentifier.setToken(token("JavaSystemIO"));
                    }
                }
                //
                if (args.isEmpty()) {
                    if ("hashCode".equals(name) || isMethodInClass(node, "length", "java.lang.String")
                            || isMethodInClass(node, "isEmpty", "java.lang.String")
                            || isMethodInClass(node, "ordinal", "java.lang.Enum")
                            || isMethodInClass(node, "values", "java.lang.Enum")) {
                        replaceNode(node, propertyAccess(node.getTarget(), nameNode));
                        return null;
                    }
                    if ("getClass".equals(name)) {
                        replaceNode(node, propertyAccess(node.getTarget(), "runtimeType"));
                        return null;
                    }
                    if (isMethodInClass(node, "getName", "java.lang.Class")
                            || isMethodInClass(node, "getSimpleName", "java.lang.Class")) {
                        nameNode.setToken(token("toString"));
                        return null;
                    }
                }
                if (name.equals("equals") && args.size() == 1) {
                    ASTNode parent = node.getParent();
                    if (parent instanceof PrefixExpression
                            && ((PrefixExpression) parent).getOperator().getType() == TokenType.BANG) {
                        replaceNode(parent, binaryExpression(node.getTarget(), TokenType.BANG_EQ, args.get(0)));
                    } else {
                        replaceNode(node, binaryExpression(node.getTarget(), TokenType.EQ_EQ, args.get(0)));
                    }
                    return null;
                }
                if (isMethodInClass(node, "printStackTrace", "java.lang.Throwable")) {
                    replaceNode(node, methodInvocation("print", node.getTarget()));
                    return null;
                }
                if (isMethodInClass(node, "isInstance", "java.lang.Class")) {
                    replaceNode(node, methodInvocation("isInstanceOf", args.get(0), node.getTarget()));
                    return null;
                }
                if (isMethodInClass(node, "charAt", "java.lang.String")) {
                    nameNode.setToken(token("codeUnitAt"));
                    return null;
                }
                if (isMethodInClass(node, "replace", "java.lang.String")) {
                    nameNode.setToken(token("replaceAll"));
                    return null;
                }
                if (isMethodInClass(node, "equalsIgnoreCase", "java.lang.String")) {
                    replaceNode(node,
                            methodInvocation("javaStringEqualsIgnoreCase", node.getTarget(), args.get(0)));
                    return null;
                }
                if (isMethodInClass(node, "indexOf", "java.lang.String")) {
                    IMethodBinding binding = (IMethodBinding) context.getNodeBinding(node);
                    if (binding != null && binding.getParameterTypes().length >= 1
                            && binding.getParameterTypes()[0].getName().equals("int")) {
                        char c = (char) ((IntegerLiteral) args.get(0)).getValue().intValue();
                        replaceNode(args.get(0), string("" + c));
                    }
                    return null;
                }
                if (isMethodInClass(node, "print", "java.io.PrintWriter")) {
                    IMethodBinding binding = (IMethodBinding) context.getNodeBinding(node);
                    if (binding != null && binding.getParameterTypes().length >= 1
                            && binding.getParameterTypes()[0].getName().equals("char")) {
                        char c = (char) ((IntegerLiteral) args.get(0)).getValue().intValue();
                        replaceNode(args.get(0), string("" + c));
                    }
                    return null;
                }
                if (isMethodInClass2(node, "println()", "java.io.PrintWriter")) {
                    nameNode.setToken(token("newLine"));
                    return null;
                }
                if (isMethodInClass2(node, "println(java.lang.String)", "java.io.PrintWriter")) {
                    nameNode.setToken(token("println"));
                    return null;
                }
                if (isMethodInClass2(node, "startsWith(java.lang.String,int)", "java.lang.String")) {
                    replaceNode(node, methodInvocation(identifier("JavaString"), "startsWithBefore",
                            node.getTarget(), args.get(0), args.get(1)));
                    return null;
                }
                if (isMethodInClass(node, "format", "java.lang.String")) {
                    replaceNode(node.getTarget(), identifier("JavaString"));
                    return null;
                }
                if (name.equals("longValue") && node.getTarget() instanceof MethodInvocation) {
                    MethodInvocation node2 = (MethodInvocation) node.getTarget();
                    if (isMethodInClass(node2, "floor", "java.lang.Math")) {
                        NodeList<Expression> args2 = node2.getArgumentList().getArguments();
                        if (args2.size() == 1 && args2.get(0) instanceof BinaryExpression) {
                            BinaryExpression binary = (BinaryExpression) args2.get(0);
                            if (binary.getOperator().getType() == TokenType.SLASH) {
                                replaceNode(node, binaryExpression(binary.getLeftOperand(), TokenType.TILDE_SLASH,
                                        binary.getRightOperand()));
                                return null;
                            }
                        }
                    }
                }
                if (isMethodInClass(node, "valueOf", "java.lang.Integer")
                        || isMethodInClass(node, "valueOf", "java.lang.Long")
                        || isMethodInClass(node, "valueOf", "java.lang.Double")
                        || isMethodInClass(node, "valueOf", "java.math.BigInteger")) {
                    replaceNode(node, args.get(0));
                    return null;
                }
                if (isMethodInClass(node, "parseInt", "java.lang.Integer")) {
                    node.setTarget(identifier("int"));
                    nameNode.setToken(token("parse"));
                    if (args.size() == 2) {
                        args.set(1, namedExpression("radix", args.get(1)));
                    }
                    return null;
                }
                if (isMethodInClass(node, "parseDouble", "java.lang.Double")) {
                    node.setTarget(identifier("double"));
                    nameNode.setToken(token("parse"));
                    return null;
                }
                if (isMethodInClass2(node, "toString(double)", "java.lang.Double")
                        || isMethodInClass2(node, "toString(int)", "java.lang.Integer")
                        || isMethodInClass2(node, "toString(long)", "java.lang.Long")) {
                    replaceNode(node, methodInvocation(args.get(0), "toString"));
                    return null;
                }
                if (isMethodInClass(node, "booleanValue", "java.lang.Boolean")
                        || isMethodInClass(node, "doubleValue", "java.lang.Double")
                        || isMethodInClass(node, "intValue", "java.lang.Integer")
                        || isMethodInClass(node, "longValue", "java.lang.Long")
                        || isMethodInClass(node, "intValue", "java.math.BigInteger")) {
                    replaceNode(node, node.getTarget());
                    return null;
                }
                if (isMethodInClass(node, "doubleValue", "java.math.BigInteger")) {
                    nameNode.setToken(token("toDouble"));
                    return null;
                }
                {
                    TokenType tokenType = null;
                    if (isMethodInClass(node, "and", "java.math.BigInteger")) {
                        tokenType = TokenType.AMPERSAND;
                    } else if (isMethodInClass(node, "or", "java.math.BigInteger")) {
                        tokenType = TokenType.BAR;
                    } else if (isMethodInClass(node, "xor", "java.math.BigInteger")) {
                        tokenType = TokenType.CARET;
                    } else if (isMethodInClass(node, "add", "java.math.BigInteger")) {
                        tokenType = TokenType.PLUS;
                    } else if (isMethodInClass(node, "subtract", "java.math.BigInteger")) {
                        tokenType = TokenType.MINUS;
                    } else if (isMethodInClass(node, "multiply", "java.math.BigInteger")) {
                        tokenType = TokenType.STAR;
                    } else if (isMethodInClass(node, "divide", "java.math.BigInteger")) {
                        tokenType = TokenType.TILDE_SLASH;
                    } else if (isMethodInClass(node, "shiftLeft", "java.math.BigInteger")) {
                        tokenType = TokenType.LT_LT;
                    } else if (isMethodInClass(node, "shiftRight", "java.math.BigInteger")) {
                        tokenType = TokenType.GT_GT;
                    }
                    if (tokenType != null) {
                        replaceNode(node, binaryExpression(node.getTarget(), tokenType, args.get(0)));
                        return null;
                    }
                }
                {
                    TokenType tokenType = null;
                    if (isMethodInClass(node, "not", "java.math.BigInteger")) {
                        tokenType = TokenType.TILDE;
                    } else if (isMethodInClass(node, "negate", "java.math.BigInteger")) {
                        tokenType = TokenType.MINUS;
                    }
                    if (tokenType != null) {
                        replaceNode(node, prefixExpression(tokenType, node.getTarget()));
                        return null;
                    }
                }
                if (isMethodInClass2(node, "append(char)", "java.lang.StringBuilder")) {
                    replaceNode(nameNode, identifier("appendChar"));
                    return null;
                }
                if (isMethodInClass(node, "length", "java.lang.AbstractStringBuilder")) {
                    replaceNode(node, propertyAccess(node.getTarget(), nameNode));
                    return null;
                }
                if (isMethodInClass(node, "setLength", "java.lang.AbstractStringBuilder")) {
                    nameNode.setToken(token("length"));
                    replaceNode(node, assignmentExpression(propertyAccess(node.getTarget(), nameNode), TokenType.EQ,
                            args.get(0)));
                    return null;
                }
                return null;
            }

            @Override
            public Void visitPropertyAccess(PropertyAccess node) {
                Expression target = node.getTarget();
                ITypeBinding targetTypeBinding = context.getNodeTypeBinding(target);
                if (target instanceof SimpleIdentifier) {
                    String targetName = ((SimpleIdentifier) target).getName();
                    if (targetName.equals("Boolean")) {
                        boolean value = node.getPropertyName().getName().equals("TRUE");
                        replaceNode(node, booleanLiteral(value));
                        return null;
                    }
                    if (targetName.equals("Integer")) {
                        int value;
                        if (node.getPropertyName().getName().equals("MIN_VALUE")) {
                            value = Integer.MIN_VALUE;
                        } else {
                            value = Integer.MAX_VALUE;
                        }
                        replaceNode(node, integer(value));
                        return null;
                    }
                    if (JavaUtils.isTypeNamed(targetTypeBinding, "java.math.BigInteger")) {
                        if (node.getPropertyName().getName().equals("ZERO")) {
                            replaceNode(node, integer(0));
                            return null;
                        }
                    }
                }
                return super.visitPropertyAccess(node);
            }

            @Override
            public Void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
                super.visitSuperConstructorInvocation(node);
                IMethodBinding binding = (IMethodBinding) context.getNodeBinding(node);
                if (isMethodInClass2(binding, "<init>(java.lang.Throwable)", "java.lang.Exception")) {
                    node.setConstructorName(identifier("withCause"));
                }
                return null;
            }

            @Override
            public Void visitTypeName(TypeName node) {
                ITypeBinding typeBinding = (ITypeBinding) context.getNodeBinding(node);
                // replace by name
                if (node.getName() instanceof SimpleIdentifier) {
                    SimpleIdentifier nameNode = (SimpleIdentifier) node.getName();
                    String name = nameNode.getName();
                    // Exception -> JavaException
                    if (JavaUtils.isTypeNamed(typeBinding, "java.lang.Exception")) {
                        replaceNode(nameNode, identifier("JavaException"));
                    }
                    if (JavaUtils.isTypeNamed(typeBinding, "java.lang.Throwable")) {
                        replaceNode(nameNode, identifier("Exception"));
                    }
                    if (JavaUtils.isTypeNamed(typeBinding, "java.lang.IndexOutOfBoundsException")) {
                        replaceNode(nameNode, identifier("RangeError"));
                    }
                    // StringBuilder -> JavaStringBuilder
                    if (name.equals("StringBuilder")) {
                        replaceNode(nameNode, identifier("JavaStringBuilder"));
                    }
                    // Class<T> -> Type
                    if (name.equals("Class")) {
                        replaceNode(node, typeName("Type"));
                    }
                }
                // done
                return super.visitTypeName(node);
            }

            @Override
            public Void visitVariableDeclaration(VariableDeclaration node) {
                super.visitVariableDeclaration(node);
                Expression initializer = node.getInitializer();
                ITypeBinding leftBinding = context.getNodeTypeBinding(node);
                ITypeBinding rightBinding = context.getNodeTypeBinding(initializer);
                if (JavaUtils.isTypeNamed(leftBinding, "java.lang.CharSequence")
                        && JavaUtils.isTypeNamed(rightBinding, "java.lang.String")) {
                    node.setInitializer(
                            instanceCreationExpression(Keyword.NEW, typeName("CharSequence"), initializer));
                    return null;
                }
                return null;
            }
        });
    }
}