package com.redhat.ceylon.eclipse.code.refactor; import static com.redhat.ceylon.compiler.typechecker.tree.TreeUtil.formatPath; import static com.redhat.ceylon.eclipse.code.correct.ImportProposals.importProposals; import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.getProjectTypeChecker; import static com.redhat.ceylon.eclipse.util.EditorUtil.getDocument; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; 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.ltk.core.refactoring.Change; 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.text.edits.DeleteEdit; 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; 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.ImportMemberOrType; import com.redhat.ceylon.compiler.typechecker.tree.Tree.ModuleDescriptor; import com.redhat.ceylon.compiler.typechecker.tree.Tree.PackageDescriptor; import com.redhat.ceylon.compiler.typechecker.tree.Visitor; import com.redhat.ceylon.eclipse.code.correct.correctJ2C; import com.redhat.ceylon.model.typechecker.model.Declaration; import com.redhat.ceylon.model.typechecker.model.Module; import com.redhat.ceylon.model.typechecker.model.Unit; public class CopyFileRefactoringParticipant extends CopyParticipant { private IFile file; @Override protected boolean initialize(Object element) { file = (IFile) element; return getProcessor() instanceof CopyProcessor && getProjectTypeChecker(file.getProject())!=null && file.getFileExtension()!=null && file.getFileExtension().equals("ceylon"); } @Override public String getName() { return "Copy file participant for Ceylon source"; } @Override public RefactoringStatus checkConditions(IProgressMonitor pm, CheckConditionsContext context) throws OperationCanceledException { return new RefactoringStatus(); } public Change createChange(IProgressMonitor pm) throws CoreException { try { IFolder dest = (IFolder) getArguments().getDestination(); final String newName = dest.getProjectRelativePath() .removeFirstSegments(1) .toPortableString() .replace('/', '.'); IFile newFile = dest.getFile(file.getName()); IPath path = file.getProjectRelativePath(); String relFilePath = path.removeFirstSegments(1) .toPortableString(); String relPath = path.removeFirstSegments(1) .removeLastSegments(1) .toPortableString(); final String oldName = relPath.replace('/', '.'); final IProject project = file.getProject(); if (newName.equals(oldName)) return null; TypeChecker tc = getProjectTypeChecker(project); if (tc==null) return null; PhasedUnit phasedUnit = tc.getPhasedUnitFromRelativePath(relFilePath); if (phasedUnit==null) return null; final List<ReplaceEdit> edits = new ArrayList<ReplaceEdit>(); final List<Declaration> declarations = phasedUnit.getDeclarations(); final Map<Declaration,String> imports = new HashMap<Declaration,String>(); phasedUnit.getCompilationUnit().visit(new Visitor() { @Override public void visit(ImportMemberOrType that) { super.visit(that); visitIt(that.getIdentifier(), that.getDeclarationModel()); } @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(ModuleDescriptor that) { super.visit(that); visitIt(that.getImportPath()); } @Override public void visit(PackageDescriptor that) { super.visit(that); visitIt(that.getImportPath()); } private void visitIt(Tree.ImportPath importPath) { String path = formatPath(importPath.getIdentifiers()); if (path.equals(oldName)) { int start = importPath.getStartIndex(); int len = oldName.length(); edits.add(new ReplaceEdit(start, len, newName)); } } private void visitIt(Tree.Identifier id, Declaration dec) { if (dec!=null && //TODO: superflous !declarations.contains(dec)) { Unit unit = dec.getUnit(); String packageName = unit.getPackage().getNameAsString(); if (packageName.equals(oldName) && !packageName.isEmpty() && !packageName.equals(Module.LANGUAGE_MODULE_NAME) && !unit.equals(id.getUnit())) { imports.put(dec, id.getText()); } } } }); try { TextFileChange change = new TextFileChange(file.getName(), newFile); Tree.CompilationUnit cu = phasedUnit.getCompilationUnit(); change.setEdit(new MultiTextEdit()); for (ReplaceEdit edit: edits) { change.addEdit(edit); } if (!imports.isEmpty()) { new correctJ2C().importEdits(change, cu, imports.keySet(), imports.values(), getDocument(change)); } Tree.Import toDelete = importProposals().findImportNode(cu, newName); if (toDelete!=null) { int start = toDelete.getStartIndex(); int len = toDelete.getDistance(); change.addEdit(new DeleteEdit(start, len)); } if (change.getEdit().hasChildren()) { return change; } } catch (Exception e) { e.printStackTrace(); } return null; } catch (Exception e) { e.printStackTrace(); return null; } } }