package edu.ucsd.arcum.interpreter.fragments; import static edu.ucsd.arcum.interpreter.ast.JDTFacade.declaredClassOf; import static edu.ucsd.arcum.interpreter.ast.JDTFacade.declaresVariableNamed; import org.eclipse.jdt.core.dom.*; import edu.ucsd.arcum.exceptions.ArcumError; import edu.ucsd.arcum.interpreter.parser.FragmentParser; import edu.ucsd.arcum.interpreter.query.EntityDataBase; import edu.ucsd.arcum.interpreter.query.IEntityLookup; import edu.ucsd.arcum.interpreter.satisfier.BindingMap; import edu.ucsd.arcum.interpreter.transformation.Conversion; public class FieldAccessPattern extends ProgramFragment { private final ProgramFragment targetPattern; private final ProgramFragment fieldPattern; private boolean isLeftHandSide = false; public FieldAccessPattern(ProgramFragment targetExpr, ProgramFragment field, FragmentParser fragmentParser) { this.targetPattern = targetExpr; this.fieldPattern = field; } @Override protected void buildString(StringBuilder buff) { buff.append(getIndenter()); buff.append(String.format("(FieldAccessPattern%n")); getIndenter().indent(); { buff.append(getIndenter()); buff.append(String.format("<targetPattern>%n")); targetPattern.buildString(buff); buff.append(String.format("%n")); buff.append(getIndenter()); buff.append(String.format("<fieldPattern>%n")); fieldPattern.buildString(buff); buff.append(")"); } getIndenter().unindent(); } private boolean partOfLHS(ASTNode node) { if (isLeftHandSide) { // then it's ok if we're part of an assignment: act like we aren't return false; } ASTNode parent = node.getParent(); if (parent instanceof Assignment) { Assignment assignment = (Assignment)parent; StructuralPropertyDescriptor spd = node.getLocationInParent(); if (spd == Assignment.LEFT_HAND_SIDE_PROPERTY) { return true; } } return false; } // Is this Name actually an expression, or just a name that appears in a // "package" statement (for example) private boolean realSubexpression(Name name) { ASTNode namesParent = name.getParent(); StructuralPropertyDescriptor spd = name.getLocationInParent(); if (spd == MethodInvocation.NAME_PROPERTY) { // the name of a method is not a sub-expression return false; } if (namesParent instanceof Expression) return true; if (namesParent instanceof ExpressionStatement) return true; if (spd == VariableDeclarationFragment.INITIALIZER_PROPERTY) return true; return false; } // Assume fieldVariable is a field and name is the node used to reference it // (possibly qualified) private ASTNode extractFieldTarget(Name name, IVariableBinding fieldVariable) { IVariableBinding fieldDecl = fieldVariable.getVariableDeclaration(); int flags = fieldDecl.getModifiers(); if (Modifier.isStatic(flags)) { // Then a Type must be the target ITypeBinding declaringClass = fieldVariable.getDeclaringClass(); Type type = FragmentParser.getType(declaringClass.getQualifiedName()); return type; } else { if (name instanceof SimpleName) { // It's a field, but just a single name: must be an implicit 'this' return name.getAST().newThisExpression(); } else { // Otherwise, some instance is the target return ((QualifiedName)name).getQualifier(); } } } public void setLeftHandSide(boolean isLeftHandSide) { this.isLeftHandSide = isLeftHandSide; } // MACNEIL: We're being sloppy with the fieldPattern here: Should treat it more // like targetPattern and get it into the bindings as well. @Override public BindingMap generateNode(IEntityLookup lookup, AST ast) { BindingMap targetMap = targetPattern.generateNode(lookup, ast); Object target = targetMap.getResult(); SimpleName fieldName = getFieldName(lookup, ast); Expression targetExpr; if (target instanceof ITypeBinding) { target = Conversion.typeBindingtoASTNode(ast, (ITypeBinding)target); } if (target instanceof Type) { // Static field: target is a Type, not an Expression targetExpr = ast.newName(((Type)target).toString()); } else { // (Probably) Non-static field: target is an Expression targetExpr = (Expression)target; } FieldAccess fieldAccess = ast.newFieldAccess(); fieldAccess.setExpression(targetExpr); fieldAccess.setName(fieldName); return bindRoot(fieldAccess).consistentMerge(targetMap); } private SimpleName getFieldName(IEntityLookup lookup, AST ast) { Object fieldEntity; if (fieldPattern instanceof ResolvedEntity) { ResolvedEntity entity = (ResolvedEntity)fieldPattern; fieldEntity = entity.getValue(); } else if (fieldPattern instanceof VariableNode) { VariableNode variableNode = (VariableNode)fieldPattern; String id = variableNode.getId(); fieldEntity = lookup.lookupEntity(id); } else { ArcumError.fatalError("Expected fieldPattern to be resolved or lookupable"); return null; } String id; if (fieldEntity instanceof SimpleName) { id = ((SimpleName)fieldEntity).getIdentifier(); } else if (fieldEntity instanceof FieldDeclaration) { FieldDeclaration fieldDecl = (FieldDeclaration)fieldEntity; VariableDeclarationFragment varDecl = (VariableDeclarationFragment)fieldDecl .fragments().get(0); id = varDecl.getName().getIdentifier(); } else if (fieldEntity instanceof String) { id = ((String)fieldEntity); } else { ArcumError.fatalError("Can't cope!%n"); return null; } return ast.newSimpleName(id); } @Override protected BindingMap matchesASTNode(ASTNode node) { if (node == null) { System.err.printf("gulp, this is bad%n"); return null; } ASTNode target; // the target expr, or class (in the case of static) ITypeBinding declaringClass; // the class that has the field String fieldName; // the field's name if (partOfLHS(node)) { return null; } if (node instanceof FieldAccess) { FieldAccess access = (FieldAccess)node; SimpleName id = access.getName(); IVariableBinding binding = (IVariableBinding)EntityDataBase.resolveBinding(id); target = access.getExpression(); declaringClass = binding.getDeclaringClass(); fieldName = id.getIdentifier(); } else if (node instanceof Name) { Name name = (Name)node; Name topName = name; for (;;) { ASTNode parent = topName.getParent(); if (!(parent instanceof Name)) break; topName = (Name)parent; } if (name instanceof SimpleName) { if (partOfLHS(topName)) { return null; } ASTNode parentExpr = topName.getParent(); if (parentExpr instanceof FieldAccess) { if (partOfLHS(parentExpr)) { return null; } } } if (!realSubexpression(topName)) { return null; } IBinding binding = EntityDataBase.resolveBinding(name); if (binding.getKind() == IBinding.VARIABLE) { IVariableBinding var = (IVariableBinding)binding; if (var.isField()) { target = extractFieldTarget(name, var); declaringClass = var.getDeclaringClass(); fieldName = var.getName(); } else return null; } else return null; } else return null; if (fieldPattern instanceof ResolvedEntity) { ResolvedEntity resEntity = (ResolvedEntity)fieldPattern; Object fieldEntity = resEntity.getValue(); if (fieldEntity instanceof ASTNode) { if (fieldEntity instanceof FieldDeclaration) { FieldDeclaration field = (FieldDeclaration)fieldEntity; if (!declaresVariableNamed(field, fieldName) || !declaredClassOf(field).isEqualTo(declaringClass)) { return null; } } else if (fieldEntity instanceof SimpleName) { SimpleName simpleName = (SimpleName)fieldEntity; if (!simpleName.getIdentifier().equals(fieldName)) { return null; } } else { ArcumError.fatalError("Can't handle ast-node case where field is" + " a %s%n", fieldEntity.getClass().toString()); } } else if (fieldEntity instanceof String) { String string = (String)fieldEntity; if (!string.equals(fieldName)) { return null; } } else { ArcumError.fatalError("Can't handle case where field is a %s%n", fieldEntity.getClass().toString()); } } else return null; BindingMap targetMatches = targetPattern.matches(target); if (targetMatches != null) { BindingMap result = bindRoot(node); result.addBindings(targetMatches); return result; } else { return null; } } }