package com.redhat.ceylon.eclipse.code.refactor;
import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.getProjectTypeChecker;
import static com.redhat.ceylon.eclipse.util.DocLinks.nameRegion;
import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.vfsJ2C;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
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.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jface.text.Region;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.CompositeChange;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.TextFileChange;
import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
import org.eclipse.ltk.core.refactoring.participants.RenameParticipant;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
import com.redhat.ceylon.compiler.typechecker.TypeChecker;
import com.redhat.ceylon.compiler.typechecker.context.PhasedUnit;
import com.redhat.ceylon.ide.common.vfs.FileVirtualFile;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.BaseMemberOrTypeExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.BaseType;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.DocLink;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.ImportMemberOrType;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.QualifiedMemberOrTypeExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.QualifiedType;
import com.redhat.ceylon.compiler.typechecker.tree.Visitor;
import com.redhat.ceylon.eclipse.core.builder.CeylonNature;
import com.redhat.ceylon.eclipse.util.DocLinks;
public class RenameJavaElementRefactoringParticipant extends RenameParticipant {
private IMember javaDeclaration;
protected boolean initialize(Object element) {
javaDeclaration = (IMember) element;
IProject project =
javaDeclaration.getJavaProject().getProject();
try {
if (!project.hasNature(CeylonNature.NATURE_ID)) {
return false;
}
}
catch (CoreException e) {
e.printStackTrace();
return false;
}
return getArguments().getUpdateReferences();
}
public String getName() {
return "Rename participant for Ceylon source";
}
public RefactoringStatus checkConditions(IProgressMonitor pm,
CheckConditionsContext context) {
return new RefactoringStatus();
}
public Change createChange(IProgressMonitor pm) throws CoreException {
//TODO: don't ignore ((RenamePackageProcessor) getProcessor()).getUpdateTextualMatches()
try {
final IProject project =
javaDeclaration.getJavaProject().getProject();
final String newName =
getArguments().getNewName();
final String oldName =
javaDeclaration.getElementName();
final HashMap<IFile,Change> changes =
new HashMap<IFile,Change>();
TypeChecker tc = getProjectTypeChecker(project);
if (tc==null) return null;
for (PhasedUnit phasedUnit:
tc.getPhasedUnits().getPhasedUnits()) {
final List<ReplaceEdit> edits =
new ArrayList<ReplaceEdit>();
Tree.CompilationUnit cu =
phasedUnit.getCompilationUnit();
cu.visit(new Visitor() {
@Override
public void visit(ImportMemberOrType that) {
super.visit(that);
visitIt(that.getIdentifier(),
that.getDeclarationModel());
}
@Override
public void visit(QualifiedMemberOrTypeExpression that) {
super.visit(that);
visitIt(that.getIdentifier(),
that.getDeclaration());
}
@Override
public void visit(BaseMemberOrTypeExpression that) {
super.visit(that);
visitIt(that.getIdentifier(),
that.getDeclaration());
}
@Override
public void visit(BaseType that) {
super.visit(that);
visitIt(that.getIdentifier(),
that.getDeclarationModel());
}
@Override
public void visit(QualifiedType that) {
super.visit(that);
visitIt(that.getIdentifier(),
that.getDeclarationModel());
}
protected void visitIt(Tree.Identifier id,
Declaration dec) {
visitIt(id.getText(), id.getStartIndex(), dec);
}
protected void visitIt(String name, int offset,
Declaration dec) {
if (dec!=null &&
dec.getQualifiedNameString()
.equals(getQualifiedName(javaDeclaration)) &&
name.equals(javaDeclaration.getElementName())) { //don't rename if aliased
edits.add(new ReplaceEdit(offset, oldName.length(), newName));
}
}
protected String getQualifiedName(IMember dec) {
IJavaElement parent = dec.getParent();
if (parent instanceof ICompilationUnit) {
return parent.getParent().getElementName() + "::" +
dec.getElementName();
}
else if (dec.getDeclaringType()!=null) {
return getQualifiedName(dec.getDeclaringType()) + "." +
dec.getElementName();
}
else {
return "@";
}
}
@Override
public void visit(DocLink that) {
super.visit(that);
Declaration base = that.getBase();
List<Declaration> qualified =
that.getQualified();
if (base!=null) {
Region region = nameRegion(that, 0);
visitIt(DocLinks.name(that, 0),
region.getOffset(), base);
if (qualified!=null) {
for (int i=0; i<qualified.size(); i++) {
visitIt(DocLinks.name(that, i+1),
nameRegion(that, i+1).getOffset(),
qualified.get(i));
}
}
}
}
});
if (!edits.isEmpty()) {
try {
FileVirtualFile<IProject, IResource, IFolder, IFile> unitFile =
vfsJ2C().getIFileVirtualFile(phasedUnit.getUnitFile());
IFile file = unitFile.getNativeResource();
TextFileChange change =
new TextFileChange(file.getName(), file);
change.setEdit(new MultiTextEdit());
changes.put(file, change);
for (ReplaceEdit edit: edits) {
change.addEdit(edit);
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
if (changes.isEmpty())
return null;
CompositeChange result =
new CompositeChange("Ceylon source changes");
for (Iterator<Change> iter = changes.values().iterator();
iter.hasNext();) {
result.add((Change) iter.next());
}
return result;
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
}