mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
+2
-2
@@ -417,7 +417,7 @@ public class HighlightControlFlowUtil {
|
||||
if (codeBlockProblems == null) {
|
||||
try {
|
||||
final ControlFlow controlFlow = getControlFlow(topBlock);
|
||||
codeBlockProblems = ControlFlowUtil.getReadBeforeWrite(controlFlow);
|
||||
codeBlockProblems = ControlFlowUtil.getReadBeforeWriteLocals(controlFlow);
|
||||
}
|
||||
catch (AnalysisCanceledException e) {
|
||||
codeBlockProblems = Collections.emptyList();
|
||||
@@ -570,7 +570,7 @@ public class HighlightControlFlowUtil {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Collection<ControlFlowUtil.VariableInfo> getFinalVariableProblemsInBlock(Map<PsiElement,Collection<ControlFlowUtil.VariableInfo>> finalVarProblems, PsiElement codeBlock) {
|
||||
public static Collection<ControlFlowUtil.VariableInfo> getFinalVariableProblemsInBlock(Map<PsiElement,Collection<ControlFlowUtil.VariableInfo>> finalVarProblems, PsiElement codeBlock) {
|
||||
Collection<ControlFlowUtil.VariableInfo> codeBlockProblems = finalVarProblems.get(codeBlock);
|
||||
if (codeBlockProblems == null) {
|
||||
try {
|
||||
|
||||
@@ -320,7 +320,7 @@ public class GenerateMembersUtil {
|
||||
super.visitReferenceElement(reference);
|
||||
final PsiElement resolve = reference.resolve();
|
||||
if (resolve instanceof PsiTypeParameter) {
|
||||
replacementMap.put(reference, factory.createReferenceElementByType((PsiClassType)substitutor.substitute((PsiTypeParameter)resolve)));
|
||||
replacementMap.put(reference, factory.createReferenceElementByType((PsiClassType)substituteType(substitutor, factory.createType((PsiTypeParameter)resolve))));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
+269
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* 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.codeInspection.varScopeCanBeNarrowed;
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightUtil;
|
||||
import com.intellij.codeInspection.InspectionsBundle;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.ProblemDescriptor;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.ScrollType;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.IJSwingUtilities;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.NotNullFunction;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* refactored from {@link com.intellij.codeInspection.varScopeCanBeNarrowed.FieldCanBeLocalInspection.MyQuickFix}
|
||||
*
|
||||
* @author Danila Ponomarenko
|
||||
*/
|
||||
public abstract class BaseConvertToLocalQuickFix<V extends PsiVariable> implements LocalQuickFix {
|
||||
private static final Logger LOG = Logger.getInstance(BaseConvertToLocalQuickFix.class);
|
||||
|
||||
@NotNull
|
||||
public final String getName() {
|
||||
return InspectionsBundle.message("inspection.convert.to.local.quickfix");
|
||||
}
|
||||
|
||||
public final void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
final V variable = getVariable(descriptor);
|
||||
final PsiFile myFile = variable.getContainingFile();
|
||||
if (variable == null || !variable.isValid()) return; //weird. should not get here when field becomes invalid
|
||||
|
||||
try {
|
||||
final PsiElement newDeclaration = moveDeclaration(project, variable);
|
||||
if (newDeclaration == null) return;
|
||||
|
||||
positionCaretToDeclaration(project, myFile, newDeclaration);
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected abstract V getVariable(@NotNull ProblemDescriptor descriptor);
|
||||
|
||||
private static void positionCaretToDeclaration(@NotNull Project project, @NotNull PsiFile psiFile, @NotNull PsiElement declaration) {
|
||||
final Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
|
||||
if (editor != null && (IJSwingUtilities.hasFocus(editor.getComponent()) || ApplicationManager.getApplication().isUnitTestMode())) {
|
||||
final PsiFile openedFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
|
||||
if (openedFile == psiFile) {
|
||||
editor.getCaretModel().moveToOffset(declaration.getTextOffset());
|
||||
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void beforeDelete(@NotNull Project project, @NotNull V variable, @NotNull PsiElement newDeclaration) {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiElement moveDeclaration(@NotNull Project project, @NotNull V variable) {
|
||||
final Collection<PsiReference> references = ReferencesSearch.search(variable).findAll();
|
||||
if (references.isEmpty()) return null;
|
||||
|
||||
final PsiCodeBlock anchorBlock = findAnchorBlock(references);
|
||||
if (anchorBlock == null) return null; //was assert, but need to fix the case when obsolete inspection highlighting is left
|
||||
if (!CodeInsightUtil.preparePsiElementsForWrite(anchorBlock)) return null;
|
||||
|
||||
final PsiElement firstElement = getLowestOffsetElement(references);
|
||||
final String localName = suggestLocalName(project, variable, anchorBlock);
|
||||
|
||||
final PsiElement anchor = getAnchorElement(anchorBlock, firstElement);
|
||||
|
||||
|
||||
final PsiAssignmentExpression anchorAssignmentExpression = searchAssignmentExpression(anchor);
|
||||
if (anchorAssignmentExpression != null && isVariableAssignment(anchorAssignmentExpression, variable)) {
|
||||
final Set<PsiReference> refsSet = new HashSet<PsiReference>(references);
|
||||
refsSet.remove(anchorAssignmentExpression.getLExpression());
|
||||
return applyChanges(
|
||||
project,
|
||||
localName,
|
||||
anchorAssignmentExpression.getRExpression(),
|
||||
variable,
|
||||
refsSet,
|
||||
new NotNullFunction<PsiDeclarationStatement, PsiElement>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElement fun(PsiDeclarationStatement declaration) {
|
||||
if (!mayBeFinal(firstElement, references)) {
|
||||
PsiUtil.setModifierProperty((PsiModifierListOwner)declaration.getDeclaredElements()[0], PsiModifier.FINAL, false);
|
||||
}
|
||||
return anchor.replace(declaration);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return applyChanges(
|
||||
project,
|
||||
localName,
|
||||
variable.getInitializer(),
|
||||
variable,
|
||||
references,
|
||||
new NotNullFunction<PsiDeclarationStatement, PsiElement>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElement fun(PsiDeclarationStatement declaration) {
|
||||
return anchorBlock.addBefore(declaration, anchor);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private PsiElement applyChanges(final @NotNull Project project,
|
||||
final @NotNull String localName,
|
||||
final @Nullable PsiExpression initializer,
|
||||
final @NotNull V variable,
|
||||
final @NotNull Collection<PsiReference> references,
|
||||
final @NotNull NotNullFunction<PsiDeclarationStatement, PsiElement> action) {
|
||||
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
|
||||
|
||||
return ApplicationManager.getApplication().runWriteAction(
|
||||
new Computable<PsiElement>() {
|
||||
@Override
|
||||
public PsiElement compute() {
|
||||
final PsiDeclarationStatement declaration =
|
||||
elementFactory.createVariableDeclarationStatement(localName, variable.getType(), initializer);
|
||||
final PsiElement newDeclaration = action.fun(declaration);
|
||||
retargetReferences(elementFactory, localName, references);
|
||||
beforeDelete(project, variable, newDeclaration);
|
||||
variable.normalizeDeclaration();
|
||||
variable.delete();
|
||||
return newDeclaration;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiAssignmentExpression searchAssignmentExpression(@NotNull PsiElement anchor) {
|
||||
if (!(anchor instanceof PsiExpressionStatement)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final PsiExpression anchorExpression = ((PsiExpressionStatement)anchor).getExpression();
|
||||
|
||||
if (!(anchorExpression instanceof PsiAssignmentExpression)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (PsiAssignmentExpression)anchorExpression;
|
||||
}
|
||||
|
||||
private static boolean isVariableAssignment(@NotNull PsiAssignmentExpression expression, @NotNull PsiVariable variable) {
|
||||
if (expression.getOperationTokenType() != JavaTokenType.EQ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(expression.getLExpression() instanceof PsiReferenceExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final PsiReferenceExpression leftExpression = (PsiReferenceExpression)expression.getLExpression();
|
||||
|
||||
if (!leftExpression.isReferenceTo(variable)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected abstract String suggestLocalName(@NotNull Project project, @NotNull V variable, @NotNull PsiCodeBlock scope);
|
||||
|
||||
private static boolean mayBeFinal(PsiElement firstElement, @NotNull Collection<PsiReference> references) {
|
||||
for (PsiReference reference : references) {
|
||||
final PsiElement element = reference.getElement();
|
||||
if (element == firstElement) continue;
|
||||
if (element instanceof PsiExpression && PsiUtil.isAccessedForWriting((PsiExpression)element)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void retargetReferences(PsiElementFactory elementFactory, String localName, Collection<PsiReference> refs)
|
||||
throws IncorrectOperationException {
|
||||
final PsiReferenceExpression refExpr = (PsiReferenceExpression)elementFactory.createExpressionFromText(localName, null);
|
||||
for (PsiReference ref : refs) {
|
||||
if (ref instanceof PsiReferenceExpression) {
|
||||
((PsiReferenceExpression)ref).replace(refExpr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getFamilyName() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
private static PsiElement getAnchorElement(PsiCodeBlock anchorBlock, @NotNull PsiElement firstElement) {
|
||||
PsiElement element = firstElement;
|
||||
while (element != null && element.getParent() != anchorBlock) {
|
||||
element = element.getParent();
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement getLowestOffsetElement(@NotNull Collection<PsiReference> refs) {
|
||||
PsiElement firstElement = null;
|
||||
for (PsiReference reference : refs) {
|
||||
final PsiElement element = reference.getElement();
|
||||
if (firstElement == null || firstElement.getTextRange().getStartOffset() > element.getTextRange().getStartOffset()) {
|
||||
firstElement = element;
|
||||
}
|
||||
}
|
||||
return firstElement;
|
||||
}
|
||||
|
||||
private static PsiCodeBlock findAnchorBlock(final Collection<PsiReference> refs) {
|
||||
PsiCodeBlock result = null;
|
||||
for (PsiReference psiReference : refs) {
|
||||
final PsiElement element = psiReference.getElement();
|
||||
PsiCodeBlock block = PsiTreeUtil.getParentOfType(element, PsiCodeBlock.class);
|
||||
if (result == null || block == null) {
|
||||
result = block;
|
||||
}
|
||||
else {
|
||||
final PsiElement commonParent = PsiTreeUtil.findCommonParent(result, block);
|
||||
result = PsiTreeUtil.getParentOfType(commonParent, PsiCodeBlock.class, false);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public boolean runForWholeFile() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+39
-179
@@ -16,21 +16,15 @@
|
||||
package com.intellij.codeInspection.varScopeCanBeNarrowed;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.CodeInsightUtil;
|
||||
import com.intellij.codeInsight.daemon.GroupNames;
|
||||
import com.intellij.codeInsight.daemon.ImplicitUsageProvider;
|
||||
import com.intellij.codeInspection.InspectionsBundle;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.ProblemDescriptor;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.codeInspection.ex.BaseLocalInspectionTool;
|
||||
import com.intellij.codeInspection.util.SpecialAnnotationsUtil;
|
||||
import com.intellij.lang.java.JavaCommenter;
|
||||
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.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.JDOMExternalizableStringList;
|
||||
@@ -41,13 +35,9 @@ import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.VariableKind;
|
||||
import com.intellij.psi.controlFlow.*;
|
||||
import com.intellij.psi.javadoc.PsiDocComment;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.refactoring.util.RefactoringUtil;
|
||||
import com.intellij.util.IJSwingUtilities;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -65,8 +55,6 @@ import java.util.Set;
|
||||
* @author ven
|
||||
*/
|
||||
public class FieldCanBeLocalInspection extends BaseLocalInspectionTool {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.varScopeCanBeNarrowed.FieldCanBeLocalInspection");
|
||||
|
||||
@NonNls public static final String SHORT_NAME = "FieldCanBeLocal";
|
||||
public final JDOMExternalizableStringList EXCLUDE_ANNOS = new JDOMExternalizableStringList();
|
||||
|
||||
@@ -110,13 +98,13 @@ public class FieldCanBeLocalInspection extends BaseLocalInspectionTool {
|
||||
@Override
|
||||
public void visitJavaFile(PsiJavaFile file) {
|
||||
for (PsiClass aClass : file.getClasses()) {
|
||||
docheckClass(aClass, holder, EXCLUDE_ANNOS);
|
||||
doCheckClass(aClass, holder, EXCLUDE_ANNOS);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void docheckClass(final PsiClass aClass, ProblemsHolder holder, final List<String> excludeAnnos) {
|
||||
private static void doCheckClass(final PsiClass aClass, ProblemsHolder holder, final List<String> excludeAnnos) {
|
||||
if (aClass.isInterface()) return;
|
||||
final PsiField[] fields = aClass.getFields();
|
||||
final Set<PsiField> candidates = new LinkedHashSet<PsiField>();
|
||||
@@ -129,6 +117,7 @@ public class FieldCanBeLocalInspection extends BaseLocalInspectionTool {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
removeFieldsReferencedFromInitializers(aClass, candidates);
|
||||
if (candidates.isEmpty()) return;
|
||||
|
||||
@@ -141,7 +130,7 @@ public class FieldCanBeLocalInspection extends BaseLocalInspectionTool {
|
||||
for (PsiField field : candidates) {
|
||||
if (usedFields.contains(field) && !hasImplicitReadOrWriteUsage(field, implicitUsageProviders)) {
|
||||
final String message = InspectionsBundle.message("inspection.field.can.be.local.problem.descriptor");
|
||||
holder.registerProblem(field.getNameIdentifier(), message, new MyQuickFix());
|
||||
holder.registerProblem(field.getNameIdentifier(), message, new ConvertFieldToLocalQuickFix());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,7 +178,7 @@ public class FieldCanBeLocalInspection extends BaseLocalInspectionTool {
|
||||
final PsiElement resolved = readBeforeWrite.resolve();
|
||||
if (resolved instanceof PsiField) {
|
||||
final PsiField field = (PsiField)resolved;
|
||||
if (!isImmutableState(field.getType()) || !PsiUtil.isConstantExpression(field.getInitializer()) || getWrittenVariables(controlFlow, writtenVariables).contains(field)){
|
||||
if (!isImmutableState(field.getType()) || !PsiUtil.isConstantExpression(field.getInitializer()) || getWrittenVariables(controlFlow, writtenVariables).contains(field)) {
|
||||
PsiElement parent = body.getParent();
|
||||
if (!(parent instanceof PsiMethod) ||
|
||||
!((PsiMethod)parent).isConstructor() ||
|
||||
@@ -222,15 +211,18 @@ public class FieldCanBeLocalInspection extends BaseLocalInspectionTool {
|
||||
|
||||
private static void removeFieldsReferencedFromInitializers(final PsiClass aClass, final Set<PsiField> candidates) {
|
||||
aClass.accept(new JavaRecursiveElementWalkingVisitor() {
|
||||
@Override public void visitMethod(PsiMethod method) {
|
||||
@Override
|
||||
public void visitMethod(PsiMethod method) {
|
||||
//do not go inside method
|
||||
}
|
||||
|
||||
@Override public void visitClassInitializer(PsiClassInitializer initializer) {
|
||||
@Override
|
||||
public void visitClassInitializer(PsiClassInitializer initializer) {
|
||||
//do not go inside class initializer
|
||||
}
|
||||
|
||||
@Override public void visitReferenceExpression(PsiReferenceExpression expression) {
|
||||
@Override
|
||||
public void visitReferenceExpression(PsiReferenceExpression expression) {
|
||||
final PsiElement resolved = expression.resolve();
|
||||
if (resolved instanceof PsiField) {
|
||||
final PsiField field = (PsiField)resolved;
|
||||
@@ -245,7 +237,7 @@ public class FieldCanBeLocalInspection extends BaseLocalInspectionTool {
|
||||
}
|
||||
|
||||
private static boolean hasImplicitReadOrWriteUsage(final PsiField field, ImplicitUsageProvider[] implicitUsageProviders) {
|
||||
for(ImplicitUsageProvider provider: implicitUsageProviders) {
|
||||
for (ImplicitUsageProvider provider : implicitUsageProviders) {
|
||||
if (provider.isImplicitRead(field) || provider.isImplicitWrite(field)) {
|
||||
return true;
|
||||
}
|
||||
@@ -253,173 +245,41 @@ public class FieldCanBeLocalInspection extends BaseLocalInspectionTool {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class MyQuickFix implements LocalQuickFix {
|
||||
@NotNull
|
||||
public String getName() {
|
||||
return InspectionsBundle.message("inspection.field.can.be.local.quickfix");
|
||||
private static class ConvertFieldToLocalQuickFix extends BaseConvertToLocalQuickFix<PsiField> {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected PsiField getVariable(@NotNull ProblemDescriptor descriptor) {
|
||||
return PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiField.class);
|
||||
}
|
||||
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
PsiElement element = descriptor.getPsiElement();
|
||||
PsiField myField = PsiTreeUtil.getParentOfType(element, PsiField.class);
|
||||
if (myField == null || !myField.isValid()) return; //weird. should not get here when field becomes invalid
|
||||
@Override
|
||||
protected void beforeDelete(@NotNull Project project, @NotNull PsiField variable, @NotNull PsiElement newDeclaration) {
|
||||
final PsiDocComment docComment = variable.getDocComment();
|
||||
if (docComment != null) moveDocCommentToDeclaration(project, docComment, newDeclaration);
|
||||
}
|
||||
|
||||
final PsiDocComment docComment = myField.getDocComment();
|
||||
final Collection<PsiReference> refs = ReferencesSearch.search(myField).findAll();
|
||||
if (refs.isEmpty()) return;
|
||||
Set<PsiReference> refsSet = new HashSet<PsiReference>(refs);
|
||||
PsiCodeBlock anchorBlock = findAnchorBlock(refs);
|
||||
if (anchorBlock == null) return; //was assert, but need to fix the case when obsolete inspection highlighting is left
|
||||
if (!CodeInsightUtil.preparePsiElementsForWrite(anchorBlock)) return;
|
||||
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory();
|
||||
@NotNull
|
||||
@Override
|
||||
protected String suggestLocalName(@NotNull Project project, @NotNull PsiField field, @NotNull PsiCodeBlock scope) {
|
||||
final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(project);
|
||||
final String propertyName = styleManager.variableNameToPropertyName(myField.getName(), VariableKind.FIELD);
|
||||
String localName = styleManager.propertyNameToVariableName(propertyName, VariableKind.LOCAL_VARIABLE);
|
||||
localName = RefactoringUtil.suggestUniqueVariableName(localName, anchorBlock, myField);
|
||||
PsiElement firstElement = getFirstElement(refs);
|
||||
boolean mayBeFinal = mayBeFinal(refsSet, firstElement);
|
||||
PsiElement newDeclaration = null;
|
||||
try {
|
||||
final PsiElement anchor = getAnchorElement(anchorBlock, firstElement);
|
||||
if (anchor instanceof PsiExpressionStatement &&
|
||||
((PsiExpressionStatement) anchor).getExpression() instanceof PsiAssignmentExpression) {
|
||||
final PsiAssignmentExpression expression = (PsiAssignmentExpression) ((PsiExpressionStatement) anchor).getExpression();
|
||||
if (expression.getOperationTokenType() == JavaTokenType.EQ &&
|
||||
expression.getLExpression() instanceof PsiReferenceExpression &&
|
||||
((PsiReference)expression.getLExpression()).isReferenceTo(myField)) {
|
||||
final PsiExpression initializer = expression.getRExpression();
|
||||
final PsiDeclarationStatement decl = elementFactory.createVariableDeclarationStatement(localName, myField.getType(), initializer);
|
||||
if (!mayBeFinal) {
|
||||
PsiUtil.setModifierProperty((PsiModifierListOwner)decl.getDeclaredElements()[0], PsiModifier.FINAL, false);
|
||||
}
|
||||
newDeclaration = anchor.replace(decl);
|
||||
refsSet.remove(expression.getLExpression());
|
||||
retargetReferences(elementFactory, localName, refsSet);
|
||||
}
|
||||
else {
|
||||
newDeclaration = addDeclarationWithFieldInitializerAndRetargetReferences(elementFactory, localName, anchorBlock, anchor, refsSet,
|
||||
myField);
|
||||
}
|
||||
}
|
||||
else {
|
||||
newDeclaration = addDeclarationWithFieldInitializerAndRetargetReferences(elementFactory, localName, anchorBlock, anchor, refsSet,
|
||||
myField);
|
||||
}
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
|
||||
if (newDeclaration != null) {
|
||||
if (docComment != null) {
|
||||
final StringBuilder buf = new StringBuilder();
|
||||
for (PsiElement psiElement : docComment.getDescriptionElements()) {
|
||||
buf.append(psiElement.getText());
|
||||
}
|
||||
if (buf.length() > 0) {
|
||||
final JavaCommenter commenter = new JavaCommenter();
|
||||
final PsiComment comment = JavaPsiFacade.getElementFactory(project)
|
||||
.createCommentFromText(commenter.getBlockCommentPrefix() +
|
||||
buf.toString() +
|
||||
commenter.getBlockCommentSuffix(), newDeclaration);
|
||||
newDeclaration.getParent().addBefore(comment, newDeclaration);
|
||||
}
|
||||
}
|
||||
final PsiFile psiFile = myField.getContainingFile();
|
||||
final Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
|
||||
if (editor != null && IJSwingUtilities.hasFocus(editor.getComponent())) {
|
||||
final PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
|
||||
if (file == psiFile) {
|
||||
editor.getCaretModel().moveToOffset(newDeclaration.getTextOffset());
|
||||
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
myField.normalizeDeclaration();
|
||||
myField.delete();
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
|
||||
final String propertyName = styleManager.variableNameToPropertyName(field.getName(), VariableKind.FIELD);
|
||||
final String localName = styleManager.propertyNameToVariableName(propertyName, VariableKind.LOCAL_VARIABLE);
|
||||
return RefactoringUtil.suggestUniqueVariableName(localName, scope, field);
|
||||
}
|
||||
|
||||
private static boolean mayBeFinal(Set<PsiReference> refsSet, PsiElement firstElement) {
|
||||
for (PsiReference ref : refsSet) {
|
||||
PsiElement element = ref.getElement();
|
||||
if (element == firstElement) continue;
|
||||
if (element instanceof PsiExpression && PsiUtil.isAccessedForWriting((PsiExpression) element)) return false;
|
||||
private static void moveDocCommentToDeclaration(@NotNull Project project, @NotNull PsiDocComment docComment, @NotNull PsiElement declaration) {
|
||||
final StringBuilder buf = new StringBuilder();
|
||||
for (PsiElement psiElement : docComment.getDescriptionElements()) {
|
||||
buf.append(psiElement.getText());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void retargetReferences(final PsiElementFactory elementFactory, final String localName, final Set<PsiReference> refs)
|
||||
throws IncorrectOperationException {
|
||||
final PsiReferenceExpression refExpr = (PsiReferenceExpression)elementFactory.createExpressionFromText(localName, null);
|
||||
for (PsiReference ref : refs) {
|
||||
if (ref instanceof PsiReferenceExpression) {
|
||||
((PsiReferenceExpression)ref).replace(refExpr);
|
||||
}
|
||||
if (buf.length() > 0) {
|
||||
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
|
||||
final JavaCommenter commenter = new JavaCommenter();
|
||||
final PsiComment comment = elementFactory.createCommentFromText(commenter.getBlockCommentPrefix() + buf.toString() + commenter.getBlockCommentSuffix(), declaration);
|
||||
declaration.getParent().addBefore(comment, declaration);
|
||||
}
|
||||
}
|
||||
|
||||
private static PsiElement addDeclarationWithFieldInitializerAndRetargetReferences(final PsiElementFactory elementFactory,
|
||||
final String localName,
|
||||
final PsiCodeBlock anchorBlock,
|
||||
final PsiElement anchor,
|
||||
final Set<PsiReference> refs,
|
||||
PsiField myField)
|
||||
throws IncorrectOperationException {
|
||||
final PsiDeclarationStatement decl = elementFactory.createVariableDeclarationStatement(localName, myField.getType(), myField.getInitializer());
|
||||
final PsiElement newDeclaration = anchorBlock.addBefore(decl, anchor);
|
||||
|
||||
retargetReferences(elementFactory, localName, refs);
|
||||
return newDeclaration;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getFamilyName() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
private static PsiElement getAnchorElement(final PsiCodeBlock anchorBlock, @NotNull PsiElement firstElement) {
|
||||
PsiElement element = firstElement;
|
||||
while (element != null && element.getParent() != anchorBlock) {
|
||||
element = element.getParent();
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
private static PsiElement getFirstElement(Collection<PsiReference> refs) {
|
||||
PsiElement firstElement = null;
|
||||
for (PsiReference reference : refs) {
|
||||
final PsiElement element = reference.getElement();
|
||||
if (firstElement == null || firstElement.getTextRange().getStartOffset() > element.getTextRange().getStartOffset()) {
|
||||
firstElement = element;
|
||||
}
|
||||
}
|
||||
return firstElement;
|
||||
}
|
||||
|
||||
private static PsiCodeBlock findAnchorBlock(final Collection<PsiReference> refs) {
|
||||
PsiCodeBlock result = null;
|
||||
for (PsiReference psiReference : refs) {
|
||||
final PsiElement element = psiReference.getElement();
|
||||
PsiCodeBlock block = PsiTreeUtil.getParentOfType(element, PsiCodeBlock.class);
|
||||
if (result == null || block == null) {
|
||||
result = block;
|
||||
}
|
||||
else {
|
||||
final PsiElement commonParent = PsiTreeUtil.findCommonParent(result, block);
|
||||
result = PsiTreeUtil.getParentOfType(commonParent, PsiCodeBlock.class, false);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
public boolean runForWholeFile() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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.codeInspection.varScopeCanBeNarrowed;
|
||||
|
||||
import com.intellij.codeInsight.daemon.GroupNames;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.controlFlow.*;
|
||||
import com.intellij.psi.search.searches.SuperMethodsSearch;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Danila Ponomarenko
|
||||
*/
|
||||
public class ParameterCanBeLocalInspection extends BaseJavaLocalInspectionTool {
|
||||
|
||||
@NonNls public static final String SHORT_NAME = "ParameterCanBeLocal";
|
||||
|
||||
@NotNull
|
||||
public String getGroupDisplayName() {
|
||||
return GroupNames.CLASS_LAYOUT_GROUP_NAME;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getDisplayName() {
|
||||
return InspectionsBundle.message("inspection.parameter.can.be.local.display.name");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getShortName() {
|
||||
return SHORT_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProblemDescriptor[] checkMethod(@NotNull PsiMethod method, @NotNull InspectionManager manager, boolean isOnTheFly) {
|
||||
final Collection<PsiParameter> parameters = filterFinal(method.getParameterList().getParameters());
|
||||
final PsiCodeBlock body = method.getBody();
|
||||
if (body == null || parameters.isEmpty() || isOverrides(method)) {
|
||||
return ProblemDescriptor.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
final List<ProblemDescriptor> result = new ArrayList<ProblemDescriptor>();
|
||||
for (PsiParameter parameter : getWriteBeforeRead(parameters, body)) {
|
||||
final PsiIdentifier identifier = parameter.getNameIdentifier();
|
||||
if (identifier != null) {
|
||||
result.add(createProblem(manager, identifier, isOnTheFly));
|
||||
}
|
||||
}
|
||||
return result.toArray(new ProblemDescriptor[result.size()]);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<PsiParameter> filterFinal(PsiParameter[] parameters) {
|
||||
final List<PsiParameter> result = new ArrayList<PsiParameter>(parameters.length);
|
||||
for (PsiParameter parameter : parameters) {
|
||||
if (!parameter.hasModifierProperty(PsiModifier.FINAL)) {
|
||||
result.add(parameter);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
private static ProblemDescriptor createProblem(@NotNull InspectionManager manager,
|
||||
@NotNull PsiIdentifier identifier,
|
||||
boolean isOnTheFly) {
|
||||
return manager.createProblemDescriptor(
|
||||
identifier,
|
||||
InspectionsBundle.message("inspection.parameter.can.be.local.problem.descriptor"),
|
||||
true,
|
||||
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
|
||||
isOnTheFly,
|
||||
new ConvertParameterToLocalQuickFix()
|
||||
);
|
||||
}
|
||||
|
||||
private static Collection<PsiParameter> getWriteBeforeRead(@NotNull Collection<PsiParameter> parameters,
|
||||
@NotNull PsiCodeBlock body) {
|
||||
final ControlFlow controlFlow = getControlFlow(body);
|
||||
if (controlFlow == null) return Collections.emptyList();
|
||||
|
||||
final Set<PsiParameter> result = filterParameters(controlFlow, parameters);
|
||||
for (final PsiReferenceExpression readBeforeWrite : ControlFlowUtil.getReadBeforeWrite(controlFlow)) {
|
||||
final PsiElement resolved = readBeforeWrite.resolve();
|
||||
if (resolved instanceof PsiParameter) {
|
||||
result.remove((PsiParameter)resolved);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Set<PsiParameter> filterParameters(@NotNull ControlFlow controlFlow, @NotNull Collection<PsiParameter> parameters) {
|
||||
final Set<PsiVariable> usedVars = new HashSet<PsiVariable>(ControlFlowUtil.getUsedVariables(controlFlow, 0, controlFlow.getSize()));
|
||||
|
||||
final Set<PsiParameter> result = new HashSet<PsiParameter>();
|
||||
for (PsiParameter parameter : parameters) {
|
||||
if (usedVars.contains(parameter)) {
|
||||
result.add(parameter);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean isOverrides(PsiMethod method) {
|
||||
return SuperMethodsSearch.search(method, null, true, false).findFirst() != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ControlFlow getControlFlow(final PsiElement context) {
|
||||
try {
|
||||
return ControlFlowFactory.getInstance(context.getProject())
|
||||
.getControlFlow(context, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance());
|
||||
}
|
||||
catch (AnalysisCanceledException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConvertParameterToLocalQuickFix extends BaseConvertToLocalQuickFix<PsiParameter> {
|
||||
@Override
|
||||
protected PsiParameter getVariable(@NotNull ProblemDescriptor descriptor) {
|
||||
return (PsiParameter)descriptor.getPsiElement().getParent();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String suggestLocalName(@NotNull Project project, @NotNull PsiParameter parameter, @NotNull PsiCodeBlock scope) {
|
||||
return parameter.getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -132,7 +132,6 @@ public class DefUseUtil {
|
||||
if (body == null) {
|
||||
return null;
|
||||
}
|
||||
List<Info> unusedDefs = new ArrayList<Info>();
|
||||
|
||||
ControlFlow flow;
|
||||
try {
|
||||
@@ -223,6 +222,8 @@ public class DefUseUtil {
|
||||
}
|
||||
}
|
||||
|
||||
List<Info> unusedDefs = new ArrayList<Info>();
|
||||
|
||||
for (int i = 0; i < instructions.size(); i++) {
|
||||
Instruction instruction = instructions.get(i);
|
||||
if (instruction instanceof WriteVariableInstruction) {
|
||||
|
||||
@@ -136,4 +136,13 @@ public interface JVMElementFactory {
|
||||
|
||||
@NotNull
|
||||
PsiTypeParameter createTypeParameter(String name, PsiClassType[] superTypes);
|
||||
|
||||
/**
|
||||
* Creates a class type for the specified class.
|
||||
*
|
||||
* @param aClass the class for which the class type is created.
|
||||
* @return the class type instance.
|
||||
*/
|
||||
@NotNull
|
||||
PsiClassType createType(@NotNull PsiClass aClass);
|
||||
}
|
||||
|
||||
@@ -154,14 +154,6 @@ public interface PsiElementFactory extends PsiJavaParserFacade, JVMElementFactor
|
||||
*/
|
||||
@NotNull PsiCodeBlock createCodeBlock();
|
||||
|
||||
/**
|
||||
* Creates a class type for the specified class.
|
||||
*
|
||||
* @param aClass the class for which the class type is created.
|
||||
* @return the class type instance.
|
||||
*/
|
||||
@NotNull PsiClassType createType(@NotNull PsiClass aClass);
|
||||
|
||||
/**
|
||||
* Creates a class type for the specified class, using the specified substitutor
|
||||
* to replace generic type parameters on the class.
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.intellij.util.containers.IntArrayList;
|
||||
import gnu.trove.THashSet;
|
||||
import gnu.trove.TIntHashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -73,7 +74,7 @@ public class ControlFlowUtil {
|
||||
}
|
||||
|
||||
public static List<PsiVariable> getSSAVariables(ControlFlow flow, int from, int to,
|
||||
boolean reportVarsIfNonInitializingPathExists) {
|
||||
boolean reportVarsIfNonInitializingPathExists) {
|
||||
List<Instruction> instructions = flow.getInstructions();
|
||||
Collection<PsiVariable> writtenVariables = getWrittenVariables(flow, from, to, false);
|
||||
ArrayList<PsiVariable> result = new ArrayList<PsiVariable>(1);
|
||||
@@ -147,23 +148,25 @@ public class ControlFlowUtil {
|
||||
|
||||
private static boolean needVariableValueAt(final PsiVariable variable, final ControlFlow flow, final int offset) {
|
||||
InstructionClientVisitor<Boolean> visitor = new InstructionClientVisitor<Boolean>() {
|
||||
final boolean[] neededBelow = new boolean[flow.getSize()+1];
|
||||
final boolean[] neededBelow = new boolean[flow.getSize() + 1];
|
||||
|
||||
@Override
|
||||
public void procedureEntered(int startOffset, int endOffset) {
|
||||
for (int i = startOffset; i < endOffset; i++) neededBelow[i] = false;
|
||||
}
|
||||
|
||||
@Override public void visitReadVariableInstruction(ReadVariableInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitReadVariableInstruction(ReadVariableInstruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > flow.getSize()) nextOffset = flow.getSize();
|
||||
boolean needed = neededBelow[nextOffset];
|
||||
if (instruction.variable.equals(variable)) {
|
||||
needed = true;
|
||||
needed = true;
|
||||
}
|
||||
neededBelow[offset] |= needed;
|
||||
}
|
||||
|
||||
@Override public void visitWriteVariableInstruction(WriteVariableInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitWriteVariableInstruction(WriteVariableInstruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > flow.getSize()) nextOffset = flow.getSize();
|
||||
boolean needed = neededBelow[nextOffset];
|
||||
if (instruction.variable.equals(variable)) {
|
||||
@@ -172,7 +175,8 @@ public class ControlFlowUtil {
|
||||
neededBelow[offset] = needed;
|
||||
}
|
||||
|
||||
@Override public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > flow.getSize()) nextOffset = flow.getSize();
|
||||
boolean needed = neededBelow[nextOffset];
|
||||
neededBelow[offset] |= needed;
|
||||
@@ -182,7 +186,7 @@ public class ControlFlowUtil {
|
||||
public Boolean getResult() {
|
||||
return neededBelow[offset];
|
||||
}
|
||||
};
|
||||
};
|
||||
depthFirstSearch(flow, visitor, offset, flow.getSize());
|
||||
return visitor.getResult().booleanValue();
|
||||
}
|
||||
@@ -267,26 +271,33 @@ public class ControlFlowUtil {
|
||||
}
|
||||
final Collection<PsiStatement> exitStatements = new THashSet<PsiStatement>();
|
||||
InstructionClientVisitor visitor = new InstructionClientVisitor() {
|
||||
@Override public void visitThrowToInstruction(ThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitThrowToInstruction(ThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
//[ven]This is a hack since Extract Method doesn't want to see throw's exit points
|
||||
processGotoStatement(classesFilter, exitStatements, findStatement(flow, offset));
|
||||
}
|
||||
|
||||
@Override public void visitBranchingInstruction(BranchingInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitBranchingInstruction(BranchingInstruction instruction, int offset, int nextOffset) {
|
||||
processGoto(flow, start, end, exitPoints, exitStatements, instruction, classesFilter, findStatement(flow, offset));
|
||||
}
|
||||
|
||||
// call/return do not incur exit points
|
||||
@Override public void visitReturnInstruction(ReturnInstruction instruction, int offset, int nextOffset) {
|
||||
}
|
||||
@Override public void visitCallInstruction(CallInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitReturnInstruction(ReturnInstruction instruction, int offset, int nextOffset) {
|
||||
}
|
||||
|
||||
@Override public void visitConditionalThrowToInstruction(ConditionalThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitCallInstruction(CallInstruction instruction, int offset, int nextOffset) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitConditionalThrowToInstruction(ConditionalThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
visitInstruction(instruction, offset, nextOffset);
|
||||
}
|
||||
|
||||
@Override public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
if (offset >= end - 1) {
|
||||
int exitOffset = end;
|
||||
exitOffset = promoteThroughGotoChain(flow, exitOffset);
|
||||
@@ -319,7 +330,8 @@ public class ControlFlowUtil {
|
||||
}
|
||||
if (gotoOffset >= end || gotoOffset < start) {
|
||||
processGotoStatement(classesFilter, exitStatements, statement);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
boolean isReturn = instruction instanceof GoToInstruction && ((GoToInstruction)instruction).isReturn;
|
||||
final Instruction gotoInstruction = flow.getInstructions().get(gotoOffset);
|
||||
isReturn |= gotoInstruction instanceof GoToInstruction && ((GoToInstruction)gotoInstruction).isReturn;
|
||||
@@ -357,7 +369,7 @@ public class ControlFlowUtil {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public static final Class[] DEFAULT_EXIT_STATEMENTS_CLASSES = new Class[] {PsiReturnStatement.class, PsiBreakStatement.class, PsiContinueStatement.class};
|
||||
public static final Class[] DEFAULT_EXIT_STATEMENTS_CLASSES = new Class[]{PsiReturnStatement.class, PsiBreakStatement.class, PsiContinueStatement.class};
|
||||
|
||||
private static PsiStatement findStatement(ControlFlow flow, int offset) {
|
||||
PsiElement element = flow.getElement(offset);
|
||||
@@ -401,22 +413,23 @@ public class ControlFlowUtil {
|
||||
/**
|
||||
* Checks possibility of extracting code fragment outside containing anonymous (local) class.
|
||||
* Also collects variables to be passed as additional parameters.
|
||||
* @return true if code fragement can be extracted outside
|
||||
* @param array Vector to collect variables to be passed as additional parameters
|
||||
* @param scope scope to be scanned (part of code fragement to be extracted)
|
||||
* @param member member containing the code to be extracted
|
||||
*
|
||||
* @param array Vector to collect variables to be passed as additional parameters
|
||||
* @param scope scope to be scanned (part of code fragement to be extracted)
|
||||
* @param member member containing the code to be extracted
|
||||
* @param targetClassMember member in target class containing code fragement
|
||||
* @return true if code fragement can be extracted outside
|
||||
*/
|
||||
public static boolean collectOuterLocals(List<PsiVariable> array, PsiElement scope, PsiElement member,
|
||||
PsiElement targetClassMember) {
|
||||
if (scope instanceof PsiMethodCallExpression) {
|
||||
final PsiMethodCallExpression call = (PsiMethodCallExpression)scope;
|
||||
if (!checkReferenceExpressionScope (call.getMethodExpression(), targetClassMember)) {
|
||||
if (!checkReferenceExpressionScope(call.getMethodExpression(), targetClassMember)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (scope instanceof PsiReferenceExpression) {
|
||||
if (!checkReferenceExpressionScope ((PsiReferenceExpression)scope, targetClassMember)) {
|
||||
if (!checkReferenceExpressionScope((PsiReferenceExpression)scope, targetClassMember)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -483,10 +496,10 @@ public class ControlFlowUtil {
|
||||
depthFirstSearch(flow, visitor);
|
||||
return visitor.getResult().booleanValue();
|
||||
}
|
||||
|
||||
|
||||
public static boolean processReturns(final ControlFlow flow, final ReturnStatementsVisitor afterVisitor) throws IncorrectOperationException {
|
||||
final ConvertReturnClientVisitor instructionsVisitor = new ConvertReturnClientVisitor(flow, afterVisitor);
|
||||
|
||||
|
||||
depthFirstSearch(flow, instructionsVisitor);
|
||||
|
||||
instructionsVisitor.afterProcessing();
|
||||
@@ -510,7 +523,7 @@ public class ControlFlowUtil {
|
||||
if (instruction.isReturn) {
|
||||
final PsiElement element = myFlow.getElement(offset);
|
||||
if (element instanceof PsiReturnStatement) {
|
||||
final PsiReturnStatement returnStatement = (PsiReturnStatement) element;
|
||||
final PsiReturnStatement returnStatement = (PsiReturnStatement)element;
|
||||
myAffectedReturns.add(returnStatement);
|
||||
}
|
||||
}
|
||||
@@ -532,26 +545,30 @@ public class ControlFlowUtil {
|
||||
isNormalCompletion[myFlow.getSize()] = true;
|
||||
}
|
||||
|
||||
@Override public void visitConditionalThrowToInstruction(ConditionalThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitConditionalThrowToInstruction(ConditionalThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > myFlow.getSize()) nextOffset = myFlow.getSize();
|
||||
boolean isNormal = instruction.offset == nextOffset && nextOffset != offset + 1 ?
|
||||
!isLeaf(nextOffset) && isNormalCompletion[nextOffset] :
|
||||
isLeaf(nextOffset) || isNormalCompletion[nextOffset];
|
||||
!isLeaf(nextOffset) && isNormalCompletion[nextOffset] :
|
||||
isLeaf(nextOffset) || isNormalCompletion[nextOffset];
|
||||
|
||||
isNormalCompletion[offset] |= isNormal;
|
||||
}
|
||||
|
||||
@Override public void visitThrowToInstruction(ThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitThrowToInstruction(ThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > myFlow.getSize()) nextOffset = myFlow.getSize();
|
||||
isNormalCompletion[offset] |= !isLeaf(nextOffset) && isNormalCompletion[nextOffset];
|
||||
}
|
||||
|
||||
@Override public void visitGoToInstruction(GoToInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitGoToInstruction(GoToInstruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > myFlow.getSize()) nextOffset = myFlow.getSize();
|
||||
isNormalCompletion[offset] |= !instruction.isReturn && isNormalCompletion[nextOffset];
|
||||
}
|
||||
|
||||
@Override public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > myFlow.getSize()) nextOffset = myFlow.getSize();
|
||||
|
||||
boolean isNormal = isLeaf(nextOffset) || isNormalCompletion[nextOffset];
|
||||
@@ -580,7 +597,8 @@ public class ControlFlowUtil {
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void visitConditionalThrowToInstruction(ConditionalThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitConditionalThrowToInstruction(ConditionalThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > flow.getSize()) nextOffset = flow.getSize();
|
||||
if (offset > endOffset) return;
|
||||
int throwToOffset = instruction.offset;
|
||||
@@ -599,7 +617,8 @@ public class ControlFlowUtil {
|
||||
isNormalCompletion[offset] |= isNormal;
|
||||
}
|
||||
|
||||
@Override public void visitThrowToInstruction(ThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitThrowToInstruction(ThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > flow.getSize()) nextOffset = flow.getSize();
|
||||
if (offset > endOffset) return;
|
||||
if (nextOffset <= endOffset) {
|
||||
@@ -608,7 +627,8 @@ public class ControlFlowUtil {
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void visitCallInstruction(CallInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitCallInstruction(CallInstruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > flow.getSize()) nextOffset = flow.getSize();
|
||||
if (offset > endOffset) return;
|
||||
if (nextOffset > endOffset && nextOffset != offset + 1) {
|
||||
@@ -618,15 +638,17 @@ public class ControlFlowUtil {
|
||||
isNormalCompletion[offset] |= isNormal;
|
||||
}
|
||||
|
||||
@Override public void visitGoToInstruction(GoToInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitGoToInstruction(GoToInstruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > flow.getSize()) nextOffset = flow.getSize();
|
||||
if (offset > endOffset) return;
|
||||
boolean isRethrowFromFinally = instruction instanceof ReturnInstruction && ((ReturnInstruction) instruction).isRethrowFromFinally();
|
||||
boolean isRethrowFromFinally = instruction instanceof ReturnInstruction && ((ReturnInstruction)instruction).isRethrowFromFinally();
|
||||
boolean isNormal = !instruction.isReturn && isNormalCompletion[nextOffset] && !isRethrowFromFinally;
|
||||
isNormalCompletion[offset] |= isNormal;
|
||||
}
|
||||
|
||||
@Override public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > flow.getSize()) nextOffset = flow.getSize();
|
||||
if (offset > endOffset) return;
|
||||
final boolean isNormal = isLeaf(nextOffset) || isNormalCompletion[nextOffset];
|
||||
@@ -647,7 +669,7 @@ public class ControlFlowUtil {
|
||||
InstructionClientVisitor[] visitors = new InstructionClientVisitor[]{
|
||||
new ReturnPresentClientVisitor(flow),
|
||||
new UnreachableStatementClientVisitor(flow),
|
||||
new ReadBeforeWriteClientVisitor(flow),
|
||||
new ReadBeforeWriteClientVisitor(flow, true),
|
||||
new InitializedTwiceClientVisitor(flow, 0),
|
||||
};
|
||||
CompositeInstructionClientVisitor visitor = new CompositeInstructionClientVisitor(visitors);
|
||||
@@ -664,10 +686,13 @@ public class ControlFlowUtil {
|
||||
// false if control flow at this offset terminates abruptly
|
||||
final boolean[] canCompleteNormally = new boolean[flow.getSize() + 1];
|
||||
|
||||
@Override public void visitConditionalGoToInstruction(ConditionalGoToInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitConditionalGoToInstruction(ConditionalGoToInstruction instruction, int offset, int nextOffset) {
|
||||
checkInstruction(offset, nextOffset, false);
|
||||
}
|
||||
@Override public void visitGoToInstruction(GoToInstruction instruction, int offset, int nextOffset) {
|
||||
|
||||
@Override
|
||||
public void visitGoToInstruction(GoToInstruction instruction, int offset, int nextOffset) {
|
||||
checkInstruction(offset, nextOffset, instruction.isReturn);
|
||||
}
|
||||
|
||||
@@ -684,7 +709,8 @@ public class ControlFlowUtil {
|
||||
canCompleteNormally[offset] |= isNormal;
|
||||
}
|
||||
|
||||
@Override public void visitConditionalThrowToInstruction(ConditionalThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitConditionalThrowToInstruction(ConditionalThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > flow.getSize()) nextOffset = flow.getSize();
|
||||
if (offset > endOffset) return;
|
||||
int throwToOffset = instruction.offset;
|
||||
@@ -698,7 +724,8 @@ public class ControlFlowUtil {
|
||||
canCompleteNormally[offset] |= isNormal;
|
||||
}
|
||||
|
||||
@Override public void visitThrowToInstruction(ThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitThrowToInstruction(ThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > flow.getSize()) nextOffset = flow.getSize();
|
||||
if (offset > endOffset) return;
|
||||
if (nextOffset <= endOffset) {
|
||||
@@ -707,7 +734,8 @@ public class ControlFlowUtil {
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void visitCallInstruction(CallInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitCallInstruction(CallInstruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > flow.getSize()) nextOffset = flow.getSize();
|
||||
if (offset > endOffset) return;
|
||||
if (nextOffset > endOffset && nextOffset != offset + 1) {
|
||||
@@ -717,7 +745,8 @@ public class ControlFlowUtil {
|
||||
canCompleteNormally[offset] |= isNormal;
|
||||
}
|
||||
|
||||
@Override public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
checkInstruction(offset, nextOffset, false);
|
||||
}
|
||||
|
||||
@@ -739,6 +768,7 @@ public class ControlFlowUtil {
|
||||
depthFirstSearch(flow, visitor);
|
||||
return visitor.getResult();
|
||||
}
|
||||
|
||||
private static class UnreachableStatementClientVisitor extends InstructionClientVisitor<PsiElement> {
|
||||
private final ControlFlow myFlow;
|
||||
|
||||
@@ -760,7 +790,7 @@ public class ControlFlowUtil {
|
||||
}
|
||||
if (element instanceof PsiStatement
|
||||
&& element.getParent() instanceof PsiForStatement
|
||||
&& element == ((PsiForStatement) element.getParent()).getUpdate()) {
|
||||
&& element == ((PsiForStatement)element.getParent()).getUpdate()) {
|
||||
continue;
|
||||
}
|
||||
//filter out generated stmts
|
||||
@@ -809,11 +839,13 @@ public class ControlFlowUtil {
|
||||
class MyVisitor extends InstructionClientVisitor<Boolean> {
|
||||
// true if from this point below there may be branch with no variable assignment
|
||||
final boolean[] maybeUnassigned = new boolean[flow.getSize() + 1];
|
||||
|
||||
{
|
||||
maybeUnassigned[maybeUnassigned.length-1] = true;
|
||||
maybeUnassigned[maybeUnassigned.length - 1] = true;
|
||||
}
|
||||
|
||||
@Override public void visitWriteVariableInstruction(WriteVariableInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitWriteVariableInstruction(WriteVariableInstruction instruction, int offset, int nextOffset) {
|
||||
if (instruction.variable == variable) {
|
||||
maybeUnassigned[offset] = false;
|
||||
}
|
||||
@@ -822,7 +854,8 @@ public class ControlFlowUtil {
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void visitConditionalThrowToInstruction(ConditionalThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitConditionalThrowToInstruction(ConditionalThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > flow.getSize()) nextOffset = flow.getSize();
|
||||
boolean unassigned = offset == flow.getSize() - 1
|
||||
|| !isLeaf(nextOffset) && maybeUnassigned[nextOffset];
|
||||
@@ -830,21 +863,24 @@ public class ControlFlowUtil {
|
||||
maybeUnassigned[offset] |= unassigned;
|
||||
}
|
||||
|
||||
@Override public void visitCallInstruction(CallInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitCallInstruction(CallInstruction instruction, int offset, int nextOffset) {
|
||||
visitInstruction(instruction, offset, nextOffset);
|
||||
// clear return statements after procedure as well
|
||||
for (int i = instruction.procBegin; i<instruction.procEnd+3;i++) {
|
||||
for (int i = instruction.procBegin; i < instruction.procEnd + 3; i++) {
|
||||
maybeUnassigned[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void visitThrowToInstruction(ThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitThrowToInstruction(ThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > flow.getSize()) nextOffset = flow.getSize();
|
||||
boolean unassigned = !isLeaf(nextOffset) && maybeUnassigned[nextOffset];
|
||||
maybeUnassigned[offset] |= unassigned;
|
||||
}
|
||||
|
||||
@Override public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > flow.getSize()) nextOffset = flow.getSize();
|
||||
|
||||
boolean unassigned = isLeaf(nextOffset) || maybeUnassigned[nextOffset];
|
||||
@@ -867,27 +903,31 @@ public class ControlFlowUtil {
|
||||
// true if from this point below there may be branch with variable assignment
|
||||
final boolean[] maybeAssigned = new boolean[flow.getSize() + 1];
|
||||
|
||||
@Override public void visitWriteVariableInstruction(WriteVariableInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitWriteVariableInstruction(WriteVariableInstruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > flow.getSize()) nextOffset = flow.getSize();
|
||||
boolean assigned = instruction.variable == variable || maybeAssigned[nextOffset];
|
||||
maybeAssigned[offset] |= assigned;
|
||||
}
|
||||
|
||||
@Override public void visitThrowToInstruction(ThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitThrowToInstruction(ThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > flow.getSize()) nextOffset = flow.getSize();
|
||||
boolean assigned = !isLeaf(nextOffset) && maybeAssigned[nextOffset];
|
||||
maybeAssigned[offset] |= assigned;
|
||||
}
|
||||
|
||||
@Override public void visitConditionalThrowToInstruction(ConditionalThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitConditionalThrowToInstruction(ConditionalThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > flow.getSize()) nextOffset = flow.getSize();
|
||||
int throwToOffset = instruction.offset;
|
||||
boolean assigned = throwToOffset == nextOffset ? !isLeaf(nextOffset) && maybeAssigned[nextOffset] :
|
||||
maybeAssigned[nextOffset];
|
||||
maybeAssigned[nextOffset];
|
||||
maybeAssigned[offset] |= assigned;
|
||||
}
|
||||
|
||||
@Override public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > flow.getSize()) nextOffset = flow.getSize();
|
||||
|
||||
boolean assigned = maybeAssigned[nextOffset];
|
||||
@@ -914,7 +954,8 @@ public class ControlFlowUtil {
|
||||
// set of exit posint reached from this offset
|
||||
final TIntHashSet[] exitPoints = new TIntHashSet[flow.getSize()];
|
||||
|
||||
@Override public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > flow.getSize()) nextOffset = flow.getSize();
|
||||
|
||||
if (exitPoints[offset] == null) {
|
||||
@@ -978,7 +1019,8 @@ public class ControlFlowUtil {
|
||||
synchronized (instructions) {
|
||||
final IntArrayList currentProcedureReturnOffsets = new IntArrayList();
|
||||
ControlFlowInstructionVisitor getNextOffsetVisitor = new ControlFlowInstructionVisitor() {
|
||||
@Override public void visitCallInstruction(CallInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitCallInstruction(CallInstruction instruction, int offset, int nextOffset) {
|
||||
instruction.execute(offset + 1);
|
||||
int newOffset = instruction.offset;
|
||||
// 'procedure' pointed by call instruction should be processed regardless of whether it was already visited or not
|
||||
@@ -997,7 +1039,8 @@ public class ControlFlowUtil {
|
||||
currentProcedureReturnOffsets.add(offset + 1);
|
||||
}
|
||||
|
||||
@Override public void visitReturnInstruction(ReturnInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitReturnInstruction(ReturnInstruction instruction, int offset, int nextOffset) {
|
||||
int newOffset = instruction.execute(false);
|
||||
if (newOffset != -1) {
|
||||
oldOffsets.add(offset);
|
||||
@@ -1008,7 +1051,8 @@ public class ControlFlowUtil {
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void visitBranchingInstruction(BranchingInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitBranchingInstruction(BranchingInstruction instruction, int offset, int nextOffset) {
|
||||
int newOffset = instruction.offset;
|
||||
oldOffsets.add(offset);
|
||||
newOffsets.add(newOffset);
|
||||
@@ -1017,7 +1061,8 @@ public class ControlFlowUtil {
|
||||
newOffsets.add(-1);
|
||||
}
|
||||
|
||||
@Override public void visitConditionalBranchingInstruction(ConditionalBranchingInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitConditionalBranchingInstruction(ConditionalBranchingInstruction instruction, int offset, int nextOffset) {
|
||||
int newOffset = instruction.offset;
|
||||
|
||||
oldOffsets.add(offset);
|
||||
@@ -1033,7 +1078,8 @@ public class ControlFlowUtil {
|
||||
newOffsets.add(-1);
|
||||
}
|
||||
|
||||
@Override public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
int newOffset = offset + 1;
|
||||
oldOffsets.add(offset);
|
||||
newOffsets.add(newOffset);
|
||||
@@ -1063,7 +1109,7 @@ public class ControlFlowUtil {
|
||||
}
|
||||
if (!currentProcedureReturnOffsets.isEmpty()) {
|
||||
int returnOffset = currentProcedureReturnOffsets.get(currentProcedureReturnOffsets.size() - 1);
|
||||
CallInstruction callInstruction = (CallInstruction) instructions.get(returnOffset - 1);
|
||||
CallInstruction callInstruction = (CallInstruction)instructions.get(returnOffset - 1);
|
||||
// check if we inside procedure but 'return offset' stack is empty, so
|
||||
// we should push back to 'return offset' stack
|
||||
synchronized (callInstruction.stack) {
|
||||
@@ -1099,6 +1145,7 @@ public class ControlFlowUtil {
|
||||
newList.list.add(value);
|
||||
return newList;
|
||||
}
|
||||
|
||||
public CopyOnWriteList remove(VariableInfo value) {
|
||||
CopyOnWriteList newList = new CopyOnWriteList();
|
||||
List<VariableInfo> list = getList();
|
||||
@@ -1116,8 +1163,17 @@ public class ControlFlowUtil {
|
||||
}
|
||||
|
||||
public CopyOnWriteList() {
|
||||
list = new LinkedList<VariableInfo>();
|
||||
this(Collections.<VariableInfo>emptyList());
|
||||
}
|
||||
|
||||
public CopyOnWriteList(VariableInfo... infos) {
|
||||
this(Arrays.asList(infos));
|
||||
}
|
||||
|
||||
public CopyOnWriteList(Collection<VariableInfo> infos) {
|
||||
list = new LinkedList<VariableInfo>(infos);
|
||||
}
|
||||
|
||||
public CopyOnWriteList addAll(CopyOnWriteList addList) {
|
||||
CopyOnWriteList newList = new CopyOnWriteList();
|
||||
List<VariableInfo> list = getList();
|
||||
@@ -1133,7 +1189,12 @@ public class ControlFlowUtil {
|
||||
}
|
||||
return newList;
|
||||
}
|
||||
|
||||
public static CopyOnWriteList add(@Nullable CopyOnWriteList list, @NotNull VariableInfo value) {
|
||||
return list == null ? new CopyOnWriteList(value) : list.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static class VariableInfo {
|
||||
private final PsiVariable variable;
|
||||
public final PsiElement expression;
|
||||
@@ -1151,68 +1212,81 @@ public class ControlFlowUtil {
|
||||
return variable.hashCode();
|
||||
}
|
||||
}
|
||||
private static void merge(int offset, CopyOnWriteList readVars, CopyOnWriteList[] readVariables) {
|
||||
if (readVars != null) {
|
||||
CopyOnWriteList existing = readVariables[offset];
|
||||
readVariables[offset] = existing == null ? readVars : existing.addAll(readVars);
|
||||
|
||||
private static void merge(int offset, CopyOnWriteList source, CopyOnWriteList[] target) {
|
||||
if (source != null) {
|
||||
CopyOnWriteList existing = target[offset];
|
||||
target[offset] = existing == null ? source : existing.addAll(source);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of PsiReferenceExpression of usages of non-initialized variables
|
||||
* @return list of PsiReferenceExpression of usages of non-initialized local variables
|
||||
*/
|
||||
public static List<PsiReferenceExpression> getReadBeforeWrite(final ControlFlow flow) {
|
||||
InstructionClientVisitor<List<PsiReferenceExpression>> visitor = new ReadBeforeWriteClientVisitor(flow);
|
||||
public static List<PsiReferenceExpression> getReadBeforeWriteLocals(ControlFlow flow) {
|
||||
final InstructionClientVisitor<List<PsiReferenceExpression>> visitor = new ReadBeforeWriteClientVisitor(flow, true);
|
||||
depthFirstSearch(flow, visitor);
|
||||
return visitor.getResult();
|
||||
}
|
||||
|
||||
public static List<PsiReferenceExpression> getReadBeforeWrite(ControlFlow flow) {
|
||||
final InstructionClientVisitor<List<PsiReferenceExpression>> visitor = new ReadBeforeWriteClientVisitor(flow, false);
|
||||
depthFirstSearch(flow, visitor);
|
||||
return visitor.getResult();
|
||||
}
|
||||
|
||||
private static class ReadBeforeWriteClientVisitor extends InstructionClientVisitor<List<PsiReferenceExpression>> {
|
||||
// map of variable->PsiReferenceExpressions for all read before written variables for this point and below in control flow
|
||||
private final CopyOnWriteList[] readVariables;
|
||||
private final ControlFlow myFlow;
|
||||
private boolean localVariablesOnly;
|
||||
|
||||
public ReadBeforeWriteClientVisitor(ControlFlow flow) {
|
||||
public ReadBeforeWriteClientVisitor(ControlFlow flow, boolean localVariablesOnly) {
|
||||
myFlow = flow;
|
||||
readVariables = new CopyOnWriteList[myFlow.getSize()+1];
|
||||
this.localVariablesOnly = localVariablesOnly;
|
||||
readVariables = new CopyOnWriteList[myFlow.getSize() + 1];
|
||||
}
|
||||
|
||||
@Override public void visitReadVariableInstruction(ReadVariableInstruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > myFlow.getSize()) nextOffset = myFlow.getSize();
|
||||
CopyOnWriteList readVars = readVariables[nextOffset];
|
||||
PsiElement element = myFlow.getElement(offset);
|
||||
@Override
|
||||
public void visitReadVariableInstruction(ReadVariableInstruction instruction, int offset, int nextOffset) {
|
||||
CopyOnWriteList readVars = readVariables[Math.min(nextOffset, myFlow.getSize())];
|
||||
final PsiVariable variable = instruction.variable;
|
||||
if (!(variable instanceof PsiParameter) || ((PsiParameter)variable).getDeclarationScope() instanceof PsiForeachStatement) {
|
||||
PsiReferenceExpression expression = getEnclosingReferenceExpression(element, variable);
|
||||
if (!localVariablesOnly || !isMethodParameter(variable)) {
|
||||
final PsiReferenceExpression expression = getEnclosingReferenceExpression(myFlow.getElement(offset), variable);
|
||||
if (expression != null) {
|
||||
VariableInfo variableInfo = new VariableInfo(variable, expression);
|
||||
if (readVars == null) {
|
||||
readVars = new CopyOnWriteList();
|
||||
readVars.list.add(variableInfo);
|
||||
}
|
||||
else {
|
||||
readVars = readVars.add(variableInfo);
|
||||
}
|
||||
readVars = CopyOnWriteList.add(readVars, new VariableInfo(variable, expression));
|
||||
}
|
||||
}
|
||||
merge(offset, readVars, readVariables);
|
||||
}
|
||||
|
||||
@Override public void visitWriteVariableInstruction(WriteVariableInstruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > myFlow.getSize()) nextOffset = myFlow.getSize();
|
||||
CopyOnWriteList readVars = readVariables[nextOffset];
|
||||
@Override
|
||||
public void visitWriteVariableInstruction(WriteVariableInstruction instruction, int offset, int nextOffset) {
|
||||
CopyOnWriteList readVars = readVariables[Math.min(nextOffset, myFlow.getSize())];
|
||||
if (readVars == null) return;
|
||||
|
||||
final PsiVariable variable = instruction.variable;
|
||||
if (readVars != null && (!(variable instanceof PsiParameter) || ((PsiParameter)variable).getDeclarationScope() instanceof PsiForeachStatement)) {
|
||||
if (!localVariablesOnly || !isMethodParameter(variable)) {
|
||||
readVars = readVars.remove(new VariableInfo(variable, null));
|
||||
}
|
||||
merge(offset, readVars, readVariables);
|
||||
}
|
||||
|
||||
@Override public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > myFlow.getSize()) nextOffset = myFlow.getSize();
|
||||
CopyOnWriteList readVars = readVariables[nextOffset];
|
||||
merge(offset, readVars, readVariables);
|
||||
private static boolean isMethodParameter(@NotNull PsiVariable variable) {
|
||||
if (variable instanceof PsiParameter) {
|
||||
final PsiParameter parameter = (PsiParameter)variable;
|
||||
return !(parameter instanceof PsiForeachStatement);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public void visitCallInstruction(CallInstruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
merge(offset, readVariables[Math.min(nextOffset, myFlow.getSize())], readVariables);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCallInstruction(CallInstruction instruction, int offset, int nextOffset) {
|
||||
visitInstruction(instruction, offset, nextOffset);
|
||||
for (int i = instruction.procBegin; i <= instruction.procEnd; i++) {
|
||||
readVariables[i] = null;
|
||||
@@ -1221,30 +1295,32 @@ public class ControlFlowUtil {
|
||||
|
||||
@Override
|
||||
public List<PsiReferenceExpression> getResult() {
|
||||
List<PsiReferenceExpression> problemsFound = new ArrayList<PsiReferenceExpression>();
|
||||
CopyOnWriteList topReadVariables = readVariables[0];
|
||||
if (topReadVariables != null) {
|
||||
List<VariableInfo> list = topReadVariables.getList();
|
||||
for (final VariableInfo variableInfo : list) {
|
||||
problemsFound.add((PsiReferenceExpression)variableInfo.expression);
|
||||
}
|
||||
final CopyOnWriteList topReadVariables = readVariables[0];
|
||||
if (topReadVariables == null) return Collections.emptyList();
|
||||
|
||||
final List<PsiReferenceExpression> result = new ArrayList<PsiReferenceExpression>();
|
||||
List<VariableInfo> list = topReadVariables.getList();
|
||||
for (final VariableInfo variableInfo : list) {
|
||||
result.add((PsiReferenceExpression)variableInfo.expression);
|
||||
}
|
||||
return problemsFound;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static final int NORMAL_COMPLETION_REASON = 1;
|
||||
public static final int RETURN_COMPLETION_REASON = 2;
|
||||
|
||||
/**
|
||||
* return reasons.normalCompletion when block can complete normally
|
||||
* reasons.returnCalled when block can complete abruptly because of return statement executed
|
||||
* reasons.returnCalled when block can complete abruptly because of return statement executed
|
||||
*/
|
||||
public static int getCompletionReasons(final ControlFlow flow, final int offset, final int endOffset) {
|
||||
class MyVisitor extends InstructionClientVisitor<Integer> {
|
||||
final boolean[] normalCompletion = new boolean[endOffset];
|
||||
final boolean[] returnCalled = new boolean[endOffset];
|
||||
|
||||
@Override public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
boolean ret = nextOffset < endOffset && returnCalled[nextOffset];
|
||||
boolean normal = nextOffset < endOffset && normalCompletion[nextOffset];
|
||||
final PsiElement element = flow.getElement(offset);
|
||||
@@ -1302,62 +1378,63 @@ public class ControlFlowUtil {
|
||||
writtenTwiceVariables = new CopyOnWriteList[myFlow.getSize() + 1];
|
||||
}
|
||||
|
||||
@Override public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset > myFlow.getSize()) nextOffset = myFlow.getSize();
|
||||
@Override
|
||||
public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
final int safeNextOffset = Math.min(nextOffset, myFlow.getSize());
|
||||
|
||||
CopyOnWriteList writeVars = writtenVariables[nextOffset];
|
||||
CopyOnWriteList writeTwiceVars = writtenTwiceVariables[nextOffset];
|
||||
CopyOnWriteList writeVars = writtenVariables[safeNextOffset];
|
||||
CopyOnWriteList writeTwiceVars = writtenTwiceVariables[safeNextOffset];
|
||||
if (instruction instanceof WriteVariableInstruction) {
|
||||
final WriteVariableInstruction writeVariableInstruction = (WriteVariableInstruction)instruction;
|
||||
final PsiVariable variable = writeVariableInstruction.variable;
|
||||
final PsiElement element = myFlow.getElement(offset);
|
||||
final PsiVariable variable = ((WriteVariableInstruction)instruction).variable;
|
||||
|
||||
final PsiElement latestWriteVarExpression = getLatestWriteVarExpression(writeVars, variable);
|
||||
|
||||
PsiElement latestWriteVarExpression = null;
|
||||
if (writeVars != null) {
|
||||
List<VariableInfo> list = writeVars.getList();
|
||||
for (final VariableInfo variableInfo : list) {
|
||||
if (variableInfo.variable == variable) {
|
||||
latestWriteVarExpression = variableInfo.expression;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (latestWriteVarExpression == null) {
|
||||
PsiElement expression = null;
|
||||
if (element instanceof PsiAssignmentExpression
|
||||
&& ((PsiAssignmentExpression)element).getLExpression() instanceof PsiReferenceExpression) {
|
||||
expression = ((PsiAssignmentExpression)element).getLExpression();
|
||||
}
|
||||
else if (element instanceof PsiPostfixExpression) {
|
||||
expression = ((PsiPostfixExpression)element).getOperand();
|
||||
}
|
||||
else if (element instanceof PsiPrefixExpression) {
|
||||
expression = ((PsiPrefixExpression)element).getOperand();
|
||||
}
|
||||
else if (element instanceof PsiDeclarationStatement) {
|
||||
//should not happen
|
||||
expression = element;
|
||||
}
|
||||
if (writeVars == null) {
|
||||
writeVars = new CopyOnWriteList();
|
||||
}
|
||||
writeVars = writeVars.add(new VariableInfo(variable, expression));
|
||||
final PsiElement expression = getExpression(myFlow.getElement(offset));
|
||||
writeVars = CopyOnWriteList.add(writeVars, new VariableInfo(variable, expression));
|
||||
}
|
||||
else {
|
||||
if (writeTwiceVars == null) {
|
||||
writeTwiceVars = new CopyOnWriteList();
|
||||
}
|
||||
writeTwiceVars = writeTwiceVars.add(new VariableInfo(variable, latestWriteVarExpression));
|
||||
writeTwiceVars = CopyOnWriteList.add(writeTwiceVars, new VariableInfo(variable, latestWriteVarExpression));
|
||||
}
|
||||
}
|
||||
merge(offset, writeVars, writtenVariables);
|
||||
merge(offset, writeTwiceVars, writtenTwiceVariables);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement getExpression(@NotNull PsiElement element) {
|
||||
if (element instanceof PsiAssignmentExpression && ((PsiAssignmentExpression)element).getLExpression() instanceof PsiReferenceExpression) {
|
||||
return ((PsiAssignmentExpression)element).getLExpression();
|
||||
}
|
||||
else if (element instanceof PsiPostfixExpression) {
|
||||
return ((PsiPostfixExpression)element).getOperand();
|
||||
}
|
||||
else if (element instanceof PsiPrefixExpression) {
|
||||
return ((PsiPrefixExpression)element).getOperand();
|
||||
}
|
||||
else if (element instanceof PsiDeclarationStatement) {
|
||||
//should not happen
|
||||
return element;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement getLatestWriteVarExpression(@Nullable CopyOnWriteList writeVars, @Nullable PsiVariable variable) {
|
||||
if (writeVars == null) return null;
|
||||
|
||||
for (final VariableInfo variableInfo : writeVars.getList()) {
|
||||
if (variableInfo.variable == variable) {
|
||||
return variableInfo.expression;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Collection<VariableInfo> getResult() {
|
||||
CopyOnWriteList writtenTwiceVariable = writtenTwiceVariables[myStartOffset];
|
||||
final CopyOnWriteList writtenTwiceVariable = writtenTwiceVariables[myStartOffset];
|
||||
if (writtenTwiceVariable == null) return Collections.emptyList();
|
||||
return writtenTwiceVariable.getList();
|
||||
}
|
||||
@@ -1370,7 +1447,8 @@ public class ControlFlowUtil {
|
||||
class MyVisitor extends InstructionClientVisitor<Boolean> {
|
||||
boolean reachable;
|
||||
|
||||
@Override public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
@Override
|
||||
public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
|
||||
if (nextOffset == instructionOffset) reachable = true;
|
||||
}
|
||||
|
||||
@@ -1410,5 +1488,4 @@ public class ControlFlowUtil {
|
||||
int startOffset = flow.getStartOffset(assignmentExpression);
|
||||
return startOffset != -1 && isInstructionReachable(flow, startOffset, startOffset);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<problems>
|
||||
<problem>
|
||||
<file>Test.java</file>
|
||||
<line>2</line>
|
||||
<description>Parameter can be converted to a variable</description>
|
||||
</problem>
|
||||
</problems>
|
||||
@@ -0,0 +1,8 @@
|
||||
class Temp {
|
||||
public Temp(int p) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
p = i;
|
||||
System.out.print(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<problems>
|
||||
<problem>
|
||||
<file>Test.java</file>
|
||||
<line>4</line>
|
||||
<description>Parameter can be converted to a variable</description>
|
||||
</problem>
|
||||
</problems>
|
||||
@@ -0,0 +1,13 @@
|
||||
class Temp {
|
||||
public boolean flag;
|
||||
|
||||
void test(int p) {
|
||||
if (flag) {
|
||||
p = 1;
|
||||
}
|
||||
else {
|
||||
p = 2;
|
||||
}
|
||||
System.out.print(p);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<problems>
|
||||
<problem>
|
||||
<file>Test.java</file>
|
||||
<line>3</line>
|
||||
<description>Parameter can be converted to a variable</description>
|
||||
</problem>
|
||||
</problems>
|
||||
@@ -0,0 +1,7 @@
|
||||
class Temp {
|
||||
|
||||
void test(int p) {
|
||||
p = 1;
|
||||
System.out.print(p);
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Convert to local variable" "true"
|
||||
class Temp {
|
||||
public Temp() {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
<caret>int p = i;
|
||||
System.out.print(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// "Convert to local variable" "true"
|
||||
class Temp {
|
||||
public boolean flag;
|
||||
|
||||
void test() {
|
||||
<caret>int p;
|
||||
if (flag) {
|
||||
p = 1;
|
||||
}
|
||||
else {
|
||||
p = 2;
|
||||
}
|
||||
System.out.print(p);
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Convert to local variable" "true"
|
||||
class Temp {
|
||||
|
||||
void test() {
|
||||
<caret>int p = 1;
|
||||
System.out.print(p);
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Convert to local variable" "true"
|
||||
class Temp {
|
||||
public Temp(int <caret>p) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
p = i;
|
||||
System.out.print(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// "Convert to local variable" "true"
|
||||
class Temp {
|
||||
public boolean flag;
|
||||
|
||||
void test(int <caret>p) {
|
||||
if (flag) {
|
||||
p = 1;
|
||||
}
|
||||
else {
|
||||
p = 2;
|
||||
}
|
||||
System.out.print(p);
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Convert to local variable" "true"
|
||||
class Temp {
|
||||
|
||||
void test(int <caret>p) {
|
||||
p = 1;
|
||||
System.out.print(p);
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* User: anna
|
||||
* Date: 16-May-2007
|
||||
*/
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.JavaTestUtil;
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase;
|
||||
import com.intellij.codeInspection.varScopeCanBeNarrowed.ParameterCanBeLocalInspection;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
|
||||
public class ConvertParameterToLocalVariableTest extends LightQuickFixTestCase {
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
return JavaTestUtil.getJavaTestDataPath() + "/inspection";
|
||||
}
|
||||
|
||||
public void test() throws Exception {
|
||||
doAllTests();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doAction(final String text, final boolean actionShouldBeAvailable, final String testFullPath, final String testName)
|
||||
throws Exception {
|
||||
|
||||
final LocalQuickFix fix = new ParameterCanBeLocalInspection.ConvertParameterToLocalQuickFix();
|
||||
final int offset = getEditor().getCaretModel().getOffset();
|
||||
final PsiElement psiElement = getFile().findElementAt(offset);
|
||||
assert psiElement != null;
|
||||
final InspectionManager manager = InspectionManager.getInstance(getProject());
|
||||
final ProblemDescriptor descriptor = manager.createProblemDescriptor(psiElement, "", fix, ProblemHighlightType.LIKE_UNUSED_SYMBOL, true);
|
||||
fix.applyFix(getProject(), descriptor);
|
||||
final String expectedFilePath = getBasePath() + "/after" + testName;
|
||||
checkResultByFile("In file :" + expectedFilePath, expectedFilePath, false);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@NonNls
|
||||
protected String getBasePath() {
|
||||
return "/quickFix/ConvertParameterToLocalVariable";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.codeInspection;
|
||||
|
||||
import com.intellij.JavaTestUtil;
|
||||
import com.intellij.codeInspection.varScopeCanBeNarrowed.FieldCanBeLocalInspection;
|
||||
import com.intellij.codeInspection.varScopeCanBeNarrowed.ParameterCanBeLocalInspection;
|
||||
import com.intellij.testFramework.InspectionTestCase;
|
||||
|
||||
public class ParameterCanBeLocalTest extends InspectionTestCase {
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
return JavaTestUtil.getJavaTestDataPath() + "/inspection";
|
||||
}
|
||||
|
||||
private void doTest() throws Exception {
|
||||
doTest("parameterCanBeLocal/" + getTestName(true), new ParameterCanBeLocalInspection());
|
||||
}
|
||||
|
||||
public void testSimple () throws Exception { doTest(); }
|
||||
public void testIf () throws Exception { doTest(); }
|
||||
public void testFor () throws Exception { doTest(); }
|
||||
}
|
||||
@@ -324,11 +324,13 @@ public interface Application extends ComponentManager {
|
||||
boolean isActive();
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #runReadAction(Runnable)} instead
|
||||
* Returns lock used for read operations, should be closed in finally block
|
||||
*/
|
||||
AccessToken acquireReadActionLock();
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #runWriteAction(Runnable)} instead
|
||||
* Returns lock used for write operations, should be closed in finally block
|
||||
*/
|
||||
AccessToken acquireWriteActionLock(@Nullable Class marker);
|
||||
|
||||
@@ -28,13 +28,13 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.IconLoader;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.ui.ColoredTreeCellRenderer;
|
||||
import com.intellij.ui.Gray;
|
||||
import com.intellij.ui.LayeredIcon;
|
||||
import com.intellij.ui.ScrollPaneFactory;
|
||||
import com.intellij.ui.treeStructure.Tree;
|
||||
import com.intellij.ui.treeStructure.treetable.TreeTableModel;
|
||||
import com.intellij.util.Alarm;
|
||||
import com.intellij.util.ui.EmptyIcon;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.intellij.util.ui.tree.MacTreeUI;
|
||||
@@ -301,14 +301,8 @@ public class ActionsTree {
|
||||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
tree.expandPath(new TreePath(((DefaultMutableTreeNode)node.getParent()).getPath()));
|
||||
|
||||
Alarm alarm = new Alarm();
|
||||
alarm.addRequest(new Runnable() {
|
||||
public void run() {
|
||||
TreeUtil.selectPath(myTree, new TreePath(node.getPath()));
|
||||
}
|
||||
}, 100);
|
||||
TreeUtil.selectInTree(node, true, tree);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -383,24 +377,31 @@ public class ActionsTree {
|
||||
|
||||
TreePath path = new TreePath(root.getPath());
|
||||
if (myTree.isPathSelected(path)){
|
||||
mySelectionPaths.add(getPath(root));
|
||||
addPathToList(root, mySelectionPaths);
|
||||
}
|
||||
if (myTree.isExpanded(path) || root.getChildCount() == 0){
|
||||
myPathsToExpand.add(getPath(root));
|
||||
addPathToList(root, myPathsToExpand);
|
||||
_storePaths(root);
|
||||
}
|
||||
}
|
||||
|
||||
private void addPathToList(DefaultMutableTreeNode root, ArrayList<String> list) {
|
||||
String path = getPath(root);
|
||||
if (!StringUtil.isEmpty(path)) {
|
||||
list.add(path);
|
||||
}
|
||||
}
|
||||
|
||||
private void _storePaths(DefaultMutableTreeNode root) {
|
||||
ArrayList<TreeNode> childNodes = childrenToArray(root);
|
||||
for (final Object childNode1 : childNodes) {
|
||||
DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)childNode1;
|
||||
TreePath path = new TreePath(childNode.getPath());
|
||||
if (myTree.isPathSelected(path)) {
|
||||
mySelectionPaths.add(getPath(childNode));
|
||||
addPathToList(childNode, mySelectionPaths);
|
||||
}
|
||||
if ((myTree.isExpanded(path) || childNode.getChildCount() == 0) && !childNode.isLeaf()) {
|
||||
myPathsToExpand.add(getPath(childNode));
|
||||
addPathToList(childNode, myPathsToExpand);
|
||||
_storePaths(childNode);
|
||||
}
|
||||
}
|
||||
@@ -412,20 +413,17 @@ public class ActionsTree {
|
||||
myTree.expandPath(new TreePath(node.getPath()));
|
||||
}
|
||||
|
||||
Alarm alarm = new Alarm().setActivationComponent(myComponent);
|
||||
alarm.addComponentRequest(new Runnable() {
|
||||
public void run() {
|
||||
final ArrayList<DefaultMutableTreeNode> nodesToSelect = getNodesByPaths(mySelectionPaths);
|
||||
if (!nodesToSelect.isEmpty()) {
|
||||
for (DefaultMutableTreeNode node : nodesToSelect) {
|
||||
TreeUtil.selectNode(myTree, node);
|
||||
}
|
||||
}
|
||||
else {
|
||||
myTree.setSelectionRow(0);
|
||||
if (myTree.getSelectionModel().getSelectionCount() == 0) {
|
||||
final ArrayList<DefaultMutableTreeNode> nodesToSelect = getNodesByPaths(mySelectionPaths);
|
||||
if (!nodesToSelect.isEmpty()) {
|
||||
for (DefaultMutableTreeNode node : nodesToSelect) {
|
||||
TreeUtil.selectInTree(node, false, myTree);
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
else {
|
||||
myTree.setSelectionRow(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ package com.intellij.openapi.keymap.impl.ui;
|
||||
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.actionSystem.ex.QuickList;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.keymap.KeymapGroup;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.ArrayList;
|
||||
@@ -136,6 +136,24 @@ public class Group implements KeymapGroup {
|
||||
}
|
||||
|
||||
public String getActionQualifiedPath(String id) {
|
||||
Group cur = myParent;
|
||||
StringBuilder answer = new StringBuilder();
|
||||
|
||||
while (cur != null && !cur.isRoot()) {
|
||||
answer.insert(0, cur.getName() + " | ");
|
||||
|
||||
cur = cur.myParent;
|
||||
}
|
||||
|
||||
String suffix = calcActionQualifiedPath(id);
|
||||
if (StringUtil.isEmpty(suffix)) return null;
|
||||
|
||||
answer.append(suffix);
|
||||
|
||||
return answer.toString();
|
||||
}
|
||||
|
||||
private String calcActionQualifiedPath(String id) {
|
||||
for (Object child : myChildren) {
|
||||
if (child instanceof QuickList) {
|
||||
child = ((QuickList)child).getActionId();
|
||||
@@ -154,7 +172,7 @@ public class Group implements KeymapGroup {
|
||||
}
|
||||
}
|
||||
else if (child instanceof Group) {
|
||||
String path = ((Group)child).getActionQualifiedPath(id);
|
||||
String path = ((Group)child).calcActionQualifiedPath(id);
|
||||
if (path != null) {
|
||||
return !isRoot() ? getName() + " | " + path : path;
|
||||
}
|
||||
@@ -168,10 +186,11 @@ public class Group implements KeymapGroup {
|
||||
}
|
||||
|
||||
public String getQualifiedPath() {
|
||||
StringBuffer path = new StringBuffer(64);
|
||||
StringBuilder path = new StringBuilder(64);
|
||||
Group group = this;
|
||||
while (group != null && !group.isRoot()) {
|
||||
path.insert(0, group.getName() + " | ");
|
||||
if (path.length() > 0) path.insert(0, " | ");
|
||||
path.insert(0, group.getName());
|
||||
group = group.myParent;
|
||||
}
|
||||
return path.toString();
|
||||
|
||||
@@ -144,7 +144,9 @@ inspection.visibility.compose.suggestion=Can be {0}
|
||||
inspection.visibility.accept.quickfix=Accept Suggested Access Level
|
||||
inspection.field.can.be.local.display.name=Field can be local
|
||||
inspection.field.can.be.local.problem.descriptor=Field can be converted to a local variable
|
||||
inspection.field.can.be.local.quickfix=Convert to local
|
||||
inspection.parameter.can.be.local.display.name=Parameter can be local
|
||||
inspection.parameter.can.be.local.problem.descriptor=Parameter can be converted to a local variable
|
||||
inspection.convert.to.local.quickfix=Convert to local
|
||||
|
||||
inspection.unused.return.value.display.name=Unused method return value
|
||||
inspection.unused.return.value.problem.descriptor=Return value of the method is never used
|
||||
@@ -648,4 +650,4 @@ inspection.javadoc.problem.pointing.to.itself=Javadoc pointing to itself
|
||||
|
||||
inspection.redirect.template=<html><body>Injected element has problem: {0} (in <a href=\"#navigation/{1}:{2}\">{3}</a>). </body></html>
|
||||
nothing.found=Nothing found
|
||||
special.annotations.list.annotation.pattern=Add Annotations Pattern
|
||||
special.annotations.list.annotation.pattern=Add Annotations Pattern
|
||||
+20
@@ -44,14 +44,17 @@ public class ModuleLibraryTable implements LibraryTable, LibraryTableBase.Modifi
|
||||
private final RootModelImpl myRootModel;
|
||||
private final ProjectRootManagerImpl myProjectRootManager;
|
||||
public static final LibraryTablePresentation MODULE_LIBRARY_TABLE_PRESENTATION = new LibraryTablePresentation() {
|
||||
@Override
|
||||
public String getDisplayName(boolean plural) {
|
||||
return ProjectBundle.message("module.library.display.name", plural ? 2 : 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return ProjectBundle.message("libraries.node.text.module");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLibraryTableEditorTitle() {
|
||||
return ProjectBundle.message("library.configure.module.title");
|
||||
}
|
||||
@@ -62,6 +65,7 @@ public class ModuleLibraryTable implements LibraryTable, LibraryTableBase.Modifi
|
||||
myProjectRootManager = projectRootManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Library[] getLibraries() {
|
||||
final ArrayList<Library> result = new ArrayList<Library>();
|
||||
@@ -70,10 +74,12 @@ public class ModuleLibraryTable implements LibraryTable, LibraryTableBase.Modifi
|
||||
return result.toArray(new Library[result.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Library createLibrary() {
|
||||
return createLibrary(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Library createLibrary(String name) {
|
||||
return createLibrary(name, null);
|
||||
}
|
||||
@@ -85,6 +91,7 @@ public class ModuleLibraryTable implements LibraryTable, LibraryTableBase.Modifi
|
||||
return orderEntry.getLibrary();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLibrary(@NotNull Library library) {
|
||||
final Iterator<OrderEntry> orderIterator = myRootModel.getOrderIterator();
|
||||
while (orderIterator.hasNext()) {
|
||||
@@ -102,6 +109,7 @@ public class ModuleLibraryTable implements LibraryTable, LibraryTableBase.Modifi
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Iterator<Library> getLibraryIterator() {
|
||||
FilteringIterator<OrderEntry, LibraryOrderEntry> filteringIterator =
|
||||
@@ -109,18 +117,22 @@ public class ModuleLibraryTable implements LibraryTable, LibraryTableBase.Modifi
|
||||
return new ConvertingIterator<LibraryOrderEntry, Library>(filteringIterator, ORDER_ENTRY_TO_LIBRARY_CONVERTOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTableLevel() {
|
||||
return LibraryTableImplUtil.MODULE_LEVEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LibraryTablePresentation getPresentation() {
|
||||
return MODULE_LIBRARY_TABLE_PRESENTATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEditable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Library getLibraryByName(@NotNull String name) {
|
||||
final Iterator<Library> libraryIterator = getLibraryIterator();
|
||||
@@ -131,14 +143,17 @@ public class ModuleLibraryTable implements LibraryTable, LibraryTableBase.Modifi
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(Listener listener) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(Listener listener, Disposable parentDisposable) {
|
||||
throw new UnsupportedOperationException("Method addListener is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(Listener listener) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
@@ -149,24 +164,29 @@ public class ModuleLibraryTable implements LibraryTable, LibraryTableBase.Modifi
|
||||
|
||||
|
||||
private static class ModuleLibraryOrderEntryCondition implements Condition<OrderEntry> {
|
||||
@Override
|
||||
public boolean value(OrderEntry entry) {
|
||||
return entry instanceof LibraryOrderEntry && ((LibraryOrderEntry)entry).isModuleLevel() && ((LibraryOrderEntry)entry).getLibrary() != null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class OrderEntryToLibraryConvertor implements Convertor<LibraryOrderEntry, Library> {
|
||||
@Override
|
||||
public Library convert(LibraryOrderEntry o) {
|
||||
return o.getLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChanged() {
|
||||
return myRootModel.isChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModifiableModel getModifiableModel() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,11 @@ public class PersistentEnumeratorDelegate<Data> implements Closeable, Forceable
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
myEnumerator.close();
|
||||
final PersistentEnumeratorBase<Data> enumerator = myEnumerator;
|
||||
//noinspection ConstantConditions
|
||||
if (enumerator != null) {
|
||||
enumerator.close();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
|
||||
@@ -191,10 +191,22 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
try {
|
||||
// attempt to close already opened resources
|
||||
close();
|
||||
}
|
||||
catch (Throwable ignored) {
|
||||
}
|
||||
throw e; // rethrow
|
||||
}
|
||||
catch (Throwable t) {
|
||||
LOG.error(t);
|
||||
try {
|
||||
// attempt to close already opened resources
|
||||
close();
|
||||
}
|
||||
catch (Throwable ignored) {
|
||||
}
|
||||
throw new PersistentEnumerator.CorruptedException(file);
|
||||
}
|
||||
}
|
||||
@@ -472,7 +484,10 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
|
||||
try {
|
||||
myAppendCacheFlusher.stop();
|
||||
myAppendCache.clear();
|
||||
myValueStorage.dispose();
|
||||
final PersistentHashMapValueStorage valueStorage = myValueStorage;
|
||||
if (valueStorage != null) {
|
||||
valueStorage.dispose();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
super.close();
|
||||
@@ -490,22 +505,26 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
|
||||
myLiveAndGarbageKeysCounter = 0;
|
||||
myReadCompactionGarbageSize = 0;
|
||||
|
||||
traverseAllRecords(new PersistentEnumerator.RecordsProcessor() {
|
||||
@Override
|
||||
public boolean process(final int keyId) throws IOException {
|
||||
final long record = readValueId(keyId);
|
||||
if (record != NULL_ADDR) {
|
||||
PersistentHashMapValueStorage.ReadResult readResult = myValueStorage.readBytes(record);
|
||||
long value = newStorage.appendBytes(readResult.buffer, 0, readResult.buffer.length, 0);
|
||||
updateValueId(keyId, value, record, null, getCurrentKey());
|
||||
myLiveAndGarbageKeysCounter += LIVE_KEY_MASK;
|
||||
try {
|
||||
traverseAllRecords(new PersistentEnumerator.RecordsProcessor() {
|
||||
@Override
|
||||
public boolean process(final int keyId) throws IOException {
|
||||
final long record = readValueId(keyId);
|
||||
if (record != NULL_ADDR) {
|
||||
PersistentHashMapValueStorage.ReadResult readResult = myValueStorage.readBytes(record);
|
||||
long value = newStorage.appendBytes(readResult.buffer, 0, readResult.buffer.length, 0);
|
||||
updateValueId(keyId, value, record, null, getCurrentKey());
|
||||
myLiveAndGarbageKeysCounter += LIVE_KEY_MASK;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
finally {
|
||||
newStorage.dispose();
|
||||
}
|
||||
|
||||
myValueStorage.dispose();
|
||||
newStorage.dispose();
|
||||
|
||||
FileUtil.rename(new File(newPath), getDataFile(myEnumerator.myFile));
|
||||
|
||||
|
||||
+3
-6
@@ -168,8 +168,9 @@ public class EclipseClasspathReader {
|
||||
srcUrl = VfsUtil.pathToUrl(linked);
|
||||
eclipseModuleManager.registerEclipseLinkedSrcVarPath(srcUrl, path);
|
||||
rootModel.addContentEntry(srcUrl).addSourceFolder(srcUrl, isTestFolder);
|
||||
} else {
|
||||
getContentEntry().addSourceFolder(srcUrl, isTestFolder);
|
||||
}
|
||||
else {
|
||||
myContentEntry.addSourceFolder(srcUrl, isTestFolder);
|
||||
}
|
||||
eclipseModuleManager.setExpectedModuleSourcePlace(rearrangeOrderEntryOfType(rootModel, ModuleSourceOrderEntry.class));
|
||||
eclipseModuleManager.registerSrcPlace(srcUrl, idx);
|
||||
@@ -438,10 +439,6 @@ public class EclipseClasspathReader {
|
||||
return idx < 0 || idx == path.length() - 1 ? null : path.substring(idx + 1);
|
||||
}
|
||||
|
||||
private ContentEntry getContentEntry() {
|
||||
return myContentEntry;
|
||||
}
|
||||
|
||||
private static String getVariableRelatedPath(String var, String path) {
|
||||
return var == null ? null : ("$" + var + "$" + (path == null ? "" : ("/" + path)));
|
||||
}
|
||||
|
||||
@@ -145,11 +145,11 @@ public class GitAnnotationProvider implements AnnotationProvider, VcsCacheableAn
|
||||
final VcsFileRevision revision,
|
||||
final List<VcsFileRevision> revisions,
|
||||
final VirtualFile file) throws VcsException {
|
||||
GitSimpleHandler h = new GitSimpleHandler(myProject, GitUtil.getGitRoot(repositoryFilePath), GitCommand.ANNOTATE);
|
||||
GitSimpleHandler h = new GitSimpleHandler(myProject, GitUtil.getGitRoot(repositoryFilePath), GitCommand.BLAME);
|
||||
h.setNoSSH(true);
|
||||
h.setStdoutSuppressed(true);
|
||||
h.setCharset(file.getCharset());
|
||||
h.addParameters("-p", "-l", "-t");
|
||||
h.addParameters("-p", "-l", "-t", "-w");
|
||||
if (revision == null) {
|
||||
h.addParameters("HEAD");
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
public class GitCommand {
|
||||
|
||||
public static final GitCommand ADD = write("add");
|
||||
public static final GitCommand ANNOTATE = read("annotate");
|
||||
public static final GitCommand BLAME = read("blame");
|
||||
public static final GitCommand BRANCH = read("branch");
|
||||
public static final GitCommand CHECKOUT = write("checkout");
|
||||
public static final GitCommand COMMIT = write("commit");
|
||||
|
||||
@@ -122,8 +122,8 @@ field.already.defined=Field ''{0}'' already defined
|
||||
import.what=Import ''{0}''
|
||||
import.class=Import Class
|
||||
create.class.family.name=Create Class
|
||||
create.class.text=Create Class ''{0}''
|
||||
create.interface.text=Create Interface ''{0}''
|
||||
create.class.text=Create Class {0}
|
||||
create.interface.text=Create Interface {0}
|
||||
static.declaration.in.inner.class=Inner classes cannot have static declarations
|
||||
constructors.are.not.allowed.in.anonymous.class=Constructors are not allowed in anonymous class
|
||||
no.such.property=Property ''{0}'' does not exist
|
||||
@@ -311,3 +311,5 @@ type.argument.0.is.not.in.its.bound.should.extend.1=Type parameter ''{0}'' is no
|
||||
catch.statement.parameter.type.should.be.a.subclass.of.throwable=Catch statement parameter type should be a subclass of Throwable
|
||||
exception.0.has.already.been.caught=Exception ''{0}'' has already been caught
|
||||
unnecessary.type=Unnecessary exception ''{0}''. ''{1}'' is already declared
|
||||
create.enum=Create Enum {0}
|
||||
create.inner.class=Create Inner Class {0}
|
||||
|
||||
@@ -1001,16 +1001,17 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator {
|
||||
}
|
||||
|
||||
private static boolean checkDiamonds(GrCodeReferenceElement refElement, AnnotationHolder holder) {
|
||||
final GroovyConfigUtils configUtils = GroovyConfigUtils.getInstance();
|
||||
if (configUtils.isVersionAtLeast(refElement, GroovyConfigUtils.GROOVY1_8)) return true;
|
||||
|
||||
GrTypeArgumentList typeArgumentList = refElement.getTypeArgumentList();
|
||||
if (typeArgumentList != null && typeArgumentList.isDiamond()) {
|
||||
if (typeArgumentList == null) return true;
|
||||
|
||||
if (!typeArgumentList.isDiamond()) return true;
|
||||
|
||||
final GroovyConfigUtils configUtils = GroovyConfigUtils.getInstance();
|
||||
if (!configUtils.isVersionAtLeast(refElement, GroovyConfigUtils.GROOVY1_8)) {
|
||||
final String message = GroovyBundle.message("diamonds.are.not.allowed.in.groovy.0", configUtils.getSDKVersion(refElement));
|
||||
holder.createErrorAnnotation(typeArgumentList, message);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1951,11 +1952,33 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator {
|
||||
annotation.registerFix(CreateClassFix.createClassFromNewAction((GrNewExpression)parent));
|
||||
}
|
||||
else {
|
||||
annotation.registerFix(CreateClassFix.createClassFixAction(refElement));
|
||||
if (shouldBeInterface(refElement)) {
|
||||
annotation.registerFix(CreateClassFix.createClassFixAction(refElement, CreateClassActionBase.Type.INTERFACE));
|
||||
}
|
||||
else if (shouldBeClass(refElement)) {
|
||||
annotation.registerFix(CreateClassFix.createClassFixAction(refElement, CreateClassActionBase.Type.CLASS));
|
||||
annotation.registerFix(CreateClassFix.createClassFixAction(refElement, CreateClassActionBase.Type.ENUM));
|
||||
}
|
||||
else {
|
||||
annotation.registerFix(CreateClassFix.createClassFixAction(refElement, CreateClassActionBase.Type.CLASS));
|
||||
annotation.registerFix(CreateClassFix.createClassFixAction(refElement, CreateClassActionBase.Type.INTERFACE));
|
||||
annotation.registerFix(CreateClassFix.createClassFixAction(refElement, CreateClassActionBase.Type.ENUM));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldBeInterface(GrReferenceElement myRefElement) {
|
||||
PsiElement parent = myRefElement.getParent();
|
||||
return parent instanceof GrImplementsClause || parent instanceof GrExtendsClause && parent.getParent() instanceof GrInterfaceDefinition;
|
||||
}
|
||||
|
||||
private static boolean shouldBeClass(GrReferenceElement myRefElement) {
|
||||
PsiElement parent = myRefElement.getParent();
|
||||
return parent instanceof GrExtendsClause && !(parent.getParent() instanceof GrInterfaceDefinition);
|
||||
}
|
||||
|
||||
|
||||
private static void highlightMember(AnnotationHolder holder, GrMember member) {
|
||||
if (member instanceof GrField) {
|
||||
GrField field = (GrField)member;
|
||||
|
||||
+28
-14
@@ -34,26 +34,35 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.groovy.GroovyBundle;
|
||||
import org.jetbrains.plugins.groovy.actions.GroovyTemplatesFactory;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrExtendsClause;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrImplementsClause;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrInterfaceDefinition;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
|
||||
|
||||
/**
|
||||
* @author ilyas
|
||||
*/
|
||||
public abstract class CreateClassActionBase implements IntentionAction {
|
||||
private Type myType;
|
||||
|
||||
protected final GrReferenceElement myRefElement;
|
||||
private static final Logger LOG = Logger.getInstance("#org.jetbrains.plugins.groovy.annotator.intentions.CreateClassActionBase");
|
||||
|
||||
public CreateClassActionBase(GrReferenceElement refElement) {
|
||||
public CreateClassActionBase(Type type, GrReferenceElement refElement) {
|
||||
myType = type;
|
||||
myRefElement = refElement;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getText() {
|
||||
String referenceName = myRefElement.getReferenceName();
|
||||
return shouldCreateInterface() ? GroovyBundle.message("create.interface.text", referenceName) : GroovyBundle.message("create.class.text", referenceName);
|
||||
switch (getType()) {
|
||||
case ENUM:
|
||||
return GroovyBundle.message("create.enum", referenceName);
|
||||
case CLASS:
|
||||
return GroovyBundle.message("create.class.text", referenceName);
|
||||
case INTERFACE:
|
||||
return GroovyBundle.message("create.interface.text", referenceName);
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -69,17 +78,17 @@ public abstract class CreateClassActionBase implements IntentionAction {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean shouldCreateInterface() {
|
||||
PsiElement parent = myRefElement.getParent();
|
||||
return parent instanceof GrImplementsClause || parent instanceof GrExtendsClause && parent.getParent() instanceof GrInterfaceDefinition;
|
||||
|
||||
protected Type getType() {
|
||||
return myType;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static GrTypeDefinition createClassByType(@NotNull final PsiDirectory directory,
|
||||
@NotNull final String name,
|
||||
@NotNull final PsiManager manager,
|
||||
@Nullable final PsiElement contextElement,
|
||||
@NotNull final String templateName) {
|
||||
public static GrTypeDefinition createClassByType(@NotNull final PsiDirectory directory,
|
||||
@NotNull final String name,
|
||||
@NotNull final PsiManager manager,
|
||||
@Nullable final PsiElement contextElement,
|
||||
@NotNull final String templateName) {
|
||||
AccessToken accessToken = WriteAction.start();
|
||||
|
||||
try {
|
||||
@@ -130,8 +139,13 @@ public abstract class CreateClassActionBase implements IntentionAction {
|
||||
if (virtualFile != null) {
|
||||
OpenFileDescriptor descriptor = new OpenFileDescriptor(project, virtualFile, textOffset);
|
||||
return FileEditorManager.getInstance(project).openTextEditor(descriptor, true);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static enum Type {
|
||||
ENUM, CLASS, INTERFACE
|
||||
}
|
||||
}
|
||||
|
||||
+19
-9
@@ -27,7 +27,6 @@ import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.groovy.GroovyBundle;
|
||||
import org.jetbrains.plugins.groovy.actions.NewGroovyClassAction;
|
||||
import org.jetbrains.plugins.groovy.intentions.base.IntentionUtils;
|
||||
import org.jetbrains.plugins.groovy.lang.editor.template.expressions.ChooseTypeExpression;
|
||||
@@ -50,7 +49,7 @@ import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
|
||||
public abstract class CreateClassFix {
|
||||
|
||||
public static IntentionAction createClassFromNewAction(final GrNewExpression expression) {
|
||||
return new CreateClassActionBase(expression.getReferenceElement()) {
|
||||
return new CreateClassActionBase(CreateClassActionBase.Type.CLASS, expression.getReferenceElement()) {
|
||||
|
||||
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
|
||||
if (!(file instanceof GroovyFileBase)) return;
|
||||
@@ -60,7 +59,7 @@ public abstract class CreateClassFix {
|
||||
final String name = myRefElement.getReferenceName();
|
||||
assert name != null;
|
||||
final Module module = ModuleUtil.findModuleForPsiElement(file);
|
||||
PsiDirectory targetDirectory = getTargetDirectory(project, qualifier, name, module);
|
||||
PsiDirectory targetDirectory = getTargetDirectory(project, qualifier, name, module, getText());
|
||||
if (targetDirectory == null) return;
|
||||
|
||||
GrTypeDefinition targetClass = createClassByType(targetDirectory, name, manager, myRefElement, NewGroovyClassAction.GROOVY_CLASS);
|
||||
@@ -96,8 +95,8 @@ public abstract class CreateClassFix {
|
||||
};
|
||||
}
|
||||
|
||||
public static IntentionAction createClassFixAction(final GrReferenceElement refElement) {
|
||||
return new CreateClassActionBase(refElement) {
|
||||
public static IntentionAction createClassFixAction(final GrReferenceElement refElement, CreateClassActionBase.Type type) {
|
||||
return new CreateClassActionBase(type, refElement) {
|
||||
|
||||
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
|
||||
if (!(file instanceof GroovyFileBase)) return;
|
||||
@@ -106,10 +105,22 @@ public abstract class CreateClassFix {
|
||||
final PsiManager manager = PsiManager.getInstance(project);
|
||||
final String name = myRefElement.getReferenceName();
|
||||
final Module module = ModuleUtil.findModuleForPsiElement(file);
|
||||
PsiDirectory targetDirectory = getTargetDirectory(project, qualifier, name, module);
|
||||
PsiDirectory targetDirectory = getTargetDirectory(project, qualifier, name, module, getText());
|
||||
if (targetDirectory == null) return;
|
||||
|
||||
String templateName = shouldCreateInterface() ? NewGroovyClassAction.GROOVY_INTERFACE : NewGroovyClassAction.GROOVY_CLASS;
|
||||
|
||||
String templateName = null;
|
||||
switch (getType()) {
|
||||
case ENUM:
|
||||
templateName = NewGroovyClassAction.GROOVY_ENUM;
|
||||
break;
|
||||
case CLASS:
|
||||
templateName = NewGroovyClassAction.GROOVY_CLASS;
|
||||
break;
|
||||
case INTERFACE:
|
||||
templateName = NewGroovyClassAction.GROOVY_INTERFACE;
|
||||
break;
|
||||
}
|
||||
assert name != null;
|
||||
PsiClass targetClass = createClassByType(targetDirectory, name, manager, myRefElement, templateName);
|
||||
if (targetClass != null) {
|
||||
@@ -122,8 +133,7 @@ public abstract class CreateClassFix {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiDirectory getTargetDirectory(Project project, String qualifier, String name, Module module) {
|
||||
String title = GroovyBundle.message("create.class.family.name");
|
||||
private static PsiDirectory getTargetDirectory(Project project, String qualifier, String name, Module module, String title) {
|
||||
GroovyCreateClassDialog dialog = new GroovyCreateClassDialog(project, title, name, qualifier, module);
|
||||
dialog.show();
|
||||
if (dialog.getExitCode() != DialogWrapper.OK_EXIT_CODE) return null;
|
||||
|
||||
+8
-6
@@ -21,10 +21,7 @@ import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.popup.JBPopup;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
@@ -141,8 +138,13 @@ public class CreateParameterFromUsageFix implements IntentionAction, MethodOrClo
|
||||
|
||||
final String name = ref.getName();
|
||||
final Set<PsiType> types = GroovyExpectedTypesProvider.getDefaultExpectedTypes(ref);
|
||||
PsiType _type = types.iterator().next();
|
||||
final PsiType type = TypesUtil.unboxPrimitiveTypeWrapper(_type);
|
||||
final PsiType type;
|
||||
if (types.isEmpty()) {
|
||||
type = PsiType.getJavaLangObject(PsiManager.getInstance(project), ref.getResolveScope());
|
||||
}
|
||||
else {
|
||||
type = TypesUtil.unboxPrimitiveTypeWrapper(types.iterator().next());
|
||||
}
|
||||
|
||||
if (method instanceof GrMethod) {
|
||||
new GrChangeSignatureDialog(project, (GrMethod)method) {
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ public class GroovyCreateClassDialog extends DialogWrapper {
|
||||
setModal(true);
|
||||
setTitle(title);
|
||||
|
||||
myInformationLabel.setText(GroovyInspectionBundle.message("dialog.create.class.label.0", targetClassName));
|
||||
myInformationLabel.setText(title);
|
||||
myPackageTextField.setText(targetPackageName != null ? targetPackageName : "");
|
||||
|
||||
init();
|
||||
|
||||
+4
-4
@@ -23,9 +23,9 @@ import com.intellij.openapi.editor.highlighter.EditorHighlighter;
|
||||
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.groovy.lang.editor.HandlerUtils;
|
||||
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
|
||||
import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
|
||||
|
||||
/**
|
||||
@@ -33,7 +33,7 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
|
||||
*/
|
||||
public class GStringTypedActionHandler extends TypedHandlerDelegate {
|
||||
@Override
|
||||
public Result charTyped(char c, Project project, Editor editor, PsiFile file) {
|
||||
public Result charTyped(char c, Project project, Editor editor, @NotNull PsiFile file) {
|
||||
if (c != '{' || project == null || !HandlerUtils.canBeInvoked(editor, project)) {
|
||||
return Result.CONTINUE;
|
||||
}
|
||||
@@ -45,9 +45,9 @@ public class GStringTypedActionHandler extends TypedHandlerDelegate {
|
||||
if (caret < 1) return Result.CONTINUE;
|
||||
|
||||
HighlighterIterator iterator = highlighter.createIterator(caret - 1);
|
||||
if (iterator.getTokenType() != GroovyElementTypes.mLCURLY) return Result.CONTINUE;
|
||||
if (iterator.getTokenType() != GroovyTokenTypes.mLCURLY) return Result.CONTINUE;
|
||||
iterator.retreat();
|
||||
if (iterator.atEnd() || iterator.getTokenType() != GroovyElementTypes.mDOLLAR) return Result.CONTINUE;
|
||||
if (iterator.atEnd() || iterator.getTokenType() != GroovyTokenTypes.mDOLLAR) return Result.CONTINUE;
|
||||
iterator.advance();
|
||||
if (iterator.atEnd()) return Result.CONTINUE;
|
||||
iterator.advance();
|
||||
|
||||
+2
-1
@@ -19,12 +19,13 @@ package org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMembersDeclaration;
|
||||
|
||||
/**
|
||||
* @author ilyas
|
||||
*/
|
||||
public interface GrMemberOwner extends PsiClass {
|
||||
<T extends GrMembersDeclaration> T addMemberDeclaration(T decl, PsiElement anchorBefore) throws IncorrectOperationException ;
|
||||
<T extends GrMembersDeclaration> T addMemberDeclaration(T decl, @Nullable PsiElement anchorBefore) throws IncorrectOperationException ;
|
||||
|
||||
}
|
||||
+31
@@ -36,6 +36,8 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlo
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrReturnStatement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrThrowStatement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrCaseLabel;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrCaseSection;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrTraditionalForClause;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
|
||||
@@ -391,6 +393,35 @@ public class GroovyExpectedTypesProvider {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCaseLabel(GrCaseLabel caseLabel) {
|
||||
final PsiElement parent = caseLabel.getParent().getParent();
|
||||
assert parent instanceof GrSwitchStatement;
|
||||
final GrExpression condition = ((GrSwitchStatement)parent).getCondition();
|
||||
if (condition == null) return;
|
||||
|
||||
final PsiType type = condition.getType();
|
||||
if (type == null) return;
|
||||
|
||||
myResult = new TypeConstraint[]{SubtypeConstraint.create(type)};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSwitchStatement(GrSwitchStatement switchStatement) {
|
||||
final GrCaseSection[] sections = switchStatement.getCaseSections();
|
||||
List<PsiType> types = new ArrayList<PsiType>(sections.length);
|
||||
for (GrCaseSection section : sections) {
|
||||
final GrExpression value = section.getCaseLabel().getValue();
|
||||
final PsiType type = value != null ? value.getType() : null;
|
||||
if (type != null) types.add(type);
|
||||
}
|
||||
|
||||
final PsiType upperBoundNullable = TypesUtil.getLeastUpperBoundNullable(types, switchStatement.getManager());
|
||||
if (upperBoundNullable == null) return;
|
||||
|
||||
myResult = new TypeConstraint[]{SubtypeConstraint.create(upperBoundNullable)};
|
||||
}
|
||||
|
||||
public TypeConstraint[] getResult() {
|
||||
return myResult;
|
||||
}
|
||||
|
||||
+6
@@ -396,6 +396,12 @@ public class GroovyPsiElementFactoryImpl extends GroovyPsiElementFactory {
|
||||
return createTypeElement(typeText);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiClassType createType(@NotNull PsiClass aClass) {
|
||||
return JavaPsiFacade.getElementFactory(myProject).createType(aClass);
|
||||
}
|
||||
|
||||
public GrParenthesizedExpression createParenthesizedExpr(GrExpression newExpr) {
|
||||
return ((GrParenthesizedExpression) getInstance(myProject).createExpressionFromText("(" + newExpr.getText() + ")"));
|
||||
}
|
||||
|
||||
+5
-1
@@ -47,6 +47,7 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.*;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrCaseLabel;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrStringInjection;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
|
||||
@@ -412,7 +413,10 @@ public abstract class GrIntroduceHandlerBase<Settings extends GrIntroduceSetting
|
||||
|
||||
@Nullable
|
||||
private static PsiElement findContainingStatement(PsiElement candidate) {
|
||||
while (candidate != null && !PsiUtil.isExpressionStatement(candidate)) candidate = candidate.getParent();
|
||||
while (candidate != null && !PsiUtil.isExpressionStatement(candidate)) {
|
||||
candidate = candidate.getParent();
|
||||
if (candidate instanceof GrCaseLabel) candidate = candidate.getParent();
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -71,6 +71,7 @@ public class IntroduceVariableTest extends LightCodeInsightFixtureTestCase {
|
||||
public void testLoop7() throws Throwable { doTest(); }
|
||||
public void testLoop8() throws Throwable { doTest(); }
|
||||
public void testInCase() {doTest();}
|
||||
public void testCaseLabel() {doTest();}
|
||||
|
||||
public void testDuplicatesInsideIf() throws Throwable { doTest(); }
|
||||
public void testFromGString() throws Throwable { doTest(); }
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
def test(int p) {
|
||||
switch (p) {
|
||||
case <begin>1<end>: println "111"
|
||||
case xxx: println "xxx"
|
||||
}
|
||||
}
|
||||
-----
|
||||
def test(int p) {
|
||||
def preved = 1
|
||||
switch (p) {
|
||||
case preved<caret>: println "111"
|
||||
case xxx: println "xxx"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<body>
|
||||
<font face="verdana" size="-1">
|
||||
This inspection searches for redundant method parameters that can be replaced with local variables.
|
||||
If all local usages of a parameter are preceded by assignments to that parameter, the
|
||||
parameter can be removed and its usages replaced with local variables.
|
||||
</font>
|
||||
</body>
|
||||
</html>
|
||||
@@ -459,6 +459,9 @@
|
||||
<localInspection language="JAVA" shortName="FieldCanBeLocal" bundle="messages.InspectionsBundle" key="inspection.field.can.be.local.display.name"
|
||||
groupName="Class structure" enabledByDefault="true" level="WARNING" runForWholeFile="true"
|
||||
implementationClass="com.intellij.codeInspection.varScopeCanBeNarrowed.FieldCanBeLocalInspection"/>
|
||||
<localInspection language="JAVA" shortName="ParameterCanBeLocal" displayName="inspection.parameter.can.be.local.display.name"
|
||||
groupName="Class structure" enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.varScopeCanBeNarrowed.ParameterCanBeLocalInspection" />
|
||||
<localInspection language="JAVA" shortName="NullableProblems" bundle="messages.InspectionsBundle" key="inspection.nullable.problems.display.name"
|
||||
groupName="Probable bugs" enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.nullable.NullableStuffInspection" />
|
||||
@@ -535,6 +538,7 @@
|
||||
groupName="Probable bugs" enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.magicConstant.MagicConstantInspection" />
|
||||
|
||||
|
||||
<intentionAction>
|
||||
<className>com.intellij.codeInsight.intention.impl.SplitIfAction</className>
|
||||
<category>Control Flow</category>
|
||||
|
||||
+25
-7
@@ -51,7 +51,6 @@ import com.intellij.psi.xml.XmlToken;
|
||||
import com.intellij.ui.components.JBList;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.xml.XmlElementDescriptor;
|
||||
import com.intellij.xml.XmlExtension;
|
||||
import com.intellij.xml.impl.schema.AnyXmlElementDescriptor;
|
||||
@@ -102,6 +101,7 @@ public class CreateNSDeclarationIntentionFix implements HintAction, LocalQuickFi
|
||||
myToken = token == null ? null : PsiAnchor.create(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getText() {
|
||||
final String alias = StringUtil.capitalize(getXmlExtension().getNamespaceAlias(getFile()));
|
||||
@@ -112,16 +112,19 @@ public class CreateNSDeclarationIntentionFix implements HintAction, LocalQuickFi
|
||||
return XmlExtension.getExtension(getFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getName() {
|
||||
return getFamilyName();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getFamilyName() {
|
||||
return getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) {
|
||||
final PsiFile containingFile = descriptor.getPsiElement().getContainingFile();
|
||||
Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
|
||||
@@ -133,15 +136,18 @@ public class CreateNSDeclarationIntentionFix implements HintAction, LocalQuickFi
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
|
||||
PsiElement element = myElement.retrieve();
|
||||
return element != null && element.isValid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException {
|
||||
if (!CodeInsightUtilBase.prepareFileForWrite(file)) return;
|
||||
|
||||
final PsiElement element = ObjectUtils.assertNotNull(myElement.retrieve());
|
||||
final PsiElement element = myElement.retrieve();
|
||||
if (element == null) return;
|
||||
final Set<String> set = getXmlExtension().guessUnboundNamespaces(element, getFile());
|
||||
final String[] namespaces = ArrayUtil.toStringArray(set);
|
||||
Arrays.sort(namespaces);
|
||||
@@ -150,6 +156,7 @@ public class CreateNSDeclarationIntentionFix implements HintAction, LocalQuickFi
|
||||
namespaces,
|
||||
project,
|
||||
new StringToAttributeProcessor() {
|
||||
@Override
|
||||
public void doSomethingWithGivenStringToProduceXmlAttributeNowPlease(@NotNull final String namespace) throws IncorrectOperationException {
|
||||
String prefix = myNamespacePrefix;
|
||||
if (StringUtil.isEmpty(prefix)) {
|
||||
@@ -173,8 +180,9 @@ public class CreateNSDeclarationIntentionFix implements HintAction, LocalQuickFi
|
||||
final RangeMarker marker = editor.getDocument().createRangeMarker(offset, offset);
|
||||
final XmlExtension extension = XmlExtension.getExtension(file);
|
||||
extension.insertNamespaceDeclaration((XmlFile)file, editor, Collections.singleton(namespace), prefix, new XmlExtension.Runner<String, IncorrectOperationException>() {
|
||||
@Override
|
||||
public void run(final String param) throws IncorrectOperationException {
|
||||
if (namespace.length() > 0) {
|
||||
if (!namespace.isEmpty()) {
|
||||
editor.getCaretModel().moveToOffset(marker.getStartOffset());
|
||||
}
|
||||
}
|
||||
@@ -189,18 +197,21 @@ public class CreateNSDeclarationIntentionFix implements HintAction, LocalQuickFi
|
||||
return XmlErrorMessages.message("select.namespace.title", StringUtil.capitalize(getXmlExtension().getNamespaceAlias(getFile())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startInWriteAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showHint(final Editor editor) {
|
||||
if (myToken == null) return false;
|
||||
XmlToken token = (XmlToken)myToken.retrieve();
|
||||
if (token == null) return false;
|
||||
if (!XmlSettings.getInstance().SHOW_XML_ADD_IMPORT_HINTS || myNamespacePrefix.length() == 0) {
|
||||
if (!XmlSettings.getInstance().SHOW_XML_ADD_IMPORT_HINTS || myNamespacePrefix.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
PsiElement element = ObjectUtils.assertNotNull(myElement.retrieve());
|
||||
final PsiElement element = myElement.retrieve();
|
||||
if (element == null) return false;
|
||||
final Set<String> namespaces = getXmlExtension().guessUnboundNamespaces(element, getFile());
|
||||
if (!namespaces.isEmpty()) {
|
||||
final String message = ShowAutoImportPass.getMessage(namespaces.size() > 1, namespaces.iterator().next());
|
||||
@@ -224,13 +235,14 @@ public class CreateNSDeclarationIntentionFix implements HintAction, LocalQuickFi
|
||||
}
|
||||
|
||||
private static boolean checkIfGivenXmlHasTheseWords(final String name, final XmlFile tldFileByUri) {
|
||||
if (name == null || name.length() == 0) return true;
|
||||
if (name == null || name.isEmpty()) return true;
|
||||
final List<String> list = StringUtil.getWordsIn(name);
|
||||
final String[] words = ArrayUtil.toStringArray(list);
|
||||
final boolean[] wordsFound = new boolean[words.length];
|
||||
final int[] wordsFoundCount = new int[1];
|
||||
|
||||
IdTableBuilding.ScanWordProcessor wordProcessor = new IdTableBuilding.ScanWordProcessor() {
|
||||
@Override
|
||||
public void run(final CharSequence chars, @Nullable char[] charsArray, int start, int end) {
|
||||
if (wordsFoundCount[0] == words.length) return;
|
||||
final int foundWordLen = end - start;
|
||||
@@ -263,7 +275,7 @@ public class CreateNSDeclarationIntentionFix implements HintAction, LocalQuickFi
|
||||
}
|
||||
|
||||
|
||||
public static void runActionOverSeveralAttributeValuesAfterLettingUserSelectTheNeededOne(final @NotNull String[] namespacesToChooseFrom,
|
||||
public static void runActionOverSeveralAttributeValuesAfterLettingUserSelectTheNeededOne(@NotNull final String[] namespacesToChooseFrom,
|
||||
final Project project, final StringToAttributeProcessor onSelection,
|
||||
String title,
|
||||
final IntentionAction requestor,
|
||||
@@ -273,6 +285,7 @@ public class CreateNSDeclarationIntentionFix implements HintAction, LocalQuickFi
|
||||
final JList list = new JBList(namespacesToChooseFrom);
|
||||
list.setCellRenderer(XmlNSRenderer.INSTANCE);
|
||||
Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final int index = list.getSelectedIndex();
|
||||
if (index < 0) return;
|
||||
@@ -281,9 +294,11 @@ public class CreateNSDeclarationIntentionFix implements HintAction, LocalQuickFi
|
||||
CommandProcessor.getInstance().executeCommand(
|
||||
project,
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ApplicationManager.getApplication().runWriteAction(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
onSelection.doSomethingWithGivenStringToProduceXmlAttributeNowPlease(namespacesToChooseFrom[index]);
|
||||
@@ -321,6 +336,7 @@ public class CreateNSDeclarationIntentionFix implements HintAction, LocalQuickFi
|
||||
else {
|
||||
ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
processExternalUrisImpl(metaHandler, file, processor);
|
||||
}
|
||||
@@ -345,6 +361,7 @@ public class CreateNSDeclarationIntentionFix implements HintAction, LocalQuickFi
|
||||
myName = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAcceptableMetaData(final PsiMetaData metaData, final String url) {
|
||||
if (metaData instanceof XmlNSDescriptorImpl) {
|
||||
final XmlNSDescriptorImpl nsDescriptor = (XmlNSDescriptorImpl)metaData;
|
||||
@@ -355,6 +372,7 @@ public class CreateNSDeclarationIntentionFix implements HintAction, LocalQuickFi
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String searchFor() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user