package com.redhat.ceylon.eclipse.code.refactor;
import static com.redhat.ceylon.compiler.typechecker.tree.TreeUtil.formatPath;
import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.getProjectTypeChecker;
import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.vfsJ2C;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
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.CopyParticipant;
import org.eclipse.ltk.core.refactoring.participants.CopyProcessor;
import org.eclipse.ltk.core.refactoring.participants.ReorgExecutionLog;
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.compiler.typechecker.tree.Tree.ImportPath;
import com.redhat.ceylon.compiler.typechecker.tree.Visitor;
public class CopyPackageRefactoringParticipant extends CopyParticipant {
private IPackageFragment javaPackageFragment;
@Override
protected boolean initialize(Object element) {
javaPackageFragment = (IPackageFragment) element;
return getProcessor() instanceof CopyProcessor &&
getProjectTypeChecker(javaPackageFragment.getJavaProject().getProject())!=null;
}
@Override
public String getName() {
return "Copy package participant for Ceylon source";
}
@Override
public RefactoringStatus checkConditions(IProgressMonitor pm,
CheckConditionsContext context) throws OperationCanceledException {
return new RefactoringStatus();
}
public Change createChange(IProgressMonitor pm) throws CoreException {
Change change = new Change() {
IResource newPackage;
@Override
public Change perform(IProgressMonitor pm) throws CoreException {
IPackageFragmentRoot dest = (IPackageFragmentRoot) getArguments().getDestination();
ReorgExecutionLog executionLog = getArguments().getExecutionLog();
final String newName = executionLog.getNewName(javaPackageFragment);
if (newName == null || newName.isEmpty()) return null;
newPackage = dest.getPackageFragment(newName).getResource();
final String oldName = javaPackageFragment.getElementName();
final IProject project = javaPackageFragment.getJavaProject().getProject();
final List<Change> changes= new ArrayList<Change>();
TypeChecker tc = getProjectTypeChecker(project);
if (tc==null) return null;
for (PhasedUnit phasedUnit: tc.getPhasedUnits().getPhasedUnits()) {
if (phasedUnit.getPackage().getNameAsString().equals(oldName)) {
updateRefsInCopiedFile(newName, oldName, changes, phasedUnit);
}
}
if (!changes.isEmpty()) {
CompositeChange result= new CompositeChange("Ceylon source changes");
for (Change change: changes) {
result.add(change);
}
result.perform(pm);
}
//no undo
return null;
}
@Override
public String getName() {
return "Copy Ceylon Package";
}
@Override
public void initializeValidationData(IProgressMonitor pm) {}
@Override
public RefactoringStatus isValid(IProgressMonitor pm)
throws CoreException, OperationCanceledException {
return new RefactoringStatus();
}
@Override
public Object getModifiedElement() {
return newPackage;
}
};
change.setEnabled(true);
return change;
}
private void updateRefsInCopiedFile(final String newName, final String oldName,
final List<Change> changes, PhasedUnit phasedUnit) {
final List<ReplaceEdit> edits = new ArrayList<ReplaceEdit>();
phasedUnit.getCompilationUnit().visit(new Visitor() {
@Override
public void visit(ImportPath that) {
super.visit(that);
if (formatPath(that.getIdentifiers()).equals(oldName)) {
edits.add(new ReplaceEdit(that.getStartIndex(),
oldName.length(), newName));
}
}
});
if (!edits.isEmpty()) {
try {
IFile file = vfsJ2C().getIFileVirtualFile(phasedUnit.getUnitFile()).getNativeResource();
IFile newFile = getMovedFile(newName, file);
TextFileChange change= new TextFileChange(newFile.getName(), newFile);
change.setEdit(new MultiTextEdit());
changes.add(change);
for (ReplaceEdit edit: edits) {
change.addEdit(edit);
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
private IFile getMovedFile(final String newName, IFile file) {
String oldPath = javaPackageFragment.getElementName().replace('.', '/');
String newPath = newName.replace('.', '/');
IPath pathInSourceFolder = file.getParent().getProjectRelativePath()
.removeFirstSegments(1); //TODO: lame, it assumes a the source folder belongs directly to the project
if (pathInSourceFolder.toPortableString().equals(oldPath)) {
return file.getProject().getFile(file.getParent().getProjectRelativePath()
.removeLastSegments(pathInSourceFolder.segmentCount())
.append(newPath).append(file.getName()));
}
return file;
}
}