Index: editor/util/plugins/com.google.dart.java2dart/src/com/google/dart/java2dart/processor/PropertySemanticProcessor.java |
diff --git a/editor/util/plugins/com.google.dart.java2dart/src/com/google/dart/java2dart/processor/PropertySemanticProcessor.java b/editor/util/plugins/com.google.dart.java2dart/src/com/google/dart/java2dart/processor/PropertySemanticProcessor.java |
index d6d98090d43a2ee5e1b3bfcee3270da014a53c2a..0a21a09be4381f41d566113e58d5296bbdfb2f9a 100644 |
--- a/editor/util/plugins/com.google.dart.java2dart/src/com/google/dart/java2dart/processor/PropertySemanticProcessor.java |
+++ b/editor/util/plugins/com.google.dart.java2dart/src/com/google/dart/java2dart/processor/PropertySemanticProcessor.java |
@@ -14,33 +14,62 @@ |
package com.google.dart.java2dart.processor; |
+import com.google.common.collect.Maps; |
+import com.google.common.collect.Sets; |
+import com.google.dart.engine.ast.AssignmentExpression; |
+import com.google.dart.engine.ast.BlockFunctionBody; |
+import com.google.dart.engine.ast.ClassDeclaration; |
+import com.google.dart.engine.ast.ClassMember; |
import com.google.dart.engine.ast.CompilationUnit; |
import com.google.dart.engine.ast.Expression; |
+import com.google.dart.engine.ast.ExpressionFunctionBody; |
+import com.google.dart.engine.ast.ExpressionStatement; |
import com.google.dart.engine.ast.FieldDeclaration; |
import com.google.dart.engine.ast.FormalParameter; |
import com.google.dart.engine.ast.MethodDeclaration; |
import com.google.dart.engine.ast.MethodInvocation; |
+import com.google.dart.engine.ast.PropertyAccess; |
+import com.google.dart.engine.ast.SimpleFormalParameter; |
import com.google.dart.engine.ast.SimpleIdentifier; |
+import com.google.dart.engine.ast.Statement; |
+import com.google.dart.engine.ast.ThisExpression; |
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.Bindings; |
import static com.google.dart.java2dart.util.ASTFactory.assignmentExpression; |
import static com.google.dart.java2dart.util.ASTFactory.propertyAccess; |
import static com.google.dart.java2dart.util.TokenFactory.token; |
import org.apache.commons.lang3.StringUtils; |
+import org.eclipse.jdt.core.dom.IBinding; |
+import org.eclipse.jdt.core.dom.IMethodBinding; |
import java.util.List; |
+import java.util.Map; |
+import java.util.Set; |
/** |
* {@link SemanticProcessor} for converting <code>getX()</code> or <code>setX(x)</code> into getter |
* or setter. |
*/ |
public class PropertySemanticProcessor extends SemanticProcessor { |
+ private static class FieldPropertyInfo { |
+ final String name; |
+ MethodDeclaration getter; |
+ MethodDeclaration setter; |
+ String getterField; |
+ String setterField; |
+ |
+ public FieldPropertyInfo(String name) { |
+ this.name = name; |
+ } |
+ } |
+ |
private static boolean hasPrefix(String name, String prefix) { |
// should start with prefix |
if (!name.startsWith(prefix)) { |
@@ -151,5 +180,187 @@ public class PropertySemanticProcessor extends SemanticProcessor { |
return super.visitMethodDeclaration(node); |
} |
}); |
+ // remember all overridden and overriding methods |
+ final Set<IMethodBinding> ignoredMethods = Sets.newHashSet(); |
+ unit.accept(new GeneralizingASTVisitor<Void>() { |
Brian Wilkerson
2013/09/04 16:48:59
It's a minor point, but if you're only invoking me
|
+ @Override |
+ public Void visitMethodDeclaration(MethodDeclaration node) { |
+ IMethodBinding binding = (IMethodBinding) context.getNodeBinding(node); |
+ if (binding != null) { |
+ IMethodBinding superBinding = Bindings.findOverriddenMethod(binding, true); |
+ if (superBinding != null) { |
+ ignoredMethods.add(binding); |
+ ignoredMethods.add(superBinding); |
+ } |
+ } |
+ return null; |
+ } |
+ }); |
+ // try to convert properties into fields |
+ unit.accept(new GeneralizingASTVisitor<Void>() { |
+ private final Map<String, FieldPropertyInfo> properties = Maps.newHashMap(); |
+ |
+ @Override |
+ public Void visitClassDeclaration(ClassDeclaration node) { |
+ properties.clear(); |
+ super.visitClassDeclaration(node); |
+ // simplify field properties |
+ for (FieldPropertyInfo property : properties.values()) { |
+ MethodDeclaration getter = property.getter; |
+ MethodDeclaration setter = property.setter; |
+ // no getter or not field |
+ if (getter == null || property.getterField == null) { |
+ continue; |
+ } |
+ // setter is not field |
+ if (setter != null && !StringUtils.equals(property.getterField, property.setterField)) { |
+ continue; |
+ } |
+ // remove getter, update field |
+ if (getter != null) { |
+ ClassDeclaration clazz = (ClassDeclaration) getter.getParent(); |
+ List<ClassMember> members = clazz.getMembers(); |
+ members.remove(getter); |
+ for (ClassMember member : members) { |
+ if (member instanceof FieldDeclaration) { |
+ FieldDeclaration fieldDeclaration = (FieldDeclaration) member; |
+ List<VariableDeclaration> variables = fieldDeclaration.getFields().getVariables(); |
+ for (VariableDeclaration field : variables) { |
+ SimpleIdentifier fieldName = field.getName(); |
+ if (fieldName.getName().equals(property.getterField)) { |
+ context.renameIdentifier(fieldName, property.name); |
+ // mark "final" is no writes |
+ { |
+ boolean readOnly = true; |
+ List<SimpleIdentifier> references = context.getReferences(fieldName); |
+ for (SimpleIdentifier reference : references) { |
+ readOnly &= reference.inGetterContext(); |
Brian Wilkerson
2013/09/04 16:48:59
Given that getter and setter contexts are not mutu
|
+ } |
+ if (readOnly) { |
+ fieldDeclaration.getFields().setKeyword(token(Keyword.FINAL)); |
+ } |
+ } |
+ // now these are field references |
+ { |
+ IBinding fieldBinding = context.getNodeBinding(fieldName); |
+ replaceMethodReferencesWithFieldBindings(getter, fieldBinding); |
+ replaceMethodReferencesWithFieldBindings(setter, fieldBinding); |
+ } |
+ // done |
+ break; |
+ } |
+ } |
+ } |
+ } |
+ } |
+ // remove setter |
+ if (setter != null) { |
+ ClassDeclaration clazz = (ClassDeclaration) setter.getParent(); |
+ List<ClassMember> members = clazz.getMembers(); |
+ members.remove(setter); |
+ } |
+ } |
+ // done |
+ return null; |
+ } |
+ |
+ @Override |
+ public Void visitMethodDeclaration(MethodDeclaration node) { |
+ // don't remove method if it overrides |
+ { |
+ IMethodBinding binding = (IMethodBinding) context.getNodeBinding(node); |
+ if (binding == null) { |
+ return null; |
+ } |
+ if (ignoredMethods.contains(binding)) { |
+ return null; |
+ } |
+ } |
+ // getter |
+ if (node.isGetter()) { |
+ String name = node.getName().getName(); |
+ FieldPropertyInfo property = getProperty(name); |
+ property.getter = node; |
+ if (node.getBody() instanceof ExpressionFunctionBody) { |
+ ExpressionFunctionBody body = (ExpressionFunctionBody) node.getBody(); |
+ Expression expression = body.getExpression(); |
+ if (expression instanceof SimpleIdentifier) { |
+ SimpleIdentifier identifier = (SimpleIdentifier) expression; |
+ property.getterField = identifier.getName(); |
+ } |
+ } |
+ } |
+ // setter |
+ if (node.isSetter()) { |
+ String name = node.getName().getName(); |
+ FieldPropertyInfo property = getProperty(name); |
+ property.setter = node; |
+ // block body |
+ if (!(node.getBody() instanceof BlockFunctionBody)) { |
+ return null; |
+ } |
+ // single statement |
+ BlockFunctionBody body = (BlockFunctionBody) node.getBody(); |
+ List<Statement> statements = body.getBlock().getStatements(); |
+ if (statements.size() != 1) { |
+ return null; |
+ } |
+ Statement statement = statements.get(0); |
+ // prepare expression |
+ if (!(statement instanceof ExpressionStatement)) { |
+ return null; |
+ } |
+ Expression expression = ((ExpressionStatement) statement).getExpression(); |
+ // should be assignment |
+ if (!(expression instanceof AssignmentExpression)) { |
+ return null; |
+ } |
+ AssignmentExpression assignment = (AssignmentExpression) expression; |
+ // simple assignment |
+ if (assignment.getOperator().getType() != TokenType.EQ) { |
+ return null; |
+ } |
+ // RHS should be just parameter name |
+ Expression rhs = assignment.getRightHandSide(); |
+ if (!(rhs instanceof SimpleIdentifier)) { |
+ return null; |
+ } |
+ SimpleIdentifier rhsName = (SimpleIdentifier) rhs; |
+ String parameterName = ((SimpleFormalParameter) node.getParameters().getParameters().get( |
+ 0)).getIdentifier().getName(); |
+ if (!rhsName.getName().equals(parameterName)) { |
+ return null; |
+ } |
+ // LHS |
+ Expression lhs = assignment.getLeftHandSide(); |
+ if (lhs instanceof PropertyAccess) { |
+ PropertyAccess access = (PropertyAccess) lhs; |
+ if (access.getTarget() instanceof ThisExpression) { |
+ property.setterField = access.getPropertyName().getName(); |
+ } |
+ } |
+ } |
+ return null; |
+ } |
+ |
+ private FieldPropertyInfo getProperty(String name) { |
+ FieldPropertyInfo property = properties.get(name); |
+ if (property == null) { |
+ property = new FieldPropertyInfo(name); |
+ properties.put(name, property); |
+ } |
+ return property; |
+ } |
+ |
+ private void replaceMethodReferencesWithFieldBindings(MethodDeclaration method, |
+ IBinding fieldBinding) { |
+ if (method == null) { |
+ return; |
+ } |
+ for (SimpleIdentifier reference : context.getReferences(method.getName())) { |
+ context.putReference(reference, fieldBinding, null); |
+ } |
+ } |
+ }); |
} |
} |