diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/AnnotationParamListElement.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/AnnotationParamListElement.java index b2e7d56a6f2d..508dd52eb2b2 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/AnnotationParamListElement.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/AnnotationParamListElement.java @@ -61,53 +61,51 @@ public class AnnotationParamListElement extends CompositeElement { @Override public TreeElement addInternal(TreeElement first, ASTNode last, ASTNode anchor, Boolean before) { - if (first.getElementType() == JavaElementType.NAME_VALUE_PAIR && last.getElementType() == JavaElementType.NAME_VALUE_PAIR) { - ASTNode lparenth = findChildByType(JavaTokenType.LPARENTH); - if (lparenth == null) { - CharTable treeCharTab = SharedImplUtil.findCharTableByTree(this); - LeafElement created = Factory.createSingleLeafElement(JavaTokenType.LPARENTH, "(", 0, 1, treeCharTab, getManager()); - super.addInternal(created, created, getFirstChildNode(), true); - } + ASTNode lparenth = findChildByType(JavaTokenType.LPARENTH); + if (lparenth == null) { + CharTable treeCharTab = SharedImplUtil.findCharTableByTree(this); + LeafElement created = Factory.createSingleLeafElement(JavaTokenType.LPARENTH, "(", 0, 1, treeCharTab, getManager()); + super.addInternal(created, created, getFirstChildNode(), true); + } - ASTNode rparenth = findChildByType(JavaTokenType.RPARENTH); - if (rparenth == null) { - CharTable treeCharTab = SharedImplUtil.findCharTableByTree(this); - LeafElement created = Factory.createSingleLeafElement(JavaTokenType.RPARENTH, ")", 0, 1, treeCharTab, getManager()); - super.addInternal(created, created, getLastChildNode(), false); - } + ASTNode rparenth = findChildByType(JavaTokenType.RPARENTH); + if (rparenth == null) { + CharTable treeCharTab = SharedImplUtil.findCharTableByTree(this); + LeafElement created = Factory.createSingleLeafElement(JavaTokenType.RPARENTH, ")", 0, 1, treeCharTab, getManager()); + super.addInternal(created, created, getLastChildNode(), false); + } - ASTNode[] nodes = getChildren(NAME_VALUE_PAIR_BIT_SET); - if (nodes.length == 1) { - ASTNode node = nodes[0]; - if (node instanceof PsiNameValuePair) { - PsiNameValuePair pair = (PsiNameValuePair)node; - if (pair.getName() == null) { - PsiAnnotationMemberValue value = pair.getValue(); - if (value != null) { - try { - PsiElementFactory factory = JavaPsiFacade.getElementFactory(getPsi().getProject()); - PsiAnnotation annotation = factory.createAnnotationFromText("@AAA(value = " + value.getText() + ")", null); - replaceChild(node, annotation.getParameterList().getAttributes()[0].getNode()); - } - catch (IncorrectOperationException e) { - LOG.error(e); - } + ASTNode[] nodes = getChildren(NAME_VALUE_PAIR_BIT_SET); + if (nodes.length == 1) { + ASTNode node = nodes[0]; + if (node instanceof PsiNameValuePair) { + PsiNameValuePair pair = (PsiNameValuePair)node; + if (pair.getName() == null) { + PsiAnnotationMemberValue value = pair.getValue(); + if (value != null) { + try { + PsiElementFactory factory = JavaPsiFacade.getElementFactory(getPsi().getProject()); + PsiAnnotation annotation = factory.createAnnotationFromText("@AAA(value = " + value.getText() + ")", null); + replaceChild(node, annotation.getParameterList().getAttributes()[0].getNode()); + } + catch (IncorrectOperationException e) { + LOG.error(e); } } } } - - if (anchor == null) { - if (before == null) before = Boolean.TRUE; - anchor = findChildByType(before ? JavaTokenType.RPARENTH : JavaTokenType.LPARENTH); - } - - TreeElement firstAdded = super.addInternal(first, last, anchor, before); - JavaSourceUtil.addSeparatingComma(this, first, NAME_VALUE_PAIR_BIT_SET); - return firstAdded; } - return super.addInternal(first, last, anchor, before); + if (anchor == null) { + if (before == null) before = Boolean.TRUE; + anchor = findChildByType(before ? JavaTokenType.RPARENTH : JavaTokenType.LPARENTH); + } + + TreeElement firstAdded = super.addInternal(first, last, anchor, before); + if (first == last && first.getElementType() == JavaElementType.NAME_VALUE_PAIR) { + JavaSourceUtil.addSeparatingComma(this, first, NAME_VALUE_PAIR_BIT_SET); + } + return firstAdded; } @Override diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaReplaceHandler.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaReplaceHandler.java index c37117e0cce0..a664a0f02010 100644 --- a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaReplaceHandler.java +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaReplaceHandler.java @@ -1,7 +1,6 @@ // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.structuralsearch; -import com.intellij.codeInsight.AnnotationUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; @@ -276,8 +275,9 @@ public class JavaReplaceHandler extends StructuralReplaceHandler { if (originalMember == null) { continue; } - for (Iterator iterator = elements.iterator(); iterator.hasNext(); ) { - PsiElement element = iterator.next(); + final Iterator iterator = elements.iterator(); + while (iterator.hasNext()) { + final PsiElement element = iterator.next(); if (PsiElementOrderComparator.getInstance().compare(element, originalMember) < 0) { addElementAndWhitespaceBeforeAnchor(replacementClass, element, anchor); iterator.remove(); @@ -327,7 +327,7 @@ public class JavaReplaceHandler extends StructuralReplaceHandler { } } - private static void copyAnnotationParameters(PsiAnnotation original, PsiAnnotation query, PsiAnnotation replacement) { + private void copyAnnotationParameters(PsiAnnotation original, PsiAnnotation query, PsiAnnotation replacement) { final PsiAnnotationParameterList originalParameters = original.getParameterList(); if (originalParameters.getTextLength() > 0) { final PsiAnnotationParameterList replacementParameters = replacement.getParameterList(); @@ -338,15 +338,14 @@ public class JavaReplaceHandler extends StructuralReplaceHandler { final List unmatchedAttributes = original.getUserData(GlobalMatchingVisitor.UNMATCHED_ELEMENTS_KEY); if (unmatchedAttributes != null) { for (PsiElement attribute : unmatchedAttributes) { + replacementParameters.add(whiteSpace(attribute.getPrevSibling(), " ")); replacementParameters.add(attribute); } } } } - private static void copyModifiersAndAnnotations(PsiModifierListOwner original, - PsiModifierListOwner query, - PsiModifierListOwner replacement) { + private void copyModifiersAndAnnotations(PsiModifierListOwner original, PsiModifierListOwner query, PsiModifierListOwner replacement) { final PsiModifierList originalModifierList = original.getModifierList(); final PsiModifierList queryModifierList = query.getModifierList(); final PsiModifierList replacementModifierList = replacement.getModifierList(); @@ -354,39 +353,58 @@ public class JavaReplaceHandler extends StructuralReplaceHandler { if (originalModifierList == null || queryModifierList == null || replacementModifierList == null) { return; } - if (originalModifierList.getTextLength() != 0) { - final PsiModifierList copy = (PsiModifierList)originalModifierList.copy(); - for (String modifier : PsiModifier.MODIFIERS) { - if (replacementModifierList.hasExplicitModifier(modifier)) { - copy.setModifierProperty(modifier, true); - } - else if (queryModifierList.hasExplicitModifier(modifier) && !replacementModifierList.hasModifierProperty(modifier)) { - copy.setModifierProperty(modifier, false); + final List unmatchedAnnotations = originalModifierList.getUserData(GlobalMatchingVisitor.UNMATCHED_ELEMENTS_KEY); + final PsiElement anchor = replacementModifierList.getFirstChild(); + boolean append = (anchor == null); + PsiElement child = originalModifierList.getFirstChild(); + while (child != null) { + if (child instanceof PsiKeyword) { + append = true; + @PsiModifier.ModifierConstant final String modifierText = child.getText(); + if (isCompatibleModifier(modifierText, replacementModifierList) && !queryModifierList.hasExplicitModifier(modifierText)) { + if (anchor != null) replacementModifierList.add(whiteSpace(child.getPrevSibling(), " ")); + replacementModifierList.add(child); } } - final List unmatchedAnnotations = originalModifierList.getUserData(GlobalMatchingVisitor.UNMATCHED_ELEMENTS_KEY); - if (unmatchedAnnotations != null) { - outer: - for (PsiAnnotation copyAnnotation : copy.getAnnotations()) { - for (PsiElement unmatchedAnnotation : unmatchedAnnotations) { - if (AnnotationUtil.equal(copyAnnotation, (PsiAnnotation)unmatchedAnnotation)) { - continue outer; - } + else if (child instanceof PsiAnnotation && (unmatchedAnnotations == null || unmatchedAnnotations.contains(child))) { + if (append) { + if (anchor != null) replacementModifierList.add(whiteSpace(child.getPrevSibling(), " ")); + replacementModifierList.add(child); + } + else { + final PsiElement next = replacementModifierList.addBefore(child, anchor).getNextSibling(); + final PsiWhiteSpace whiteSpace = whiteSpace(child.getNextSibling(), " "); + if (!(next instanceof PsiWhiteSpace)) { + replacementModifierList.addBefore(whiteSpace, anchor); + } + else { + next.replace(whiteSpace); } - copyAnnotation.delete(); } } - for (PsiAnnotation annotation : replacementModifierList.getAnnotations()) { - copy.addBefore(annotation, copy.getFirstChild()); - } - replacementModifierList.replace(copy); + child = child.getNextSibling(); } } + private PsiWhiteSpace whiteSpace(PsiElement element, @SuppressWarnings("SameParameterValue") String defaultWs) { + return element instanceof PsiWhiteSpace + ? (PsiWhiteSpace)element + : (PsiWhiteSpace)PsiParserFacade.SERVICE.getInstance(myProject).createWhiteSpaceFromText(defaultWs); + } + + private static boolean isCompatibleModifier(String modifier, PsiModifierList modifierList) { + if (PsiModifier.PUBLIC.equals(modifier) || PsiModifier.PROTECTED.equals(modifier) || PsiModifier.PRIVATE.equals(modifier)) { + return !modifierList.hasExplicitModifier(PsiModifier.PUBLIC) + && !modifierList.hasExplicitModifier(PsiModifier.PROTECTED) + && !modifierList.hasExplicitModifier(PsiModifier.PRIVATE); + } + return true; + } + private PsiElement handleSymbolReplacement(PsiElement replacement, final PsiElement el) { - PsiNamedElement nameElement = getSymbolReplacementTarget(el); + final PsiNamedElement nameElement = getSymbolReplacementTarget(el); if (nameElement != null) { - PsiElement oldReplacement = replacement; + final PsiElement oldReplacement = replacement; replacement = el.copy(); ((PsiNamedElement)replacement).setName(oldReplacement.getText()); } diff --git a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralReplaceTest.java b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralReplaceTest.java index b3be2cbaf77c..91c94b62a667 100644 --- a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralReplaceTest.java +++ b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralReplaceTest.java @@ -2108,9 +2108,9 @@ public class StructuralReplaceTest extends StructuralReplaceTestCase { } public void testReplaceAnnotation() { - String in1 = "@SuppressWarnings(\"ALL\")\n" + - "public class A {}"; - String what = "@SuppressWarnings(\"ALL\")"; + final String in1 = "@SuppressWarnings(\"ALL\")\n" + + "public class A {}"; + final String what = "@SuppressWarnings(\"ALL\")"; final String expected1a = "public class A {}"; assertEquals(expected1a, replace(in1, what, "")); @@ -2175,7 +2175,7 @@ public class StructuralReplaceTest extends StructuralReplaceTestCase { "}"; assertEquals(expected4, replace(in4, "@'_A('_p*=1)", "@$A$($p$=1, three=1)")); - final String expected4b = "class X { @Anno(one=2,two=1) String s;}"; + final String expected4b = "class X { @Anno(one=2, two=1) String s;}"; assertEquals(expected4b, replace(in4, "@'_A('_p:one =1)", "@$A$($p$=2)")); final String in5 = "@RunWith(SpringJUnit4ClassRunner.class)\n" + @@ -2195,6 +2195,33 @@ public class StructuralReplaceTest extends StructuralReplaceTestCase { assertEquals(expected5, replace(in5, "@ContextConfiguration(classes = {'_X*})", "@ContextHierarchy(classes = {\n" + " @ContextConfiguration(classes = {$X$, Object.class})\n" + "})")); + + final String in6 = "class X {\n" + + " @WastingTime @Override\n" + + " public @Constant @Sorrow String value() {\n" + + " return null;\n" + + " }\n" + + "}"; + final String expected6 = "class X {\n" + + " @WastingTime @Override\n" + + " private @Constant @Sorrow String value() {\n" + + " return null;\n" + + " }\n" + + "}"; + assertEquals(expected6, replace(in6, "'_ReturnType '_method('_ParameterType '_parameter*);", + "private $ReturnType$ $method$($ParameterType$ $parameter$);")); + + final String in7 = "public class IssueLink {\n" + + " @XmlAttribute(name = \"default\", namespace = \"space\")\n" + + " @Deprecated\n" + + " public String typeInward;\n" + + "}"; + final String expected7 = "public class IssueLink {\n" + + " @XmlAttribute(name=\"default\", namespace = \"space\")\n" + + " public String typeInward;\n" + + "}"; + assertEquals(expected7, replace(in7, "@XmlAttribute(name=\"default\") @Deprecated '_Type '_field;", + "@XmlAttribute(name=\"default\") $Type$ $field$;")); } public void testReplacePolyadicExpression() {