com.intellij.refactoring.move.moveInstanceMethod.MoveInstanceMethodHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.refactoring.move.moveInstanceMethod.MoveInstanceMethodHandler.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.
 */
package com.intellij.refactoring.move.moveInstanceMethod;

import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.LangDataKeys;
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.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.SuggestedNameInfo;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.HelpID;
import com.intellij.refactoring.RefactoringActionHandler;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.makeStatic.MakeStaticHandler;
import com.intellij.refactoring.move.MoveInstanceMembersUtil;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;

/**
 * @author ven
 */
public class MoveInstanceMethodHandler implements RefactoringActionHandler {
    private static final Logger LOG = Logger
            .getInstance("#com.intellij.refactoring.move.moveInstanceMethod.MoveInstanceMethodHandler");
    static final String REFACTORING_NAME = RefactoringBundle.message("move.instance.method.title");

    public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) {
        PsiElement element = LangDataKeys.PSI_ELEMENT.getData(dataContext);
        editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
        if (element == null) {
            element = file.findElementAt(editor.getCaretModel().getOffset());
        }

        if (element == null)
            return;
        if (element instanceof PsiIdentifier)
            element = element.getParent();

        if (!(element instanceof PsiMethod)) {
            String message = RefactoringBundle
                    .getCannotRefactorMessage(RefactoringBundle.message("error.wrong.caret.position.method"));
            CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME,
                    HelpID.MOVE_INSTANCE_METHOD);
            return;
        }
        if (LOG.isDebugEnabled()) {
            LOG.debug("Move Instance Method invoked");
        }
        invoke(project, new PsiElement[] { element }, dataContext);
    }

    public void invoke(@NotNull final Project project, @NotNull final PsiElement[] elements,
            final DataContext dataContext) {
        if (elements.length != 1 || !(elements[0] instanceof PsiMethod))
            return;
        final PsiMethod method = (PsiMethod) elements[0];
        String message = null;
        if (!method.getManager().isInProject(method)) {
            message = "Move method is not supported for non-project methods";
        } else if (method.isConstructor()) {
            message = RefactoringBundle.message("move.method.is.not.supported.for.constructors");
        } else if (method.getLanguage() != JavaLanguage.INSTANCE) {
            message = RefactoringBundle.message("move.method.is.not.supported.for.0",
                    method.getLanguage().getDisplayName());
        } else {
            final PsiClass containingClass = method.getContainingClass();
            if (containingClass != null && PsiUtil.typeParametersIterator(containingClass).hasNext()
                    && TypeParametersSearcher.hasTypeParameters(method)) {
                message = RefactoringBundle.message("move.method.is.not.supported.for.generic.classes");
            } else if (method.findSuperMethods().length > 0
                    || OverridingMethodsSearch.search(method, true).toArray(PsiMethod.EMPTY_ARRAY).length > 0) {
                message = RefactoringBundle
                        .message("move.method.is.not.supported.when.method.is.part.of.inheritance.hierarchy");
            } else {
                final Set<PsiClass> classes = MoveInstanceMembersUtil.getThisClassesToMembers(method).keySet();
                for (PsiClass aClass : classes) {
                    /* if (aClass instanceof JspClass) {
                       message = RefactoringBundle.message("synthetic.jsp.class.is.referenced.in.the.method");
                       Editor editor = PlatformDataKeys.EDITOR.getData(dataContext);
                       CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.MOVE_INSTANCE_METHOD);
                       break;
                     }  */
                }
            }
        }
        if (message != null) {
            showErrorHint(project, dataContext, message);
            return;
        }

        final List<PsiVariable> suitableVariables = new ArrayList<PsiVariable>();
        message = collectSuitableVariables(method, suitableVariables);
        if (message != null) {
            final String unableToMakeStaticMessage = MakeStaticHandler.validateTarget(method);
            if (unableToMakeStaticMessage != null) {
                showErrorHint(project, dataContext, message);
            } else {
                final String suggestToMakeStaticMessage = "Would you like to make method \'" + method.getName()
                        + "\' static and then move?";
                if (Messages.showYesNoCancelDialog(project, message + ". " + suggestToMakeStaticMessage,
                        REFACTORING_NAME, Messages.getErrorIcon()) == DialogWrapper.OK_EXIT_CODE) {
                    MakeStaticHandler.invoke(method);
                }
            }
            return;
        }

        new MoveInstanceMethodDialog(method, suitableVariables.toArray(new PsiVariable[suitableVariables.size()]))
                .show();
    }

    private static void showErrorHint(Project project, DataContext dataContext, String message) {
        Editor editor = dataContext == null ? null : PlatformDataKeys.EDITOR.getData(dataContext);
        CommonRefactoringUtil.showErrorHint(project, editor, RefactoringBundle.getCannotRefactorMessage(message),
                REFACTORING_NAME, HelpID.MOVE_INSTANCE_METHOD);
    }

    @Nullable
    private static String collectSuitableVariables(final PsiMethod method,
            final List<PsiVariable> suitableVariables) {
        final List<PsiVariable> allVariables = new ArrayList<PsiVariable>();
        ContainerUtil.addAll(allVariables, method.getParameterList().getParameters());
        ContainerUtil.addAll(allVariables, method.getContainingClass().getFields());
        boolean classTypesFound = false;
        boolean resolvableClassesFound = false;
        boolean classesInProjectFound = false;
        for (PsiVariable variable : allVariables) {
            final PsiType type = variable.getType();
            if (type instanceof PsiClassType && !((PsiClassType) type).hasParameters()) {
                classTypesFound = true;
                final PsiClass psiClass = ((PsiClassType) type).resolve();
                if (psiClass != null && !(psiClass instanceof PsiTypeParameter)) {
                    resolvableClassesFound = true;
                    final boolean inProject = method.getManager().isInProject(psiClass);
                    if (inProject) {
                        classesInProjectFound = true;
                        suitableVariables.add(variable);
                    }
                }
            }
        }

        if (suitableVariables.isEmpty()) {
            if (!classTypesFound) {
                return RefactoringBundle.message("there.are.no.variables.that.have.reference.type");
            } else if (!resolvableClassesFound) {
                return RefactoringBundle.message("all.candidate.variables.have.unknown.types");
            } else if (!classesInProjectFound) {
                return RefactoringBundle.message("all.candidate.variables.have.types.not.in.project");
            }
        }
        return null;
    }

    public static String suggestParameterNameForThisClass(final PsiClass thisClass) {
        PsiManager manager = thisClass.getManager();
        PsiType type = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createType(thisClass);
        final SuggestedNameInfo suggestedNameInfo = JavaCodeStyleManager.getInstance(manager.getProject())
                .suggestVariableName(VariableKind.PARAMETER, null, null, type);
        return suggestedNameInfo.names.length > 0 ? suggestedNameInfo.names[0] : "";
    }

    public static Map<PsiClass, String> suggestParameterNames(final PsiMethod method,
            final PsiVariable targetVariable) {
        final Map<PsiClass, Set<PsiMember>> classesToMembers = MoveInstanceMembersUtil
                .getThisClassesToMembers(method);
        Map<PsiClass, String> result = new LinkedHashMap<PsiClass, String>();
        for (Map.Entry<PsiClass, Set<PsiMember>> entry : classesToMembers.entrySet()) {
            PsiClass aClass = entry.getKey();
            final Set<PsiMember> members = entry.getValue();
            if (members.size() == 1 && members.contains(targetVariable))
                continue;
            result.put(aClass, suggestParameterNameForThisClass(aClass));
        }
        return result;
    }

    private static class TypeParametersSearcher extends PsiTypeVisitor<Boolean> {
        public static boolean hasTypeParameters(PsiElement element) {
            final TypeParametersSearcher searcher = new TypeParametersSearcher();
            final boolean[] hasParameters = new boolean[] { false };
            element.accept(new JavaRecursiveElementWalkingVisitor() {
                @Override
                public void visitTypeElement(PsiTypeElement type) {
                    super.visitTypeElement(type);
                    hasParameters[0] |= type.getType().accept(searcher);
                }
            });
            return hasParameters[0];
        }

        @Override
        public Boolean visitClassType(PsiClassType classType) {
            final PsiClass psiClass = PsiUtil.resolveClassInType(classType);
            if (psiClass instanceof PsiTypeParameter) {
                return Boolean.TRUE;
            }
            return super.visitClassType(classType);
        }

        @Override
        public Boolean visitWildcardType(PsiWildcardType wildcardType) {
            final PsiType bound = wildcardType.getBound();
            if (PsiUtil.resolveClassInType(bound) instanceof PsiTypeParameter) {
                return Boolean.TRUE;
            }
            return super.visitWildcardType(wildcardType);
        }

        @Override
        public Boolean visitType(PsiType type) {
            return Boolean.FALSE;
        }
    }
}