- /*
- * 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.generation;
-
- import com.intellij.codeInsight.AnnotationUtil;
- import com.intellij.codeInsight.CodeInsightActionHandler;
- import com.intellij.codeInsight.CodeInsightBundle;
- import com.intellij.codeInsight.MethodImplementor;
- import com.intellij.codeInsight.intention.AddAnnotationFix;
- import com.intellij.featureStatistics.FeatureUsageTracker;
- import com.intellij.featureStatistics.ProductivityFeatureNames;
- import com.intellij.icons.AllIcons;
- import com.intellij.ide.fileTemplates.FileTemplate;
- import com.intellij.ide.fileTemplates.FileTemplateManager;
- import com.intellij.ide.fileTemplates.FileTemplateUtil;
- import com.intellij.ide.fileTemplates.JavaTemplateUtil;
- import com.intellij.ide.util.MemberChooser;
- import com.intellij.ide.util.PropertiesComponent;
- import com.intellij.lang.java.JavaLanguage;
- import com.intellij.openapi.actionSystem.*;
- import com.intellij.openapi.application.ApplicationManager;
- import com.intellij.openapi.application.Result;
- import com.intellij.openapi.command.WriteCommandAction;
- import com.intellij.openapi.diagnostic.Logger;
- import com.intellij.openapi.editor.Editor;
- import com.intellij.openapi.editor.ScrollType;
- import com.intellij.openapi.extensions.Extensions;
- import com.intellij.openapi.fileEditor.FileEditorManager;
- import com.intellij.openapi.fileEditor.OpenFileDescriptor;
- import com.intellij.openapi.fileTypes.FileType;
- import com.intellij.openapi.fileTypes.FileTypeManager;
- import com.intellij.openapi.keymap.Keymap;
- import com.intellij.openapi.keymap.KeymapManager;
- import com.intellij.openapi.module.Module;
- import com.intellij.openapi.module.ModuleUtil;
- import com.intellij.openapi.project.Project;
- import com.intellij.openapi.ui.DialogWrapper;
- import com.intellij.openapi.ui.Messages;
- import com.intellij.openapi.util.Ref;
- import com.intellij.openapi.util.text.StringUtil;
- import com.intellij.psi.*;
- import com.intellij.psi.codeStyle.CodeStyleManager;
- import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
- import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
- import com.intellij.psi.codeStyle.JavaCodeStyleManager;
- import com.intellij.psi.impl.source.jsp.jspJava.JspClass;
- import com.intellij.psi.infos.CandidateInfo;
- import com.intellij.psi.javadoc.PsiDocComment;
- import com.intellij.psi.search.GlobalSearchScope;
- import com.intellij.psi.util.*;
- import com.intellij.util.ArrayUtil;
- import com.intellij.util.Function;
- import com.intellij.util.IncorrectOperationException;
- import com.intellij.util.containers.ContainerUtil;
- import org.jetbrains.annotations.NonNls;
- import org.jetbrains.annotations.NotNull;
- import org.jetbrains.annotations.Nullable;
-
- import javax.swing.*;
- import java.awt.event.ActionEvent;
- import java.awt.event.InputEvent;
- import java.awt.event.KeyEvent;
- import java.util.*;
-
- public class OverrideImplementUtil {
- private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.generation.OverrideImplementUtil");
-
- @NonNls private static final String PROP_COMBINED_OVERRIDE_IMPLEMENT = "OverrideImplement.combined";
-
- private OverrideImplementUtil() {
- }
-
- @NotNull
- public static Collection<CandidateInfo> getMethodsToOverrideImplement(PsiClass aClass, boolean toImplement) {
- return getMapToOverrideImplement(aClass, toImplement).values();
- }
-
- @NotNull
- public static Collection<MethodSignature> getMethodSignaturesToImplement(@NotNull PsiClass aClass) {
- return getMapToOverrideImplement(aClass, true).keySet();
- }
-
- @NotNull
- public static Collection<MethodSignature> getMethodSignaturesToOverride(@NotNull PsiClass aClass) {
- return getMapToOverrideImplement(aClass, false).keySet();
- }
-
- @NotNull
- private static Map<MethodSignature, CandidateInfo> getMapToOverrideImplement(PsiClass aClass, boolean toImplement) {
- Map<MethodSignature, PsiMethod> abstracts = new LinkedHashMap<MethodSignature,PsiMethod>();
- Map<MethodSignature, PsiMethod> finals = new LinkedHashMap<MethodSignature,PsiMethod>();
- Map<MethodSignature, PsiMethod> concretes = new LinkedHashMap<MethodSignature,PsiMethod>();
-
- LOG.assertTrue(aClass.isValid());
- Collection<HierarchicalMethodSignature> allMethodSigs = aClass.getVisibleSignatures();
- PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(aClass.getProject()).getResolveHelper();
- for (HierarchicalMethodSignature signature : allMethodSigs) {
- PsiMethod method = signature.getMethod();
- LOG.assertTrue(method.isValid(), aClass);
-
- if (method.hasModifierProperty(PsiModifier.STATIC) || !resolveHelper.isAccessible(method, aClass, aClass)) continue;
- PsiClass hisClass = method.getContainingClass();
- if (hisClass == null) continue;
- // filter non-immediate super constructors
- if (method.isConstructor() && (!aClass.isInheritor(hisClass, false) || aClass instanceof PsiAnonymousClass || aClass.isEnum())) {
- continue;
- }
- // filter already implemented
- if (MethodSignatureUtil.findMethodBySignature(aClass, signature, false) != null) {
- continue;
- }
-
- if (method.hasModifierProperty(PsiModifier.FINAL)) {
- finals.put(signature, method);
- continue;
- }
-
- Map<MethodSignature, PsiMethod> map = hisClass.isInterface() || method.hasModifierProperty(PsiModifier.ABSTRACT) ? abstracts : concretes;
- PsiMethod other = map.get(signature);
- if (other == null || preferLeftForImplement(method, other)) {
- map.put(signature, method);
- }
- }
-
- final Map<MethodSignature, CandidateInfo> result = new TreeMap<MethodSignature,CandidateInfo>(new MethodSignatureComparator());
- if (toImplement || aClass.isInterface()) {
- collectMethodsToImplement(aClass, abstracts, finals, concretes, result);
- }
- else {
- for (Map.Entry<MethodSignature, PsiMethod> entry : concretes.entrySet()) {
- MethodSignature signature = entry.getKey();
- PsiMethod concrete = entry.getValue();
- if (finals.get(signature) == null) {
- PsiMethod abstractOne = abstracts.get(signature);
- if (abstractOne == null || !abstractOne.getContainingClass().isInheritor(concrete.getContainingClass(), true) ||
- CommonClassNames.JAVA_LANG_OBJECT.equals(concrete.getContainingClass().getQualifiedName())) {
- PsiSubstitutor subst = GenerateMembersUtil.correctSubstitutor(concrete, signature.getSubstitutor());
- CandidateInfo info = new CandidateInfo(concrete, subst);
- result.put(signature, info);
- }
- }
- }
- }
-
- return result;
- }
-
- public static void collectMethodsToImplement(PsiClass aClass,
- Map<MethodSignature, PsiMethod> abstracts,
- Map<MethodSignature, PsiMethod> finals,
- Map<MethodSignature, PsiMethod> concretes,
- Map<MethodSignature, CandidateInfo> result) {
- for (Map.Entry<MethodSignature, PsiMethod> entry : abstracts.entrySet()) {
- MethodSignature signature = entry.getKey();
- PsiMethod abstractOne = entry.getValue();
- PsiMethod concrete = concretes.get(signature);
- if (concrete == null
- || PsiUtil.getAccessLevel(concrete.getModifierList()) < PsiUtil.getAccessLevel(abstractOne.getModifierList())
- || !abstractOne.getContainingClass().isInterface() && abstractOne.getContainingClass().isInheritor(concrete.getContainingClass(), true)) {
- if (finals.get(signature) == null) {
- PsiSubstitutor subst = GenerateMembersUtil.correctSubstitutor(abstractOne, signature.getSubstitutor());
- CandidateInfo info = new CandidateInfo(abstractOne, subst);
- result.put(signature, info);
- }
- }
- }
-
- for (final MethodImplementor implementor : getImplementors()) {
- for (final PsiMethod method : implementor.getMethodsToImplement(aClass)) {
- MethodSignature signature = MethodSignatureUtil.createMethodSignature(method.getName(), method.getParameterList(),
- method.getTypeParameterList(), PsiSubstitutor.EMPTY, method.isConstructor());
- CandidateInfo info = new CandidateInfo(method, PsiSubstitutor.EMPTY);
- result.put(signature, info);
- }
- }
- }
-
- private static boolean preferLeftForImplement(PsiMethod left, PsiMethod right) {
- if (PsiUtil.getAccessLevel(left.getModifierList()) > PsiUtil.getAccessLevel(right.getModifierList())) return true;
- if (!left.getContainingClass().isInterface()) return true;
- if (!right.getContainingClass().isInterface()) return false;
- // implement annotated method
- PsiAnnotation[] leftAnnotations = left.getModifierList().getAnnotations();
- PsiAnnotation[] rightAnnotations = right.getModifierList().getAnnotations();
- return leftAnnotations.length > rightAnnotations.length;
- }
-
- private static MethodImplementor[] getImplementors() {
- return Extensions.getExtensions(MethodImplementor.EXTENSION_POINT_NAME);
- }
-
- /**
- * generate methods (with bodies) corresponding to given method declaration
- * there are maybe two method implementations for one declaration
- * (e.g. EJB' create() -> ejbCreate(), ejbPostCreate() )
- * @param aClass context for method implementations
- * @param method method to override or implement
- * @param toCopyJavaDoc true if copy JavaDoc from method declaration
- * @return list of method prototypes
- */
- @NotNull
- public static Collection<PsiMethod> overrideOrImplementMethod(PsiClass aClass, PsiMethod method, boolean toCopyJavaDoc) throws IncorrectOperationException {
- final PsiClass containingClass = method.getContainingClass();
- LOG.assertTrue(containingClass != null);
- PsiSubstitutor substitutor = aClass.isInheritor(containingClass, true)
- ? TypeConversionUtil.getSuperClassSubstitutor(containingClass, aClass, PsiSubstitutor.EMPTY)
- : PsiSubstitutor.EMPTY;
- return overrideOrImplementMethod(aClass, method, substitutor, toCopyJavaDoc, CodeStyleSettingsManager.getSettings(aClass.getProject()).INSERT_OVERRIDE_ANNOTATION);
- }
-
- public static boolean isInsertOverride(PsiMethod superMethod, PsiClass targetClass) {
- if (!CodeStyleSettingsManager.getSettings(targetClass.getProject()).INSERT_OVERRIDE_ANNOTATION) {
- return false;
- }
- return canInsertOverride(superMethod, targetClass);
- }
-
- public static boolean canInsertOverride(PsiMethod superMethod, PsiClass targetClass) {
- if (superMethod.isConstructor() || superMethod.hasModifierProperty(PsiModifier.STATIC)) {
- return false;
- }
- if (!PsiUtil.isLanguageLevel5OrHigher(targetClass)) {
- return false;
- }
- if (PsiUtil.isLanguageLevel6OrHigher(targetClass)) return true;
- if (targetClass.isInterface()) return true;
- PsiClass superClass = superMethod.getContainingClass();
- return !superClass.isInterface();
- }
-
- @NotNull
- private static Collection<PsiMethod> overrideOrImplementMethod(PsiClass aClass,
- PsiMethod method,
- PsiSubstitutor substitutor,
- boolean toCopyJavaDoc,
- boolean insertOverrideIfPossible) throws IncorrectOperationException {
- if (!method.isValid() || !substitutor.isValid()) return Collections.emptyList();
-
- List<PsiMethod> results = new ArrayList<PsiMethod>();
- for (final MethodImplementor implementor : getImplementors()) {
- final PsiMethod[] prototypes = implementor.createImplementationPrototypes(aClass, method);
- if (implementor.isBodyGenerated()) {
- ContainerUtil.addAll(results, prototypes);
- }
- else {
- for (PsiMethod prototype : prototypes) {
- results.add(decorateMethod(aClass, method, toCopyJavaDoc, insertOverrideIfPossible, prototype));
- }
- }
- }
- if (results.isEmpty()) {
- PsiMethod method1 = GenerateMembersUtil.substituteGenericMethod(method, substitutor, aClass);
-
- PsiElementFactory factory = JavaPsiFacade.getInstance(method.getProject()).getElementFactory();
- PsiMethod result = (PsiMethod)factory.createClass("Dummy").add(method1);
- if (result instanceof PsiAnnotationMethod) {
- PsiAnnotationMemberValue defaultValue = ((PsiAnnotationMethod)result).getDefaultValue();
- if (defaultValue != null) {
- PsiElement defaultKeyword = defaultValue;
- while (!(defaultKeyword instanceof PsiKeyword) && defaultKeyword != null) {
- defaultKeyword = defaultKeyword.getPrevSibling();
- }
- if (defaultKeyword == null) defaultKeyword = defaultValue;
- defaultValue.getParent().deleteChildRange(defaultKeyword, defaultValue);
- }
- }
- results.add(decorateMethod(aClass, method, toCopyJavaDoc, insertOverrideIfPossible, result));
- }
-
- for (Iterator<PsiMethod> iterator = results.iterator(); iterator.hasNext();) {
- if (aClass.findMethodBySignature(iterator.next(), false) != null) {
- iterator.remove();
- }
- }
-
- return results;
- }
-
- private static PsiMethod decorateMethod(PsiClass aClass,
- PsiMethod method,
- boolean toCopyJavaDoc,
- boolean insertOverrideIfPossible,
- PsiMethod result) {
- PsiUtil.setModifierProperty(result, PsiModifier.ABSTRACT, aClass.isInterface());
- PsiUtil.setModifierProperty(result, PsiModifier.NATIVE, false);
-
- if (!toCopyJavaDoc){
- PsiDocComment comment = result.getDocComment();
- if (comment != null){
- comment.delete();
- }
- }
-
- //method type params are not allowed when overriding from raw type
- final PsiTypeParameterList list = result.getTypeParameterList();
- if (list != null) {
- final PsiClass containingClass = method.getContainingClass();
- if (containingClass != null) {
- for (PsiClassType classType : aClass.getSuperTypes()) {
- if (InheritanceUtil.isInheritorOrSelf(PsiUtil.resolveClassInType(classType), containingClass, true) && classType.isRaw()) {
- list.replace(JavaPsiFacade.getElementFactory(aClass.getProject()).createTypeParameterList());
- break;
- }
- }
- }
- }
-
- annotateOnOverrideImplement(result, aClass, method, insertOverrideIfPossible);
-
- if (CodeStyleSettingsManager.getSettings(aClass.getProject()).REPEAT_SYNCHRONIZED && method.hasModifierProperty(PsiModifier.SYNCHRONIZED)) {
- result.getModifierList().setModifierProperty(PsiModifier.SYNCHRONIZED, true);
- }
-
- final PsiCodeBlock body = JavaPsiFacade.getInstance(method.getProject()).getElementFactory().createCodeBlockFromText("{}", null);
- PsiCodeBlock oldbody = result.getBody();
- if (oldbody != null){
- oldbody.replace(body);
- }
- else{
- result.add(body);
- }
-
- setupMethodBody(result, method, aClass);
-
- // probably, it's better to reformat the whole method - it can go from other style sources
- final Project project = method.getProject();
- CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
- CommonCodeStyleSettings javaSettings = CodeStyleSettingsManager.getSettings(project).getCommonSettings(JavaLanguage.INSTANCE);
- boolean keepBreaks = javaSettings.KEEP_LINE_BREAKS;
- javaSettings.KEEP_LINE_BREAKS = false;
- result = (PsiMethod)JavaCodeStyleManager.getInstance(project).shortenClassReferences(result);
- result = (PsiMethod)codeStyleManager.reformat(result);
- javaSettings.KEEP_LINE_BREAKS = keepBreaks;
- return result;
- }
-
- public static void annotateOnOverrideImplement(PsiMethod method, PsiClass targetClass, PsiMethod overridden) {
- annotateOnOverrideImplement(method, targetClass, overridden,
- CodeStyleSettingsManager.getSettings(method.getProject()).INSERT_OVERRIDE_ANNOTATION);
- }
-
- public static void annotateOnOverrideImplement(PsiMethod method, PsiClass targetClass, PsiMethod overridden, boolean insertOverride) {
- if (insertOverride && canInsertOverride(overridden, targetClass)) {
- annotate(method, Override.class.getName());
- }
- final Module module = ModuleUtil.findModuleForPsiElement(targetClass);
- final GlobalSearchScope moduleScope = module != null ? GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module) : null;
- final Project project = targetClass.getProject();
- final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
- for (OverrideImplementsAnnotationsHandler each : Extensions.getExtensions(OverrideImplementsAnnotationsHandler.EP_NAME)) {
- for (String annotation : each.getAnnotations(project)) {
- if (moduleScope != null && facade.findClass(annotation, moduleScope) == null) continue;
- if (AnnotationUtil.isAnnotated(overridden, annotation, false)) {
- annotate(method, annotation, each.annotationsToRemove(project, annotation));
- }
- }
- }
- }
-
- public static void annotate(@NotNull PsiMethod result, String fqn, String... annosToRemove) throws IncorrectOperationException {
- Project project = result.getProject();
- AddAnnotationFix fix = new AddAnnotationFix(fqn, result, annosToRemove);
- if (fix.isAvailable(project, null, result.getContainingFile())) {
- fix.invoke(project, null, result.getContainingFile());
- }
- }
-
- public static boolean isOverridable(PsiMethod method) {
- return !method.isConstructor()
- && !method.hasModifierProperty(PsiModifier.STATIC)
- && !method.hasModifierProperty(PsiModifier.FINAL)
- && !method.hasModifierProperty(PsiModifier.PRIVATE);
- }
-
- @NotNull
- public static List<PsiGenerationInfo<PsiMethod>> overrideOrImplementMethods(PsiClass aClass,
- Collection<PsiMethodMember> candidates,
- boolean toCopyJavaDoc,
- boolean toInsertAtOverride)
- throws IncorrectOperationException {
- List<CandidateInfo> candidateInfos = ContainerUtil.map2List(candidates, new Function<PsiMethodMember, CandidateInfo>() {
- public CandidateInfo fun(final PsiMethodMember s) {
- return new CandidateInfo(s.getElement(), s.getSubstitutor());
- }
- });
- final List<PsiMethod> methods = overrideOrImplementMethodCandidates(aClass, candidateInfos, toCopyJavaDoc, toInsertAtOverride);
- return convert2GenerationInfos(methods);
- }
-
- @NotNull
- public static List<PsiMethod> overrideOrImplementMethodCandidates(PsiClass aClass,
- Collection<CandidateInfo> candidates,
- boolean toCopyJavaDoc,
- boolean insertOverrideWherePossible) throws IncorrectOperationException {
- List<PsiMethod> result = new ArrayList<PsiMethod>();
- for (CandidateInfo candidateInfo : candidates) {
- result.addAll(overrideOrImplementMethod(aClass, (PsiMethod)candidateInfo.getElement(), candidateInfo.getSubstitutor(),
- toCopyJavaDoc, insertOverrideWherePossible));
- }
- return result;
- }
-
- public static List<PsiGenerationInfo<PsiMethod>> convert2GenerationInfos(final Collection<PsiMethod> methods) {
- return ContainerUtil.map2List(methods, new Function<PsiMethod, PsiGenerationInfo<PsiMethod>>() {
- public PsiGenerationInfo<PsiMethod> fun(final PsiMethod s) {
- return createGenerationInfo(s);
- }
- });
- }
-
- public static PsiGenerationInfo<PsiMethod> createGenerationInfo(PsiMethod s) {
- return createGenerationInfo(s, true);
- }
-
- public static PsiGenerationInfo<PsiMethod> createGenerationInfo(PsiMethod s, boolean mergeIfExists) {
- for (MethodImplementor implementor : getImplementors()) {
- final GenerationInfo info = implementor.createGenerationInfo(s, mergeIfExists);
- if (info instanceof PsiGenerationInfo) return (PsiGenerationInfo<PsiMethod>)info;
- }
- return new PsiGenerationInfo<PsiMethod>(s);
- }
-
- @NotNull
- public static String callSuper (PsiMethod superMethod, PsiMethod overriding) {
- @NonNls StringBuilder buffer = new StringBuilder();
- if (!superMethod.isConstructor() && superMethod.getReturnType() != PsiType.VOID) {
- buffer.append("return ");
- }
- buffer.append("super");
- PsiParameter[] parms = overriding.getParameterList().getParameters();
- if (!superMethod.isConstructor()){
- buffer.append(".");
- buffer.append(superMethod.getName());
- }
- buffer.append("(");
- for (int i = 0; i < parms.length; i++) {
- String name = parms[i].getName();
- if (i > 0) buffer.append(",");
- buffer.append(name);
- }
- buffer.append(")");
- return buffer.toString();
- }
-
- public static void setupMethodBody(PsiMethod result, PsiMethod originalMethod, PsiClass targetClass) throws IncorrectOperationException {
- String templName = originalMethod.hasModifierProperty(PsiModifier.ABSTRACT) ?
- JavaTemplateUtil.TEMPLATE_IMPLEMENTED_METHOD_BODY : JavaTemplateUtil.TEMPLATE_OVERRIDDEN_METHOD_BODY;
- FileTemplate template = FileTemplateManager.getInstance().getCodeTemplate(templName);
- setupMethodBody(result, originalMethod, targetClass, template);
- }
-
- public static void setupMethodBody(final PsiMethod result, final PsiMethod originalMethod, final PsiClass targetClass,
- final FileTemplate template) throws IncorrectOperationException {
- if (targetClass.isInterface()) {
- final PsiCodeBlock body = result.getBody();
- if (body != null) body.delete();
- }
-
- FileType fileType = FileTypeManager.getInstance().getFileTypeByExtension(template.getExtension());
- PsiType returnType = result.getReturnType();
- if (returnType == null) {
- returnType = PsiType.VOID;
- }
- Properties properties = new Properties();
- properties.setProperty(FileTemplate.ATTRIBUTE_RETURN_TYPE, returnType.getPresentableText());
- properties.setProperty(FileTemplate.ATTRIBUTE_DEFAULT_RETURN_VALUE, PsiTypesUtil.getDefaultValueOfType(returnType));
- properties.setProperty(FileTemplate.ATTRIBUTE_CALL_SUPER, callSuper(originalMethod, result));
- JavaTemplateUtil.setClassAndMethodNameProperties(properties, targetClass, result);
-
- JVMElementFactory factory = JVMElementFactories.getFactory(targetClass.getLanguage(), originalMethod.getProject());
- if (factory == null) factory = JavaPsiFacade.getInstance(originalMethod.getProject()).getElementFactory();
- @NonNls String methodText;
- try {
- String bodyText = template.getText(properties);
- if (bodyText != null && !bodyText.isEmpty()) bodyText += "\n";
- methodText = "void foo () {\n" + bodyText + "}";
- methodText = FileTemplateUtil.indent(methodText, result.getProject(), fileType);
- } catch (Exception e) {
- throw new IncorrectOperationException("Failed to parse file template",e);
- }
- if (methodText != null) {
- PsiMethod m;
- try {
- m = factory.createMethodFromText(methodText, originalMethod);
- }
- catch (IncorrectOperationException e) {
- ApplicationManager.getApplication().invokeLater(new Runnable() {
- public void run() {
- Messages.showErrorDialog(CodeInsightBundle.message("override.implement.broken.file.template.message"),
- CodeInsightBundle.message("override.implement.broken.file.template.title"));
- }
- });
- return;
- }
- PsiCodeBlock oldBody = result.getBody();
- if (oldBody != null) {
- oldBody.replace(m.getBody());
- }
- }
- }
-
- public static void chooseAndOverrideMethods(Project project, Editor editor, PsiClass aClass){
- FeatureUsageTracker.getInstance().triggerFeatureUsed(ProductivityFeatureNames.CODEASSISTS_OVERRIDE_IMPLEMENT);
- chooseAndOverrideOrImplementMethods(project, editor, aClass, false);
- }
-
- public static void chooseAndImplementMethods(Project project, Editor editor, PsiClass aClass){
- FeatureUsageTracker.getInstance().triggerFeatureUsed(ProductivityFeatureNames.CODEASSISTS_OVERRIDE_IMPLEMENT);
- chooseAndOverrideOrImplementMethods(project, editor, aClass, true);
- }
-
- public static void chooseAndOverrideOrImplementMethods(final Project project,
- final Editor editor,
- final PsiClass aClass,
- final boolean toImplement){
- LOG.assertTrue(aClass.isValid());
- ApplicationManager.getApplication().assertReadAccessAllowed();
-
- Collection<CandidateInfo> candidates = getMethodsToOverrideImplement(aClass, toImplement);
- Collection<CandidateInfo> secondary = toImplement || aClass.isInterface() ? Collections.<CandidateInfo>emptyList() : getMethodsToOverrideImplement(aClass, true);
-
- final MemberChooser<PsiMethodMember> chooser = showOverrideImplementChooser(editor, aClass, toImplement, candidates, secondary);
- if (chooser == null) return;
-
- final List<PsiMethodMember> selectedElements = chooser.getSelectedElements();
- if (selectedElements == null || selectedElements.isEmpty()) return;
-
- LOG.assertTrue(aClass.isValid());
- new WriteCommandAction(project, aClass.getContainingFile()) {
- protected void run(final Result result) throws Throwable {
- overrideOrImplementMethodsInRightPlace(editor, aClass, selectedElements, chooser.isCopyJavadoc(), chooser.isInsertOverrideAnnotation());
- }
- }.execute();
- }
-
- @Nullable
- public static MemberChooser<PsiMethodMember> showOverrideImplementChooser(Editor editor,
- final PsiElement aClass,
- final boolean toImplement,
- Collection<CandidateInfo> candidates,
- Collection<CandidateInfo> secondary) {
- Project project = aClass.getProject();
- if (candidates.isEmpty() && secondary.isEmpty()) return null;
-
- final PsiMethodMember[] onlyPrimary = convertToMethodMembers(candidates);
- final PsiMethodMember[] all = ArrayUtil.mergeArrays(onlyPrimary, convertToMethodMembers(secondary));
-
- final String toMerge = PropertiesComponent.getInstance(project).getValue(PROP_COMBINED_OVERRIDE_IMPLEMENT);
- final Ref<Boolean> merge = Ref.create(!"false".equals(toMerge));
-
- final boolean isAll = merge.get().booleanValue();
- final MemberChooser<PsiMethodMember> chooser = new MemberChooser<PsiMethodMember>(isAll ? all : onlyPrimary, false, true, project,
- PsiUtil.isLanguageLevel5OrHigher(aClass)) {
-
- @Override
- protected void fillToolbarActions(DefaultActionGroup group) {
- super.fillToolbarActions(group);
- if (toImplement) return;
-
- final ToggleAction mergeAction = new ToggleAction("Show methods to implement", "Show methods to implement",
- AllIcons.General.Show_to_implement) {
- @Override
- public boolean isSelected(AnActionEvent e) {
- return merge.get().booleanValue();
- }
-
- @Override
- public void setSelected(AnActionEvent e, boolean state) {
- merge.set(state);
- resetElements(state ? all : onlyPrimary);
- setTitle(getChooserTitle(false, merge));
- }
- };
- mergeAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_I, InputEvent.ALT_MASK)), myTree);
-
- Shortcut[] shortcuts = KeymapManager.getInstance().getActiveKeymap().getShortcuts("OverrideMethods");
- mergeAction.registerCustomShortcutSet(new CustomShortcutSet(shortcuts), myTree);
-
- group.add(mergeAction);
- }
- };
- chooser.setTitle(getChooserTitle(toImplement, merge));
- registerHandlerForComplementaryAction(project, editor, aClass, toImplement, chooser);
-
- chooser.setCopyJavadocVisible(true);
-
- if (toImplement) {
- chooser.selectElements(isAll ? all : onlyPrimary);
- }
-
- if (ApplicationManager.getApplication().isUnitTestMode()) {
- chooser.selectElements(all);
- chooser.close(DialogWrapper.OK_EXIT_CODE);
- return chooser;
- }
-
- chooser.show();
- if (chooser.getExitCode() != DialogWrapper.OK_EXIT_CODE) return null;
-
- PropertiesComponent.getInstance(project).setValue(PROP_COMBINED_OVERRIDE_IMPLEMENT, merge.get().toString());
- return chooser;
- }
-
- private static String getChooserTitle(boolean toImplement, Ref<Boolean> merge) {
- return toImplement
- ? CodeInsightBundle.message("methods.to.implement.chooser.title")
- : merge.get().booleanValue()
- ? CodeInsightBundle.message("methods.to.override.implement.chooser.title")
- : CodeInsightBundle.message("methods.to.override.chooser.title");
- }
-
- private static PsiMethodMember[] convertToMethodMembers(Collection<CandidateInfo> candidates) {
- return ContainerUtil.map2Array(candidates, PsiMethodMember.class, new Function<CandidateInfo, PsiMethodMember>() {
- public PsiMethodMember fun(final CandidateInfo s) {
- return new PsiMethodMember(s);
- }
- });
- }
-
- private static void registerHandlerForComplementaryAction(final Project project, final Editor editor, final PsiElement aClass,
- final boolean toImplement,
- final MemberChooser<PsiMethodMember> chooser) {
- final JComponent preferredFocusedComponent = chooser.getPreferredFocusedComponent();
- final Keymap keymap = KeymapManager.getInstance().getActiveKeymap();
-
- @NonNls final String s = toImplement ? "OverrideMethods" : "ImplementMethods";
- final Shortcut[] shortcuts = keymap.getShortcuts(s);
-
- if (shortcuts.length > 0 && shortcuts[0] instanceof KeyboardShortcut) {
- preferredFocusedComponent.getInputMap().put(
- ((KeyboardShortcut)shortcuts[0]).getFirstKeyStroke(), s
- );
-
- preferredFocusedComponent.getActionMap().put(
- s,
- new AbstractAction() {
- public void actionPerformed(final ActionEvent e) {
- chooser.close(DialogWrapper.CANCEL_EXIT_CODE);
-
- // invoke later in order to close previous modal dialog
- ApplicationManager.getApplication().invokeLater(new Runnable() {
- public void run() {
- final CodeInsightActionHandler handler = toImplement ? new OverrideMethodsHandler(): new ImplementMethodsHandler();
- handler.invoke(project, editor, aClass.getContainingFile());
- }
- });
- }
- }
- );
- }
- }
-
- public static void overrideOrImplementMethodsInRightPlace(Editor editor,
- PsiClass aClass,
- Collection<PsiMethodMember> candidates,
- boolean copyJavadoc,
- boolean insertOverrideWherePossible) {
- try {
- int offset = editor.getCaretModel().getOffset();
- if (aClass.getLBrace() == null) {
- PsiClass psiClass = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createClass("X");
- aClass.addRangeAfter(psiClass.getLBrace(), psiClass.getRBrace(), aClass.getLastChild());
- }
-
- int lbraceOffset = aClass.getLBrace().getTextOffset();
- List<PsiGenerationInfo<PsiMethod>> resultMembers;
- if (offset <= lbraceOffset || aClass.isEnum()) {
- resultMembers = new ArrayList<PsiGenerationInfo<PsiMethod>>();
- for (PsiMethodMember candidate : candidates) {
- Collection<PsiMethod> prototypes =
- overrideOrImplementMethod(aClass, candidate.getElement(), candidate.getSubstitutor(), copyJavadoc, insertOverrideWherePossible);
- List<PsiGenerationInfo<PsiMethod>> infos = convert2GenerationInfos(prototypes);
- for (PsiGenerationInfo<PsiMethod> info : infos) {
- PsiElement anchor = getDefaultAnchorToOverrideOrImplement(aClass, candidate.getElement(), candidate.getSubstitutor());
- info.insert(aClass, anchor, true);
- resultMembers.add(info);
- }
- }
- }
- else {
- List<PsiGenerationInfo<PsiMethod>> prototypes = overrideOrImplementMethods(aClass, candidates, copyJavadoc, insertOverrideWherePossible);
- resultMembers = GenerateMembersUtil.insertMembersAtOffset(aClass.getContainingFile(), offset, prototypes);
- }
-
- if (!resultMembers.isEmpty()) {
- resultMembers.get(0).positionCaret(editor, true);
- }
- }
- catch (IncorrectOperationException e) {
- LOG.error(e);
- }
- }
-
- @Nullable
- public static PsiElement getDefaultAnchorToOverrideOrImplement(PsiClass aClass, PsiMethod baseMethod, PsiSubstitutor substitutor){
- PsiMethod prevBaseMethod = PsiTreeUtil.getPrevSiblingOfType(baseMethod, PsiMethod.class);
- while(prevBaseMethod != null) {
- String name = prevBaseMethod.isConstructor() ? aClass.getName() : prevBaseMethod.getName();
- //Happens when aClass instanceof PsiAnonymousClass
- if (name != null) {
- MethodSignature signature = MethodSignatureUtil.createMethodSignature(name, prevBaseMethod.getParameterList(), prevBaseMethod.getTypeParameterList(), substitutor, prevBaseMethod.isConstructor());
- PsiMethod prevMethod = MethodSignatureUtil.findMethodBySignature(aClass, signature, false);
- if (prevMethod != null){
- return prevMethod.getNextSibling();
- }
- }
- prevBaseMethod = PsiTreeUtil.getPrevSiblingOfType(prevBaseMethod, PsiMethod.class);
- }
-
- PsiMethod nextBaseMethod = PsiTreeUtil.getNextSiblingOfType(baseMethod, PsiMethod.class);
- while(nextBaseMethod != null) {
- String name = nextBaseMethod.isConstructor() ? aClass.getName() : nextBaseMethod.getName();
- if (name != null) {
- MethodSignature signature = MethodSignatureUtil.createMethodSignature(name, nextBaseMethod.getParameterList(), nextBaseMethod.getTypeParameterList(), substitutor, nextBaseMethod.isConstructor());
- PsiMethod nextMethod = MethodSignatureUtil.findMethodBySignature(aClass, signature, false);
- if (nextMethod != null){
- return nextMethod;
- }
- }
- nextBaseMethod = PsiTreeUtil.getNextSiblingOfType(nextBaseMethod, PsiMethod.class);
- }
-
- return null;
- }
-
- public static void overrideOrImplement(PsiClass psiClass, @NotNull PsiMethod baseMethod) throws IncorrectOperationException {
- FileEditorManager fileEditorManager = FileEditorManager.getInstance(baseMethod.getProject());
-
- List<PsiGenerationInfo<PsiMethod>> prototypes = convert2GenerationInfos(overrideOrImplementMethod(psiClass, baseMethod, false));
- if (prototypes.isEmpty()) return;
-
- PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(baseMethod.getContainingClass(), psiClass, PsiSubstitutor.EMPTY);
- PsiElement anchor = getDefaultAnchorToOverrideOrImplement(psiClass, baseMethod, substitutor);
- List<PsiGenerationInfo<PsiMethod>> results = GenerateMembersUtil.insertMembersBeforeAnchor(psiClass, anchor, prototypes);
-
- PsiFile psiFile = psiClass.getContainingFile();
- Editor editor = fileEditorManager.openTextEditor(new OpenFileDescriptor(psiFile.getProject(), psiFile.getVirtualFile()), false);
- if (editor == null) return;
-
- results.get(0).positionCaret(editor, true);
- editor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
- }
-
- @Nullable
- public static PsiClass getContextClass(Project project, Editor editor, PsiFile file, boolean allowInterface) {
- PsiDocumentManager.getInstance(project).commitAllDocuments();
-
- int offset = editor.getCaretModel().getOffset();
- PsiElement element = file.findElementAt(offset);
- do {
- element = PsiTreeUtil.getParentOfType(element, PsiClass.class);
- }
- while (element instanceof PsiTypeParameter);
-
- final PsiClass aClass = (PsiClass)element;
- if (aClass instanceof JspClass) return null;
- return aClass == null || !allowInterface && aClass.isInterface() ? null : aClass;
- }
-
- public static void overrideOrImplementMethodsInRightPlace(Editor editor1, PsiClass aClass, Collection<PsiMethodMember> members, boolean copyJavadoc) {
- boolean insert = CodeStyleSettingsManager.getSettings(aClass.getProject()).INSERT_OVERRIDE_ANNOTATION;
- overrideOrImplementMethodsInRightPlace(editor1, aClass, members, copyJavadoc, insert);
- }
-
- public static List<PsiMethod> overrideOrImplementMethodCandidates(PsiClass aClass, Collection<CandidateInfo> candidatesToImplement,
- boolean copyJavadoc) throws IncorrectOperationException {
- boolean insert = CodeStyleSettingsManager.getSettings(aClass.getProject()).INSERT_OVERRIDE_ANNOTATION;
- return overrideOrImplementMethodCandidates(aClass, candidatesToImplement, copyJavadoc, insert);
- }
-
- public static class MethodSignatureComparator implements Comparator<MethodSignature> {
- // signatures should appear in the order of declaration
- public int compare(MethodSignature o1, MethodSignature o2) {
- if (o1 instanceof MethodSignatureBackedByPsiMethod && o2 instanceof MethodSignatureBackedByPsiMethod) {
- PsiMethod m1 = ((MethodSignatureBackedByPsiMethod)o1).getMethod();
- PsiMethod m2 = ((MethodSignatureBackedByPsiMethod)o2).getMethod();
- PsiClass c1 = m1.getContainingClass();
- PsiClass c2 = m2.getContainingClass();
- if (c1 != null && c2 != null) {
- if (c1 == c2) {
- final List<PsiMethod> methods = Arrays.asList(c1.getMethods());
- return methods.indexOf(m1) - methods.indexOf(m2);
- }
-
- if (c1.isInheritor(c2, true)) return -1;
- if (c2.isInheritor(c1, true)) return 1;
-
- return StringUtil.notNullize(c1.getName()).compareTo(StringUtil.notNullize(c2.getName()));
- }
- return m1.getTextOffset() - m2.getTextOffset();
- }
- return 0;
- }
- }
- }
+ /*\r
+ * Copyright 2000-2011 JetBrains s.r.o.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+ package com.intellij.codeInsight.generation;\r
+ \r
+ import com.intellij.codeInsight.AnnotationUtil;\r
+ import com.intellij.codeInsight.CodeInsightActionHandler;\r
+ import com.intellij.codeInsight.CodeInsightBundle;\r
+ import com.intellij.codeInsight.MethodImplementor;\r
+ import com.intellij.codeInsight.intention.AddAnnotationFix;\r
+ import com.intellij.featureStatistics.FeatureUsageTracker;\r
+ import com.intellij.featureStatistics.ProductivityFeatureNames;\r
+ import com.intellij.icons.AllIcons;\r
+ import com.intellij.ide.fileTemplates.FileTemplate;\r
+ import com.intellij.ide.fileTemplates.FileTemplateManager;\r
+ import com.intellij.ide.fileTemplates.FileTemplateUtil;\r
+ import com.intellij.ide.fileTemplates.JavaTemplateUtil;\r
+ import com.intellij.ide.util.MemberChooser;\r
+ import com.intellij.ide.util.PropertiesComponent;\r
+ import com.intellij.lang.java.JavaLanguage;\r
+ import com.intellij.openapi.actionSystem.*;\r
+ import com.intellij.openapi.application.ApplicationManager;\r
+ import com.intellij.openapi.application.Result;\r
+ import com.intellij.openapi.command.WriteCommandAction;\r
+ import com.intellij.openapi.diagnostic.Logger;\r
+ import com.intellij.openapi.editor.Editor;\r
+ import com.intellij.openapi.editor.ScrollType;\r
+ import com.intellij.openapi.extensions.Extensions;\r
+ import com.intellij.openapi.fileEditor.FileEditorManager;\r
+ import com.intellij.openapi.fileEditor.OpenFileDescriptor;\r
+ import com.intellij.openapi.fileTypes.FileType;\r
+ import com.intellij.openapi.fileTypes.FileTypeManager;\r
+ import com.intellij.openapi.keymap.Keymap;\r
+ import com.intellij.openapi.keymap.KeymapManager;\r
+ import com.intellij.openapi.module.Module;\r
+ import com.intellij.openapi.module.ModuleUtil;\r
+ import com.intellij.openapi.project.Project;\r
+ import com.intellij.openapi.ui.DialogWrapper;\r
+ import com.intellij.openapi.ui.Messages;\r
+ import com.intellij.openapi.util.Ref;\r
+ import com.intellij.openapi.util.text.StringUtil;\r
+ import com.intellij.psi.*;\r
+ import com.intellij.psi.codeStyle.CodeStyleManager;\r
+ import com.intellij.psi.codeStyle.CodeStyleSettingsManager;\r
+ import com.intellij.psi.codeStyle.CommonCodeStyleSettings;\r
+ import com.intellij.psi.codeStyle.JavaCodeStyleManager;\r
+ import com.intellij.psi.impl.source.jsp.jspJava.JspClass;\r
+ import com.intellij.psi.infos.CandidateInfo;\r
+ import com.intellij.psi.javadoc.PsiDocComment;\r
+ import com.intellij.psi.search.GlobalSearchScope;\r
+ import com.intellij.psi.util.*;\r
+ import com.intellij.util.ArrayUtil;\r
+ import com.intellij.util.Function;\r
+ import com.intellij.util.IncorrectOperationException;\r
+ import com.intellij.util.containers.ContainerUtil;\r
+ import org.jetbrains.annotations.NonNls;\r
+ import org.jetbrains.annotations.NotNull;\r
+ import org.jetbrains.annotations.Nullable;\r
+ \r
+ import javax.swing.*;\r
+ import java.awt.event.ActionEvent;\r
+ import java.awt.event.InputEvent;\r
+ import java.awt.event.KeyEvent;\r
+ import java.util.*;\r
+ \r
+ public class OverrideImplementUtil {\r
+ private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.generation.OverrideImplementUtil");\r
+ \r
+ @NonNls private static final String PROP_COMBINED_OVERRIDE_IMPLEMENT = "OverrideImplement.combined";\r
+ \r
+ private OverrideImplementUtil() {\r
+ }\r
+ \r
+ @NotNull\r
+ public static Collection<CandidateInfo> getMethodsToOverrideImplement(PsiClass aClass, boolean toImplement) {\r
+ return getMapToOverrideImplement(aClass, toImplement).values();\r
+ }\r
+ \r
+ @NotNull\r
+ public static Collection<MethodSignature> getMethodSignaturesToImplement(@NotNull PsiClass aClass) {\r
+ return getMapToOverrideImplement(aClass, true).keySet();\r
+ }\r
+ \r
+ @NotNull\r
+ public static Collection<MethodSignature> getMethodSignaturesToOverride(@NotNull PsiClass aClass) {\r
+ return getMapToOverrideImplement(aClass, false).keySet();\r
+ }\r
+ \r
+ @NotNull\r
+ private static Map<MethodSignature, CandidateInfo> getMapToOverrideImplement(PsiClass aClass, boolean toImplement) {\r
+ Map<MethodSignature, PsiMethod> abstracts = new LinkedHashMap<MethodSignature,PsiMethod>();\r
+ Map<MethodSignature, PsiMethod> finals = new LinkedHashMap<MethodSignature,PsiMethod>();\r
+ Map<MethodSignature, PsiMethod> concretes = new LinkedHashMap<MethodSignature,PsiMethod>();\r
+ \r
+ LOG.assertTrue(aClass.isValid());\r
+ Collection<HierarchicalMethodSignature> allMethodSigs = aClass.getVisibleSignatures();\r
+ PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(aClass.getProject()).getResolveHelper();\r
+ for (HierarchicalMethodSignature signature : allMethodSigs) {\r
+ PsiMethod method = signature.getMethod();\r
+ LOG.assertTrue(method.isValid(), aClass);\r
+ \r
+ if (method.hasModifierProperty(PsiModifier.STATIC) || !resolveHelper.isAccessible(method, aClass, aClass)) continue;\r
+ PsiClass hisClass = method.getContainingClass();\r
+ if (hisClass == null) continue;\r
+ // filter non-immediate super constructors\r
+ if (method.isConstructor() && (!aClass.isInheritor(hisClass, false) || aClass instanceof PsiAnonymousClass || aClass.isEnum())) {\r
+ continue;\r
+ }\r
+ // filter already implemented\r
+ if (MethodSignatureUtil.findMethodBySignature(aClass, signature, false) != null) {\r
+ continue;\r
+ }\r
+ \r
+ if (method.hasModifierProperty(PsiModifier.FINAL)) {\r
+ finals.put(signature, method);\r
+ continue;\r
+ }\r
+ \r
+ Map<MethodSignature, PsiMethod> map = hisClass.isInterface() || method.hasModifierProperty(PsiModifier.ABSTRACT) ? abstracts : concretes;\r
+ PsiMethod other = map.get(signature);\r
+ if (other == null || preferLeftForImplement(method, other)) {\r
+ map.put(signature, method);\r
+ }\r
+ }\r
+ \r
+ final Map<MethodSignature, CandidateInfo> result = new TreeMap<MethodSignature,CandidateInfo>(new MethodSignatureComparator());\r
+ if (toImplement || aClass.isInterface()) {\r
+ collectMethodsToImplement(aClass, abstracts, finals, concretes, result);\r
+ }\r
+ else {\r
+ for (Map.Entry<MethodSignature, PsiMethod> entry : concretes.entrySet()) {\r
+ MethodSignature signature = entry.getKey();\r
+ PsiMethod concrete = entry.getValue();\r
+ if (finals.get(signature) == null) {\r
+ PsiMethod abstractOne = abstracts.get(signature);\r
+ if (abstractOne == null || !abstractOne.getContainingClass().isInheritor(concrete.getContainingClass(), true) ||\r
+ CommonClassNames.JAVA_LANG_OBJECT.equals(concrete.getContainingClass().getQualifiedName())) {\r
+ PsiSubstitutor subst = GenerateMembersUtil.correctSubstitutor(concrete, signature.getSubstitutor());\r
+ CandidateInfo info = new CandidateInfo(concrete, subst);\r
+ result.put(signature, info);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ \r
+ return result;\r
+ }\r
+ \r
+ public static void collectMethodsToImplement(PsiClass aClass,\r
+ Map<MethodSignature, PsiMethod> abstracts,\r
+ Map<MethodSignature, PsiMethod> finals,\r
+ Map<MethodSignature, PsiMethod> concretes,\r
+ Map<MethodSignature, CandidateInfo> result) {\r
+ for (Map.Entry<MethodSignature, PsiMethod> entry : abstracts.entrySet()) {\r
+ MethodSignature signature = entry.getKey();\r
+ PsiMethod abstractOne = entry.getValue();\r
+ PsiMethod concrete = concretes.get(signature);\r
+ if (concrete == null\r
+ || PsiUtil.getAccessLevel(concrete.getModifierList()) < PsiUtil.getAccessLevel(abstractOne.getModifierList())\r
+ || !abstractOne.getContainingClass().isInterface() && abstractOne.getContainingClass().isInheritor(concrete.getContainingClass(), true)) {\r
+ if (finals.get(signature) == null) {\r
+ PsiSubstitutor subst = GenerateMembersUtil.correctSubstitutor(abstractOne, signature.getSubstitutor());\r
+ CandidateInfo info = new CandidateInfo(abstractOne, subst);\r
+ result.put(signature, info);\r
+ }\r
+ }\r
+ }\r
+ \r
+ for (final MethodImplementor implementor : getImplementors()) {\r
+ for (final PsiMethod method : implementor.getMethodsToImplement(aClass)) {\r
+ MethodSignature signature = MethodSignatureUtil.createMethodSignature(method.getName(), method.getParameterList(),\r
+ method.getTypeParameterList(), PsiSubstitutor.EMPTY, method.isConstructor());\r
+ CandidateInfo info = new CandidateInfo(method, PsiSubstitutor.EMPTY);\r
+ result.put(signature, info);\r
+ }\r
+ }\r
+ }\r
+ \r
+ private static boolean preferLeftForImplement(PsiMethod left, PsiMethod right) {\r
+ if (PsiUtil.getAccessLevel(left.getModifierList()) > PsiUtil.getAccessLevel(right.getModifierList())) return true;\r
+ if (!left.getContainingClass().isInterface()) return true;\r
+ if (!right.getContainingClass().isInterface()) return false;\r
+ // implement annotated method\r
+ PsiAnnotation[] leftAnnotations = left.getModifierList().getAnnotations();\r
+ PsiAnnotation[] rightAnnotations = right.getModifierList().getAnnotations();\r
+ return leftAnnotations.length > rightAnnotations.length;\r
+ }\r
+ \r
+ private static MethodImplementor[] getImplementors() {\r
+ return Extensions.getExtensions(MethodImplementor.EXTENSION_POINT_NAME);\r
+ }\r
+ \r
+ /**\r
+ * generate methods (with bodies) corresponding to given method declaration\r
+ * there are maybe two method implementations for one declaration\r
+ * (e.g. EJB' create() -> ejbCreate(), ejbPostCreate() )\r
+ * @param aClass context for method implementations\r
+ * @param method method to override or implement\r
+ * @param toCopyJavaDoc true if copy JavaDoc from method declaration\r
+ * @return list of method prototypes\r
+ */\r
+ @NotNull\r
+ public static Collection<PsiMethod> overrideOrImplementMethod(PsiClass aClass, PsiMethod method, boolean toCopyJavaDoc) throws IncorrectOperationException {\r
+ final PsiClass containingClass = method.getContainingClass();\r
+ LOG.assertTrue(containingClass != null);\r
+ PsiSubstitutor substitutor = aClass.isInheritor(containingClass, true)\r
+ ? TypeConversionUtil.getSuperClassSubstitutor(containingClass, aClass, PsiSubstitutor.EMPTY)\r
+ : PsiSubstitutor.EMPTY;\r
+ return overrideOrImplementMethod(aClass, method, substitutor, toCopyJavaDoc, CodeStyleSettingsManager.getSettings(aClass.getProject()).INSERT_OVERRIDE_ANNOTATION);\r
+ }\r
+ \r
+ public static boolean isInsertOverride(PsiMethod superMethod, PsiClass targetClass) {\r
+ if (!CodeStyleSettingsManager.getSettings(targetClass.getProject()).INSERT_OVERRIDE_ANNOTATION) {\r
+ return false;\r
+ }\r
+ return canInsertOverride(superMethod, targetClass);\r
+ }\r
+ \r
+ public static boolean canInsertOverride(PsiMethod superMethod, PsiClass targetClass) {\r
+ if (superMethod.isConstructor() || superMethod.hasModifierProperty(PsiModifier.STATIC)) {\r
+ return false;\r
+ }\r
+ if (!PsiUtil.isLanguageLevel5OrHigher(targetClass)) {\r
+ return false;\r
+ }\r
+ if (PsiUtil.isLanguageLevel6OrHigher(targetClass)) return true;\r
+ if (targetClass.isInterface()) return true;\r
+ PsiClass superClass = superMethod.getContainingClass();\r
+ return !superClass.isInterface();\r
+ }\r
+ \r
+ @NotNull\r
+ private static Collection<PsiMethod> overrideOrImplementMethod(PsiClass aClass,\r
+ PsiMethod method,\r
+ PsiSubstitutor substitutor,\r
+ boolean toCopyJavaDoc,\r
+ boolean insertOverrideIfPossible) throws IncorrectOperationException {\r
+ if (!method.isValid() || !substitutor.isValid()) return Collections.emptyList();\r
+ \r
+ List<PsiMethod> results = new ArrayList<PsiMethod>();\r
+ for (final MethodImplementor implementor : getImplementors()) {\r
+ final PsiMethod[] prototypes = implementor.createImplementationPrototypes(aClass, method);\r
+ if (implementor.isBodyGenerated()) {\r
+ ContainerUtil.addAll(results, prototypes);\r
+ }\r
+ else {\r
+ for (PsiMethod prototype : prototypes) {\r
+ results.add(decorateMethod(aClass, method, toCopyJavaDoc, insertOverrideIfPossible, prototype));\r
+ }\r
+ }\r
+ }\r
+ if (results.isEmpty()) {\r
+ PsiMethod method1 = GenerateMembersUtil.substituteGenericMethod(method, substitutor, aClass);\r
+ \r
+ PsiElementFactory factory = JavaPsiFacade.getInstance(method.getProject()).getElementFactory();\r
+ PsiMethod result = (PsiMethod)factory.createClass("Dummy").add(method1);\r
+ if (result instanceof PsiAnnotationMethod) {\r
+ PsiAnnotationMemberValue defaultValue = ((PsiAnnotationMethod)result).getDefaultValue();\r
+ if (defaultValue != null) {\r
+ PsiElement defaultKeyword = defaultValue;\r
+ while (!(defaultKeyword instanceof PsiKeyword) && defaultKeyword != null) {\r
+ defaultKeyword = defaultKeyword.getPrevSibling();\r
+ }\r
+ if (defaultKeyword == null) defaultKeyword = defaultValue;\r
+ defaultValue.getParent().deleteChildRange(defaultKeyword, defaultValue);\r
+ }\r
+ }\r
+ results.add(decorateMethod(aClass, method, toCopyJavaDoc, insertOverrideIfPossible, result));\r
+ }\r
+ \r
+ for (Iterator<PsiMethod> iterator = results.iterator(); iterator.hasNext();) {\r
+ if (aClass.findMethodBySignature(iterator.next(), false) != null) {\r
+ iterator.remove();\r
+ }\r
+ }\r
+ \r
+ return results;\r
+ }\r
+ \r
+ private static PsiMethod decorateMethod(PsiClass aClass,\r
+ PsiMethod method,\r
+ boolean toCopyJavaDoc,\r
+ boolean insertOverrideIfPossible,\r
+ PsiMethod result) {\r
+ PsiUtil.setModifierProperty(result, PsiModifier.ABSTRACT, aClass.isInterface());\r
+ PsiUtil.setModifierProperty(result, PsiModifier.NATIVE, false);\r
+ \r
+ if (!toCopyJavaDoc){\r
+ PsiDocComment comment = result.getDocComment();\r
+ if (comment != null){\r
+ comment.delete();\r
+ }\r
+ }\r
+ \r
+ //method type params are not allowed when overriding from raw type\r
+ final PsiTypeParameterList list = result.getTypeParameterList();\r
+ if (list != null) {\r
+ final PsiClass containingClass = method.getContainingClass();\r
+ if (containingClass != null) {\r
+ for (PsiClassType classType : aClass.getSuperTypes()) {\r
+ if (InheritanceUtil.isInheritorOrSelf(PsiUtil.resolveClassInType(classType), containingClass, true) && classType.isRaw()) {\r
+ list.replace(JavaPsiFacade.getElementFactory(aClass.getProject()).createTypeParameterList());\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ \r
+ annotateOnOverrideImplement(result, aClass, method, insertOverrideIfPossible);\r
+ \r
+ if (CodeStyleSettingsManager.getSettings(aClass.getProject()).REPEAT_SYNCHRONIZED && method.hasModifierProperty(PsiModifier.SYNCHRONIZED)) {\r
+ result.getModifierList().setModifierProperty(PsiModifier.SYNCHRONIZED, true);\r
+ }\r
+ \r
+ final PsiCodeBlock body = JavaPsiFacade.getInstance(method.getProject()).getElementFactory().createCodeBlockFromText("{}", null);\r
+ PsiCodeBlock oldbody = result.getBody();\r
+ if (oldbody != null){\r
+ oldbody.replace(body);\r
+ }\r
+ else{\r
+ result.add(body);\r
+ }\r
+ \r
+ setupMethodBody(result, method, aClass);\r
+ \r
+ // probably, it's better to reformat the whole method - it can go from other style sources\r
+ final Project project = method.getProject();\r
+ CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);\r
+ CommonCodeStyleSettings javaSettings = CodeStyleSettingsManager.getSettings(project).getCommonSettings(JavaLanguage.INSTANCE);\r
+ boolean keepBreaks = javaSettings.KEEP_LINE_BREAKS;\r
+ javaSettings.KEEP_LINE_BREAKS = false;\r
+ result = (PsiMethod)JavaCodeStyleManager.getInstance(project).shortenClassReferences(result);\r
+ result = (PsiMethod)codeStyleManager.reformat(result);\r
+ javaSettings.KEEP_LINE_BREAKS = keepBreaks;\r
+ return result;\r
+ }\r
+ \r
+ public static void annotateOnOverrideImplement(PsiMethod method, PsiClass targetClass, PsiMethod overridden) {\r
+ annotateOnOverrideImplement(method, targetClass, overridden,\r
+ CodeStyleSettingsManager.getSettings(method.getProject()).INSERT_OVERRIDE_ANNOTATION);\r
+ }\r
+ \r
+ public static void annotateOnOverrideImplement(PsiMethod method, PsiClass targetClass, PsiMethod overridden, boolean insertOverride) {\r
+ if (insertOverride && canInsertOverride(overridden, targetClass)) {\r
+ annotate(method, Override.class.getName());\r
+ }\r
+ final Module module = ModuleUtil.findModuleForPsiElement(targetClass);\r
+ final GlobalSearchScope moduleScope = module != null ? GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module) : null;\r
+ final Project project = targetClass.getProject();\r
+ final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);\r
+ for (OverrideImplementsAnnotationsHandler each : Extensions.getExtensions(OverrideImplementsAnnotationsHandler.EP_NAME)) {\r
+ for (String annotation : each.getAnnotations(project)) {\r
+ if (moduleScope != null && facade.findClass(annotation, moduleScope) == null) continue;\r
+ if (AnnotationUtil.isAnnotated(overridden, annotation, false)) {\r
+ annotate(method, annotation, each.annotationsToRemove(project, annotation));\r
+ }\r
+ }\r
+ }\r
+ }\r
+ \r
+ public static void annotate(@NotNull PsiMethod result, String fqn, String... annosToRemove) throws IncorrectOperationException {\r
+ Project project = result.getProject();\r
+ AddAnnotationFix fix = new AddAnnotationFix(fqn, result, annosToRemove);\r
+ if (fix.isAvailable(project, null, result.getContainingFile())) {\r
+ fix.invoke(project, null, result.getContainingFile());\r
+ }\r
+ }\r
+ \r
+ public static boolean isOverridable(PsiMethod method) {\r
+ return !method.isConstructor()\r
+ && !method.hasModifierProperty(PsiModifier.STATIC)\r
+ && !method.hasModifierProperty(PsiModifier.FINAL)\r
+ && !method.hasModifierProperty(PsiModifier.PRIVATE);\r
+ }\r
+ \r
+ @NotNull\r
+ public static List<PsiGenerationInfo<PsiMethod>> overrideOrImplementMethods(PsiClass aClass,\r
+ Collection<PsiMethodMember> candidates,\r
+ boolean toCopyJavaDoc,\r
+ boolean toInsertAtOverride)\r
+ throws IncorrectOperationException {\r
+ List<CandidateInfo> candidateInfos = ContainerUtil.map2List(candidates, new Function<PsiMethodMember, CandidateInfo>() {\r
+ public CandidateInfo fun(final PsiMethodMember s) {\r
+ return new CandidateInfo(s.getElement(), s.getSubstitutor());\r
+ }\r
+ });\r
+ final List<PsiMethod> methods = overrideOrImplementMethodCandidates(aClass, candidateInfos, toCopyJavaDoc, toInsertAtOverride);\r
+ return convert2GenerationInfos(methods);\r
+ }\r
+ \r
+ @NotNull\r
+ public static List<PsiMethod> overrideOrImplementMethodCandidates(PsiClass aClass,\r
+ Collection<CandidateInfo> candidates,\r
+ boolean toCopyJavaDoc,\r
+ boolean insertOverrideWherePossible) throws IncorrectOperationException {\r
+ List<PsiMethod> result = new ArrayList<PsiMethod>();\r
+ for (CandidateInfo candidateInfo : candidates) {\r
+ result.addAll(overrideOrImplementMethod(aClass, (PsiMethod)candidateInfo.getElement(), candidateInfo.getSubstitutor(),\r
+ toCopyJavaDoc, insertOverrideWherePossible));\r
+ }\r
+ return result;\r
+ }\r
+ \r
+ public static List<PsiGenerationInfo<PsiMethod>> convert2GenerationInfos(final Collection<PsiMethod> methods) {\r
+ return ContainerUtil.map2List(methods, new Function<PsiMethod, PsiGenerationInfo<PsiMethod>>() {\r
+ public PsiGenerationInfo<PsiMethod> fun(final PsiMethod s) {\r
+ return createGenerationInfo(s);\r
+ }\r
+ });\r
+ }\r
+ \r
+ public static PsiGenerationInfo<PsiMethod> createGenerationInfo(PsiMethod s) {\r
+ return createGenerationInfo(s, true);\r
+ }\r
+ \r
+ public static PsiGenerationInfo<PsiMethod> createGenerationInfo(PsiMethod s, boolean mergeIfExists) {\r
+ for (MethodImplementor implementor : getImplementors()) {\r
+ final GenerationInfo info = implementor.createGenerationInfo(s, mergeIfExists);\r
+ if (info instanceof PsiGenerationInfo) return (PsiGenerationInfo<PsiMethod>)info;\r
+ }\r
+ return new PsiGenerationInfo<PsiMethod>(s);\r
+ }\r
+ \r
+ @NotNull\r
+ public static String callSuper (PsiMethod superMethod, PsiMethod overriding) {\r
+ @NonNls StringBuilder buffer = new StringBuilder();\r
+ if (!superMethod.isConstructor() && superMethod.getReturnType() != PsiType.VOID) {\r
+ buffer.append("return ");\r
+ }\r
+ buffer.append("super");\r
+ PsiParameter[] parms = overriding.getParameterList().getParameters();\r
+ if (!superMethod.isConstructor()){\r
+ buffer.append(".");\r
+ buffer.append(superMethod.getName());\r
+ }\r
+ buffer.append("(");\r
+ for (int i = 0; i < parms.length; i++) {\r
+ String name = parms[i].getName();\r
+ if (i > 0) buffer.append(",");\r
+ buffer.append(name);\r
+ }\r
+ buffer.append(")");\r
+ return buffer.toString();\r
+ }\r
+ \r
+ public static void setupMethodBody(PsiMethod result, PsiMethod originalMethod, PsiClass targetClass) throws IncorrectOperationException {\r
+ String templName = originalMethod.hasModifierProperty(PsiModifier.ABSTRACT) ?\r
+ JavaTemplateUtil.TEMPLATE_IMPLEMENTED_METHOD_BODY : JavaTemplateUtil.TEMPLATE_OVERRIDDEN_METHOD_BODY;\r
+ FileTemplate template = FileTemplateManager.getInstance().getCodeTemplate(templName);\r
+ setupMethodBody(result, originalMethod, targetClass, template);\r
+ }\r
+ \r
+ public static void setupMethodBody(final PsiMethod result, final PsiMethod originalMethod, final PsiClass targetClass,\r
+ final FileTemplate template) throws IncorrectOperationException {\r
+ if (targetClass.isInterface()) {\r
+ final PsiCodeBlock body = result.getBody();\r
+ if (body != null) body.delete();\r
+ }\r
+ \r
+ FileType fileType = FileTypeManager.getInstance().getFileTypeByExtension(template.getExtension());\r
+ PsiType returnType = result.getReturnType();\r
+ if (returnType == null) {\r
+ returnType = PsiType.VOID;\r
+ }\r
+ Properties properties = new Properties();\r
+ properties.setProperty(FileTemplate.ATTRIBUTE_RETURN_TYPE, returnType.getPresentableText());\r
+ properties.setProperty(FileTemplate.ATTRIBUTE_DEFAULT_RETURN_VALUE, PsiTypesUtil.getDefaultValueOfType(returnType));\r
+ properties.setProperty(FileTemplate.ATTRIBUTE_CALL_SUPER, callSuper(originalMethod, result));\r
+ JavaTemplateUtil.setClassAndMethodNameProperties(properties, targetClass, result);\r
+ \r
- PsiElementFactory factory = JavaPsiFacade.getInstance(originalMethod.getProject()).getElementFactory();\r
++ JVMElementFactory factory = JVMElementFactories.getFactory(targetClass.getLanguage(), originalMethod.getProject());\r
++ if (factory == null) factory = JavaPsiFacade.getInstance(originalMethod.getProject()).getElementFactory();\r
+ @NonNls String methodText;\r
+ try {\r
+ String bodyText = template.getText(properties);\r
- if (!"".equals(bodyText)) bodyText += "\n";\r
++ if (bodyText != null && !bodyText.isEmpty()) bodyText += "\n";\r
+ methodText = "void foo () {\n" + bodyText + "}";\r
+ methodText = FileTemplateUtil.indent(methodText, result.getProject(), fileType);\r
+ } catch (Exception e) {\r
+ throw new IncorrectOperationException("Failed to parse file template",e);\r
+ }\r
+ if (methodText != null) {\r
+ PsiMethod m;\r
+ try {\r
+ m = factory.createMethodFromText(methodText, originalMethod);\r
+ }\r
+ catch (IncorrectOperationException e) {\r
+ ApplicationManager.getApplication().invokeLater(new Runnable() {\r
+ public void run() {\r
+ Messages.showErrorDialog(CodeInsightBundle.message("override.implement.broken.file.template.message"),\r
+ CodeInsightBundle.message("override.implement.broken.file.template.title"));\r
+ }\r
+ });\r
+ return;\r
+ }\r
+ PsiCodeBlock oldBody = result.getBody();\r
+ if (oldBody != null) {\r
+ oldBody.replace(m.getBody());\r
+ }\r
+ }\r
+ }\r
+ \r
+ public static void chooseAndOverrideMethods(Project project, Editor editor, PsiClass aClass){\r
+ FeatureUsageTracker.getInstance().triggerFeatureUsed(ProductivityFeatureNames.CODEASSISTS_OVERRIDE_IMPLEMENT);\r
+ chooseAndOverrideOrImplementMethods(project, editor, aClass, false);\r
+ }\r
+ \r
+ public static void chooseAndImplementMethods(Project project, Editor editor, PsiClass aClass){\r
+ FeatureUsageTracker.getInstance().triggerFeatureUsed(ProductivityFeatureNames.CODEASSISTS_OVERRIDE_IMPLEMENT);\r
+ chooseAndOverrideOrImplementMethods(project, editor, aClass, true);\r
+ }\r
+ \r
+ public static void chooseAndOverrideOrImplementMethods(final Project project,\r
+ final Editor editor,\r
+ final PsiClass aClass,\r
+ final boolean toImplement){\r
+ LOG.assertTrue(aClass.isValid());\r
+ ApplicationManager.getApplication().assertReadAccessAllowed();\r
+ \r
+ Collection<CandidateInfo> candidates = getMethodsToOverrideImplement(aClass, toImplement);\r
+ Collection<CandidateInfo> secondary = toImplement || aClass.isInterface() ? Collections.<CandidateInfo>emptyList() : getMethodsToOverrideImplement(aClass, true);\r
+ \r
+ final MemberChooser<PsiMethodMember> chooser = showOverrideImplementChooser(editor, aClass, toImplement, candidates, secondary);\r
+ if (chooser == null) return;\r
+ \r
+ final List<PsiMethodMember> selectedElements = chooser.getSelectedElements();\r
+ if (selectedElements == null || selectedElements.isEmpty()) return;\r
+ \r
+ LOG.assertTrue(aClass.isValid());\r
+ new WriteCommandAction(project, aClass.getContainingFile()) {\r
+ protected void run(final Result result) throws Throwable {\r
+ overrideOrImplementMethodsInRightPlace(editor, aClass, selectedElements, chooser.isCopyJavadoc(), chooser.isInsertOverrideAnnotation());\r
+ }\r
+ }.execute();\r
+ }\r
+ \r
+ @Nullable\r
+ public static MemberChooser<PsiMethodMember> showOverrideImplementChooser(Editor editor,\r
+ final PsiElement aClass,\r
+ final boolean toImplement,\r
+ Collection<CandidateInfo> candidates,\r
+ Collection<CandidateInfo> secondary) {\r
+ Project project = aClass.getProject();\r
+ if (candidates.isEmpty() && secondary.isEmpty()) return null;\r
+ \r
+ final PsiMethodMember[] onlyPrimary = convertToMethodMembers(candidates);\r
+ final PsiMethodMember[] all = ArrayUtil.mergeArrays(onlyPrimary, convertToMethodMembers(secondary));\r
+ \r
+ final String toMerge = PropertiesComponent.getInstance(project).getValue(PROP_COMBINED_OVERRIDE_IMPLEMENT);\r
+ final Ref<Boolean> merge = Ref.create(!"false".equals(toMerge));\r
+ \r
+ final boolean isAll = merge.get().booleanValue();\r
+ final MemberChooser<PsiMethodMember> chooser = new MemberChooser<PsiMethodMember>(isAll ? all : onlyPrimary, false, true, project,\r
+ PsiUtil.isLanguageLevel5OrHigher(aClass)) {\r
+ \r
+ @Override\r
+ protected void fillToolbarActions(DefaultActionGroup group) {\r
+ super.fillToolbarActions(group);\r
+ if (toImplement) return;\r
+ \r
+ final ToggleAction mergeAction = new ToggleAction("Show methods to implement", "Show methods to implement",\r
+ AllIcons.General.Show_to_implement) {\r
+ @Override\r
+ public boolean isSelected(AnActionEvent e) {\r
+ return merge.get().booleanValue();\r
+ }\r
+ \r
+ @Override\r
+ public void setSelected(AnActionEvent e, boolean state) {\r
+ merge.set(state);\r
+ resetElements(state ? all : onlyPrimary);\r
+ setTitle(getChooserTitle(false, merge));\r
+ }\r
+ };\r
+ mergeAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_I, InputEvent.ALT_MASK)), myTree);\r
+ \r
+ Shortcut[] shortcuts = KeymapManager.getInstance().getActiveKeymap().getShortcuts("OverrideMethods");\r
+ mergeAction.registerCustomShortcutSet(new CustomShortcutSet(shortcuts), myTree);\r
+ \r
+ group.add(mergeAction);\r
+ }\r
+ };\r
+ chooser.setTitle(getChooserTitle(toImplement, merge));\r
+ registerHandlerForComplementaryAction(project, editor, aClass, toImplement, chooser);\r
+ \r
+ chooser.setCopyJavadocVisible(true);\r
+ \r
+ if (toImplement) {\r
+ chooser.selectElements(isAll ? all : onlyPrimary);\r
+ }\r
+ \r
+ if (ApplicationManager.getApplication().isUnitTestMode()) {\r
+ chooser.selectElements(all);\r
+ chooser.close(DialogWrapper.OK_EXIT_CODE);\r
+ return chooser;\r
+ }\r
+ \r
+ chooser.show();\r
+ if (chooser.getExitCode() != DialogWrapper.OK_EXIT_CODE) return null;\r
+ \r
+ PropertiesComponent.getInstance(project).setValue(PROP_COMBINED_OVERRIDE_IMPLEMENT, merge.get().toString());\r
+ return chooser;\r
+ }\r
+ \r
+ private static String getChooserTitle(boolean toImplement, Ref<Boolean> merge) {\r
+ return toImplement\r
+ ? CodeInsightBundle.message("methods.to.implement.chooser.title")\r
+ : merge.get().booleanValue()\r
+ ? CodeInsightBundle.message("methods.to.override.implement.chooser.title")\r
+ : CodeInsightBundle.message("methods.to.override.chooser.title");\r
+ }\r
+ \r
+ private static PsiMethodMember[] convertToMethodMembers(Collection<CandidateInfo> candidates) {\r
+ return ContainerUtil.map2Array(candidates, PsiMethodMember.class, new Function<CandidateInfo, PsiMethodMember>() {\r
+ public PsiMethodMember fun(final CandidateInfo s) {\r
+ return new PsiMethodMember(s);\r
+ }\r
+ });\r
+ }\r
+ \r
+ private static void registerHandlerForComplementaryAction(final Project project, final Editor editor, final PsiElement aClass,\r
+ final boolean toImplement,\r
+ final MemberChooser<PsiMethodMember> chooser) {\r
+ final JComponent preferredFocusedComponent = chooser.getPreferredFocusedComponent();\r
+ final Keymap keymap = KeymapManager.getInstance().getActiveKeymap();\r
+ \r
+ @NonNls final String s = toImplement ? "OverrideMethods" : "ImplementMethods";\r
+ final Shortcut[] shortcuts = keymap.getShortcuts(s);\r
+ \r
+ if (shortcuts.length > 0 && shortcuts[0] instanceof KeyboardShortcut) {\r
+ preferredFocusedComponent.getInputMap().put(\r
+ ((KeyboardShortcut)shortcuts[0]).getFirstKeyStroke(), s\r
+ );\r
+ \r
+ preferredFocusedComponent.getActionMap().put(\r
+ s,\r
+ new AbstractAction() {\r
+ public void actionPerformed(final ActionEvent e) {\r
+ chooser.close(DialogWrapper.CANCEL_EXIT_CODE);\r
+ \r
+ // invoke later in order to close previous modal dialog\r
+ ApplicationManager.getApplication().invokeLater(new Runnable() {\r
+ public void run() {\r
+ final CodeInsightActionHandler handler = toImplement ? new OverrideMethodsHandler(): new ImplementMethodsHandler();\r
+ handler.invoke(project, editor, aClass.getContainingFile());\r
+ }\r
+ });\r
+ }\r
+ }\r
+ );\r
+ }\r
+ }\r
+ \r
+ public static void overrideOrImplementMethodsInRightPlace(Editor editor,\r
+ PsiClass aClass,\r
+ Collection<PsiMethodMember> candidates,\r
+ boolean copyJavadoc,\r
+ boolean insertOverrideWherePossible) {\r
+ try {\r
+ int offset = editor.getCaretModel().getOffset();\r
+ if (aClass.getLBrace() == null) {\r
+ PsiClass psiClass = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createClass("X");\r
+ aClass.addRangeAfter(psiClass.getLBrace(), psiClass.getRBrace(), aClass.getLastChild());\r
+ }\r
+ \r
+ int lbraceOffset = aClass.getLBrace().getTextOffset();\r
+ List<PsiGenerationInfo<PsiMethod>> resultMembers;\r
+ if (offset <= lbraceOffset || aClass.isEnum()) {\r
+ resultMembers = new ArrayList<PsiGenerationInfo<PsiMethod>>();\r
+ for (PsiMethodMember candidate : candidates) {\r
+ Collection<PsiMethod> prototypes =\r
+ overrideOrImplementMethod(aClass, candidate.getElement(), candidate.getSubstitutor(), copyJavadoc, insertOverrideWherePossible);\r
+ List<PsiGenerationInfo<PsiMethod>> infos = convert2GenerationInfos(prototypes);\r
+ for (PsiGenerationInfo<PsiMethod> info : infos) {\r
+ PsiElement anchor = getDefaultAnchorToOverrideOrImplement(aClass, candidate.getElement(), candidate.getSubstitutor());\r
+ info.insert(aClass, anchor, true);\r
+ resultMembers.add(info);\r
+ }\r
+ }\r
+ }\r
+ else {\r
+ List<PsiGenerationInfo<PsiMethod>> prototypes = overrideOrImplementMethods(aClass, candidates, copyJavadoc, insertOverrideWherePossible);\r
+ resultMembers = GenerateMembersUtil.insertMembersAtOffset(aClass.getContainingFile(), offset, prototypes);\r
+ }\r
+ \r
+ if (!resultMembers.isEmpty()) {\r
+ resultMembers.get(0).positionCaret(editor, true);\r
+ }\r
+ }\r
+ catch (IncorrectOperationException e) {\r
+ LOG.error(e);\r
+ }\r
+ }\r
+ \r
+ @Nullable\r
+ public static PsiElement getDefaultAnchorToOverrideOrImplement(PsiClass aClass, PsiMethod baseMethod, PsiSubstitutor substitutor){\r
+ PsiMethod prevBaseMethod = PsiTreeUtil.getPrevSiblingOfType(baseMethod, PsiMethod.class);\r
+ while(prevBaseMethod != null) {\r
+ String name = prevBaseMethod.isConstructor() ? aClass.getName() : prevBaseMethod.getName();\r
+ //Happens when aClass instanceof PsiAnonymousClass\r
+ if (name != null) {\r
+ MethodSignature signature = MethodSignatureUtil.createMethodSignature(name, prevBaseMethod.getParameterList(), prevBaseMethod.getTypeParameterList(), substitutor, prevBaseMethod.isConstructor());\r
+ PsiMethod prevMethod = MethodSignatureUtil.findMethodBySignature(aClass, signature, false);\r
+ if (prevMethod != null){\r
+ return prevMethod.getNextSibling();\r
+ }\r
+ }\r
+ prevBaseMethod = PsiTreeUtil.getPrevSiblingOfType(prevBaseMethod, PsiMethod.class);\r
+ }\r
+ \r
+ PsiMethod nextBaseMethod = PsiTreeUtil.getNextSiblingOfType(baseMethod, PsiMethod.class);\r
+ while(nextBaseMethod != null) {\r
+ String name = nextBaseMethod.isConstructor() ? aClass.getName() : nextBaseMethod.getName();\r
+ if (name != null) {\r
+ MethodSignature signature = MethodSignatureUtil.createMethodSignature(name, nextBaseMethod.getParameterList(), nextBaseMethod.getTypeParameterList(), substitutor, nextBaseMethod.isConstructor());\r
+ PsiMethod nextMethod = MethodSignatureUtil.findMethodBySignature(aClass, signature, false);\r
+ if (nextMethod != null){\r
+ return nextMethod;\r
+ }\r
+ }\r
+ nextBaseMethod = PsiTreeUtil.getNextSiblingOfType(nextBaseMethod, PsiMethod.class);\r
+ }\r
+ \r
+ return null;\r
+ }\r
+ \r
+ public static void overrideOrImplement(PsiClass psiClass, @NotNull PsiMethod baseMethod) throws IncorrectOperationException {\r
+ FileEditorManager fileEditorManager = FileEditorManager.getInstance(baseMethod.getProject());\r
+ \r
+ List<PsiGenerationInfo<PsiMethod>> prototypes = convert2GenerationInfos(overrideOrImplementMethod(psiClass, baseMethod, false));\r
+ if (prototypes.isEmpty()) return;\r
+ \r
+ PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(baseMethod.getContainingClass(), psiClass, PsiSubstitutor.EMPTY);\r
+ PsiElement anchor = getDefaultAnchorToOverrideOrImplement(psiClass, baseMethod, substitutor);\r
+ List<PsiGenerationInfo<PsiMethod>> results = GenerateMembersUtil.insertMembersBeforeAnchor(psiClass, anchor, prototypes);\r
+ \r
+ PsiFile psiFile = psiClass.getContainingFile();\r
+ Editor editor = fileEditorManager.openTextEditor(new OpenFileDescriptor(psiFile.getProject(), psiFile.getVirtualFile()), false);\r
+ if (editor == null) return;\r
+ \r
+ results.get(0).positionCaret(editor, true);\r
+ editor.getScrollingModel().scrollToCaret(ScrollType.CENTER);\r
+ }\r
+ \r
+ @Nullable\r
+ public static PsiClass getContextClass(Project project, Editor editor, PsiFile file, boolean allowInterface) {\r
+ PsiDocumentManager.getInstance(project).commitAllDocuments();\r
+ \r
+ int offset = editor.getCaretModel().getOffset();\r
+ PsiElement element = file.findElementAt(offset);\r
+ do {\r
+ element = PsiTreeUtil.getParentOfType(element, PsiClass.class);\r
+ }\r
+ while (element instanceof PsiTypeParameter);\r
+ \r
+ final PsiClass aClass = (PsiClass)element;\r
+ if (aClass instanceof JspClass) return null;\r
+ return aClass == null || !allowInterface && aClass.isInterface() ? null : aClass;\r
+ }\r
+ \r
- private static PsiSubstitutor getContextSubstitutor(PsiClass aClass) {\r
- if (aClass instanceof PsiAnonymousClass) {\r
- return ((PsiAnonymousClass)aClass).getBaseClassType().resolveGenerics().getSubstitutor();\r
- }\r
-\r
- return PsiSubstitutor.EMPTY;\r
- }\r
-\r
+ public static void overrideOrImplementMethodsInRightPlace(Editor editor1, PsiClass aClass, Collection<PsiMethodMember> members, boolean copyJavadoc) {\r
+ boolean insert = CodeStyleSettingsManager.getSettings(aClass.getProject()).INSERT_OVERRIDE_ANNOTATION;\r
+ overrideOrImplementMethodsInRightPlace(editor1, aClass, members, copyJavadoc, insert);\r
+ }\r
+ \r
+ public static List<PsiMethod> overrideOrImplementMethodCandidates(PsiClass aClass, Collection<CandidateInfo> candidatesToImplement,\r
+ boolean copyJavadoc) throws IncorrectOperationException {\r
+ boolean insert = CodeStyleSettingsManager.getSettings(aClass.getProject()).INSERT_OVERRIDE_ANNOTATION;\r
+ return overrideOrImplementMethodCandidates(aClass, candidatesToImplement, copyJavadoc, insert);\r
+ }\r
+ \r
+ public static class MethodSignatureComparator implements Comparator<MethodSignature> {\r
+ // signatures should appear in the order of declaration\r
+ public int compare(MethodSignature o1, MethodSignature o2) {\r
+ if (o1 instanceof MethodSignatureBackedByPsiMethod && o2 instanceof MethodSignatureBackedByPsiMethod) {\r
+ PsiMethod m1 = ((MethodSignatureBackedByPsiMethod)o1).getMethod();\r
+ PsiMethod m2 = ((MethodSignatureBackedByPsiMethod)o2).getMethod();\r
+ PsiClass c1 = m1.getContainingClass();\r
+ PsiClass c2 = m2.getContainingClass();\r
+ if (c1 != null && c2 != null) {\r
+ if (c1 == c2) {\r
+ final List<PsiMethod> methods = Arrays.asList(c1.getMethods());\r
+ return methods.indexOf(m1) - methods.indexOf(m2);\r
+ }\r
+ \r
+ if (c1.isInheritor(c2, true)) return -1;\r
+ if (c2.isInheritor(c1, true)) return 1;\r
+ \r
+ return StringUtil.notNullize(c1.getName()).compareTo(StringUtil.notNullize(c2.getName()));\r
+ }\r
+ return m1.getTextOffset() - m2.getTextOffset();\r
+ }\r
+ return 0;\r
+ }\r
+ }\r
+ }\r