/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2014-2014 AS3Boyan
* Copyright 2014-2014 Elias Ku
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.plugins.haxe.ide.refactoring.memberPullUp;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.plugins.haxe.lang.psi.*;
import com.intellij.plugins.haxe.lang.psi.impl.AbstractHaxePsiClass;
import com.intellij.psi.*;
import com.intellij.refactoring.HelpID;
import com.intellij.refactoring.RefactoringActionHandler;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.classMembers.MemberInfoBase;
import com.intellij.refactoring.lang.ElementsHandler;
import com.intellij.refactoring.ui.ConflictsDialog;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.refactoring.util.classMembers.MemberInfo;
import com.intellij.refactoring.util.classMembers.MemberInfoStorage;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
/**
* Created by as3boyan on 10.09.14.
* Based on https://github.com/JetBrains/intellij-community/blob/master/java/java-impl/src/com/intellij/refactoring/memberPullUp/JavaPullUpHandler.java
*/
public class HaxePullUpHandler implements RefactoringActionHandler, HaxePullUpDialog.Callback, ElementsHandler {
private static final Logger LOG = Logger.getInstance("#com.intellij.plugins.haxe.ide.refactoring.memberPullUp.HaxePullUpHandler");
public static final String REFACTORING_NAME = RefactoringBundle.message("pull.members.up.title");
private PsiClass mySubclass;
private Project myProject;
/*@Override
public boolean checkConflicts(PullUpDialog dialog) {
return false;
}*/
@Override
public boolean isEnabledOnElements(PsiElement[] elements) {
return elements.length == 1 && elements[0] instanceof PsiClass;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext context) {
int offset = editor.getCaretModel().getOffset();
editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
PsiElement element = file.findElementAt(offset);
//HaxeClassDeclaration classDeclaration;
//PsiElement parentElement;
while (true) {
if (element == null || element instanceof PsiFile) {
String message = RefactoringBundle
.getCannotRefactorMessage(RefactoringBundle.message("the.caret.should.be.positioned.inside.a.class.to.pull.members.from"));
CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.MEMBERS_PULL_UP);
return;
}
if (!CommonRefactoringUtil.checkReadOnlyStatus(project, element)) return;
/*classDeclaration = PsiTreeUtil.getParentOfType(element, HaxeClassDeclaration.class, false);
parentElement = null;
parentElement = PsiTreeUtil.getParentOfType(element, HaxeVarDeclaration.class, false);
if (parentElement == null) {
parentElement = PsiTreeUtil.getParentOfType(element, HaxeFunctionDeclarationWithAttributes.class, false);
}*/
if (element instanceof HaxeClassDeclaration || element instanceof HaxeInterfaceDeclaration || element instanceof PsiField || element instanceof PsiMethod) {
invoke(project, new PsiElement[]{element}, context);
return;
}
//if (classDeclaration != null) {
// invoke(project, context, classDeclaration, parentElement);
// return;
//}
element = element.getParent();
}
}
@Override
public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext context) {
if (elements.length != 1) return;
myProject = project;
PsiElement element = elements[0];
PsiClass aClass;
PsiElement aMember = null;
if (element instanceof HaxeClassDeclaration || element instanceof HaxeInterfaceDeclaration) {
aClass = (AbstractHaxePsiClass)element;
}
else if (element instanceof PsiMethod) {
aClass = ((PsiMethod)element).getContainingClass();
aMember = element;
}
else if (element instanceof PsiField) {
aClass = ((PsiField)element).getContainingClass();
aMember = element;
}
else {
return;
}
invoke(project, context, aClass, aMember);
}
private void invoke(Project project, DataContext dataContext, PsiClass psiClass, PsiElement aMember) {
AbstractHaxePsiClass aClass = (AbstractHaxePsiClass)psiClass;
final Editor editor = dataContext != null ? CommonDataKeys.EDITOR.getData(dataContext) : null;
if (aClass == null) {
String message =
RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("is.not.supported.in.the.current.context", REFACTORING_NAME));
CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.MEMBERS_PULL_UP);
return;
}
List<HaxeType> extendsList = aClass.getHaxeExtendsList();
List<HaxeType> implementsList = aClass.getHaxeImplementsList();
if (extendsList.isEmpty() && implementsList.isEmpty()) {
final AbstractHaxePsiClass containingClass = aClass;
if (containingClass != null) {
invoke(project, dataContext, containingClass, aClass);
return;
}
String message = RefactoringBundle.getCannotRefactorMessage(
RefactoringBundle.message("class.does.not.have.base.classes.interfaces.in.current.project", aClass.getQualifiedName()));
CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.MEMBERS_PULL_UP);
return;
}
mySubclass = aClass;
MemberInfoStorage memberInfoStorage = new MemberInfoStorage(mySubclass, new MemberInfo.Filter<PsiMember>() {
@Override
public boolean includeMember(PsiMember element) {
return true;
}
});
List<MemberInfo> members = memberInfoStorage.getClassMemberInfos(mySubclass);
PsiManager manager = mySubclass.getManager();
for (MemberInfoBase<PsiMember> member : members) {
if (manager.areElementsEquivalent(member.getMember(), aMember)) {
member.setChecked(true);
break;
}
}
List<PsiClass> psiClasses = new ArrayList<PsiClass>();
HaxeClassResolveResult result;
HaxeClass haxeClass;
for (int i = 0; i < extendsList.size(); i++) {
result = extendsList.get(i).getReferenceExpression().resolveHaxeClass();
if (result != null) {
haxeClass = result.getHaxeClass();
if (haxeClass != null) {
psiClasses.add(haxeClass);
}
}
}
for (int i = 0; i < implementsList.size(); i++) {
result = implementsList.get(i).getReferenceExpression().resolveHaxeClass();
if (result != null) {
haxeClass = result.getHaxeClass();
if (haxeClass != null) {
psiClasses.add(haxeClass);
}
}
}
final HaxePullUpDialog dialog = new HaxePullUpDialog(project, aClass, psiClasses, memberInfoStorage, this);
dialog.show();
}
@Override
public boolean checkConflicts(final HaxePullUpDialog dialog) {
final List<MemberInfo> infos = dialog.getSelectedMemberInfos();
final MemberInfo[] memberInfos = infos.toArray(new MemberInfo[infos.size()]);
final PsiClass superClass = dialog.getSuperClass();
if (!checkWritable(superClass, memberInfos)) return false;
final MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
//final PsiDirectory targetDirectory = superClass.getContainingFile().getContainingDirectory();
//final PsiPackage targetPackage = targetDirectory != null ? JavaDirectoryService.getInstance().getPackage(targetDirectory) : null;
//conflicts
// .putAllValues(PullUpConflictsUtil.checkConflicts(memberInfos, mySubclass, superClass, targetPackage, targetDirectory,
// dialog.getContainmentVerifier()));
}
});
}
}, RefactoringBundle.message("detecting.possible.conflicts"), true, myProject)) return false;
if (!conflicts.isEmpty()) {
ConflictsDialog conflictsDialog = new ConflictsDialog(myProject, conflicts);
conflictsDialog.show();
final boolean ok = conflictsDialog.isOK();
if (!ok && conflictsDialog.isShowConflicts()) dialog.close(DialogWrapper.CANCEL_EXIT_CODE);
return ok;
}
return true;
}
private boolean checkWritable(PsiClass superClass, MemberInfo[] infos) {
if (!CommonRefactoringUtil.checkReadOnlyStatus(myProject, superClass)) return false;
for (MemberInfo info : infos) {
if (info.getMember() instanceof PsiClass && info.getOverrides() != null) continue;
if (!CommonRefactoringUtil.checkReadOnlyStatus(myProject, info.getMember())) return false;
}
return true;
}
}