Java tutorial
/* * Copyright 2000-2012 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 java.awt.event.ActionEvent; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Properties; import javax.swing.AbstractAction; import javax.swing.JComponent; import javax.swing.KeyStroke; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; 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.codeInsight.intention.AddAnnotationPsiFix; 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.AnActionEvent; import com.intellij.openapi.actionSystem.CustomShortcutSet; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.actionSystem.KeyboardShortcut; import com.intellij.openapi.actionSystem.Shortcut; import com.intellij.openapi.actionSystem.ToggleAction; 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.ModuleUtilCore; 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.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.infos.CandidateInfo; import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.MethodSignature; import com.intellij.psi.util.MethodSignatureUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTypesUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.Consumer; import com.intellij.util.Function; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.ContainerUtil; public class OverrideImplementUtil extends OverrideImplementExploreUtil { 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() { } protected 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 List<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 List<PsiMethod> overrideOrImplementMethod(PsiClass aClass, PsiMethod method, PsiSubstitutor substitutor, boolean toCopyJavaDoc, boolean insertOverrideIfPossible) throws IncorrectOperationException { return overrideOrImplementMethod(aClass, method, substitutor, createDefaultDecorator(aClass, method, toCopyJavaDoc, insertOverrideIfPossible)); } public static List<PsiMethod> overrideOrImplementMethod(PsiClass aClass, PsiMethod method, PsiSubstitutor substitutor, Consumer<PsiMethod> decorator) 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) { decorator.consume(prototype); results.add(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 (PsiUtil.isAnnotationMethod(result)) { 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); } } decorator.consume(result); results.add(result); } for (Iterator<PsiMethod> iterator = results.iterator(); iterator.hasNext();) { if (aClass.findMethodBySignature(iterator.next(), false) != null) { iterator.remove(); } } return results; } public static Consumer<PsiMethod> createDefaultDecorator(final PsiClass aClass, final PsiMethod method, final boolean toCopyJavaDoc, final boolean insertOverrideIfPossible) { return new Consumer<PsiMethod>() { @Override public void consume(PsiMethod result) { decorateMethod(aClass, method, toCopyJavaDoc, insertOverrideIfPossible, result); } }; } 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) { deleteDocComment(result); } //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 deleteDocComment(PsiMethod result) { PsiDocComment comment = result.getDocComment(); if (comment != null) { comment.delete(); } } 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)) { final String overrideAnnotationName = Override.class.getName(); if (!AnnotationUtil.isAnnotated(method, overrideAnnotationName, false, true)) { AddAnnotationPsiFix.addPhysicalAnnotation(overrideAnnotationName, PsiNameValuePair.EMPTY_ARRAY, method.getModifierList()); } } final Module module = ModuleUtilCore.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, 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()); } } @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>() { @Override 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>>() { @Override 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) { @SuppressWarnings({ "unchecked" }) final PsiGenerationInfo<PsiMethod> psiGenerationInfo = (PsiGenerationInfo<PsiMethod>) info; return psiGenerationInfo; } } 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[] parameters = overriding.getParameterList().getParameters(); if (!superMethod.isConstructor()) { buffer.append("."); buffer.append(superMethod.getName()); } buffer.append("("); for (int i = 0; i < parameters.length; i++) { String name = parameters[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 { boolean isAbstract = originalMethod.hasModifierProperty(PsiModifier.ABSTRACT) || originalMethod.hasModifierProperty(PsiModifier.DEFAULT); String templateName = isAbstract ? JavaTemplateUtil.TEMPLATE_IMPLEMENTED_METHOD_BODY : JavaTemplateUtil.TEMPLATE_OVERRIDDEN_METHOD_BODY; FileTemplate template = FileTemplateManager.getInstance().getCodeTemplate(templateName); 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 = FileTemplateManager.getInstance().getDefaultProperties(targetClass.getProject()); 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 { methodText = "void foo () {\n" + template.getText(properties) + "\n}"; 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() { @Override 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() ? ContainerUtil.<CandidateInfo>newArrayList() : getMethodsToOverrideImplement(aClass, true); if (toImplement && PsiUtil.isLanguageLevel8OrHigher(aClass)) { for (Iterator<CandidateInfo> iterator = candidates.iterator(); iterator.hasNext();) { CandidateInfo candidate = iterator.next(); PsiElement element = candidate.getElement(); if (element instanceof PsiMethod && ((PsiMethod) element).hasModifierProperty(PsiModifier.DEFAULT)) { iterator.remove(); secondary.add(candidate); } } } 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()) { @Override 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 Ref<Boolean> merge = Ref.create( PropertiesComponent.getInstance(project).getBoolean(PROP_COMBINED_OVERRIDE_IMPLEMENT, true)); final MemberChooser<PsiMethodMember> chooser = new MemberChooser<PsiMethodMember>( toImplement || merge.get() ? 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(onlyPrimary); } if (ApplicationManager.getApplication().isUnitTestMode()) { if (!toImplement || onlyPrimary.length == 0) { 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>() { @Override 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() { @Override 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() { @Override 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 List<PsiGenerationInfo<PsiMethod>> overrideOrImplement(PsiClass psiClass, @NotNull PsiMethod baseMethod) throws IncorrectOperationException { FileEditorManager fileEditorManager = FileEditorManager.getInstance(baseMethod.getProject()); List<PsiGenerationInfo<PsiMethod>> results = new ArrayList<PsiGenerationInfo<PsiMethod>>(); try { List<PsiGenerationInfo<PsiMethod>> prototypes = convert2GenerationInfos( overrideOrImplementMethod(psiClass, baseMethod, false)); if (prototypes.isEmpty()) { return null; } PsiSubstitutor substitutor = TypeConversionUtil .getSuperClassSubstitutor(baseMethod.getContainingClass(), psiClass, PsiSubstitutor.EMPTY); PsiElement anchor = getDefaultAnchorToOverrideOrImplement(psiClass, baseMethod, substitutor); results = GenerateMembersUtil.insertMembersBeforeAnchor(psiClass, anchor, prototypes); return results; } finally { PsiFile psiFile = psiClass.getContainingFile(); Editor editor = fileEditorManager .openTextEditor(new OpenFileDescriptor(psiFile.getProject(), psiFile.getVirtualFile()), false); if (editor != null && !results.isEmpty()) { 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); } }