mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-05-08 06:10:59 +07:00
annotations are removed by default for both return and parameters, custom OverrideImplementHandlers can provide predefined annotations which must be repeat in overriders, custom annotations can be configured by the user
714 lines
33 KiB
Java
714 lines
33 KiB
Java
/*
|
|
* Copyright 2000-2016 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.ExceptionUtil;
|
|
import com.intellij.codeInsight.NullableNotNullManager;
|
|
import com.intellij.codeInsight.daemon.impl.quickfix.CreateFromUsageUtils;
|
|
import com.intellij.lang.ASTNode;
|
|
import com.intellij.openapi.diagnostic.Logger;
|
|
import com.intellij.openapi.editor.Document;
|
|
import com.intellij.openapi.editor.Editor;
|
|
import com.intellij.openapi.editor.RangeMarker;
|
|
import com.intellij.openapi.editor.ScrollType;
|
|
import com.intellij.openapi.project.Project;
|
|
import com.intellij.openapi.util.Comparing;
|
|
import com.intellij.openapi.util.text.StringUtil;
|
|
import com.intellij.psi.*;
|
|
import com.intellij.psi.codeStyle.*;
|
|
import com.intellij.psi.impl.light.LightTypeElement;
|
|
import com.intellij.psi.impl.source.codeStyle.JavaCodeStyleManagerImpl;
|
|
import com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl;
|
|
import com.intellij.psi.javadoc.PsiDocComment;
|
|
import com.intellij.psi.search.GlobalSearchScope;
|
|
import com.intellij.psi.util.PropertyUtil;
|
|
import com.intellij.psi.util.PsiTreeUtil;
|
|
import com.intellij.psi.util.PsiUtil;
|
|
import com.intellij.psi.util.TypeConversionUtil;
|
|
import com.intellij.refactoring.util.RefactoringUtil;
|
|
import com.intellij.util.IncorrectOperationException;
|
|
import com.intellij.util.VisibilityUtil;
|
|
import com.intellij.util.containers.HashMap;
|
|
import com.intellij.util.text.UniqueNameGenerator;
|
|
import org.jetbrains.annotations.Contract;
|
|
import org.jetbrains.annotations.NotNull;
|
|
import org.jetbrains.annotations.Nullable;
|
|
import org.jetbrains.java.generate.GenerationUtil;
|
|
import org.jetbrains.java.generate.exception.GenerateCodeException;
|
|
import org.jetbrains.java.generate.template.TemplatesManager;
|
|
|
|
import java.util.*;
|
|
|
|
public class GenerateMembersUtil {
|
|
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.generation.GenerateMembersUtil");
|
|
|
|
private GenerateMembersUtil() {
|
|
}
|
|
|
|
@NotNull
|
|
public static <T extends GenerationInfo> List<T> insertMembersAtOffset(PsiFile file, int offset, @NotNull List<T> memberPrototypes) throws IncorrectOperationException {
|
|
if (memberPrototypes.isEmpty()) return memberPrototypes;
|
|
final PsiElement leaf = file.findElementAt(offset);
|
|
if (leaf == null) return Collections.emptyList();
|
|
|
|
PsiClass aClass = findClassAtOffset(file, leaf);
|
|
if (aClass == null) return Collections.emptyList();
|
|
PsiElement anchor = memberPrototypes.get(0).findInsertionAnchor(aClass, leaf);
|
|
|
|
if (anchor instanceof PsiWhiteSpace) {
|
|
final ASTNode spaceNode = anchor.getNode();
|
|
anchor = anchor.getNextSibling();
|
|
|
|
assert spaceNode != null;
|
|
if (spaceNode.getStartOffset() <= offset && spaceNode.getStartOffset() + spaceNode.getTextLength() >= offset) {
|
|
String whiteSpace = spaceNode.getText().substring(0, offset - spaceNode.getStartOffset());
|
|
if (!StringUtil.containsLineBreak(whiteSpace)) {
|
|
// There is a possible case that the caret is located at the end of the line that already contains expression, say, we
|
|
// want to override particular method while caret is located after the field.
|
|
// Example - consider that we want to override toString() method at the class below:
|
|
// class Test {
|
|
// int i;<caret>
|
|
// }
|
|
// We want to add line feed then in order to avoid situation like below:
|
|
// class Test {
|
|
// int i;@Override String toString() {
|
|
// super.toString();
|
|
// }
|
|
// }
|
|
whiteSpace += "\n";
|
|
}
|
|
final PsiParserFacade parserFacade = PsiParserFacade.SERVICE.getInstance(file.getProject());
|
|
final ASTNode singleNewLineWhitespace = parserFacade.createWhiteSpaceFromText(whiteSpace).getNode();
|
|
if (singleNewLineWhitespace != null) {
|
|
spaceNode.getTreeParent().replaceChild(spaceNode, singleNewLineWhitespace); // See http://jetbrains.net/jira/browse/IDEADEV-12837
|
|
}
|
|
}
|
|
}
|
|
|
|
// Q: shouldn't it be somewhere in PSI?
|
|
PsiElement element = anchor;
|
|
while (true) {
|
|
if (element == null) break;
|
|
if (element instanceof PsiField || element instanceof PsiMethod || element instanceof PsiClassInitializer) break;
|
|
element = element.getNextSibling();
|
|
}
|
|
if (element instanceof PsiField) {
|
|
PsiField field = (PsiField)element;
|
|
PsiTypeElement typeElement = field.getTypeElement();
|
|
if (typeElement != null && !field.equals(typeElement.getParent())) {
|
|
field.normalizeDeclaration();
|
|
anchor = field;
|
|
}
|
|
}
|
|
|
|
return insertMembersBeforeAnchor(aClass, anchor, memberPrototypes);
|
|
}
|
|
|
|
@NotNull
|
|
public static <T extends GenerationInfo> List<T> insertMembersBeforeAnchor(PsiClass aClass, @Nullable PsiElement anchor, @NotNull List<T> memberPrototypes) throws IncorrectOperationException {
|
|
boolean before = true;
|
|
for (T memberPrototype : memberPrototypes) {
|
|
memberPrototype.insert(aClass, anchor, before);
|
|
before = false;
|
|
anchor = memberPrototype.getPsiMember();
|
|
}
|
|
return memberPrototypes;
|
|
}
|
|
|
|
/**
|
|
* @see GenerationInfo#positionCaret(Editor, boolean)
|
|
*/
|
|
public static void positionCaret(@NotNull Editor editor, @NotNull PsiElement firstMember, boolean toEditMethodBody) {
|
|
LOG.assertTrue(firstMember.isValid());
|
|
Project project = firstMember.getProject();
|
|
|
|
if (toEditMethodBody) {
|
|
PsiMethod method = (PsiMethod)firstMember;
|
|
PsiCodeBlock body = method.getBody();
|
|
if (body != null) {
|
|
PsiElement firstBodyElement = body.getFirstBodyElement();
|
|
PsiElement l = firstBodyElement;
|
|
while (l instanceof PsiWhiteSpace) l = l.getNextSibling();
|
|
if (l == null) l = body;
|
|
PsiElement lastBodyElement = body.getLastBodyElement();
|
|
PsiElement r = lastBodyElement;
|
|
while (r instanceof PsiWhiteSpace) r = r.getPrevSibling();
|
|
if (r == null) r = body;
|
|
|
|
int start = l.getTextRange().getStartOffset();
|
|
int end = r.getTextRange().getEndOffset();
|
|
|
|
boolean adjustLineIndent = false;
|
|
|
|
// body is whitespace
|
|
if (start > end &&
|
|
firstBodyElement == lastBodyElement &&
|
|
firstBodyElement instanceof PsiWhiteSpaceImpl
|
|
) {
|
|
CharSequence chars = ((PsiWhiteSpaceImpl)firstBodyElement).getChars();
|
|
if (chars.length() > 1 && chars.charAt(0) == '\n' && chars.charAt(1) == '\n') {
|
|
start = end = firstBodyElement.getTextRange().getStartOffset() + 1;
|
|
adjustLineIndent = true;
|
|
}
|
|
}
|
|
|
|
editor.getCaretModel().moveToOffset(Math.min(start, end));
|
|
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
|
|
if (start < end) {
|
|
//Not an empty body
|
|
editor.getSelectionModel().setSelection(start, end);
|
|
} else if (adjustLineIndent) {
|
|
Document document = editor.getDocument();
|
|
RangeMarker marker = document.createRangeMarker(start, start);
|
|
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(document);
|
|
if (marker.isValid()) {
|
|
CodeStyleManager.getInstance(project).adjustLineIndent(document, marker.getStartOffset());
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
int offset;
|
|
if (firstMember instanceof PsiMethod) {
|
|
PsiMethod method = (PsiMethod)firstMember;
|
|
PsiCodeBlock body = method.getBody();
|
|
if (body == null) {
|
|
offset = method.getTextRange().getStartOffset();
|
|
}
|
|
else {
|
|
PsiJavaToken lBrace = body.getLBrace();
|
|
assert lBrace != null : firstMember.getText();
|
|
offset = lBrace.getTextRange().getEndOffset();
|
|
}
|
|
}
|
|
else {
|
|
offset = firstMember.getTextRange().getStartOffset();
|
|
}
|
|
|
|
editor.getCaretModel().moveToOffset(offset);
|
|
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
|
|
editor.getSelectionModel().removeSelection();
|
|
}
|
|
|
|
public static PsiElement insert(@NotNull PsiClass aClass, @NotNull PsiMember member, @Nullable PsiElement anchor, boolean before) throws IncorrectOperationException {
|
|
if (member instanceof PsiMethod) {
|
|
if (!aClass.isInterface()) {
|
|
final PsiParameter[] parameters = ((PsiMethod)member).getParameterList().getParameters();
|
|
final boolean generateFinals = CodeStyleSettingsManager.getSettings(aClass.getProject()).GENERATE_FINAL_PARAMETERS;
|
|
for (final PsiParameter parameter : parameters) {
|
|
final PsiModifierList modifierList = parameter.getModifierList();
|
|
assert modifierList != null;
|
|
modifierList.setModifierProperty(PsiModifier.FINAL, generateFinals);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (anchor != null) {
|
|
return before ? aClass.addBefore(member, anchor) : aClass.addAfter(member, anchor);
|
|
}
|
|
else {
|
|
return aClass.add(member);
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
private static PsiClass findClassAtOffset(PsiFile file, PsiElement leaf) {
|
|
PsiElement element = leaf;
|
|
while (element != null && !(element instanceof PsiFile)) {
|
|
if (element instanceof PsiClass && !(element instanceof PsiTypeParameter)) {
|
|
final PsiClass psiClass = (PsiClass)element;
|
|
if (psiClass.isEnum()) {
|
|
PsiElement lastChild = null;
|
|
for (PsiElement child : psiClass.getChildren()) {
|
|
if (child instanceof PsiJavaToken && ";".equals(child.getText())) {
|
|
lastChild = child;
|
|
break;
|
|
}
|
|
else if (child instanceof PsiJavaToken && ",".equals(child.getText()) || child instanceof PsiEnumConstant) {
|
|
lastChild = child;
|
|
}
|
|
}
|
|
if (lastChild != null) {
|
|
int adjustedOffset = lastChild.getTextRange().getEndOffset();
|
|
if (leaf.getTextRange().getEndOffset() <= adjustedOffset) return findClassAtOffset(file, file.findElementAt(adjustedOffset));
|
|
}
|
|
}
|
|
return psiClass;
|
|
}
|
|
element = element.getParent();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static PsiMethod substituteGenericMethod(PsiMethod method, final PsiSubstitutor substitutor) {
|
|
return substituteGenericMethod(method, substitutor, null);
|
|
}
|
|
|
|
public static PsiMethod substituteGenericMethod(@NotNull PsiMethod sourceMethod,
|
|
@NotNull PsiSubstitutor substitutor,
|
|
@Nullable PsiElement target) {
|
|
final Project project = sourceMethod.getProject();
|
|
final JVMElementFactory factory = getFactory(sourceMethod.getProject(), target);
|
|
final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project);
|
|
|
|
try {
|
|
final PsiMethod resultMethod = createMethod(factory, sourceMethod, target);
|
|
copyModifiers(sourceMethod.getModifierList(), resultMethod.getModifierList());
|
|
final PsiSubstitutor collisionResolvedSubstitutor =
|
|
substituteTypeParameters(factory, target, sourceMethod.getTypeParameterList(), resultMethod.getTypeParameterList(), substitutor, sourceMethod);
|
|
substituteReturnType(PsiManager.getInstance(project), resultMethod, sourceMethod.getReturnType(), collisionResolvedSubstitutor);
|
|
substituteParameters(factory, codeStyleManager, sourceMethod.getParameterList(), resultMethod.getParameterList(), collisionResolvedSubstitutor, target);
|
|
copyDocComment(sourceMethod, resultMethod, factory);
|
|
GlobalSearchScope scope = sourceMethod.getResolveScope();
|
|
final List<PsiClassType> thrownTypes = ExceptionUtil.collectSubstituted(collisionResolvedSubstitutor, sourceMethod.getThrowsList().getReferencedTypes(),
|
|
scope);
|
|
if (target instanceof PsiClass) {
|
|
final PsiMethod[] methods = ((PsiClass)target).findMethodsBySignature(sourceMethod, true);
|
|
for (PsiMethod psiMethod : methods) {
|
|
if (psiMethod != null && psiMethod != sourceMethod) {
|
|
PsiClass aSuper = psiMethod.getContainingClass();
|
|
if (aSuper != null && aSuper != target) {
|
|
PsiSubstitutor superClassSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(aSuper, (PsiClass)target, PsiSubstitutor.EMPTY);
|
|
ExceptionUtil.retainExceptions(thrownTypes, ExceptionUtil.collectSubstituted(superClassSubstitutor, psiMethod.getThrowsList().getReferencedTypes(), scope));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
substituteThrows(factory, resultMethod.getThrowsList(), collisionResolvedSubstitutor, sourceMethod, thrownTypes);
|
|
return resultMethod;
|
|
}
|
|
catch (IncorrectOperationException e) {
|
|
LOG.error(e);
|
|
return sourceMethod;
|
|
}
|
|
}
|
|
|
|
private static void copyModifiers(@NotNull PsiModifierList sourceModifierList,
|
|
@NotNull PsiModifierList targetModifierList) {
|
|
VisibilityUtil.setVisibility(targetModifierList, VisibilityUtil.getVisibilityModifier(sourceModifierList));
|
|
}
|
|
|
|
@NotNull
|
|
private static PsiSubstitutor substituteTypeParameters(@NotNull JVMElementFactory factory,
|
|
@Nullable PsiElement target,
|
|
@Nullable PsiTypeParameterList sourceTypeParameterList,
|
|
@Nullable PsiTypeParameterList targetTypeParameterList,
|
|
@NotNull PsiSubstitutor substitutor,
|
|
@NotNull PsiMethod sourceMethod) {
|
|
if (sourceTypeParameterList == null || targetTypeParameterList == null || PsiUtil.isRawSubstitutor(sourceMethod, substitutor)) {
|
|
return substitutor;
|
|
}
|
|
|
|
final Map<PsiTypeParameter, PsiType> substitutionMap = new HashMap<>(substitutor.getSubstitutionMap());
|
|
for (PsiTypeParameter typeParam : sourceTypeParameterList.getTypeParameters()) {
|
|
final PsiTypeParameter substitutedTypeParam = substituteTypeParameter(factory, typeParam, substitutor, sourceMethod);
|
|
|
|
final PsiTypeParameter resolvedTypeParam = resolveTypeParametersCollision(factory, sourceTypeParameterList, target,
|
|
substitutedTypeParam, substitutor);
|
|
targetTypeParameterList.add(resolvedTypeParam);
|
|
if (substitutedTypeParam != resolvedTypeParam) {
|
|
substitutionMap.put(typeParam, factory.createType(resolvedTypeParam));
|
|
}
|
|
}
|
|
return substitutionMap.isEmpty() ? substitutor : factory.createSubstitutor(substitutionMap);
|
|
}
|
|
|
|
@NotNull
|
|
private static PsiTypeParameter resolveTypeParametersCollision(@NotNull JVMElementFactory factory,
|
|
@NotNull PsiTypeParameterList sourceTypeParameterList,
|
|
@Nullable PsiElement target,
|
|
@NotNull PsiTypeParameter typeParam,
|
|
@NotNull PsiSubstitutor substitutor) {
|
|
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());
|
|
substitutor.put(typeParam, factory.createType(newTypeParameter));
|
|
return newTypeParameter;
|
|
}
|
|
}
|
|
return factory.createTypeParameter(typeParam.getName(), typeParam.getSuperTypes());
|
|
}
|
|
|
|
@NotNull
|
|
private static String suggestUniqueTypeParameterName(String baseName, @NotNull PsiTypeParameterList typeParameterList, @Nullable PsiClass targetClass) {
|
|
int i = 0;
|
|
while (true) {
|
|
final String newName = baseName + ++i;
|
|
if (checkUniqueTypeParameterName(newName, typeParameterList) && (targetClass == null || checkUniqueTypeParameterName(newName, targetClass.getTypeParameterList()))) {
|
|
return newName;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private static boolean checkUniqueTypeParameterName(@NotNull String baseName, @Nullable PsiTypeParameterList typeParameterList) {
|
|
if (typeParameterList == null) return true;
|
|
|
|
for (PsiTypeParameter typeParameter : typeParameterList.getTypeParameters()) {
|
|
if (Comparing.equal(typeParameter.getName(), baseName)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
@NotNull
|
|
private static PsiTypeParameter substituteTypeParameter(final @NotNull JVMElementFactory factory,
|
|
@NotNull PsiTypeParameter typeParameter,
|
|
final @NotNull PsiSubstitutor substitutor,
|
|
@NotNull final PsiMethod sourceMethod) {
|
|
final PsiElement copy = typeParameter.copy();
|
|
final Map<PsiElement, PsiElement> replacementMap = new HashMap<>();
|
|
copy.accept(new JavaRecursiveElementVisitor() {
|
|
@Override
|
|
public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
|
|
super.visitReferenceElement(reference);
|
|
final PsiElement resolve = reference.resolve();
|
|
if (resolve instanceof PsiTypeParameter) {
|
|
final PsiType type = factory.createType((PsiTypeParameter)resolve);
|
|
replacementMap.put(reference, factory.createReferenceElementByType((PsiClassType)substituteType(substitutor, type, sourceMethod)));
|
|
}
|
|
}
|
|
});
|
|
return (PsiTypeParameter)RefactoringUtil.replaceElementsWithMap(copy, replacementMap);
|
|
}
|
|
|
|
private static void substituteParameters(@NotNull JVMElementFactory factory,
|
|
@NotNull JavaCodeStyleManager codeStyleManager,
|
|
@NotNull PsiParameterList sourceParameterList,
|
|
@NotNull PsiParameterList targetParameterList,
|
|
@NotNull PsiSubstitutor substitutor, PsiElement target) {
|
|
final PsiParameter[] parameters = sourceParameterList.getParameters();
|
|
final PsiParameter[] newParameters = overriddenParameters(parameters, factory, codeStyleManager, substitutor, target);
|
|
for (int i = 0; i < newParameters.length; i++) {
|
|
final PsiParameter newParameter = newParameters[i];
|
|
copyOrReplaceModifierList(parameters[i], target, newParameter);
|
|
targetParameterList.add(newParameter);
|
|
}
|
|
}
|
|
|
|
public static PsiParameter[] overriddenParameters(PsiParameter[] parameters,
|
|
@NotNull JVMElementFactory factory,
|
|
@NotNull JavaCodeStyleManager codeStyleManager,
|
|
@NotNull PsiSubstitutor substitutor,
|
|
PsiElement target) {
|
|
PsiParameter[] result = new PsiParameter[parameters.length];
|
|
UniqueNameGenerator generator = new UniqueNameGenerator();
|
|
|
|
for (int i = 0; i < parameters.length; i++) {
|
|
PsiParameter parameter = parameters[i];
|
|
final PsiType parameterType = parameter.getType();
|
|
final PsiType substituted = substituteType(substitutor, parameterType, (PsiMethod)parameter.getDeclarationScope());
|
|
String paramName = parameter.getName();
|
|
boolean isBaseNameGenerated = true;
|
|
final boolean isSubstituted = substituted.equals(parameterType);
|
|
if (!isSubstituted && isBaseNameGenerated(codeStyleManager, TypeConversionUtil.erasure(parameterType), paramName)) {
|
|
isBaseNameGenerated = false;
|
|
}
|
|
|
|
if (paramName == null ||
|
|
isBaseNameGenerated && !isSubstituted && isBaseNameGenerated(codeStyleManager, parameterType, paramName) ||
|
|
!factory.isValidParameterName(paramName)) {
|
|
String[] names = codeStyleManager.suggestVariableName(VariableKind.PARAMETER, null, null, substituted).names;
|
|
if (names.length > 0) {
|
|
paramName = generator.generateUniqueName(names[0]);
|
|
}
|
|
else {
|
|
paramName = generator.generateUniqueName("p");
|
|
}
|
|
}
|
|
else if (!generator.value(paramName)) {
|
|
paramName = generator.generateUniqueName(paramName);
|
|
}
|
|
generator.addExistingName(paramName);
|
|
result[i] = factory.createParameter(paramName, GenericsUtil.getVariableTypeByExpressionType(substituted), target);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static void substituteThrows(@NotNull JVMElementFactory factory,
|
|
@NotNull PsiReferenceList targetThrowsList,
|
|
@NotNull PsiSubstitutor substitutor,
|
|
@NotNull PsiMethod sourceMethod,
|
|
List<PsiClassType> thrownTypes) {
|
|
for (PsiClassType thrownType : thrownTypes) {
|
|
targetThrowsList.add(factory.createReferenceElementByType((PsiClassType)substituteType(substitutor, thrownType, sourceMethod)));
|
|
}
|
|
}
|
|
|
|
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(factory.createDocCommentFromText(docComment.getText()), null);
|
|
}
|
|
}
|
|
final PsiParameter[] sourceParameters = source.getParameterList().getParameters();
|
|
final PsiParameterList targetParameterList = target.getParameterList();
|
|
RefactoringUtil.fixJavadocsForParams(target, new HashSet<>(Arrays.asList(targetParameterList.getParameters())), pair -> {
|
|
final int parameterIndex = targetParameterList.getParameterIndex(pair.first);
|
|
if (parameterIndex >= 0 && parameterIndex < sourceParameters.length) {
|
|
return Comparing.strEqual(pair.second, sourceParameters[parameterIndex].getName());
|
|
}
|
|
return false;
|
|
});
|
|
}
|
|
|
|
@NotNull
|
|
private static PsiMethod createMethod(@NotNull JVMElementFactory factory,
|
|
@NotNull PsiMethod method, PsiElement target) {
|
|
if (method.isConstructor()) {
|
|
return factory.createConstructor(method.getName(), target);
|
|
}
|
|
return factory.createMethod(method.getName(), PsiType.VOID, target);
|
|
}
|
|
|
|
private static void substituteReturnType(@NotNull PsiManager manager,
|
|
@NotNull PsiMethod method,
|
|
@Nullable PsiType returnType,
|
|
@NotNull PsiSubstitutor substitutor) {
|
|
final PsiTypeElement returnTypeElement = method.getReturnTypeElement();
|
|
if (returnTypeElement == null || returnType == null) {
|
|
return;
|
|
}
|
|
final PsiType substitutedReturnType = substituteType(substitutor, returnType, method);
|
|
|
|
returnTypeElement.replace(new LightTypeElement(manager, substitutedReturnType instanceof PsiWildcardType ? TypeConversionUtil.erasure(substitutedReturnType) : substitutedReturnType));
|
|
}
|
|
|
|
@NotNull
|
|
private static JVMElementFactory getFactory(@NotNull Project p, @Nullable PsiElement target) {
|
|
return target == null ? JavaPsiFacade.getInstance(p).getElementFactory() : JVMElementFactories.requireFactory(target.getLanguage(), p);
|
|
}
|
|
|
|
private static boolean isBaseNameGenerated(JavaCodeStyleManager csManager, PsiType parameterType, String paramName) {
|
|
if (Arrays.asList(csManager.suggestVariableName(VariableKind.PARAMETER, null, null, parameterType).names).contains(paramName)) {
|
|
return true;
|
|
}
|
|
final String typeName = JavaCodeStyleManagerImpl.getTypeName(parameterType);
|
|
return typeName != null &&
|
|
NameUtil.getSuggestionsByName(typeName, "", "", false, false, parameterType instanceof PsiArrayType).contains(paramName);
|
|
}
|
|
|
|
private static PsiType substituteType(final PsiSubstitutor substitutor, final PsiType type, @NotNull PsiTypeParameterListOwner owner) {
|
|
if (PsiUtil.isRawSubstitutor(owner, substitutor)) {
|
|
return TypeConversionUtil.erasure(type);
|
|
}
|
|
final PsiType psiType = substitutor.substitute(type);
|
|
if (psiType != null) {
|
|
final PsiType deepComponentType = psiType.getDeepComponentType();
|
|
if (!(deepComponentType instanceof PsiCapturedWildcardType || deepComponentType instanceof PsiWildcardType)){
|
|
return psiType;
|
|
}
|
|
}
|
|
return TypeConversionUtil.erasure(type);
|
|
}
|
|
|
|
public static boolean isChildInRange(PsiElement child, PsiElement first, PsiElement last) {
|
|
if (child.equals(first)) return true;
|
|
while (true) {
|
|
if (child.equals(first)) return false; // before first
|
|
if (child.equals(last)) return true;
|
|
child = child.getNextSibling();
|
|
if (child == null) return false;
|
|
}
|
|
}
|
|
|
|
public static void setupGeneratedMethod(PsiMethod method) {
|
|
PsiClass containingClass = method.getContainingClass();
|
|
PsiClass base = containingClass == null ? null : containingClass.getSuperClass();
|
|
PsiMethod overridden = base == null ? null : base.findMethodBySignature(method, true);
|
|
|
|
boolean emptyTemplate = true;
|
|
PsiCodeBlock body = method.getBody();
|
|
if (body != null) {
|
|
PsiJavaToken lBrace = body.getLBrace();
|
|
int left = lBrace != null ? lBrace.getStartOffsetInParent() + 1 : 0;
|
|
PsiJavaToken rBrace = body.getRBrace();
|
|
int right = rBrace != null ? rBrace.getStartOffsetInParent() : body.getTextLength();
|
|
emptyTemplate = StringUtil.isEmptyOrSpaces(body.getText().substring(left, right));
|
|
}
|
|
|
|
if (overridden == null) {
|
|
if (emptyTemplate) {
|
|
CreateFromUsageUtils.setupMethodBody(method, containingClass);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (emptyTemplate) {
|
|
OverrideImplementUtil.setupMethodBody(method, overridden, containingClass);
|
|
}
|
|
OverrideImplementUtil.annotateOnOverrideImplement(method, base, overridden);
|
|
}
|
|
|
|
/**
|
|
* to be deleted in 2017.2
|
|
*/
|
|
@Deprecated
|
|
public static void copyOrReplaceModifierList(@NotNull PsiModifierListOwner sourceParam, @NotNull PsiModifierListOwner targetParam) {
|
|
copyOrReplaceModifierList(sourceParam, null, targetParam);
|
|
}
|
|
|
|
public static void copyOrReplaceModifierList(@NotNull PsiModifierListOwner sourceParam, @Nullable PsiElement targetClass, @NotNull PsiModifierListOwner targetParam) {
|
|
PsiModifierList sourceModifierList = sourceParam.getModifierList();
|
|
PsiModifierList targetModifierList = targetParam.getModifierList();
|
|
|
|
if (sourceModifierList != null && targetModifierList != null) {
|
|
for (@PsiModifier.ModifierConstant String m : PsiModifier.MODIFIERS) {
|
|
targetModifierList.setModifierProperty(m, sourceParam.hasModifierProperty(m));
|
|
}
|
|
|
|
OverrideImplementsAnnotationsHandler.repeatAnnotationsFromSource(sourceParam, targetClass, targetParam);
|
|
}
|
|
}
|
|
|
|
//java bean getters/setters
|
|
public static PsiMethod generateSimpleGetterPrototype(@NotNull PsiField field) {
|
|
return generatePrototype(field, PropertyUtil.generateGetterPrototype(field));
|
|
}
|
|
|
|
public static PsiMethod generateSimpleSetterPrototype(@NotNull PsiField field) {
|
|
return generatePrototype(field, PropertyUtil.generateSetterPrototype(field));
|
|
}
|
|
|
|
public static PsiMethod generateSimpleSetterPrototype(PsiField field, PsiClass targetClass) {
|
|
return generatePrototype(field, PropertyUtil.generateSetterPrototype(field, targetClass));
|
|
}
|
|
|
|
//custom getters/setters
|
|
public static String suggestGetterName(PsiField field) {
|
|
final PsiMethod prototype = generateGetterPrototype(field);
|
|
return prototype != null ? prototype.getName() : PropertyUtil.suggestGetterName(field);
|
|
}
|
|
|
|
public static String suggestGetterName(String name, PsiType type, Project project) {
|
|
return suggestGetterName(JavaPsiFacade.getElementFactory(project).createField(name, type instanceof PsiEllipsisType ? ((PsiEllipsisType)type).toArrayType() : type));
|
|
}
|
|
|
|
public static String suggestSetterName(PsiField field) {
|
|
final PsiMethod prototype = generateSetterPrototype(field);
|
|
return prototype != null ? prototype.getName() : PropertyUtil.suggestSetterName(field);
|
|
}
|
|
|
|
public static String suggestSetterName(String name, PsiType type, Project project) {
|
|
return suggestSetterName(JavaPsiFacade.getElementFactory(project).createField(name, type instanceof PsiEllipsisType ? ((PsiEllipsisType)type).toArrayType() : type));
|
|
}
|
|
|
|
public static PsiMethod generateGetterPrototype(@NotNull PsiField field) {
|
|
return generateGetterPrototype(field, true);
|
|
}
|
|
|
|
public static PsiMethod generateSetterPrototype(@NotNull PsiField field) {
|
|
return generateSetterPrototype(field, true);
|
|
}
|
|
|
|
public static PsiMethod generateSetterPrototype(@NotNull PsiField field, PsiClass aClass) {
|
|
return generatePrototype(field, aClass, true, SetterTemplatesManager.getInstance());
|
|
}
|
|
|
|
static PsiMethod generateGetterPrototype(@NotNull PsiField field, boolean ignoreInvalidTemplate) {
|
|
return generatePrototype(field, field.getContainingClass(), ignoreInvalidTemplate, GetterTemplatesManager.getInstance());
|
|
}
|
|
|
|
static PsiMethod generateSetterPrototype(@NotNull PsiField field, boolean ignoreInvalidTemplate) {
|
|
return generatePrototype(field, field.getContainingClass(), ignoreInvalidTemplate, SetterTemplatesManager.getInstance());
|
|
}
|
|
|
|
private static PsiMethod generatePrototype(@NotNull PsiField field,
|
|
PsiClass psiClass,
|
|
boolean ignoreInvalidTemplate,
|
|
TemplatesManager templatesManager) {
|
|
Project project = field.getProject();
|
|
PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
|
|
String template = templatesManager.getDefaultTemplate().getTemplate();
|
|
String methodText = GenerationUtil.velocityGenerateCode(psiClass, Collections.singletonList(field), new HashMap<>(), template, 0, false);
|
|
|
|
boolean isGetter = templatesManager instanceof GetterTemplatesManager;
|
|
PsiMethod result;
|
|
try {
|
|
result = factory.createMethodFromText(methodText, psiClass);
|
|
}
|
|
catch (IncorrectOperationException e) {
|
|
if (ignoreInvalidTemplate) {
|
|
LOG.info(e);
|
|
result = isGetter ? PropertyUtil.generateGetterPrototype(field) : PropertyUtil.generateSetterPrototype(field);
|
|
assert result != null : field.getText();
|
|
}
|
|
else {
|
|
throw new GenerateCodeException(e);
|
|
}
|
|
}
|
|
result = (PsiMethod)CodeStyleManager.getInstance(project).reformat(result);
|
|
|
|
PsiModifierListOwner annotationTarget;
|
|
if (isGetter) {
|
|
annotationTarget = result;
|
|
}
|
|
else {
|
|
final PsiParameter[] parameters = result.getParameterList().getParameters();
|
|
annotationTarget = parameters.length == 1 ? parameters[0] : null;
|
|
}
|
|
if (annotationTarget != null) {
|
|
NullableNotNullManager.getInstance(project).copyNullableOrNotNullAnnotation(field, annotationTarget);
|
|
}
|
|
|
|
return generatePrototype(field, result);
|
|
}
|
|
|
|
@Nullable
|
|
private static PsiMethod generatePrototype(@NotNull PsiField field, PsiMethod result) {
|
|
return setVisibility(field, annotateOnOverrideImplement(field.getContainingClass(), result));
|
|
}
|
|
|
|
@Contract("_, null -> null")
|
|
public static PsiMethod setVisibility(PsiMember member, PsiMethod prototype) {
|
|
if (prototype == null) return null;
|
|
|
|
String visibility = CodeStyleSettingsManager.getSettings(member.getProject()).VISIBILITY;
|
|
|
|
@PsiModifier.ModifierConstant String newVisibility;
|
|
if (VisibilityUtil.ESCALATE_VISIBILITY.equals(visibility)) {
|
|
PsiClass aClass = member instanceof PsiClass ? (PsiClass)member : member.getContainingClass();
|
|
newVisibility = PsiUtil.getMaximumModifierForMember(aClass, false);
|
|
}
|
|
else {
|
|
//noinspection MagicConstant
|
|
newVisibility = visibility;
|
|
}
|
|
VisibilityUtil.setVisibility(prototype.getModifierList(), newVisibility);
|
|
|
|
return prototype;
|
|
}
|
|
|
|
@Nullable
|
|
public static PsiMethod annotateOnOverrideImplement(@Nullable PsiClass targetClass, @Nullable PsiMethod generated) {
|
|
if (generated == null || targetClass == null) return generated;
|
|
|
|
if (CodeStyleSettingsManager.getSettings(targetClass.getProject()).INSERT_OVERRIDE_ANNOTATION) {
|
|
PsiMethod superMethod = targetClass.findMethodBySignature(generated, true);
|
|
if (superMethod != null && superMethod.getContainingClass() != targetClass) {
|
|
OverrideImplementUtil.annotateOnOverrideImplement(generated, targetClass, superMethod, true);
|
|
}
|
|
}
|
|
return generated;
|
|
}
|
|
}
|