package com.redhat.ceylon.eclipse.code.correct; import static com.redhat.ceylon.eclipse.util.Nodes.getReferencedDeclaration; import static com.redhat.ceylon.model.typechecker.model.ModelUtil.isConstructor; import java.util.Collection; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.ltk.core.refactoring.TextChange; import org.eclipse.ltk.core.refactoring.TextFileChange; import org.eclipse.text.edits.DeleteEdit; import org.eclipse.text.edits.MultiTextEdit; import com.redhat.ceylon.compiler.typechecker.tree.Node; import com.redhat.ceylon.compiler.typechecker.tree.Tree; import com.redhat.ceylon.ide.common.model.ModifiableSourceFile; import com.redhat.ceylon.ide.common.typechecker.ModifiablePhasedUnit; import com.redhat.ceylon.ide.common.util.FindDeclarationNodeVisitor; import com.redhat.ceylon.model.typechecker.model.ClassOrInterface; import com.redhat.ceylon.model.typechecker.model.Declaration; import com.redhat.ceylon.model.typechecker.model.Referenceable; import com.redhat.ceylon.model.typechecker.model.Scope; import com.redhat.ceylon.model.typechecker.model.TypeDeclaration; import com.redhat.ceylon.model.typechecker.model.Unit; class RemoveAnnotionProposal extends CorrectionProposal { private final Declaration dec; private final String annotation; RemoveAnnotionProposal(Declaration dec, String annotation, int offset, String desc, TextChange change) { super(desc, change, new Region(offset, 0)); this.dec = dec; this.annotation = annotation; } @Override public boolean equals(Object obj) { if (obj instanceof RemoveAnnotionProposal) { RemoveAnnotionProposal that = (RemoveAnnotionProposal) obj; return that.dec.equals(dec) && that.annotation.equals(annotation); } else { return super.equals(obj); } } @Override public int hashCode() { return dec.hashCode(); } static void addRemoveAnnotationProposal(Node node, String annotation, Collection<ICompletionProposal> proposals, IProject project) { Referenceable dec = getReferencedDeclaration(node); if (dec instanceof Declaration) { addRemoveAnnotationProposal(node, annotation, "Make Non" + annotation, (Declaration) dec, proposals, project); } } static void addMakeContainerNonfinalProposal( Collection<ICompletionProposal> proposals, IProject project, Node node) { Declaration dec; if (node instanceof Tree.Declaration) { Tree.Declaration decNode = (Tree.Declaration) node; Scope container = decNode.getDeclarationModel() .getContainer(); if (container instanceof Declaration) { dec = (Declaration) container; } else { return; } } else { dec = (Declaration) node.getScope(); } addRemoveAnnotationProposal(node, "final", "Make Nonfinal", dec, proposals, project); } static void addRemoveAnnotationProposal(Node node, String annotation, String desc, Declaration dec, Collection<ICompletionProposal> proposals, IProject project) { if (dec!=null && dec.getName()!=null) { Unit u = dec.getUnit(); if (u instanceof ModifiableSourceFile) { ModifiableSourceFile<IProject,IResource,IFolder,IFile> cu = (ModifiableSourceFile<IProject,IResource,IFolder,IFile>) u; ModifiablePhasedUnit<IProject,IResource,IFolder,IFile> unit = cu.getPhasedUnit(); //TODO: "object" declarations? FindDeclarationNodeVisitor fdv = new FindDeclarationNodeVisitor(dec); unit.getCompilationUnit().visit(fdv); Tree.Declaration decNode = (Tree.Declaration) fdv.getDeclarationNode(); if (decNode!=null) { addRemoveAnnotationProposal( annotation, desc, dec, proposals, unit, decNode); } } } } private static void addRemoveAnnotationProposal( String annotation, String desc, Declaration dec, Collection<ICompletionProposal> proposals, ModifiablePhasedUnit<IProject,IResource,IFolder,IFile> unit, Tree.Declaration decNode) { IFile file = unit.getResourceFile(); if (file == null) { return; } TextFileChange change = new TextFileChange(desc, file); change.setEdit(new MultiTextEdit()); Integer offset = decNode.getStartIndex(); for (Tree.Annotation a: decNode.getAnnotationList() .getAnnotations()) { Tree.BaseMemberExpression bme = (Tree.BaseMemberExpression) a.getPrimary(); Tree.Identifier id = bme.getIdentifier(); if (id!=null) { if (id.getText().equals(annotation)) { Tree.PositionalArgumentList pal = a.getPositionalArgumentList(); boolean args = pal!=null && pal.getToken()!=null || a.getNamedArgumentList()!=null; change.addEdit(new DeleteEdit(a.getStartIndex(), a.getDistance() + (args?0:1))); //get rid of the trailing space } } } RemoveAnnotionProposal p = new RemoveAnnotionProposal(dec, annotation, offset, description(annotation, dec), change); if (!proposals.contains(p)) { proposals.add(p); } } private static String description( String annotation, Declaration dec) { String name = dec.getName(); if (name==null) { if (isConstructor(dec)) { name = "default constructor "; } else { name = ""; } } else { name = "'" + name + "' "; } String descr = "Make " + name + "non-" + annotation; Scope container = dec.getContainer(); if (container instanceof ClassOrInterface) { TypeDeclaration td = (ClassOrInterface) container; descr += " in '" + td.getName() + "'"; } return descr; } static void addRemoveAnnotationDecProposal( Collection<ICompletionProposal> proposals, String annotation, IProject project, Node node) { if (node instanceof Tree.Declaration) { Tree.Declaration decNode = (Tree.Declaration) node; addRemoveAnnotationProposal(node, annotation, "Make Non" + annotation, decNode.getDeclarationModel(), proposals, project); } } }