package com.redhat.ceylon.eclipse.code.refactor;
import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.getUnits;
import static com.redhat.ceylon.eclipse.util.EditorUtil.getSelection;
import static com.redhat.ceylon.eclipse.util.Nodes.findNode;
import java.util.ArrayList;
import java.util.List;
import org.antlr.runtime.CommonToken;
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.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.CompositeChange;
import org.eclipse.ltk.core.refactoring.DocumentChange;
import org.eclipse.ltk.core.refactoring.TextChange;
import org.eclipse.ltk.core.refactoring.TextFileChange;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import com.redhat.ceylon.compiler.typechecker.context.PhasedUnit;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Expression;
import com.redhat.ceylon.eclipse.code.editor.CeylonEditor;
import com.redhat.ceylon.eclipse.code.parse.CeylonParseController;
import com.redhat.ceylon.eclipse.util.EditorUtil;
import com.redhat.ceylon.ide.common.model.CrossProjectBinaryUnit;
import com.redhat.ceylon.ide.common.model.CrossProjectSourceFile;
import com.redhat.ceylon.ide.common.model.EditedSourceFile;
import com.redhat.ceylon.ide.common.model.IResourceAware;
import com.redhat.ceylon.ide.common.model.ProjectSourceFile;
import com.redhat.ceylon.ide.common.typechecker.ModifiablePhasedUnit;
import com.redhat.ceylon.ide.common.typechecker.ProjectPhasedUnit;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.Package;
import com.redhat.ceylon.model.typechecker.model.Unit;
abstract class AbstractRefactoring extends Refactoring {
final IProject project;
final IFile sourceFile;
final List<CommonToken> tokens;
final IDocument document;
final CeylonEditor editor;
final Tree.CompilationUnit rootNode;
Node node;
/*public AbstractRefactoring(IQuickFixInvocationContext context) {
sourceFile = context.getModel().getFile();
project = sourceFile.getProject();
PhasedUnit pu = CeylonBuilder.getPhasedUnit(sourceFile);
rootNode = pu.getCompilationUnit();
tokenStream = pu.getTokenStream();
node = CeylonSourcePositionLocator.findNode(rootNode, context.getOffset(),
context.getOffset()+context.getLength());
}*/
public AbstractRefactoring(IEditorPart editor) {
if (editor instanceof CeylonEditor) {
CeylonEditor ce = (CeylonEditor) editor;
this.editor = ce;
document =
ce.getDocumentProvider()
.getDocument(editor.getEditorInput());
project = EditorUtil.getProject(editor);
CeylonParseController cpc = ce.getParseController();
tokens = cpc.getTokens();
rootNode = cpc.getTypecheckedRootNode();
IEditorInput input = editor.getEditorInput();
if (rootNode!=null && input instanceof IFileEditorInput) {
sourceFile = EditorUtil.getFile(input);
node = findNode(rootNode, tokens, getSelection(ce));
}
else {
sourceFile = null;
node = null;
}
}
else {
this.editor = null;
document = null;
tokens = null;
rootNode = null;
sourceFile = null;
node = null;
project = null;
}
}
boolean inSameProject(Declaration declaration) {
Unit unit = declaration.getUnit();
if (unit instanceof CrossProjectSourceFile ||
unit instanceof CrossProjectBinaryUnit) {
return false;
}
if (unit instanceof IResourceAware) {
IResourceAware<IProject, IFolder, IFile> ra =
(IResourceAware<IProject,IFolder,IFile>) unit;
IProject project = ra.getResourceProject();
if (project==null) {
return false;
}
else {
return project.equals(this.project);
}
}
return false;
}
boolean getEditable() {
return rootNode.getUnit() instanceof EditedSourceFile ||
rootNode.getUnit() instanceof ProjectSourceFile;
}
Tree.Term unparenthesize(Tree.Term term) {
if (term instanceof Tree.Expression) {
Expression e = (Tree.Expression) term;
if (!(e.getTerm() instanceof Tree.Tuple)) {
return unparenthesize(e.getTerm());
}
}
return term;
}
DocumentChange newDocumentChange() {
DocumentChange dc =
new DocumentChange(
editor.getEditorInput().getName() +
" \u2014 current editor",
document);
dc.setTextType("ceylon");
return dc;
}
TextFileChange newTextFileChange(
IResourceAware<IProject,IFolder,IFile> pu) {
TextFileChange tfc =
new TextFileChange(getName(),
pu.getResourceFile());
tfc.setTextType("ceylon");
return tfc;
}
protected boolean searchInEditor() {
return editor!=null && editor.isDirty();
}
protected boolean searchInFile(PhasedUnit pu) {
return !searchInEditor() || rootNode == null ||
!pu.getUnit().equals(rootNode.getUnit());
}
protected boolean inSamePackage(PhasedUnit pu) {
Package editorPackage =
editor.getParseController()
.getLastCompilationUnit()
.getUnit()
.getPackage();
return pu.getPackage()
.equals(editorPackage);
}
TextChange newLocalChange() {
TextChange tc = searchInEditor() ?
new DocumentChange(getName(), document) :
new TextFileChange(getName(), sourceFile);
tc.setTextType("ceylon");
return tc;
}
protected List<PhasedUnit> getAllUnits() {
List<PhasedUnit> units = new ArrayList<PhasedUnit>();
units.addAll(getUnits(project));
for (IProject p: project.getReferencingProjects()) {
units.addAll(getUnits(p));
}
return units;
}
protected abstract boolean isAffectingOtherFiles();
protected int countDeclarationOccurrences() {
int count = 0;
if (isAffectingOtherFiles()) {
for (PhasedUnit pu: getAllUnits()) {
if (searchInFile(pu)) {
count += countReferences(pu.getCompilationUnit());
}
}
}
if (!isAffectingOtherFiles() || searchInEditor()) {
count += countReferences(rootNode);
}
return count;
}
int countReferences(Tree.CompilationUnit cu) {
return 0;
}
int getSaveMode() {
return isAffectingOtherFiles() ?
RefactoringSaveHelper.SAVE_CEYLON_REFACTORING :
RefactoringSaveHelper.SAVE_NOTHING;
}
public Change createChange(IProgressMonitor pm)
throws CoreException,
OperationCanceledException {
CompositeChange change =
new CompositeChange(getName());
int i=0;
if (isAffectingOtherFiles()) {
List<PhasedUnit> units = getAllUnits();
pm.beginTask(getName(), units.size());
for (PhasedUnit pu: units) {
if (searchInFile(pu)) {
ProjectPhasedUnit ppu =
(ProjectPhasedUnit) pu;
refactorInFile(newTextFileChange(ppu),
change,
pu.getCompilationUnit(),
pu.getTokens());
pm.worked(i++);
}
}
}
else {
pm.beginTask(getName(), 1);
PhasedUnit pu =
editor.getParseController()
.getLastPhasedUnit();
if (searchInFile(pu)) {
ModifiablePhasedUnit ppu =
(ModifiablePhasedUnit) pu;
refactorInFile(newTextFileChange(ppu),
change,
ppu.getCompilationUnit(),
ppu.getTokens());
pm.worked(i++);
}
}
if (searchInEditor()) {
CeylonParseController pc =
editor.getParseController();
refactorInFile(newDocumentChange(),
change,
pc.getLastCompilationUnit(),
pc.getTokens());
pm.worked(i++);
}
pm.done();
return change;
}
abstract void refactorInFile(TextChange textChange,
CompositeChange compositChange,
Tree.CompilationUnit rootNode,
List<CommonToken> tokens);
}