Groovy: support 'Insert @Override' and 'copy doc comments' options in 'Override method' dialog

This commit is contained in:
Max Medvedev
2013-07-11 08:59:08 +04:00
parent ddf1d376d7
commit 7ce722cd52
8 changed files with 126 additions and 155 deletions
@@ -261,7 +261,7 @@ public class GenerateMembersUtil {
try {
final PsiMethod resultMethod = createMethod(factory, sourceMethod, target);
copyDocComment(sourceMethod, resultMethod);
copyDocComment(sourceMethod, resultMethod, factory);
copyModifiers(sourceMethod.getModifierList(), resultMethod.getModifierList());
final PsiSubstitutor collisionResolvedSubstitutor =
substituteTypeParameters(factory, target, sourceMethod.getTypeParameterList(), resultMethod.getTypeParameterList(), substitutor, sourceMethod);
@@ -306,7 +306,8 @@ public class GenerateMembersUtil {
for (PsiTypeParameter typeParam : sourceTypeParameterList.getTypeParameters()) {
final PsiTypeParameter substitutedTypeParam = substituteTypeParameter(factory, typeParam, substitutor, sourceMethod);
final PsiTypeParameter resolvedTypeParam = resolveTypeParametersCollision(factory, sourceTypeParameterList, target, substitutedTypeParam, substitutor);
final PsiTypeParameter resolvedTypeParam = resolveTypeParametersCollision(factory, sourceTypeParameterList, target,
substitutedTypeParam, substitutor);
targetTypeParameterList.add(resolvedTypeParam);
if (substitutedTypeParam != resolvedTypeParam) {
substitutionMap.put(typeParam, factory.createType(resolvedTypeParam));
@@ -315,6 +316,14 @@ public class GenerateMembersUtil {
return substitutionMap.isEmpty() ? substitutor : factory.createSubstitutor(substitutionMap);
}
@NotNull
private static PsiClassType[] filterTypeParameterSuperTypes(@NotNull PsiClassType[] types) {
if (types.length == 1 && types[0].equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) {
return PsiClassType.EMPTY_ARRAY;
}
return types;
}
@NotNull
private static PsiTypeParameter resolveTypeParametersCollision(@NotNull JVMElementFactory factory,
@NotNull PsiTypeParameterList sourceTypeParameterList,
@@ -324,12 +333,12 @@ public class GenerateMembersUtil {
for (PsiType type : substitutor.getSubstitutionMap().values()) {
if (type != null && Comparing.equal(type.getCanonicalText(), typeParam.getName())) {
final String newName = suggestUniqueTypeParameterName(typeParam.getName(), sourceTypeParameterList, PsiTreeUtil.getParentOfType(target, PsiClass.class, false));
final PsiTypeParameter newTypeParameter = factory.createTypeParameter(newName, typeParam.getSuperTypes());
final PsiTypeParameter newTypeParameter = factory.createTypeParameter(newName, filterTypeParameterSuperTypes(typeParam.getSuperTypes()));
substitutor.put(typeParam, factory.createType(newTypeParameter));
return newTypeParameter;
}
}
return typeParam;
return factory.createTypeParameter(typeParam.getName(), filterTypeParameterSuperTypes(typeParam.getSuperTypes()));
}
@NotNull
@@ -428,12 +437,12 @@ public class GenerateMembersUtil {
}
}
private static void copyDocComment(PsiMethod source, PsiMethod target) {
private static void copyDocComment(PsiMethod source, PsiMethod target, JVMElementFactory factory) {
final PsiElement navigationElement = source.getNavigationElement();
if (navigationElement instanceof PsiDocCommentOwner) {
final PsiDocComment docComment = ((PsiDocCommentOwner)navigationElement).getDocComment();
if (docComment != null) {
target.addAfter(docComment, null);
target.addAfter(factory.createDocCommentFromText(docComment.getText()), null);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 0-2 JetBrains s.r.o.
* Copyright 2000-2013 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.
@@ -119,34 +119,22 @@ public class OverrideImplementUtil extends OverrideImplementExploreUtil {
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 {
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) {
decorator.consume(prototype);
results.add(prototype);
}
for (PsiMethod prototype : prototypes) {
implementor.createDecorator(aClass, method, toCopyJavaDoc, insertOverrideIfPossible).consume(prototype);
results.add(prototype);
}
}
if (results.isEmpty()) {
PsiMethod method1 = GenerateMembersUtil.substituteGenericMethod(method, substitutor, aClass);
@@ -163,6 +151,7 @@ public class OverrideImplementUtil extends OverrideImplementExploreUtil {
defaultValue.getParent().deleteChildRange(defaultKeyword, defaultValue);
}
}
Consumer<PsiMethod> decorator = createDefaultDecorator(aClass, method, toCopyJavaDoc, insertOverrideIfPossible);
decorator.consume(result);
results.add(result);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2013 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.
@@ -19,6 +19,7 @@ import com.intellij.codeInsight.generation.GenerationInfo;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.util.Consumer;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -32,8 +33,9 @@ public interface MethodImplementor extends MemberImplementorExplorer {
@NotNull
PsiMethod[] createImplementationPrototypes(final PsiClass inClass, PsiMethod method) throws IncorrectOperationException;
boolean isBodyGenerated();
@Nullable
GenerationInfo createGenerationInfo(PsiMethod method, boolean mergeIfExists);
@NotNull
Consumer<PsiMethod> createDecorator(PsiClass targetClass, PsiMethod baseMethod, boolean toCopyJavaDoc, boolean insertOverrideIfPossible);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2013 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.
@@ -26,6 +26,9 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes;
import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeParameter;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeParameterList;
import org.jetbrains.plugins.groovy.lang.psi.impl.GrStubElementBase;
@@ -79,6 +82,8 @@ public class GrTypeParameterListImpl extends GrStubElementBase<EmptyStub> implem
@Override
public ASTNode addInternal(ASTNode first, ASTNode last, ASTNode anchor, Boolean before) {
appendParenthesesIfNeeded();
if (first == last && first.getPsi() instanceof GrTypeParameter) {
boolean hasParams = getTypeParameters().length > 0;
@@ -107,4 +112,25 @@ public class GrTypeParameterListImpl extends GrStubElementBase<EmptyStub> implem
return super.addInternal(first, last, anchor, before);
}
}
private void appendParenthesesIfNeeded() {
PsiElement first = getFirstChild();
if (first == null) {
getNode().addLeaf(GroovyTokenTypes.mLT, "<", null);
}
PsiElement last = getLastChild();
if (last.getNode().getElementType() != GroovyTokenTypes.mGT) {
getNode().addLeaf(GroovyTokenTypes.mGT, ">", null);
}
PsiElement parent = getParent();
if (parent instanceof GrMethod) {
GrModifierList list = ((GrMethod)parent).getModifierList();
PsiElement[] modifiers = list.getModifiers();
if (modifiers.length == 0) {
list.setModifierProperty(GrModifier.DEF, true);
}
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* Copyright 2000-2013 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.
@@ -17,13 +17,18 @@ package org.jetbrains.plugins.groovy.overrideImplement;
import com.intellij.codeInsight.MethodImplementor;
import com.intellij.codeInsight.generation.GenerationInfo;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiSubstitutor;
import com.intellij.codeInsight.generation.OverrideImplementUtil;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.Consumer;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.actions.generate.GroovyGenerationInfo;
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocComment;
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.impl.GrDocCommentUtil;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
@@ -48,11 +53,6 @@ public class GroovyMethodImplementor implements MethodImplementor {
return new PsiMethod[]{GroovyOverrideImplementUtil.generateMethodPrototype((GrTypeDefinition)inClass, method, substitutor)};
}
@Override
public boolean isBodyGenerated() {
return true;
}
@Override
public GenerationInfo createGenerationInfo(PsiMethod method, boolean mergeIfExists) {
if (method instanceof GrMethod) {
@@ -60,4 +60,47 @@ public class GroovyMethodImplementor implements MethodImplementor {
}
return null;
}
@NotNull
@Override
public Consumer<PsiMethod> createDecorator(final PsiClass targetClass,
final PsiMethod baseMethod,
final boolean toCopyJavaDoc,
final boolean insertOverrideIfPossible) {
return new Consumer<PsiMethod>() {
@Override
public void consume(PsiMethod method) {
Project project = targetClass.getProject();
if (toCopyJavaDoc) {
PsiDocComment baseMethodDocComment = baseMethod.getDocComment();
if (baseMethodDocComment != null) {
GrDocComment docComment =
GroovyPsiElementFactory.getInstance(project).createDocCommentFromText(baseMethodDocComment.getText());
GrDocCommentUtil.setDocComment(((GrMethod)method), docComment);
}
}
else {
PsiDocComment docComment = method.getDocComment();
if (docComment != null) {
docComment.delete();
}
}
if (insertOverrideIfPossible) {
if (OverrideImplementUtil.canInsertOverride(method, targetClass) &&
JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_LANG_OVERRIDE, targetClass.getResolveScope()) != null &&
method.getModifierList().findAnnotation(CommonClassNames.JAVA_LANG_OVERRIDE) == null) {
method.getModifierList().addAnnotation(CommonClassNames.JAVA_LANG_OVERRIDE);
}
}
else {
PsiAnnotation annotation = method.getModifierList().findAnnotation(CommonClassNames.JAVA_LANG_OVERRIDE);
if (annotation != null) {
annotation.delete();
}
}
}
};
}
}
@@ -15,6 +15,7 @@
*/
package org.jetbrains.plugins.groovy.overrideImplement;
import com.intellij.codeInsight.generation.GenerateMembersUtil;
import com.intellij.codeInsight.generation.OverrideImplementUtil;
import com.intellij.ide.fileTemplates.FileTemplate;
import com.intellij.ide.fileTemplates.FileTemplateManager;
@@ -28,6 +29,7 @@ import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrCodeBlock;
@@ -36,13 +38,8 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefini
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement;
import org.jetbrains.plugins.groovy.refactoring.GroovyChangeContextUtil;
import org.jetbrains.plugins.groovy.refactoring.GroovyNamesUtil;
import org.jetbrains.plugins.groovy.refactoring.convertToJava.ModifierListGenerator;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
@@ -51,17 +48,6 @@ import java.util.Properties;
*/
public class GroovyOverrideImplementUtil {
private static final Logger LOG = Logger.getInstance("org.jetbrains.plugins.groovy.overrideImplement.GroovyOverrideImplementUtil");
private static final String JAVA_LANG_OVERRIDE = "java.lang.Override";
private static final String[] GROOVY_MODIFIERS = new String[]{
//PsiModifier.PUBLIC,
PsiModifier.PROTECTED,
PsiModifier.PRIVATE,
PsiModifier.STATIC,
//PsiModifier.ABSTRACT,
PsiModifier.FINAL,
PsiModifier.SYNCHRONIZED,
};
private GroovyOverrideImplementUtil() {
}
@@ -74,10 +60,11 @@ public class GroovyOverrideImplementUtil {
String templName = isAbstract ? JavaTemplateUtil.TEMPLATE_IMPLEMENTED_METHOD_BODY : JavaTemplateUtil.TEMPLATE_OVERRIDDEN_METHOD_BODY;
final FileTemplate template = FileTemplateManager.getInstance().getCodeTemplate(templName);
final GrMethod result = createOverrideImplementMethodSignature(project, method, substitutor, aClass);
final GrMethod result = (GrMethod)GenerateMembersUtil.substituteGenericMethod(method, substitutor, aClass);
setupModifierList(result);
setupOverridingMethodBody(project, method, result, template, substitutor);
setupReturnType(result, method);
setupAnnotations(aClass, method, result);
@@ -85,9 +72,19 @@ public class GroovyOverrideImplementUtil {
return result;
}
private static void setupReturnType(GrMethod result, PsiMethod method) {
if (method instanceof GrMethod && ((GrMethod)method).getReturnTypeElementGroovy() == null) {
result.setReturnType(null);
GrModifierList modifierList = result.getModifierList();
if (!modifierList.hasExplicitVisibilityModifiers()) {
modifierList.setModifierProperty(GrModifier.DEF, true);
}
}
}
private static void setupAnnotations(@NotNull GrTypeDefinition aClass, @NotNull PsiMethod method, @NotNull GrMethod result) {
if (OverrideImplementUtil.isInsertOverride(method, aClass)) {
result.getModifierList().addAnnotation(JAVA_LANG_OVERRIDE);
result.getModifierList().addAnnotation(CommonClassNames.JAVA_LANG_OVERRIDE);
}
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(method.getProject());
@@ -118,101 +115,6 @@ public class GroovyOverrideImplementUtil {
}
@NotNull
private static GrMethod createOverrideImplementMethodSignature(@NotNull Project project,
@NotNull PsiMethod superMethod,
@NotNull PsiSubstitutor substitutor,
@NotNull PsiClass aClass) {
StringBuilder buffer = new StringBuilder();
final boolean hasModifiers = ModifierListGenerator.writeModifiers(buffer, superMethod.getModifierList(), GROOVY_MODIFIERS, false);
final PsiTypeParameter[] superTypeParameters = superMethod.getTypeParameters();
final List<PsiTypeParameter> typeParameters = new ArrayList<PsiTypeParameter>();
final Map<PsiTypeParameter, PsiType> map = substitutor.getSubstitutionMap();
for (PsiTypeParameter parameter : superTypeParameters) {
if (!map.containsKey(parameter)) {
typeParameters.add(parameter);
}
}
final PsiType returnType = substitutor.substitute(getSuperReturnType(superMethod));
boolean isConstructor = superMethod.isConstructor();
if (!isConstructor && (!hasModifiers && returnType == null || typeParameters.size() > 0)) {
buffer.append("def ");
}
if (typeParameters.size() > 0) {
LOG.assertTrue(!isConstructor);
buffer.append('<');
for (PsiTypeParameter parameter : typeParameters) {
buffer.append(parameter.getText());
buffer.append(", ");
}
buffer.replace(buffer.length() - 2, buffer.length(), ">");
}
final String name;
if (isConstructor) {
name = aClass.getName();
}
else {
if (returnType != null) {
buffer.append(returnType.getCanonicalText()).append(" ");
}
name = superMethod.getName();
}
buffer.append(name);
buffer.append("(");
final PsiParameter[] parameters = superMethod.getParameterList().getParameters();
for (int i = 0; i < parameters.length; i++) {
if (i > 0) buffer.append(", ");
PsiParameter parameter = parameters[i];
if (!(parameter instanceof GrParameter && ((GrParameter)parameter).getTypeElementGroovy() == null)) {
final PsiType parameterType = substitutor.substitute(parameter.getType());
buffer.append(parameterType.getCanonicalText());
buffer.append(" ");
}
final String paramName = parameter.getName();
if (paramName != null) {
if (GroovyNamesUtil.isKeyword(paramName)) {
buffer.append('_');
}
buffer.append(paramName);
}
else if (parameter instanceof PsiCompiledElement) {
buffer.append(((PsiParameter)((PsiCompiledElement)parameter).getMirror()).getName());
}
}
buffer.append(") ");
final PsiReferenceList list = superMethod.getThrowsList();
final PsiClassType[] types = list.getReferencedTypes();
if (types.length > 0) {
buffer.append("throws ");
for (PsiClassType type : types) {
buffer.append(type.getCanonicalText());
buffer.append(" ,");
}
buffer.delete(buffer.length() - 2, buffer.length());
}
buffer.append("{}");
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(project);
if (isConstructor) {
return factory.createConstructorFromText(name, buffer.toString(), superMethod);
}
else {
return factory.createMethodFromText(buffer.toString(), superMethod);
}
}
@Nullable
private static PsiType getSuperReturnType(@NotNull PsiMethod superMethod) {
if (superMethod instanceof GrMethod) {
@@ -40,7 +40,7 @@ class <caret>Inheritor extends Base {
''', '''\
class Inheritor extends Base {
/**
* my doc
* my doc
*/
Inheritor(int x) {
super(x)
@@ -92,7 +92,7 @@ class Test<T> extends Base<T> {<caret>}
myFixture.checkResult """
class Test<T> extends Base<T> {
@Override
def <T> T[] toArray(T[] t) {
def <T1> T1[] toArray(T1[] t) {
return super.toArray(t)
}
}