mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
IDEA-20892 implemented
This commit is contained in:
+2
-1
@@ -288,6 +288,7 @@ public class HighlightControlFlowUtil {
|
||||
TextRange range = HighlightNamesUtil.getFieldDeclarationTextRange(field);
|
||||
final HighlightInfo highlightInfo = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, range.getStartOffset(), range.getEndOffset(), description);
|
||||
QuickFixAction.registerQuickFixAction(highlightInfo, HighlightMethodUtil.getFixRange(field), new CreateConstructorParameterFromFieldFix(field));
|
||||
QuickFixAction.registerQuickFixAction(highlightInfo, HighlightMethodUtil.getFixRange(field), new InitializeFinalFieldInConstructorFix(field));
|
||||
final PsiClass containingClass = field.getContainingClass();
|
||||
if (containingClass != null && !containingClass.isInterface()) {
|
||||
IntentionAction fix = QUICK_FIX_FACTORY.createModifierListFix(field, PsiModifier.FINAL, false, false);
|
||||
@@ -317,7 +318,7 @@ public class HighlightControlFlowUtil {
|
||||
if (scope instanceof PsiCodeBlock && scope.getParent() instanceof PsiSwitchStatement) {
|
||||
scope = PsiTreeUtil.getParentOfType(scope, PsiCodeBlock.class);
|
||||
}
|
||||
|
||||
|
||||
topBlock = JspPsiUtil.isInJspFile(scope) && scope instanceof PsiFile ? scope : PsiUtil.getTopLevelEnclosingCodeBlock(expression, scope);
|
||||
if (variable instanceof PsiField) {
|
||||
// non final field already initialized with default value
|
||||
|
||||
+248
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* 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.daemon.impl.quickfix;
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightUtilBase;
|
||||
import com.intellij.codeInsight.daemon.QuickFixBundle;
|
||||
import com.intellij.codeInsight.generation.PsiMethodMember;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.ide.util.MemberChooser;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.ScrollType;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.refactoring.util.RefactoringUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class InitializeFinalFieldInConstructorFix implements IntentionAction {
|
||||
private final PsiField myField;
|
||||
|
||||
public InitializeFinalFieldInConstructorFix(@NotNull PsiField field) {
|
||||
myField = field;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getText() {
|
||||
return QuickFixBundle.message("initialize.final.field.in.constructor.name");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
|
||||
PsiClass containingClass = myField == null ? null : myField.getContainingClass();
|
||||
return myField != null
|
||||
&& myField.getManager().isInProject(myField)
|
||||
&& !myField.hasModifierProperty(PsiModifier.STATIC)
|
||||
&& myField.isValid()
|
||||
&& !myField.hasInitializer()
|
||||
&& containingClass != null
|
||||
&& containingClass.getName() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException {
|
||||
if (!CodeInsightUtilBase.prepareFileForWrite(file)) return;
|
||||
|
||||
final PsiClass myClass = myField.getContainingClass();
|
||||
|
||||
if (myClass.getConstructors().length == 0) {
|
||||
createDefaultConstructor(myClass, project, editor, file);
|
||||
}
|
||||
|
||||
final List<PsiMethod> constructors = choose(sort(filterIfFieldAlreadyAssigned(myField, myClass.getConstructors())), project);
|
||||
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final List<PsiExpressionStatement> statements = addFieldInitialization(constructors, myField, project);
|
||||
final PsiExpressionStatement highestStatement = getHighestElement(statements);
|
||||
if (highestStatement == null) return;
|
||||
|
||||
final PsiAssignmentExpression expression = (PsiAssignmentExpression)highestStatement.getExpression();
|
||||
final PsiElement rightExpression = expression.getRExpression();
|
||||
|
||||
final TextRange expressionRange = rightExpression.getTextRange();
|
||||
editor.getCaretModel().moveToOffset(expressionRange.getStartOffset());
|
||||
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
|
||||
editor.getSelectionModel().setSelection(expressionRange.getStartOffset(), expressionRange.getEndOffset());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private <T extends PsiElement> T getHighestElement(@NotNull List<T> elements) {
|
||||
T highest = null;
|
||||
int highestTextOffset = Integer.MAX_VALUE;
|
||||
for (T element : elements) {
|
||||
final T forcedElem = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(element);
|
||||
final int startOffset = forcedElem.getTextOffset();
|
||||
if (startOffset < highestTextOffset) {
|
||||
highest = forcedElem;
|
||||
highestTextOffset = startOffset;
|
||||
}
|
||||
}
|
||||
return highest;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<PsiExpressionStatement> addFieldInitialization(@NotNull List<PsiMethod> constructors,
|
||||
@NotNull PsiField field,
|
||||
@NotNull Project project) {
|
||||
final List<PsiExpressionStatement> statements = new ArrayList<PsiExpressionStatement>();
|
||||
for (PsiMethod constructor : constructors) {
|
||||
final PsiExpressionStatement statement = addFieldInitialization(constructor, field, project);
|
||||
if (statement != null) {
|
||||
statements.add(statement);
|
||||
}
|
||||
}
|
||||
return statements;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiExpressionStatement addFieldInitialization(@NotNull PsiMethod constructor,
|
||||
@NotNull PsiField field,
|
||||
@NotNull Project project) {
|
||||
PsiCodeBlock methodBody = constructor.getBody();
|
||||
if (methodBody == null) return null;
|
||||
|
||||
final String fieldName = field.getName();
|
||||
String stmtText = fieldName + " = " + suggestInitValue(field) + ";";
|
||||
if (methodContainsParameterWithName(constructor, fieldName)) {
|
||||
stmtText = "this." + stmtText;
|
||||
}
|
||||
|
||||
final PsiManager psiManager = PsiManager.getInstance(project);
|
||||
final PsiElementFactory factory = JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory();
|
||||
final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
|
||||
|
||||
return (PsiExpressionStatement)methodBody.add(codeStyleManager.reformat(factory.createStatementFromText(stmtText, methodBody)));
|
||||
}
|
||||
|
||||
private static boolean methodContainsParameterWithName(@NotNull PsiMethod constructor, @NotNull String name) {
|
||||
for (PsiParameter parameter : constructor.getParameterList().getParameters()) {
|
||||
if (name.equals(parameter.getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<PsiMethod> choose(@NotNull PsiMethod[] ctors, @NotNull final Project project) {
|
||||
if (ctors.length == 1) {
|
||||
return Arrays.asList(ctors[0]);
|
||||
}
|
||||
|
||||
if (ctors.length > 1) {
|
||||
final MemberChooser<PsiMethodMember> chooser = new MemberChooser<PsiMethodMember>(toPsiMethodMemberArray(ctors), false, true, project);
|
||||
chooser.setTitle(QuickFixBundle.message("initialize.final.field.in.constructor.choose.dialog.title"));
|
||||
chooser.show();
|
||||
|
||||
final List<PsiMethodMember> chosenMembers = chooser.getSelectedElements();
|
||||
if (chosenMembers != null) {
|
||||
return Arrays.asList(toPsiMethodArray(chosenMembers));
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static PsiMethodMember[] toPsiMethodMemberArray(@NotNull PsiMethod[] methods) {
|
||||
final PsiMethodMember[] result = new PsiMethodMember[methods.length];
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
result[i] = new PsiMethodMember(methods[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static PsiMethod[] toPsiMethodArray(@NotNull List<PsiMethodMember> methodMembers) {
|
||||
final PsiMethod[] result = new PsiMethod[methodMembers.size()];
|
||||
int i = 0;
|
||||
for (PsiMethodMember methodMember : methodMembers) {
|
||||
result[i++] = methodMember.getElement();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void createDefaultConstructor(PsiClass psiClass, @NotNull final Project project, final Editor editor, final PsiFile file) {
|
||||
final AddDefaultConstructorFix defaultConstructorFix = new AddDefaultConstructorFix(psiClass);
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
defaultConstructorFix.invoke(project, editor, file);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static PsiMethod[] filterIfFieldAlreadyAssigned(@NotNull PsiField field, @NotNull PsiMethod[] ctors) {
|
||||
final List<PsiMethod> result = new ArrayList<PsiMethod>(Arrays.asList(ctors));
|
||||
for (PsiReference reference : ReferencesSearch.search(field, new LocalSearchScope(ctors))) {
|
||||
final PsiElement element = reference.getElement();
|
||||
if (element instanceof PsiReferenceExpression && PsiUtil.isOnAssignmentLeftHand((PsiExpression)element)) {
|
||||
result.remove(PsiTreeUtil.getParentOfType(element, PsiMethod.class));
|
||||
}
|
||||
}
|
||||
return result.toArray(new PsiMethod[result.size()]);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PsiMethod[] sort(@NotNull PsiMethod[] ctors) {
|
||||
Arrays.sort(ctors, new Comparator<PsiMethod>() {
|
||||
@Override
|
||||
public int compare(PsiMethod c1, PsiMethod c2) {
|
||||
final PsiMethod cc1 = RefactoringUtil.getChainedConstructor(c1);
|
||||
final PsiMethod cc2 = RefactoringUtil.getChainedConstructor(c2);
|
||||
if (cc1 == c2) return 1;
|
||||
if (cc2 == c1) return -1;
|
||||
if (cc1 == null) {
|
||||
return cc2 == null ? 0 : compare(c1, cc2);
|
||||
} else {
|
||||
return cc2 == null ? compare(cc1, c2) : compare(cc1, cc2);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return ctors;
|
||||
}
|
||||
|
||||
private static String suggestInitValue(@NotNull PsiField field) {
|
||||
PsiType type = field.getType();
|
||||
return PsiTypesUtil.getDefaultValueOfType(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startInWriteAction() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user