com.intellij.refactoring.memberPullUp.JavaPullUpGenHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.refactoring.memberPullUp.JavaPullUpGenHandler.java

Source

/*
 * Copyright 2000-2009 JetBrains s.r.o.
 *
 * 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.
 */

/*
 * Extended by Julien Cohen (Ascola team, Univ. Nantes), Feb/March 2012.
 * Copyright 2012 Universit de Nantes for those contributions.            
 */

package com.intellij.refactoring.memberPullUp;

import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
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.psi.*;
import com.intellij.refactoring.HelpID;
import com.intellij.refactoring.RefactoringActionHandler;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.lang.ElementsHandler;
import com.intellij.refactoring.ui.ConflictsDialog;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.refactoring.util.RefactoringHierarchyUtil;
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;

public class JavaPullUpGenHandler implements RefactoringActionHandler, PullUpGenDialog.Callback, ElementsHandler {
    private static final Logger LOG = Logger
            .getInstance("#com.intellij.refactoring.memberPullUp.JavaPullUpGenHandler");
    public static final String REFACTORING_NAME = RefactoringBundle.message("pull.members.up.title");
    private PsiClass mySubclass;
    private Project myProject;

    public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) {
        int offset = editor.getCaretModel().getOffset();
        editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
        PsiElement element = file.findElementAt(offset);

        // (rem Julien) Find the containing element (class, field, method)
        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;

            if (element instanceof PsiClass || element instanceof PsiField || element instanceof PsiMethod) {
                invoke(project, new PsiElement[] { element }, dataContext);
                return;
            }
            element = element.getParent();
        }
    }

    public void invoke(@NotNull final Project project, @NotNull PsiElement[] elements, DataContext dataContext) {
        if (elements.length != 1)
            return; // Julien: accept only one element
        myProject = project;

        PsiElement element = elements[0];
        PsiClass aClass;
        PsiElement aMember = null;

        if (element instanceof PsiClass) {
            aClass = (PsiClass) 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, dataContext, aClass, aMember);
    }

    private void invoke(Project project, DataContext dataContext, PsiClass aClass, PsiElement aMember) {
        final Editor editor = dataContext != null ? PlatformDataKeys.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;
        }

        ArrayList<PsiClass> bases = RefactoringHierarchyUtil.createBasesList(aClass, false, true);

        if (bases.isEmpty()) {
            final PsiClass containingClass = aClass.getContainingClass();
            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>() {
            public boolean includeMember(PsiMember element) {
                return true;
            }
        });
        List<MemberInfo> members = memberInfoStorage.getClassMemberInfos(mySubclass);
        PsiManager manager = mySubclass.getManager();

        MemberInfo m = members.get(0); // J : default value
        for (MemberInfo member : members) {
            if (manager.areElementsEquivalent(member.getMember(), aMember)) {
                member.setChecked(true); /* rem Julien : checks the checkbox of the user-selected member */
                member.setToAbstract(
                        true); /* Added by julien : selected methods become abstract in order to introduce  the type variable. */
                // TODO (Julien) : ideally should have member.setGenerify(true), but impossible here.
                m = member;
                break;
            }
        }

        final PullUpGenDialog dialog = new PullUpGenDialog(project, aClass, bases, memberInfoStorage, this);

        dialog.show();
    }

    public boolean checkConflicts(final PullUpGenDialog 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() {
            public void run() {
                final PsiDirectory targetDirectory = superClass.getContainingFile().getContainingDirectory();
                final PsiPackage targetPackage = targetDirectory != null
                        ? JavaDirectoryService.getInstance().getPackage(targetDirectory)
                        : null;
                conflicts.putAllValues(PullUpGenConflictsUtil.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;
    }

    public boolean isEnabledOnElements(PsiElement[] elements) {
        /*
        if (elements.length == 1) {
          return elements[0] instanceof PsiClass || elements[0] instanceof PsiField || elements[0] instanceof PsiMethod;
        }
        else if (elements.length > 1){
          for (int  idx = 0;  idx < elements.length;  idx++) {
            PsiElement element = elements[idx];
            if (!(element instanceof PsiField || element instanceof PsiMethod)) return false;
          }
          return true;
        }
        return false;
        */
        // todo: multiple selection etc
        return elements.length == 1 && elements[0] instanceof PsiClass;
    }
}