Java tutorial
/* * Copyright 2011 Google Inc. All Rights Reserved. * * 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 com.google.devtools.j2cpp.translate; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.devtools.j2cpp.sym.Symbols; import com.google.devtools.j2cpp.types.GeneratedMethodBinding; import com.google.devtools.j2cpp.types.GeneratedVariableBinding; import com.google.devtools.j2cpp.types.Types; import com.google.devtools.j2cpp.util.TypeTrackingVisitor; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ArrayCreation; import org.eclipse.jdt.core.dom.Assignment; import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.ChildPropertyDescriptor; import org.eclipse.jdt.core.dom.ClassInstanceCreation; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.ConstructorInvocation; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.IExtendedModifier; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.IVariableBinding; import org.eclipse.jdt.core.dom.InfixExpression; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.Modifier; import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.Statement; import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor; import org.eclipse.jdt.core.dom.SuperConstructorInvocation; import org.eclipse.jdt.core.dom.SynchronizedStatement; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import java.util.List; import java.util.Set; /** * Base class for the anonymous class converter and inner class extractor, * containing shared methods. * * @author Tom Ball */ public abstract class ClassConverter extends TypeTrackingVisitor { protected final CompilationUnit unit; private final Set<IMethodBinding> updatedConstructors = Sets.newLinkedHashSet(); protected ClassConverter(CompilationUnit unit) { this.unit = unit; } /** * Returns a list of inner variables that need to be added to an inner type, * to resolve its references to outer classes. */ protected List<IVariableBinding> getInnerVars(List<ReferenceDescription> references) { List<IVariableBinding> innerVars = Lists.newArrayList(); outer: for (ReferenceDescription desc : references) { ITypeBinding declaringClass = desc.declaringClass; if (declaringClass == null) { declaringClass = desc.binding.getType(); } declaringClass = declaringClass.getTypeDeclaration(); if (desc.binding.isField()) { // Combine references to a type and its supertypes. for (int i = 0; i < innerVars.size(); i++) { IVariableBinding var = innerVars.get(i); ITypeBinding varType = var.getDeclaringClass(); if (varType != null && varType.isAssignmentCompatible(declaringClass)) { desc.declaringClass = varType; continue outer; } else if (varType == null) { desc.declaringMethod = var.getDeclaringMethod(); } } } if (!innerVars.contains(desc.binding)) { innerVars.add(desc.binding); } } return innerVars; } protected FieldDeclaration createField(String name, ITypeBinding varType, ITypeBinding declaringClass, AST ast) { VariableDeclarationFragment fragment = ast.newVariableDeclarationFragment(); SimpleName fieldName = ast.newSimpleName(name); GeneratedVariableBinding fieldBinding = new GeneratedVariableBinding(fieldName.getIdentifier(), Modifier.PRIVATE | Modifier.FINAL, varType, true, false, declaringClass, null); Types.addBinding(fieldName, fieldBinding); fragment.setName(fieldName); Types.addBinding(fragment, fieldBinding); FieldDeclaration field = ast.newFieldDeclaration(fragment); field.setType(Types.makeType(varType)); @SuppressWarnings("unchecked") List<IExtendedModifier> mods = field.modifiers(); // safe by definition mods.add(ast.newModifier(ModifierKeyword.PRIVATE_KEYWORD)); mods.add(ast.newModifier(ModifierKeyword.FINAL_KEYWORD)); return field; } @SuppressWarnings("unchecked") protected List<SingleVariableDeclaration> createConstructorArguments(List<IVariableBinding> innerFields, IMethodBinding constructor, AST ast, String prefix) { int nameOffset = constructor.getParameterTypes().length; List<SingleVariableDeclaration> args = Lists.newArrayList(); for (int i = 0; i < innerFields.size(); i++) { IVariableBinding field = innerFields.get(i); String argName = prefix + (i + nameOffset); SimpleName name = ast.newSimpleName(argName); GeneratedVariableBinding binding = new GeneratedVariableBinding(argName, Modifier.FINAL, field.getType(), false, true, constructor.getDeclaringClass(), constructor); Types.addBinding(name, binding); SingleVariableDeclaration newArg = ast.newSingleVariableDeclaration(); newArg.setName(name); newArg.setType(Types.makeType(field.getType())); newArg.modifiers().add(ast.newModifier(ModifierKeyword.FINAL_KEYWORD)); Types.addBinding(newArg, binding); args.add(newArg); } return args; } protected void addInnerParameters(MethodDeclaration constructor, GeneratedMethodBinding binding, List<IVariableBinding> innerFields, AST ast, boolean methodVars) { // Add parameters and initializers for each field. @SuppressWarnings("unchecked") // safe by definition List<SingleVariableDeclaration> parameters = constructor.parameters(); List<SingleVariableDeclaration> newParams = createConstructorArguments(innerFields, Types.getMethodBinding(constructor), ast, "outer$"); Block body = constructor.getBody(); @SuppressWarnings("unchecked") // safe by definition List<Statement> statements = body.statements(); Statement first = statements.size() > 0 ? statements.get(0) : null; int offset = first != null && (first instanceof SuperConstructorInvocation || first instanceof ConstructorInvocation) ? 1 : 0; boolean firstIsThisCall = first != null && first instanceof ConstructorInvocation; // If superclass constructor takes an outer$ parameter, create or edit // an invocation for it first if (!innerFields.isEmpty() && !methodVars) { if (firstIsThisCall) { ConstructorInvocation thisCall = (ConstructorInvocation) first; IMethodBinding cons = Types.getMethodBinding(thisCall); GeneratedMethodBinding newCons = new GeneratedMethodBinding(cons.getMethodDeclaration()); // Create a new this invocation to the updated constructor. @SuppressWarnings("unchecked") List<Expression> args = ((ConstructorInvocation) first).arguments(); int index = 0; for (SingleVariableDeclaration param : newParams) { IVariableBinding paramBinding = Types.getVariableBinding(param); newCons.addParameter(index, Types.getTypeBinding(param)); args.add(index++, makeFieldRef(paramBinding, ast)); } Types.addBinding(thisCall, newCons); } else { ITypeBinding superType = binding.getDeclaringClass().getSuperclass().getTypeDeclaration(); if ((superType.getDeclaringClass() != null || superType.getDeclaringMethod() != null) && (superType.getModifiers() & Modifier.STATIC) == 0) { // There may be more than one outer var supplied, find the right one. IVariableBinding outerVar = null; for (SingleVariableDeclaration param : newParams) { IVariableBinding paramBinding = Types.getVariableBinding(param); if (paramBinding.getType().isAssignmentCompatible(superType.getDeclaringClass())) { outerVar = paramBinding; } } assert outerVar != null; IMethodBinding cons = null; if (offset > 0) { cons = Types.getMethodBinding(statements.get(0)); } else { for (IMethodBinding method : superType.getDeclaredMethods()) { // The super class's constructor may or may not have been already // modified. if (method.isConstructor()) { if (method.getParameterTypes().length == 0) { cons = method; break; } else if (method.getParameterTypes().length == 1 && outerVar.getType().isAssignmentCompatible(method.getParameterTypes()[0]) && method instanceof GeneratedMethodBinding) { cons = method; break; } } } } assert cons != null; if (!updatedConstructors.contains(cons)) { GeneratedMethodBinding newSuperCons = new GeneratedMethodBinding( cons.getMethodDeclaration()); newSuperCons.addParameter(0, superType.getDeclaringClass()); cons = newSuperCons; } SimpleName outerRef = makeFieldRef(outerVar, ast); SuperConstructorInvocation superInvocation = offset == 0 ? ast.newSuperConstructorInvocation() : (SuperConstructorInvocation) statements.get(0); @SuppressWarnings("unchecked") List<Expression> args = superInvocation.arguments(); // safe by definition args.add(0, outerRef); if (offset == 0) { statements.add(0, superInvocation); offset = 1; } Types.addBinding(superInvocation, cons); } } } for (int i = 0; i < newParams.size(); i++) { SingleVariableDeclaration parameter = newParams.get(i); // Only add an assignment statement for fields. if (innerFields.get(i).isField()) { statements.add(i + offset, createAssignment(innerFields.get(i), Types.getVariableBinding(parameter), ast)); } // Add methodVars at the end of the method invocation. if (methodVars) { parameters.add(parameter); binding.addParameter(Types.getVariableBinding(parameter)); } else { parameters.add(i, parameter); binding.addParameter(i, Types.getVariableBinding(parameter)); } } Symbols.scanAST(constructor); updatedConstructors.add(binding); assert constructor.parameters().size() == binding.getParameterTypes().length; } protected Statement createAssignment(IVariableBinding field, IVariableBinding param, AST ast) { SimpleName fieldName = ast.newSimpleName(field.getName()); Types.addBinding(fieldName, field); SimpleName paramName = ast.newSimpleName(param.getName()); Types.addBinding(paramName, param); Assignment assign = ast.newAssignment(); assign.setLeftHandSide(fieldName); assign.setRightHandSide(paramName); Types.addBinding(assign, field.getType()); return ast.newExpressionStatement(assign); } protected SimpleName makeFieldRef(IVariableBinding newVar, AST ast) { SimpleName fieldRef = ast.newSimpleName(newVar.getName()); Types.addBinding(fieldRef, newVar); return fieldRef; } @SuppressWarnings("unchecked") public static void setProperty(ASTNode node, Expression expr) { ASTNode parent = node.getParent(); StructuralPropertyDescriptor locator = node.getLocationInParent(); if (locator instanceof ChildPropertyDescriptor) { parent.setStructuralProperty(locator, expr); } else { // JDT doesn't directly support ChildListProperty replacement. List<Expression> args; if (parent instanceof MethodInvocation) { args = ((MethodInvocation) parent).arguments(); } else if (parent instanceof ClassInstanceCreation) { args = ((ClassInstanceCreation) parent).arguments(); } else if (parent instanceof InfixExpression) { args = ((InfixExpression) parent).extendedOperands(); } else if (parent instanceof SynchronizedStatement) { SynchronizedStatement stmt = (SynchronizedStatement) parent; if (node.equals(stmt.getExpression())) { stmt.setExpression((Expression) node); } return; } else if (parent instanceof SuperConstructorInvocation) { args = ((SuperConstructorInvocation) parent).arguments(); } else if (parent instanceof ArrayCreation) { args = ((ArrayCreation) parent).dimensions(); } else { throw new AssertionError("unknown parent node type: " + parent.getClass().getSimpleName()); } for (int i = 0; i < args.size(); i++) { if (node.equals(args.get(i))) { args.set(i, expr); } } } } @SuppressWarnings("unchecked") public static void setProperty(ASTNode node, Statement stmt) { ASTNode parent = node.getParent(); StructuralPropertyDescriptor locator = node.getLocationInParent(); if (locator instanceof ChildPropertyDescriptor) { parent.setStructuralProperty(locator, stmt); } else { // JDT doesn't directly support ChildListProperty replacement. List<Statement> args; if (parent instanceof Block) { args = ((Block) parent).statements(); } else { throw new AssertionError("unknown parent node type: " + parent.getClass().getSimpleName()); } for (int i = 0; i < args.size(); i++) { if (node.equals(args.get(i))) { args.set(i, stmt); } } } } }