/*
 * Copyright 2000-2011 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.codeInsight.daemon.impl.quickfix;

import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.navigation.NavigationUtil;
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
import com.intellij.ide.util.PsiClassListCellRenderer;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.PsiElementProcessor;
import com.intellij.refactoring.extractInterface.ExtractInterfaceHandler;
import com.intellij.refactoring.extractSuperclass.ExtractSuperclassHandler;
import com.intellij.refactoring.memberPullUp.JavaPullUpHandler;
import com.intellij.refactoring.memberPullUp.PullUpHelper;
import com.intellij.refactoring.util.DocCommentPolicy;
import com.intellij.refactoring.util.classMembers.MemberInfo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.LinkedHashSet;

public class PullAsAbstractUpFix extends LocalQuickFixAndIntentionActionOnPsiElement {
  private static final Logger LOG = Logger.getInstance("#" + PullAsAbstractUpFix.class.getName());
  private String myName;

  public PullAsAbstractUpFix(PsiMethod psiMethod, final String name) {
    super(psiMethod);
    myName = name;
  }

  @Override
  @NotNull
  public String getText() {
    return myName;
  }

  @Override
  @NotNull
  public String getFamilyName() {
    return "Pull up";
  }

  @Override
  public boolean isAvailable(@NotNull Project project,
                             @NotNull PsiFile file,
                             @NotNull PsiElement startElement,
                             @NotNull PsiElement endElement) {
    return startElement instanceof PsiMethod && startElement.isValid() && ((PsiMethod)startElement).getContainingClass() != null;
  }

  @Override
  public void invoke(@NotNull Project project,
                     @NotNull PsiFile file,
                     @Nullable("is null when called from inspection") Editor editor,
                     @NotNull PsiElement startElement,
                     @NotNull PsiElement endElement) {
    final PsiMethod method = (PsiMethod)startElement;
    if (!CodeInsightUtilBase.prepareFileForWrite(method.getContainingFile())) return;

    final PsiClass containingClass = method.getContainingClass();
    LOG.assertTrue(containingClass != null);

    PsiManager manager = containingClass.getManager();
    if (containingClass instanceof PsiAnonymousClass) {
      final PsiClassType baseClassType = ((PsiAnonymousClass)containingClass).getBaseClassType();
      final PsiClass baseClass = baseClassType.resolve();
      if (baseClass != null && manager.isInProject(baseClass)) {
        pullUp(method, containingClass, baseClass);
      }
    }
    else {
      final LinkedHashSet<PsiClass> classesToPullUp = new LinkedHashSet<PsiClass>();
      collectClassesToPullUp(manager, classesToPullUp, containingClass.getExtendsListTypes());
      collectClassesToPullUp(manager, classesToPullUp, containingClass.getImplementsListTypes());

      if (classesToPullUp.size() == 0) {
        //check visibility
        new ExtractInterfaceHandler().invoke(project, new PsiElement[]{containingClass}, null);
      }
      else if (classesToPullUp.size() == 1) {
        pullUp(method, containingClass, classesToPullUp.iterator().next());
      }
      else if (editor != null) {
        NavigationUtil.getPsiElementPopup(classesToPullUp.toArray(new PsiClass[classesToPullUp.size()]), new PsiClassListCellRenderer(),
                                          "Choose super class",
                                          new PsiElementProcessor<PsiClass>() {
                                            @Override
                                            public boolean execute(@NotNull PsiClass aClass) {
                                              pullUp(method, containingClass, aClass);
                                              return false;
                                            }
                                          }, classesToPullUp.iterator().next()).showInBestPositionFor(editor);
      }
    }
  }


  private static void collectClassesToPullUp(PsiManager manager, LinkedHashSet<PsiClass> classesToPullUp, PsiClassType[] extendsListTypes) {
    for (PsiClassType extendsListType : extendsListTypes) {
      PsiClass resolve = extendsListType.resolve();
      if (resolve != null && manager.isInProject(resolve)) {
        classesToPullUp.add(resolve);
      }
    }
  }

  private static void pullUp(PsiMethod method, PsiClass containingClass, PsiClass baseClass) {
    final MemberInfo memberInfo = new MemberInfo(method);
    memberInfo.setChecked(true);
    memberInfo.setToAbstract(true);
    new PullUpHelper(containingClass, baseClass, new MemberInfo[]{memberInfo}, new DocCommentPolicy(DocCommentPolicy.ASIS)).run();
  }

  @Override
  public boolean startInWriteAction() {
    return false;
  }

  public static void registerQuickFix(HighlightInfo highlightInfo, PsiMethod methodWithOverrides) {
    PsiClass containingClass = methodWithOverrides.getContainingClass();
    if (containingClass == null) return;
    final PsiManager manager = containingClass.getManager();

    boolean canBePulledUp = true;
    String name = "Pull method \'" + methodWithOverrides.getName() + "\' up";
    if (containingClass instanceof PsiAnonymousClass) {
      final PsiClassType baseClassType = ((PsiAnonymousClass)containingClass).getBaseClassType();
      final PsiClass baseClass = baseClassType.resolve();
      if (baseClass == null) return;
      if (!manager.isInProject(baseClass)) return;
      if (!baseClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
        name = "Pull method \'" + methodWithOverrides.getName() + "\' up and make it abstract";
      }
    } else {
      final LinkedHashSet<PsiClass> classesToPullUp = new LinkedHashSet<PsiClass>();
      collectClassesToPullUp(manager, classesToPullUp, containingClass.getExtendsListTypes());
      collectClassesToPullUp(manager, classesToPullUp, containingClass.getImplementsListTypes());
      if (classesToPullUp.size() == 0) {
        name = "Extract method \'" + methodWithOverrides.getName() + "\' to new interface";
        canBePulledUp = false;
      } else if (classesToPullUp.size() == 1) {
        final PsiClass baseClass = classesToPullUp.iterator().next();
        name = "Pull method \'" + methodWithOverrides.getName() + "\' to \'" + baseClass.getName() + "\'";
        if (!baseClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
           name+= " and make it abstract";
        }
      }
      QuickFixAction.registerQuickFixAction(highlightInfo, new RunRefactoringIntention(new ExtractInterfaceHandler(), "Extract interface"));
      QuickFixAction.registerQuickFixAction(highlightInfo, new RunRefactoringIntention(new ExtractSuperclassHandler(), "Extract superclass"));
    }


    if (canBePulledUp) {
      QuickFixAction.registerQuickFixAction(highlightInfo, new RunRefactoringIntention(new JavaPullUpHandler(), "Pull members up"));
    }
    QuickFixAction.registerQuickFixAction(highlightInfo, new PullAsAbstractUpFix(methodWithOverrides, name));
  }
}

@@ -5337,24 +5337,113 @@
 aseClass) %7B%0A
+    if (!CodeInsightUtilBase.prepareFileForWrite(baseClass.getContainingFile())) return;%0A
     final Me

check for writable: EA-33580 - IOE: CheckUtil.checkWritable