package edu.ucsd.arcum.interpreter.fragments; import java.util.List; import org.eclipse.jdt.core.dom.*; import com.google.common.collect.Lists; import edu.ucsd.arcum.exceptions.ArcumError; import edu.ucsd.arcum.exceptions.Unreachable; import edu.ucsd.arcum.interpreter.ast.ASTUtil; import edu.ucsd.arcum.interpreter.query.Entity; 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; import edu.ucsd.arcum.util.StringUtil; public class DeclarationElement extends ProgramFragment { private ITypeBinding typeBinding; private List<IExtendedModifier> modifiers; // XXX (!!!) -- This code does not support the "package" access idea well, leading // to some matches not being made. public DeclarationElement(ITypeBinding typeBinding, List<IExtendedModifier> modifiers) { this.typeBinding = typeBinding; this.modifiers = modifiers; } @Override protected void buildString(StringBuilder buff) { buff.append(getIndenter()); buff.append(typeBinding.getBinaryName()); } @Override public BindingMap generateNode(IEntityLookup lookup, AST ast) { ASTNodeReplacer declarationNodeReplacer = this.new DeclarationNodeReplacer(ast); BindingMap result = bindRoot(declarationNodeReplacer); return result; } @Override protected BindingMap matchesASTNode(ASTNode node) { ITypeBinding foundType; List foundModifiers; if (node instanceof Type && modifiers.isEmpty()) { foundModifiers = Lists.newArrayList(); } else { foundModifiers = ASTUtil.getExtendedModifiers(node); } if (node instanceof Type) { // GETDONE: We really only want return types and casts, not everywhere // a Type would appear: Although maybe the database is only populated // with the right kind of Type instances in the first place. Type type = (Type)node; foundType = type.resolveBinding(); } else if (node instanceof SingleVariableDeclaration || node instanceof VariableDeclarationFragment) { VariableDeclaration varDecl = (VariableDeclaration)node; foundType = varDecl.resolveBinding().getType(); } else if (node instanceof VariableDeclarationStatement) { VariableDeclarationStatement varDeclStmt = (VariableDeclarationStatement)node; List frags = varDeclStmt.fragments(); if (frags.size() != 1) { ArcumError.fatalError("Desugaring error: %s", //-> StringUtil.debugDisplay(node)); } VariableDeclarationFragment frag = (VariableDeclarationFragment)frags.get(0); // GETDONE: and below // foundType = frag.resolveBinding().getType(); foundType = ((IVariableBinding)EntityDataBase.resolveBinding(frag)).getType(); } else if (node instanceof FieldDeclaration) { FieldDeclaration fieldDecl = (FieldDeclaration)node; List frags = fieldDecl.fragments(); if (frags.size() != 1) { ArcumError.fatalError("Desugaring error: %s", //-> StringUtil.debugDisplay(node)); } VariableDeclarationFragment frag = (VariableDeclarationFragment)frags.get(0); // GETDONE: What's the right thing we're looking for? // foundType = frag.resolveBinding().getType(); foundType = ((IVariableBinding)EntityDataBase.resolveBinding(frag)).getType(); } else { ArcumError.fatalError("Unhandled DeclarationElement case: %s", //-> StringUtil.debugDisplay(node)); return null; } boolean typesMatch = (Entity.compareTo(this.typeBinding, foundType) == 0); if (typesMatch) { boolean modifiersSubsetMatch = Entity.subsetOf(modifiers, foundModifiers); if (modifiersSubsetMatch) { BindingMap result = bindRoot(node); return result; } } return super.matchesASTNode(node); } private class DeclarationNodeReplacer implements ASTNodeReplacer { private AST ast; public DeclarationNodeReplacer(AST ast) { this.ast = ast; } @Override public ASTNode generateReplacement(ASTNode original) { Type typeNode = Conversion.typeBindingtoASTNode(ast, typeBinding); if (original instanceof Type) { // VERSION2: How do annotations get added to something like // List<@ReadOnly Map<String, String>>... answer: They can't! We // need to work around this (e.g. making wrappers) or see what // the next version of Java does. // // Wrappers: List<ReadOnlyMap<String, String>> ... // class ReadOnlyMap<T,U> { Map<T,U> baseMap } // where the wrapper is an automatically created delegator Type type = (Type)original; if (!modifiers.isEmpty()) { StructuralPropertyDescriptor spd = type.getLocationInParent(); if (spd == MethodDeclaration.RETURN_TYPE2_PROPERTY) { } ArcumError.fatalError("How do annotations get added to this?"); throw new Unreachable(); } return typeNode; } else if (original instanceof SingleVariableDeclaration) { SingleVariableDeclaration svd = (SingleVariableDeclaration)original; SingleVariableDeclaration result = Entity.copySubtree(ast, svd); result.setType(typeNode); insertModifiers(result.modifiers(), modifiers, ast); return result; } else if (original instanceof VariableDeclarationFragment) { // GETDONE: Handle this case and the similar one with field declarations // below: We need to change the sugared form, and separate it from // the original form (sometimes in three separate pieces to preserve // order). E.g., // Object a=f(), b[]=new Object[5], c=null; // becomes: // Object a=f(); // Foo b[] = new Foo[5]; // Object c=null; // Also need to mind changes to the other elements, if they need to // be changed too. This is somewhat related to the problem of adding // an annotation to a method's modifiers list. VariableDeclarationFragment vdf = (VariableDeclarationFragment)original; ArcumError.fatalError("Unsupported case: Replacing %s", ASTUtil.getDebugString(vdf)); throw new Unreachable(); } else if (original instanceof VariableDeclarationStatement) { VariableDeclarationStatement vds = (VariableDeclarationStatement)original; VariableDeclarationStatement result = Entity.copySubtree(ast, vds); result.setType(typeNode); insertModifiers(result.modifiers(), modifiers, ast); return result; } else if (original instanceof FieldDeclaration) { // GETDONE: handle case where a FieldDeclaration needs to be broken up FieldDeclaration fd = (FieldDeclaration)original; FieldDeclaration result = Entity.copySubtree(ast, fd); result.setType(typeNode); insertModifiers(result.modifiers(), modifiers, ast); return result; } else { ArcumError.fatalError("Unhandled case: %s", //-> ASTUtil.getDebugString(original)); throw new Unreachable(); } } @Override public String toString() { return typeBinding.getQualifiedName(); } } private static void insertModifiers(List toAdd, List<IExtendedModifier> modifiers, AST ast) { for (IExtendedModifier modifier : modifiers) { ASTNode node; if (modifier.isAnnotation()) { node = (Annotation)modifier; } else if (modifier.isModifier()){ node = (Modifier)modifier; } else { ArcumError.fatalError("Impossible condition"); throw new Unreachable(); } ASTNode copy = Entity.copySubtree(ast, node); toAdd.add(copy); } } }