diff --git a/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java b/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java index 0489b789379e..af43233a8b43 100644 --- a/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java +++ b/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java @@ -143,40 +143,66 @@ public class JavaDocInfoGenerator { } interface DocTagLocator { - T find(PsiDocComment comment); + T find(PsiDocCommentOwner owner, PsiDocComment comment); } - private static DocTagLocator parameterLocator(final String name) { + private static DocTagLocator parameterLocator(final int parameterIndex) { return new DocTagLocator() { @Override - public PsiDocTag find(PsiDocComment comment) { - if (comment == null) { - return null; - } + public PsiDocTag find(PsiDocCommentOwner owner, PsiDocComment comment) { + if (parameterIndex < 0 || comment == null || !(owner instanceof PsiMethod)) return null; - PsiDocTag[] tags = comment.findTagsByName("param"); - - for (PsiDocTag tag : tags) { - PsiDocTagValue value = tag.getValueElement(); - - if (value != null) { - String text = value.getText(); - - if (text != null && text.equals(name)) { - return tag; - } - } - } - - return null; + PsiParameter[] parameters = ((PsiMethod)owner).getParameterList().getParameters(); + if (parameterIndex >= parameters.length) return null; + + String name = parameters[parameterIndex].getName(); + return getParamTagByName(comment, name); } }; } + private static DocTagLocator typeParameterLocator(final int parameterIndex) { + return new DocTagLocator() { + @Override + public PsiDocTag find(PsiDocCommentOwner owner, PsiDocComment comment) { + if (parameterIndex < 0 || comment == null || !(owner instanceof PsiTypeParameterListOwner)) return null; + + PsiTypeParameter[] parameters = ((PsiTypeParameterListOwner)owner).getTypeParameters(); + if (parameterIndex >= parameters.length) return null; + + String rawName = parameters[parameterIndex].getName(); + if (rawName == null) return null; + String name = "<" + rawName + ">"; + return getParamTagByName(comment, name); + } + }; + } + + private static PsiDocTag getParamTagByName(@NotNull PsiDocComment comment, String name) { + PsiDocTag[] tags = comment.findTagsByName("param"); + return getTagByName(tags, name); + } + + private static PsiDocTag getTagByName(@NotNull PsiDocTag[] tags, String name) { + for (PsiDocTag tag : tags) { + PsiDocTagValue value = tag.getValueElement(); + + if (value != null) { + String text = value.getText(); + + if (text != null && text.equals(name)) { + return tag; + } + } + } + + return null; + } + private static DocTagLocator exceptionLocator(final String name) { return new DocTagLocator() { @Override - public PsiDocTag find(PsiDocComment comment) { + public PsiDocTag find(PsiDocCommentOwner owner, PsiDocComment comment) { if (comment == null) { return null; } @@ -467,23 +493,24 @@ public class JavaDocInfoGenerator { } private void generateTypeParametersSection(final StringBuilder buffer, final PsiClass aClass) { - final LinkedList>> result = - new LinkedList>>(); + final LinkedList result = new LinkedList(); final PsiTypeParameter[] typeParameters = aClass.getTypeParameters(); - for (PsiTypeParameter typeParameter : typeParameters) { - final DocTagLocator locator = parameterLocator("<" + typeParameter.getName() + ">"); + for (int i = 0; i < typeParameters.length; i++) { + PsiTypeParameter typeParameter = typeParameters[i]; + String name = "<" + typeParameter.getName() + ">"; + final DocTagLocator locator = typeParameterLocator(i); final Pair> inClassComment = findInClassComment(aClass, locator); if (inClassComment != null) { - result.add(inClassComment); + result.add(new ParamInfo(name, inClassComment)); } else { final Pair> pair = findInHierarchy(aClass, locator); if (pair != null) { - result.add(pair); + result.add(new ParamInfo(name, pair)); } } } - generateTypeParametersSection(buffer, result); + generateParametersSection(buffer, CodeInsightBundle.message("javadoc.type.parameters"), result); } @Nullable @@ -500,7 +527,7 @@ public class JavaDocInfoGenerator { } private static Pair> findInClassComment(final PsiClass psiClass, final DocTagLocator locator) { - final PsiDocTag tag = locator.find(getDocComment(psiClass)); + final PsiDocTag tag = locator.find(psiClass, getDocComment(psiClass)); if (tag != null) { return new Pair>(tag, new InheritDocProvider() { @Override @@ -541,17 +568,7 @@ public class JavaDocInfoGenerator { if (generatePrologueAndEpilogue) generatePrologue(buffer); - PsiClass parentClass = field.getContainingClass(); - if (parentClass != null) { - String qName = parentClass.getQualifiedName(); - if (qName != null) { - buffer.append(""); - //buffer.append(qName); - generateLink(buffer, qName, qName, field, false); - buffer.append(""); - //buffer.append("
"); - } - } + generateLinkToParentIfNeeded(buffer, field); buffer.append("
");
     generateFieldSignature(buffer, field, true);
@@ -929,8 +946,11 @@ public class JavaDocInfoGenerator {
   }
 
   private void generateMethodParameterJavaDoc(@NonNls StringBuilder buffer, PsiParameter parameter, boolean generatePrologueAndEpilogue) {
-    if (generatePrologueAndEpilogue)
+    String parameterName = parameter.getName();
+    
+    if (generatePrologueAndEpilogue) {
       generatePrologue(buffer);
+    }
 
     buffer.append("
");
     String modifiers = PsiFormatUtil.formatModifiers(parameter, PsiFormatUtilBase.JAVADOC_MODIFIERS_ONLY);
@@ -942,7 +962,7 @@ public class JavaDocInfoGenerator {
     generateType(buffer, parameter.getType(), parameter);
     buffer.append(" ");
     buffer.append("");
-    buffer.append(parameter.getName());
+    buffer.append(parameterName);
     appendInitializer(buffer, parameter);
     buffer.append("");
     buffer.append("
"); @@ -950,14 +970,14 @@ public class JavaDocInfoGenerator { final PsiElement method = PsiTreeUtil.getParentOfType(parameter, PsiMethod.class, PsiLambdaExpression.class); if (method instanceof PsiMethod) { - final PsiDocComment docComment = getDocComment((PsiMethod)method); + PsiMethod psiMethod = (PsiMethod)method; + final PsiDocComment docComment = getDocComment(psiMethod); final PsiDocTag[] localTags = docComment != null ? docComment.getTags() : PsiDocTag.EMPTY_ARRAY; - final Pair> tagInfoProvider = - findDocTag(localTags, parameter.getName(), (PsiMethod)method); + int parameterIndex = psiMethod.getParameterList().getParameterIndex(parameter); + final ParamInfo tagInfoProvider = findDocTag(localTags, parameterName, psiMethod, parameterLocator(parameterIndex)); if (tagInfoProvider != null) { - PsiElement[] elements = tagInfoProvider.first.getDataElements(); - if (elements.length != 0) generateOneParameter(elements, buffer, tagInfoProvider); + generateOneParameter(buffer, tagInfoProvider); } } @@ -969,17 +989,7 @@ public class JavaDocInfoGenerator { if (generatePrologueAndEpilogue) generatePrologue(buffer); - PsiClass parentClass = method.getContainingClass(); - if (parentClass != null) { - String qName = parentClass.getQualifiedName(); - if (qName != null) { - buffer.append(""); - generateLink(buffer, qName, qName, method, false); - //buffer.append(qName); - buffer.append(""); - //buffer.append("
"); - } - } + generateLinkToParentIfNeeded(buffer, method); buffer.append("
");
     generateMethodSignature(buffer, method, true, false);
@@ -999,7 +1009,7 @@ public class JavaDocInfoGenerator {
     }
 
     generateParametersSection(buffer, method, comment);
-    generateTypeParametersSection(buffer, method);
+    generateTypeParametersSection(buffer, method, comment);
     generateReturnsSection(buffer, method, comment);
     generateThrowsSection(buffer, method, comment);
 
@@ -1013,6 +1023,18 @@ public class JavaDocInfoGenerator {
       generateEpilogue(buffer);
   }
 
+  private static void generateLinkToParentIfNeeded(StringBuilder buffer, PsiMember member) {
+    PsiClass parentClass = member.getContainingClass();
+    if (parentClass != null) {
+      String qName = parentClass.getQualifiedName();
+      if (qName != null) {
+        buffer.append("");
+        generateLink(buffer, qName, qName, member, false);
+        buffer.append("");
+      }
+    }
+  }
+
   private static void generateMethodSignature(StringBuilder buffer, PsiMethod method, boolean generateLink, boolean useShortNames) {
     generateAnnotations(buffer, method, generateLink, true, useShortNames);
     String modifiers = PsiFormatUtil.formatModifiers(method, PsiFormatUtilBase.JAVADOC_MODIFIERS_ONLY);
@@ -1218,7 +1240,7 @@ public class JavaDocInfoGenerator {
   private void generateMethodDescription(@NonNls StringBuilder buffer, final PsiMethod method, final PsiDocComment comment) {
     final DocTagLocator descriptionLocator = new DocTagLocator() {
       @Override
-      public PsiElement[] find(PsiDocComment comment) {
+      public PsiElement[] find(PsiDocCommentOwner owner, PsiDocComment comment) {
         if (comment == null) {
           return null;
         }
@@ -1537,113 +1559,86 @@ public class JavaDocInfoGenerator {
     PsiParameter[] params = method.getParameterList().getParameters();
     PsiDocTag[] localTags = comment != null ? comment.findTagsByName("param") : PsiDocTag.EMPTY_ARRAY;
 
-    LinkedList>> collectedTags =
-      new LinkedList>>();
+    LinkedList collectedTags = new LinkedList();
 
-    for (PsiParameter param : params) {
+    for (int i = 0; i < params.length; i++) {
+      PsiParameter param = params[i];
       final String paramName = param.getName();
-      Pair> parmTag = findDocTag(localTags, paramName, method);
+      DocTagLocator tagLocator = parameterLocator(i);
+      ParamInfo parmTag = findDocTag(localTags, paramName, method, tagLocator);
 
       if (parmTag != null) {
         collectedTags.addLast(parmTag);
       }
     }
 
-    if (!collectedTags.isEmpty()) {
-      buffer.append("
"); - buffer.append("
").append(CodeInsightBundle.message("javadoc.parameters")).append(""); - for (Pair> tag : collectedTags) { - PsiElement[] elements = tag.first.getDataElements(); - if (elements.length == 0) continue; - generateOneParameter(elements, buffer, tag); - } - buffer.append("
"); - } + generateParametersSection(buffer, CodeInsightBundle.message("javadoc.parameters"), collectedTags); } - private void generateTypeParametersSection(final StringBuilder buffer, final PsiMethod method) { - final PsiDocComment docComment = method.getDocComment(); - if (docComment == null) return; - final PsiDocTag[] localTags = docComment.findTagsByName("param"); + private void generateTypeParametersSection(final StringBuilder buffer, final PsiMethod method, PsiDocComment comment) { + final PsiDocTag[] localTags = comment == null ? PsiDocTag.EMPTY_ARRAY : comment.findTagsByName("param"); final PsiTypeParameter[] typeParameters = method.getTypeParameters(); - final LinkedList>> collectedTags = new LinkedList>>(); - for (PsiTypeParameter typeParameter : typeParameters) { + final LinkedList collectedTags = new LinkedList(); + for (int i = 0; i < typeParameters.length; i++) { + PsiTypeParameter typeParameter = typeParameters[i]; final String paramName = "<" + typeParameter.getName() + ">"; - Pair> parmTag = findDocTag(localTags, paramName, method); + DocTagLocator tagLocator = typeParameterLocator(i); + ParamInfo parmTag = findDocTag(localTags, paramName, method, tagLocator); if (parmTag != null) { collectedTags.addLast(parmTag); } } - generateTypeParametersSection(buffer, collectedTags); + generateParametersSection(buffer, CodeInsightBundle.message("javadoc.type.parameters"), collectedTags); } - private void generateTypeParametersSection(final StringBuilder buffer, final LinkedList>> collectedTags) { + private void generateParametersSection(StringBuilder buffer, String titleMessage, LinkedList collectedTags) { if (!collectedTags.isEmpty()) { buffer.append("
"); - buffer.append("
").append(CodeInsightBundle.message("javadoc.type.parameters")).append(""); - for (Pair> tag : collectedTags) { - PsiElement[] elements = tag.first.getDataElements(); - if (elements.length == 0) continue; - generateOneParameter(elements, buffer, tag); + buffer.append("
").append(titleMessage).append(""); + for (ParamInfo tag : collectedTags) { + generateOneParameter(buffer, tag); } buffer.append("
"); } } - @Nullable private Pair> findDocTag(final PsiDocTag[] localTags, - final String paramName, - final PsiMethod method) { - Pair> parmTag = null; - for (PsiDocTag localTag : localTags) { - PsiDocTagValue value = localTag.getValueElement(); - - if (value != null) { - String tagName = value.getText(); - - if (tagName != null && tagName.equals(paramName)) { - parmTag = - new Pair> - (localTag, - new InheritDocProvider() { - @Override - public Pair> getInheritDoc() { - return findInheritDocTag(method, parameterLocator(paramName)); - } - - @Override - public PsiClass getElement() { - return method.getContainingClass(); - } - }); - break; + @Nullable private ParamInfo findDocTag(final PsiDocTag[] localTags, + final String paramName, final PsiMethod method, final DocTagLocator tagLocator) { + PsiDocTag localTag = getTagByName(localTags, paramName); + if (localTag != null) { + return new ParamInfo(paramName, localTag, new InheritDocProvider() { + @Override + public Pair> getInheritDoc() { + return findInheritDocTag(method, tagLocator); } - } - } - if (parmTag == null) { - parmTag = findInheritDocTag(method, parameterLocator(paramName)); + @Override + public PsiClass getElement() { + return method.getContainingClass(); + } + }); } - return parmTag; + Pair> tag = findInheritDocTag(method, tagLocator); + return tag == null ? null : new ParamInfo(paramName, tag); } @SuppressWarnings({"HardCodedStringLiteral"}) - private void generateOneParameter(final PsiElement[] elements, - final StringBuilder buffer, - final Pair> tag) { + private void generateOneParameter(StringBuilder buffer, ParamInfo tag) { + PsiElement[] elements = tag.docTag.getDataElements(); + if (elements.length == 0) return; String text = elements[0].getText(); buffer.append("
"); int spaceIndex = text.indexOf(' '); if (spaceIndex < 0) { spaceIndex = text.length(); } - String parmName = text.substring(0, spaceIndex); buffer.append(""); - buffer.append(StringUtil.escapeXml(parmName)); + buffer.append(StringUtil.escapeXml(tag.name)); buffer.append(""); buffer.append(" - "); buffer.append(text.substring(spaceIndex)); - generateValue(buffer, elements, 1, mapProvider(tag.second, true)); + generateValue(buffer, elements, 1, mapProvider(tag.inheritDocTagProvider, true)); } @SuppressWarnings({"HardCodedStringLiteral"}) @@ -2073,7 +2068,7 @@ public class JavaDocInfoGenerator { final PsiMethod overriden = findMethodInSuperClass(method, aSuper); if (overriden != null) { - T tag = loc.find(getDocComment(overriden)); + T tag = loc.find(overriden, getDocComment(overriden)); if (tag != null) { return new Pair> @@ -2167,10 +2162,26 @@ public class JavaDocInfoGenerator { return findInheritDocTagInClass(method, aClass, loc, new HashSet()); } + + private static class ParamInfo { + private final String name; + private final PsiDocTag docTag; + private final InheritDocProvider inheritDocTagProvider; + + private ParamInfo(String paramName, PsiDocTag tag, InheritDocProvider provider) { + name = paramName; + docTag = tag; + inheritDocTagProvider = provider; + } + + private ParamInfo(String paramName, @NotNull Pair> tagWithInheritProvider) { + this(paramName, tagWithInheritProvider.first, tagWithInheritProvider.second); + } + } private static class ReturnTagLocator implements DocTagLocator { @Override - public PsiDocTag find(PsiDocComment comment) { + public PsiDocTag find(PsiDocCommentOwner owner, PsiDocComment comment) { if (comment == null) { return null; } diff --git a/java/java-impl/src/com/intellij/codeInsight/navigation/JavaGotoTargetRendererProvider.java b/java/java-impl/src/com/intellij/codeInsight/navigation/JavaGotoTargetRendererProvider.java index 7441bb53dc72..d8c3edac662d 100644 --- a/java/java-impl/src/com/intellij/codeInsight/navigation/JavaGotoTargetRendererProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/navigation/JavaGotoTargetRendererProvider.java @@ -21,13 +21,14 @@ import com.intellij.ide.util.PsiElementListCellRenderer; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMethod; +import org.jetbrains.annotations.NotNull; /** * @author yole */ public class JavaGotoTargetRendererProvider implements GotoTargetRendererProvider { @Override - public PsiElementListCellRenderer getRenderer(final PsiElement element, GotoTargetHandler.GotoData gotoData) { + public PsiElementListCellRenderer getRenderer(@NotNull final PsiElement element, @NotNull GotoTargetHandler.GotoData gotoData) { if (element instanceof PsiMethod) { return new MethodCellRenderer(gotoData.hasDifferentNames()); } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsRepositoryPsiElement.java b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsRepositoryPsiElement.java index d821b37c7e29..4faaa6a34df3 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsRepositoryPsiElement.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsRepositoryPsiElement.java @@ -15,8 +15,6 @@ */ package com.intellij.psi.impl.compiled; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.ProjectCoreUtil; import com.intellij.psi.*; import com.intellij.psi.stubs.IStubElementType; import com.intellij.psi.stubs.PsiFileStub; @@ -45,10 +43,6 @@ public abstract class ClsRepositoryPsiElement extends Cls @Override public PsiManager getManager() { - Project project = ProjectCoreUtil.theOnlyOpenProject(); - if (project != null) { - return PsiManager.getInstance(project); - } final PsiFile file = getContainingFile(); if (file == null) throw new PsiInvalidElementAccessException(this); return file.getManager(); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/light/LightPsiClassBuilder.java b/java/java-psi-impl/src/com/intellij/psi/impl/light/LightPsiClassBuilder.java index 06939dd1c15a..5b3f6892e6c1 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/light/LightPsiClassBuilder.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/light/LightPsiClassBuilder.java @@ -16,17 +16,21 @@ package com.intellij.psi.impl.light; import com.intellij.psi.*; +import com.intellij.psi.impl.PsiClassImplUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; +import static com.intellij.psi.PsiReferenceList.Role.EXTENDS_LIST; +import static com.intellij.psi.PsiReferenceList.Role.IMPLEMENTS_LIST; + public class LightPsiClassBuilder extends LightPsiClassBase implements OriginInfoAwareElement { private final LightModifierList myModifierList = new LightModifierList(getManager()); - private final LightReferenceListBuilder myImplementsList = new LightReferenceListBuilder(getManager(), PsiReferenceList.Role.EXTENDS_LIST); - private final LightReferenceListBuilder myExtendsList = new LightReferenceListBuilder(getManager(), PsiReferenceList.Role.EXTENDS_LIST); + private final LightReferenceListBuilder myImplementsList = new LightReferenceListBuilder(getManager(), IMPLEMENTS_LIST); + private final LightReferenceListBuilder myExtendsList = new LightReferenceListBuilder(getManager(), EXTENDS_LIST); private final LightTypeParameterListBuilder myTypeParametersList = new LightTypeParameterListBuilder(getManager(), getLanguage()); private final Collection myMethods = ContainerUtil.newArrayList(); private PsiElement myScope; @@ -104,6 +108,11 @@ public class LightPsiClassBuilder extends LightPsiClassBase implements OriginInf return myTypeParametersList; } + @Override + public boolean isEquivalentTo(PsiElement another) { + return PsiClassImplUtil.isClassEquivalentTo(this, another); + } + public LightPsiClassBuilder setOriginInfo(String originInfo) { myOriginInfo = originInfo; return this; diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/light/LightTypeParameterBuilder.java b/java/java-psi-impl/src/com/intellij/psi/impl/light/LightTypeParameterBuilder.java new file mode 100644 index 000000000000..1003a8871ee9 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/light/LightTypeParameterBuilder.java @@ -0,0 +1,70 @@ +/* + * Copyright 2000-2015 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.psi.impl.light; + +import com.intellij.psi.PsiAnnotation; +import com.intellij.psi.PsiTypeParameter; +import com.intellij.psi.PsiTypeParameterListOwner; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class LightTypeParameterBuilder extends LightPsiClassBuilder implements PsiTypeParameter { + + private final PsiTypeParameterListOwner myOwner; + private final int myIndex; + + public LightTypeParameterBuilder(@NotNull String name, PsiTypeParameterListOwner owner, int index) { + super(owner, name); + myOwner = owner; + myIndex = index; + } + + @Nullable + @Override + public PsiTypeParameterListOwner getOwner() { + return myOwner; + } + + @Override + public int getIndex() { + return myIndex; + } + + @NotNull + @Override + public PsiAnnotation[] getAnnotations() { + return getModifierList().getAnnotations(); + } + + @NotNull + @Override + public PsiAnnotation[] getApplicableAnnotations() { + return getModifierList().getApplicableAnnotations(); + } + + @Nullable + @Override + public PsiAnnotation findAnnotation(@NotNull @NonNls String qualifiedName) { + return getModifierList().findAnnotation(qualifiedName); + } + + @NotNull + @Override + public PsiAnnotation addAnnotation(@NotNull @NonNls String qualifiedName) { + return getModifierList().addAnnotation(qualifiedName); + } +} diff --git a/java/java-tests/testData/codeInsight/javadocIG/matchingParameterNameFromParent.html b/java/java-tests/testData/codeInsight/javadocIG/matchingParameterNameFromParent.html new file mode 100644 index 000000000000..6e09c3961e7e --- /dev/null +++ b/java/java-tests/testData/codeInsight/javadocIG/matchingParameterNameFromParent.html @@ -0,0 +1,2 @@ + Bar
+void m(int bar)
Overrides:
m in class Foo
Parameters:
bar - description
\ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/javadocIG/matchingParameterNameFromParent.java b/java/java-tests/testData/codeInsight/javadocIG/matchingParameterNameFromParent.java new file mode 100644 index 000000000000..78671d51eaee --- /dev/null +++ b/java/java-tests/testData/codeInsight/javadocIG/matchingParameterNameFromParent.java @@ -0,0 +1,13 @@ +import java.lang.Override; + +class Foo { + /** + * @param foo description + */ + void m(int foo) {} +} + +class Bar extends Foo { + @Override + void m(int bar) {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/javadocIG/matchingTypeParameterNameFromParent.html b/java/java-tests/testData/codeInsight/javadocIG/matchingTypeParameterNameFromParent.html new file mode 100644 index 000000000000..39ed7e32f77d --- /dev/null +++ b/java/java-tests/testData/codeInsight/javadocIG/matchingTypeParameterNameFromParent.html @@ -0,0 +1,2 @@ + Bar
+<U> void m(U u)
Overrides:
m in class Foo
Type parameters:
<U> - description
\ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/javadocIG/matchingTypeParameterNameFromParent.java b/java/java-tests/testData/codeInsight/javadocIG/matchingTypeParameterNameFromParent.java new file mode 100644 index 000000000000..808b4f506ab7 --- /dev/null +++ b/java/java-tests/testData/codeInsight/javadocIG/matchingTypeParameterNameFromParent.java @@ -0,0 +1,13 @@ +import java.lang.Override; + +class Foo { + /** + * @param description + */ + void m(T t) {} +} + +class Bar extends Foo { + @Override + void m(U u) {} +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.groovy index e79fc1b9d356..4a018f51bef1 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.groovy @@ -677,7 +677,7 @@ interface TxANotAnno {} public void testLiveTemplateOrdering() { LiveTemplateCompletionContributor.setShowTemplatesInTests(true, getTestRootDisposable()) checkPreferredItems(0, 'return') - assert lookup.items[-1].lookupString == 'ritar' + assert lookup.items.find { it.lookupString == 'ritar'} != null } public void testPreferLocalToExpectedTypedMethod() { diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/javadoc/JavaDocInfoGeneratorTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/javadoc/JavaDocInfoGeneratorTest.java index 165699bc59f3..6e5f94a36540 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/javadoc/JavaDocInfoGeneratorTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/javadoc/JavaDocInfoGeneratorTest.java @@ -277,6 +277,20 @@ public class JavaDocInfoGeneratorTest extends CodeInsightTestCase { useJava7(); verifyJavaDoc(getTestClass()); } + + public void testMatchingParameterNameFromParent() throws Exception { + configureByFile("/codeInsight/javadocIG/" + getTestName(true) + ".java"); + PsiClass psiClass = ((PsiJavaFile)myFile).getClasses()[1]; + PsiMethod method = psiClass.getMethods()[0]; + verifyJavaDoc(method); + } + + public void testMatchingTypeParameterNameFromParent() throws Exception { + configureByFile("/codeInsight/javadocIG/" + getTestName(true) + ".java"); + PsiClass psiClass = ((PsiJavaFile)myFile).getClasses()[1]; + PsiMethod method = psiClass.getMethods()[0]; + verifyJavaDoc(method); + } @Override protected String getTestDataPath() { diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/tree/RecursiveLighterASTNodeWalkingVisitor.java b/platform/core-impl/src/com/intellij/psi/impl/source/tree/RecursiveLighterASTNodeWalkingVisitor.java index e2752dc0342d..0f34d6f54b60 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/tree/RecursiveLighterASTNodeWalkingVisitor.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/tree/RecursiveLighterASTNodeWalkingVisitor.java @@ -85,7 +85,6 @@ public abstract class RecursiveLighterASTNodeWalkingVisitor extends LighterASTNo parent.put(child, element); } parent.put(children.get(0), element); - ast.disposeChildren(children); return children.get(0); } diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/DvcsUtil.java b/platform/dvcs-impl/src/com/intellij/dvcs/DvcsUtil.java index e37bfb15d6fe..3c4dec24dad2 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/DvcsUtil.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/DvcsUtil.java @@ -15,6 +15,7 @@ */ package com.intellij.dvcs; +import com.intellij.dvcs.push.PushSupport; import com.intellij.dvcs.repo.RepoStateException; import com.intellij.dvcs.repo.Repository; import com.intellij.dvcs.repo.RepositoryManager; @@ -24,6 +25,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.FileEditorManager; @@ -422,4 +424,14 @@ public class DvcsUtil { } return groupedCommits; } + + @Nullable + public static PushSupport getPushSupport(@NotNull final AbstractVcs vcs) { + return ContainerUtil.find(Extensions.getExtensions(PushSupport.PUSH_SUPPORT_EP, vcs.getProject()), new Condition() { + @Override + public boolean value(final PushSupport support) { + return support.getVcs().equals(vcs); + } + }); + } } diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/PushController.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/PushController.java index c6b50152b077..defce1de442d 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/PushController.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/PushController.java @@ -23,7 +23,6 @@ import com.intellij.dvcs.repo.VcsRepositoryManager; import com.intellij.dvcs.ui.DvcsBundle; import com.intellij.openapi.Disposable; import com.intellij.openapi.components.ServiceManager; -import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; @@ -31,6 +30,7 @@ import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.vcs.AbstractVcs; import com.intellij.ui.CheckedTreeNode; import com.intellij.util.ConcurrencyUtil; import com.intellij.util.Function; @@ -62,7 +62,7 @@ public class PushController implements Disposable { @NotNull private final Project myProject; @NotNull private final List myPreselectedRepositories; @NotNull private final VcsRepositoryManager myGlobalRepositoryManager; - @NotNull private final List> myPushSupports; + @NotNull private final List> myPushSupports; @NotNull private final PushLog myPushLog; @NotNull private final VcsPushDialog myDialog; @NotNull private final PushSettings myPushSettings; @@ -83,7 +83,7 @@ public class PushController implements Disposable { myExcludedRepositoryRoots = ContainerUtil.newHashSet(myPushSettings.getExcludedRepoRoots()); myPreselectedRepositories = preselectedRepositories; myCurrentlyOpenedRepository = currentRepo; - myPushSupports = getAffectedSupports(myProject); + myPushSupports = getAffectedSupports(); mySingleRepoProject = isSingleRepoProject(); myDialog = dialog; CheckedTreeNode rootNode = new CheckedTreeNode(null); @@ -106,13 +106,12 @@ public class PushController implements Disposable { private boolean isSyncStrategiesAllowed() { return !mySingleRepoProject && - ContainerUtil.and(getAffectedSupports(myProject), - new Condition>() { - @Override - public boolean value(PushSupport support) { - return support.mayChangeTargetsSync(); - } - }); + ContainerUtil.and(getAffectedSupports(), new Condition>() { + @Override + public boolean value(PushSupport support) { + return support.mayChangeTargetsSync(); + } + }); } private boolean isSingleRepoProject() { @@ -120,17 +119,19 @@ public class PushController implements Disposable { } @NotNull - private List> getAffectedSupports(@NotNull Project project) { - final Collection repositories = myGlobalRepositoryManager.getRepositories(); - return ContainerUtil.filter(Extensions.getExtensions(PushSupport.PUSH_SUPPORT_EP, project), new Condition() { + private List> getAffectedSupports() { + Collection repositories = myGlobalRepositoryManager.getRepositories(); + Collection vcss = ContainerUtil.map2Set(repositories, new Function() { @Override - public boolean value(final PushSupport support) { - return ContainerUtil.exists(repositories, new Condition() { - @Override - public boolean value(Repository repository) { - return support.getVcs().equals(repository.getVcs()); - } - }); + public AbstractVcs fun(@NotNull Repository repository) { + return repository.getVcs(); + } + }); + return ContainerUtil.map(vcss, new Function>() { + @Override + public PushSupport fun(AbstractVcs vcs) { + //noinspection unchecked + return DvcsUtil.getPushSupport(vcs); } }); } @@ -308,13 +309,12 @@ public class PushController implements Disposable { public boolean isPushAllowed(final boolean force) { JTree tree = myPushLog.getTree(); return !tree.isEditing() && - ContainerUtil - .exists(myPushSupports, new Condition>() { - @Override - public boolean value(PushSupport support) { - return isPushAllowed(support, force); - } - }); + ContainerUtil.exists(myPushSupports, new Condition>() { + @Override + public boolean value(PushSupport support) { + return isPushAllowed(support, force); + } + }); } private boolean isPushAllowed(@NotNull PushSupport pushSupport, boolean force) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/GotoTargetHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/GotoTargetHandler.java index f2069874326f..159d5ebb8c96 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/navigation/GotoTargetHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/GotoTargetHandler.java @@ -242,7 +242,8 @@ public abstract class GotoTargetHandler implements CodeInsightActionHandler { }; } - public static PsiElementListCellRenderer createRenderer(GotoData gotoData, PsiElement eachTarget) { + @NotNull + public static PsiElementListCellRenderer createRenderer(@NotNull GotoData gotoData, @NotNull PsiElement eachTarget) { PsiElementListCellRenderer renderer = null; for (GotoTargetRendererProvider eachProvider : Extensions.getExtensions(GotoTargetRendererProvider.EP_NAME)) { renderer = eachProvider.getRenderer(eachTarget, gotoData); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/GotoTargetRendererProvider.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/GotoTargetRendererProvider.java index 0e97ae92b706..f6204785be6d 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/navigation/GotoTargetRendererProvider.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/GotoTargetRendererProvider.java @@ -19,6 +19,7 @@ package com.intellij.codeInsight.navigation; import com.intellij.ide.util.PsiElementListCellRenderer; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -28,5 +29,5 @@ public interface GotoTargetRendererProvider { ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.gotoTargetRendererProvider"); @Nullable - PsiElementListCellRenderer getRenderer(PsiElement element, GotoTargetHandler.GotoData gotoData); + PsiElementListCellRenderer getRenderer(@NotNull PsiElement element, @NotNull GotoTargetHandler.GotoData gotoData); } diff --git a/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupConfigurable.java b/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupConfigurable.java new file mode 100644 index 000000000000..aa95294d78e6 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupConfigurable.java @@ -0,0 +1,311 @@ +/* + * Copyright 2000-2015 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.execution.startup; + +import com.intellij.execution.Executor; +import com.intellij.execution.ExecutorRegistry; +import com.intellij.execution.RunManager; +import com.intellij.execution.RunnerAndConfigurationSettings; +import com.intellij.execution.actions.ChooseRunConfigurationPopup; +import com.intellij.execution.actions.ExecutorProvider; +import com.intellij.execution.impl.EditConfigurationsDialog; +import com.intellij.execution.impl.RunManagerImpl; +import com.intellij.icons.AllIcons; +import com.intellij.idea.ActionsBundle; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.options.SearchableConfigurable; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.wm.ToolWindowId; +import com.intellij.ui.*; +import com.intellij.ui.awt.RelativePoint; +import com.intellij.ui.components.JBList; +import com.intellij.ui.popup.PopupFactoryImpl; +import com.intellij.ui.treeStructure.Tree; +import com.intellij.util.ui.FormBuilder; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.tree.TreePath; +import javax.swing.tree.TreeSelectionModel; +import java.awt.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * @author Irina.Chernushina on 8/19/2015. + */ +public class ProjectStartupConfigurable implements SearchableConfigurable, Configurable.NoScroll { + private final Project myProject; + private Tree myTree; + private ProjectStartupConfiguration myConfiguration; + private ToolbarDecorator myDecorator; + + public ProjectStartupConfigurable(Project project) { + myProject = project; + myConfiguration = ProjectStartupConfiguration.getInstance(myProject); + } + + @NotNull + @Override + public String getId() { + return "preferences.startup.tasks"; + } + + @Nullable + @Override + public Runnable enableSearch(String option) { + return null; + } + + @Nls + @Override + public String getDisplayName() { + return "Project Startup Tasks"; + } + + @Nullable + @Override + public String getHelpTopic() { + return null; + } + + @Nullable + @Override + public JComponent createComponent() { + myTree = new Tree(); + installRenderer(); + myDecorator = ToolbarDecorator.createDecorator(myTree) + .setAddAction(new AnActionButtonRunnable() { + @Override + public void run(AnActionButton button) { + selectAndAddConfiguration(button); + } + }) + .setEditAction(new AnActionButtonRunnable() { + @Override + public void run(AnActionButton button) { + RunnerAndConfigurationSettings selected = getSelectedSettings(); + if (selected == null) return; + + final RunManager runManager = RunManagerImpl.getInstance(myProject); + final RunnerAndConfigurationSettings was = runManager.getSelectedConfiguration(); + try { + runManager.setSelectedConfiguration(selected); + new EditConfigurationsDialog(myProject).showAndGet(); + } finally { + runManager.setSelectedConfiguration(was); + } + } + }) + .setEditActionUpdater(new AnActionButtonUpdater() { + @Override + public boolean isEnabled(AnActionEvent e) { + return getSelectedSettings() != null; + } + }) + .setRemoveAction(new AnActionButtonRunnable() { + @Override + public void run(AnActionButton button) { + RunnerAndConfigurationSettings selected = getSelectedSettings(); + if (selected == null) return; + final ProjectStartupTasksTreeModel oldModel = (ProjectStartupTasksTreeModel)myTree.getModel(); + final List configurations = oldModel.getConfigurations(); + if (!configurations.contains(selected)) { + return; + } + configurations.remove(selected); + Collections.sort(configurations, new Comparator() { + @Override + public int compare(RunnerAndConfigurationSettings o1, RunnerAndConfigurationSettings o2) { + return o1.getName().compareToIgnoreCase(o2.getName()); + } + }); + setModel(new ProjectStartupTasksTreeModel(configurations)); + } + }); + myTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); + final JPanel tasksPanel = myDecorator.createPanel(); + return FormBuilder.createFormBuilder() // todo bundle + .addLabeledComponentFillVertically("Tasks to be executed right after opening the project.", tasksPanel) + .getPanel(); + } + + @Nullable + private RunnerAndConfigurationSettings getSelectedSettings() { + final TreePath path = myTree.getSelectionPath(); + if (path == null) return null; + if (! (path.getLastPathComponent() instanceof RunnerAndConfigurationSettings)) return null; + return (RunnerAndConfigurationSettings)path.getLastPathComponent(); + } + + private ChooseRunConfigurationPopup.ItemWrapper createEditWrapper() { + return new ChooseRunConfigurationPopup.ItemWrapper(null) { + @Override + public Icon getIcon() { + return AllIcons.Actions.EditSource; + } + + @Override + public String getText() { + return UIUtil.removeMnemonic(ActionsBundle.message("action.editRunConfigurations.text")); + } + + @Override + public void perform(@NotNull final Project project, @NotNull final Executor executor, @NotNull DataContext context) { + if (new EditConfigurationsDialog(project).showAndGet()) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + RunnerAndConfigurationSettings configuration = RunManager.getInstance(project).getSelectedConfiguration(); + if (configuration != null) { + addConfigurationToList(configuration); + } + } + }, project.getDisposed()); + } + } + + @Override + public boolean available(Executor executor) { + return true; + } + }; + } + + private void selectAndAddConfiguration(final AnActionButton button) { + final Executor executor = ExecutorRegistry.getInstance().getExecutorById(ToolWindowId.DEBUG); + final List wrappers = new ArrayList(); + wrappers.add(createEditWrapper()); + final ChooseRunConfigurationPopup.ItemWrapper[] allSettings = + ChooseRunConfigurationPopup.createSettingsList(myProject, new ExecutorProvider() { + @Override + public Executor getExecutor() { + return executor; + } + }, false); + for (ChooseRunConfigurationPopup.ItemWrapper setting : allSettings) { + if (setting.getValue() instanceof RunnerAndConfigurationSettings) { + // todo maybe auto save temporary? + if (! ((RunnerAndConfigurationSettings)setting.getValue()).isTemporary()) { + wrappers.add(setting); + } + } + } + final JBList list = new JBList(wrappers); + list.setCellRenderer(new ColoredListCellRenderer() { + @Override + protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) { + if (value instanceof ChooseRunConfigurationPopup.ItemWrapper) { + setIcon(((ChooseRunConfigurationPopup.ItemWrapper)value).getIcon()); + append(((ChooseRunConfigurationPopup.ItemWrapper)value).getText()); + } + } + }); + final JBPopup popup = PopupFactoryImpl.getInstance() + .createListPopupBuilder(list) + .setItemChoosenCallback(new Runnable() { + @Override + public void run() { + final int index = list.getSelectedIndex(); + if (index < 0) return; + final ChooseRunConfigurationPopup.ItemWrapper at = (ChooseRunConfigurationPopup.ItemWrapper)list.getModel().getElementAt(index); + if (at.getValue() instanceof RunnerAndConfigurationSettings) { + addConfigurationToList((RunnerAndConfigurationSettings)at.getValue()); + } else { + at.perform(myProject, executor, button.getDataContext()); + } + } + }) + .createPopup(); + popup.show(new RelativePoint(myDecorator.getActionsPanel(), new Point(10, 10))); + } + + private void addConfigurationToList(final RunnerAndConfigurationSettings settings) { + if (settings != null) { + final ProjectStartupTasksTreeModel oldModel = (ProjectStartupTasksTreeModel)myTree.getModel(); + final List configurations = oldModel.getConfigurations(); + if (!configurations.contains(settings)) { + configurations.add(settings); + } + Collections.sort(configurations, new Comparator() { + @Override + public int compare(RunnerAndConfigurationSettings o1, RunnerAndConfigurationSettings o2) { + return o1.getName().compareToIgnoreCase(o2.getName()); + } + }); + setModel(new ProjectStartupTasksTreeModel(configurations)); + } + } + + @Override + public boolean isModified() { + final List recorded = myConfiguration.getStartupConfigurations(); + final List current = ((ProjectStartupTasksTreeModel)myTree.getModel()).getConfigurations(); + return ! Comparing.equal(recorded, current); + } + + @Override + public void apply() throws ConfigurationException { + myConfiguration.setStartupConfigurations(((ProjectStartupTasksTreeModel)myTree.getModel()).getConfigurations(), false); + } + + @Override + public void reset() { + final ProjectStartupTasksTreeModel model = new ProjectStartupTasksTreeModel(myConfiguration.getStartupConfigurations()); + setModel(model); + } + + private void setModel(ProjectStartupTasksTreeModel model) { + myTree.setModel(model); + myTree.setShowsRootHandles(false); + myTree.setRootVisible(false); + } + + @Override + public void disposeUIResources() { + + } + + private void installRenderer() { + myTree.setCellRenderer(new ColoredTreeCellRenderer() { + @Override + public void customizeCellRenderer(@NotNull JTree tree, + Object value, + boolean selected, + boolean expanded, + boolean leaf, + int row, + boolean hasFocus) { + if (value instanceof RunnerAndConfigurationSettings) { + final RunnerAndConfigurationSettings settings = (RunnerAndConfigurationSettings)value; + setIcon(settings.getConfiguration().getIcon()); + append(settings.getName()); + } + } + }); + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupConfiguration.java b/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupConfiguration.java new file mode 100644 index 000000000000..d85479919218 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupConfiguration.java @@ -0,0 +1,107 @@ +/* + * Copyright 2000-2015 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.execution.startup; + +import com.intellij.execution.RunManager; +import com.intellij.execution.RunManagerEx; +import com.intellij.execution.RunnerAndConfigurationSettings; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.project.Project; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Irina.Chernushina on 8/19/2015. + */ +public class ProjectStartupConfiguration { + private final Project myProject; + private final ProjectStartupSharedConfiguration myShared; + private final ProjectStartupLocalConfiguration myLocal; + private final RunManagerEx myRunManager; + + public static ProjectStartupConfiguration getInstance(@NotNull final Project project) { + return ServiceManager.getService(project, ProjectStartupConfiguration.class); + } + + public ProjectStartupConfiguration(Project project, ProjectStartupSharedConfiguration shared, ProjectStartupLocalConfiguration local, + @NotNull RunManager runManager) { + myProject = project; + myShared = shared; + myLocal = local; + myRunManager = (RunManagerEx)runManager; + } + + public List getStartupConfigurations() { + final List result = new ArrayList(); + if (! myShared.isEmpty()) { + myLocal.clear(); + return fillResult(result, myShared.getList()); + } + return fillResult(result, myLocal.getList()); + } + + private List fillResult(List result, List list) { + for (String s : list) { + final RunnerAndConfigurationSettings settings = myRunManager.findConfigurationByName(s); + if (settings != null) { + result.add(settings); + } + } + return result; + } + + public void setStartupConfigurations(final @NotNull List list, final boolean shared) { + final List names = ContainerUtil.map(list, new Function() { + @Override + public String fun(RunnerAndConfigurationSettings settings) { + return settings.getName(); + } + }); + if (shared) { + myLocal.clear(); + myShared.setList(names); + } else { + myShared.clear(); + myLocal.setList(names); + } + } + + public boolean canBeShared() { + if (isShared()) return true; + if (isEmpty()) return true; + + final List list = myLocal.getList(); + for (String s : list) { + final RunnerAndConfigurationSettings settings = myRunManager.findConfigurationByName(s); + if (settings != null) { + if (! myRunManager.isConfigurationShared(settings)) return false; + } + } + return true; + } + + public boolean isShared() { + return ! myShared.isEmpty(); + } + + public boolean isEmpty() { + return myShared.isEmpty() && myLocal.isEmpty(); + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupConfigurationBase.java b/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupConfigurationBase.java new file mode 100644 index 000000000000..5911b62f8871 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupConfigurationBase.java @@ -0,0 +1,106 @@ +/* + * Copyright 2000-2015 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.execution.startup; + +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.util.text.StringUtil; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Irina.Chernushina on 8/19/2015. + */ +public class ProjectStartupConfigurationBase implements PersistentStateComponent { + private final static String TOP_ELEMENT = "startup-tasks"; + private final static String TASK = "task"; + private final static String NAME = "name"; + private final static String TYPE = "type"; + + private final List myList; + + protected ProjectStartupConfigurationBase() { + myList = new ArrayList(); + } + + @Nullable + @Override + public Element getState() { + if (myList.isEmpty()) return null; + final Element element = new Element(TOP_ELEMENT); + for (String name : myList) { + final Element child = new Element(TASK); + child.setAttribute(NAME, name); + + element.addContent(child); + } + return element; + } + + @Override + public void loadState(Element state) { + myList.clear(); + final List children = state.getChildren(); + for (Element child : children) { + if (TASK.equals(child.getName())) { + final String name = child.getAttributeValue(NAME); + if (! StringUtil.isEmptyOrSpaces(name)) { + myList.add(name); + } + } + } + } + + public void clear() { + myList.clear(); + } + + public List getList() { + return myList; + } + + public void setList(@NotNull final List list) { + myList.clear(); + myList.addAll(list); + } + + public boolean isEmpty() { + return myList.isEmpty(); + } + + public static class ConfigurationDescriptor { + private final @NotNull String myTypeId; + private final @NotNull String myName; + + public ConfigurationDescriptor(@NotNull String typeId, @NotNull String name) { + myTypeId = typeId; + myName = name; + } + + @NotNull + public String getTypeId() { + return myTypeId; + } + + @NotNull + public String getName() { + return myName; + } + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupLocalConfiguration.java b/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupLocalConfiguration.java new file mode 100644 index 000000000000..f8747a48beb6 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupLocalConfiguration.java @@ -0,0 +1,32 @@ +/* + * Copyright 2000-2015 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.execution.startup; + +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.components.StoragePathMacros; + +/** + * @author Irina.Chernushina on 8/19/2015. + */ +@State( + name = "ProjectStartupLocalConfiguration", + storages = { + @Storage(file = StoragePathMacros.WORKSPACE_FILE) + } +) +public class ProjectStartupLocalConfiguration extends ProjectStartupConfigurationBase { +} diff --git a/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupRunner.java b/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupRunner.java new file mode 100644 index 000000000000..1d36c9b7eb7d --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupRunner.java @@ -0,0 +1,72 @@ +/* + * Copyright 2000-2015 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.execution.startup; + +import com.intellij.execution.Executor; +import com.intellij.execution.ProgramRunnerUtil; +import com.intellij.execution.RunnerAndConfigurationSettings; +import com.intellij.execution.executors.DefaultRunExecutor; +import com.intellij.ide.startup.StartupManagerEx; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.startup.StartupActivity; +import com.intellij.openapi.startup.StartupManager; +import com.intellij.util.Alarm; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * @author Irina.Chernushina on 8/19/2015. + */ +public class ProjectStartupRunner implements StartupActivity { + public static final int DELAY_MILLIS = 200; + + @Override + public void runActivity(@NotNull Project project) { + if (ProjectStartupConfiguration.getInstance(project).isEmpty()) return; + + final Alarm alarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, project); + alarm.addRequest(createRequest(project, alarm), DELAY_MILLIS); + } + + private Runnable createRequest(final Project project, final Alarm alarm) { + return new Runnable() { + @Override + public void run() { + if (! ((StartupManagerEx) StartupManager.getInstance(project)).postStartupActivityPassed()) { + alarm.addRequest(createRequest(project, alarm), DELAY_MILLIS); + } else { + runActivities(project); + } + } + }; + } + + private void runActivities(final Project project) { + final List configurations = ProjectStartupConfiguration.getInstance(project).getStartupConfigurations(); + final Executor executor = DefaultRunExecutor.getRunExecutorInstance(); + for (final RunnerAndConfigurationSettings configuration : configurations) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + ProgramRunnerUtil.executeConfiguration(project, configuration, executor); + } + }, ModalityState.any()); + } + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupSharedConfiguration.java b/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupSharedConfiguration.java new file mode 100644 index 000000000000..ba25ed21bc13 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupSharedConfiguration.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2015 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.execution.startup; + +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.components.StoragePathMacros; +import com.intellij.openapi.components.StorageScheme; + +/** + * @author Irina.Chernushina on 8/19/2015. + */ +@State( + name = "ProjectStartupSharedConfiguration", + storages = { + @Storage(file = StoragePathMacros.PROJECT_FILE), + @Storage(file = StoragePathMacros.PROJECT_CONFIG_DIR + "/startup.xml", scheme = StorageScheme.DEFAULT) + } +) +public class ProjectStartupSharedConfiguration extends ProjectStartupConfigurationBase { +} diff --git a/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupTasksTreeModel.java b/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupTasksTreeModel.java new file mode 100644 index 000000000000..c850d08a66d5 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupTasksTreeModel.java @@ -0,0 +1,92 @@ +/* + * Copyright 2000-2015 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.execution.startup; + +import com.intellij.execution.RunnerAndConfigurationSettings; + +import javax.swing.event.TreeModelListener; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; +import java.util.List; + +/** + * @author Irina.Chernushina on 8/19/2015. + */ +public class ProjectStartupTasksTreeModel implements TreeModel { + private final Object myRoot = new Object(); + private final List myConfigurations; + + public ProjectStartupTasksTreeModel(List configurations) { + myConfigurations = configurations; + } + + @Override + public Object getRoot() { + return myRoot; + } + + @Override + public Object getChild(Object parent, int index) { + if (parent == myRoot) { + return myConfigurations.get(index); + } + return null; + } + + @Override + public int getChildCount(Object parent) { + if (parent == myRoot) { + return myConfigurations.size(); + } + return 0; + } + + @Override + public boolean isLeaf(Object node) { + return getChildCount(node) == 0; + } + + @Override + public void valueForPathChanged(TreePath path, Object newValue) { + } + + @Override + public int getIndexOfChild(Object parent, Object child) { + if (parent == myRoot) { + for (int i = 0; i < myConfigurations.size(); i++) { + final RunnerAndConfigurationSettings configuration = myConfigurations.get(i); + if (configuration == child) { + return i; + } + } + } + return 0; + } + + @Override + public void addTreeModelListener(TreeModelListener l) { + + } + + @Override + public void removeTreeModelListener(TreeModelListener l) { + + } + + public List getConfigurations() { + return myConfigurations; + } +} diff --git a/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java b/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java index 5f78c056dbac..2e47619ace92 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java @@ -1182,7 +1182,7 @@ public abstract class DialogWrapper { ensureEventDispatchThread(); myErrorText = new ErrorText(); myErrorText.setVisible(false); - myErrorText.myLabel.addComponentListener(new ComponentAdapter() { + final ComponentAdapter resizeListener = new ComponentAdapter() { private int myHeight; @Override @@ -1203,6 +1203,13 @@ public abstract class DialogWrapper { myResizeInProgress = false; } } + }; + myErrorText.myLabel.addComponentListener(resizeListener); + Disposer.register(myDisposable, new Disposable() { + @Override + public void dispose() { + myErrorText.myLabel.removeComponentListener(resizeListener); + } }); final JPanel root = new JPanel(createRootLayout()); diff --git a/platform/util/src/com/intellij/util/io/BaseOutputReader.java b/platform/util/src/com/intellij/util/io/BaseOutputReader.java index de7ed192fa02..bab4a0866a00 100644 --- a/platform/util/src/com/intellij/util/io/BaseOutputReader.java +++ b/platform/util/src/com/intellij/util/io/BaseOutputReader.java @@ -29,6 +29,9 @@ import java.nio.charset.Charset; public abstract class BaseOutputReader extends BaseDataReader { protected final Reader myReader; + private final char[] myInputBuffer = new char[8192]; + private final StringBuilder myLineBuffer = new StringBuilder(); + public BaseOutputReader(@NotNull InputStream inputStream, @Nullable Charset charset) { this(inputStream, charset, null); } @@ -62,18 +65,16 @@ public abstract class BaseOutputReader extends BaseDataReader { * @throws IOException If an I/O error occurs */ protected final boolean readAvailableNonBlocking() throws IOException { - char[] buffer = new char[8192]; - StringBuilder line = new StringBuilder(); boolean read = false; int n; - while (myReader.ready() && (n = myReader.read(buffer)) > 0) { + while (myReader.ready() && (n = myReader.read(myInputBuffer)) > 0) { read = true; - processLine(buffer, line, n); + processLine(myInputBuffer, myLineBuffer, n); } - if (line.length() > 0) { - sendLine(line); + if (myLineBuffer.length() > 0) { + sendLine(myLineBuffer); } return read; @@ -89,23 +90,21 @@ public abstract class BaseOutputReader extends BaseDataReader { * @throws IOException If an I/O error occurs */ protected final boolean readAvailableBlocking() throws IOException { - char[] buffer = new char[8192]; - StringBuilder line = new StringBuilder(); boolean read = false; int n; - while ((n = myReader.read(buffer)) > 0) { + while ((n = myReader.read(myInputBuffer)) > 0) { read = true; - processLine(buffer, line, n); + processLine(myInputBuffer, myLineBuffer, n); if (!myReader.ready()) { - if (line.length() > 0) sendLine(line); + if (myLineBuffer.length() > 0) sendLine(myLineBuffer); onBufferExhaustion(); } } - if (line.length() > 0) { - sendLine(line); + if (myLineBuffer.length() > 0) { + sendLine(myLineBuffer); } return read; diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/versionBrowser/ChangeBrowserSettings.java b/platform/vcs-api/src/com/intellij/openapi/vcs/versionBrowser/ChangeBrowserSettings.java index cd2806d15eda..c8fdd345192f 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/versionBrowser/ChangeBrowserSettings.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/versionBrowser/ChangeBrowserSettings.java @@ -18,6 +18,7 @@ package com.intellij.openapi.vcs.versionBrowser; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.*; import com.intellij.util.text.SyncDateFormat; +import com.intellij.util.xmlb.annotations.Transient; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -54,6 +55,8 @@ public class ChangeBrowserSettings implements JDOMExternalizable { public String USER = ""; public boolean STOP_ON_COPY = false; + @Transient public boolean STRICTLY_AFTER = false; + public void readExternal(Element element) throws InvalidDataException { DefaultJDOMExternalizer.readExternal(this, element); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CommittedChangesCache.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CommittedChangesCache.java index 43d805494c50..0e481fcd6000 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CommittedChangesCache.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CommittedChangesCache.java @@ -84,7 +84,6 @@ public class CommittedChangesCache implements PersistentStateComponent myNewIncomingChanges = new LinkedHashSet(); private final ProjectLevelVcsManager myVcsManager; - public static final Change[] ALL_CHANGES = new Change[0]; private MyRefreshRunnable myRefresnRunnable; private final Map>> myExternallyLoadedChangeLists; @@ -538,7 +537,7 @@ public class CommittedChangesCache implements PersistentStateComponent refreshCache(final ChangesCacheFile cacheFile) throws VcsException, IOException { final List newLists = new ArrayList(); @@ -571,6 +570,7 @@ public class CommittedChangesCache implements PersistentStateComponent newChanges = provider.getCommittedChanges(defaultSettings, location, maxCount); debug("Loaded " + newChanges.size() + " new changelists"); newLists.addAll(appendLoadedChanges(cacheFile, location, newChanges)); diff --git a/platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/AbstractVcsTestCase.java b/platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/AbstractVcsTestCase.java index d459e971b910..694e07557cb5 100644 --- a/platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/AbstractVcsTestCase.java +++ b/platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/AbstractVcsTestCase.java @@ -22,11 +22,13 @@ import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.diff.LineTokenizer; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.EmptyRunnable; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.annotate.AnnotationProvider; import com.intellij.openapi.vcs.annotate.FileAnnotation; import com.intellij.openapi.vcs.changes.*; +import com.intellij.openapi.vcs.changes.committed.CommittedChangesCache; import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; @@ -160,6 +162,7 @@ public abstract class AbstractVcsTestCase { protected void tearDownProject() throws Exception { if (myProject != null) { ((ChangeListManagerImpl)ChangeListManager.getInstance(myProject)).stopEveryThingIfInTestMode(); + CommittedChangesCache.getInstance(myProject).clearCaches(EmptyRunnable.INSTANCE); myProject = null; } if (myProjectFixture != null) { diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/XDebuggerTreePanel.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/XDebuggerTreePanel.java index 0cc1cf7b285d..d9c87176a414 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/XDebuggerTreePanel.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/XDebuggerTreePanel.java @@ -52,6 +52,12 @@ public class XDebuggerTreePanel implements DnDSource { myMainPanel = new JPanel(new BorderLayout()); myMainPanel.add(ScrollPaneFactory.createScrollPane(myTree), BorderLayout.CENTER); Disposer.register(parentDisposable, myTree); + Disposer.register(parentDisposable, new Disposable() { + @Override + public void dispose() { + myMainPanel.removeAll(); + } + }); } public XDebuggerTree getTree() { diff --git a/plugins/devkit/src/dom/IdeaPlugin.java b/plugins/devkit/src/dom/IdeaPlugin.java index d1d3c355f93f..0be5e9853988 100644 --- a/plugins/devkit/src/dom/IdeaPlugin.java +++ b/plugins/devkit/src/dom/IdeaPlugin.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -105,6 +105,7 @@ public interface IdeaPlugin extends DomElement { @NotNull @SubTagList("extensions") + @Stubbed List getExtensions(); Extensions addExtensions(); diff --git a/plugins/devkit/src/dom/impl/PluginXmlDomFileDescription.java b/plugins/devkit/src/dom/impl/PluginXmlDomFileDescription.java index 091c172d0a60..d55248376888 100644 --- a/plugins/devkit/src/dom/impl/PluginXmlDomFileDescription.java +++ b/plugins/devkit/src/dom/impl/PluginXmlDomFileDescription.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -43,6 +43,6 @@ public class PluginXmlDomFileDescription extends DomFileDescription @Override public int getStubVersion() { - return 3; + return 4; } } diff --git a/plugins/devkit/testData/pluginXmlDomStubs/pluginXmlStubs.xml b/plugins/devkit/testData/pluginXmlDomStubs/pluginXmlStubs.xml index 30e95aa28cd1..d05f6d47ff4a 100644 --- a/plugins/devkit/testData/pluginXmlDomStubs/pluginXmlStubs.xml +++ b/plugins/devkit/testData/pluginXmlDomStubs/pluginXmlStubs.xml @@ -13,4 +13,6 @@ + + \ No newline at end of file diff --git a/plugins/devkit/testSources/codeInsight/PluginXmlDomStubsTest.java b/plugins/devkit/testSources/codeInsight/PluginXmlDomStubsTest.java index f920bfb0e374..ecea03a9c204 100644 --- a/plugins/devkit/testSources/codeInsight/PluginXmlDomStubsTest.java +++ b/plugins/devkit/testSources/codeInsight/PluginXmlDomStubsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -40,7 +40,10 @@ public class PluginXmlDomStubsTest extends DomStubTest { " Attribute:implements:SomeImplements\n" + " Element:extensionPoint\n" + " Attribute:qualifiedName:qualifiedName\n" + - " Attribute:beanClass:BeanClass\n"); + " Attribute:beanClass:BeanClass\n" + + " Element:extensions\n" + + " Attribute:defaultExtensionNs:defaultExtensionNs\n" + + " Attribute:xmlns:extensionXmlNs\n"); } public void testXInclude() throws Exception { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearcher.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearcher.java index 640c49f86440..e989158a4f21 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearcher.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -224,6 +224,7 @@ public class GroovyConstructorUsagesSearcher extends QueryExecutorBase result = ContainerUtil.newArrayList(); final GrTypeDefinitionBody body = myDefinition.getBody(); if (body != null) ContainerUtil.addAll(result, body.getInnerClasses()); - result.addAll(AstTransformContributor.runContributorsForClasses(myDefinition)); + result.addAll(AstTransformContributor.runContributors(myDefinition).getClasses()); return Result.create(result.toArray(new PsiClass[result.size()]), myTreeChangeTracker); } }); @@ -135,16 +135,17 @@ public class GrTypeDefinitionMembersCache { private List getFieldsImpl() { List fields = ContainerUtil.newArrayList(myDefinition.getCodeFields()); + fields.addAll(new TraitCollector().collectFields()); fields.addAll(getSyntheticFields()); return fields; } - private List getSyntheticFields() { - return CachedValuesManager.getCachedValue(myDefinition, new CachedValueProvider>() { + private Collection getSyntheticFields() { + return CachedValuesManager.getCachedValue(myDefinition, new CachedValueProvider>() { @Nullable @Override - public Result> compute() { - return Result.create(AstTransformContributor.runContributorsForFields(myDefinition), myTreeChangeTracker, + public Result> compute() { + return Result.create(AstTransformContributor.runContributors(myDefinition).getFields(), myTreeChangeTracker, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT); } }); @@ -154,10 +155,11 @@ public class GrTypeDefinitionMembersCache { return CachedValuesManager.getCachedValue(myDefinition, new CachedValueProvider() { @Override public Result compute() { - List result = ContainerUtil.newArrayList(); - GrClassImplUtil.collectMethodsFromBody(myDefinition, result); + Collection result = ContainerUtil.newLinkedHashSet(); - for (PsiMethod method : AstTransformContributor.runContributorsForMethods(myDefinition)) { + GrClassImplUtil.collectMethodsFromBody(myDefinition, result); + result.addAll(new TraitCollector().collectMethods(result)); + for (PsiMethod method : AstTransformContributor.runContributors(myDefinition).getMethods()) { GrClassImplUtil.addExpandingReflectedMethods(result, method); } @@ -166,6 +168,9 @@ public class GrTypeDefinitionMembersCache { ContainerUtil.addIfNotNull(result, field.getSetter()); Collections.addAll(result, field.getGetters()); } + + result = GrClassImplUtil.filterOutAccessors(result); + return Result.create(result.toArray(new PsiMethod[result.size()]), myTreeChangeTracker, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT); } @@ -176,8 +181,8 @@ public class GrTypeDefinitionMembersCache { myTreeChangeTracker.incModificationCount(); } - public static class TraitCollector extends AstTransformContributor { - private abstract static class TraitProcessor { + private class TraitCollector { + private abstract class TraitProcessor { private final ArrayList result = ContainerUtil.newArrayList(); private final Set processed = ContainerUtil.newHashSet(); @@ -213,18 +218,17 @@ public class GrTypeDefinitionMembersCache { } } - @Override - public void collectMethods(@NotNull GrTypeDefinition clazz, Collection collector) { - if (clazz.isInterface() && !clazz.isTrait()) return; + @NotNull + public List collectMethods(@NotNull Collection codeMethods) { + if (myDefinition.isInterface() && !myDefinition.isTrait()) return Collections.emptyList(); - GrImplementsClause clause = clazz.getImplementsClause(); - if (clause == null) return; + GrImplementsClause clause = myDefinition.getImplementsClause(); + if (clause == null) return Collections.emptyList(); PsiClassType[] types = clause.getReferencedTypes(); List traits = getSuperTraitsByCorrectOrder(types); - if (traits.isEmpty()) return; + if (traits.isEmpty()) return Collections.emptyList(); - PsiMethod[] codeMethods = clazz.getCodeMethods(); Set existingSignatures = ContainerUtil.newHashSet(ContainerUtil.map(codeMethods, new Function() { @Override public MethodSignature fun(PsiMethod method) { @@ -232,6 +236,8 @@ public class GrTypeDefinitionMembersCache { } })); + List result = ContainerUtil.newArrayList(); + for (PsiClassType.ClassResolveResult resolveResult : traits) { GrTypeDefinition trait = (GrTypeDefinition)resolveResult.getElement(); LOG.assertTrue(trait != null); @@ -257,29 +263,31 @@ public class GrTypeDefinitionMembersCache { } }.getResult(); for (CandidateInfo candidateInfo : concreteTraitMethods) { - List methodsToAdd = getExpandingMethods(clazz, candidateInfo); + List methodsToAdd = getExpandingMethods(candidateInfo); for (GrMethod impl : methodsToAdd) { if (existingSignatures.add(impl.getSignature(PsiSubstitutor.EMPTY))) { - collector.add(impl); + result.add(impl); } } } } + return result; } - @Override - public void collectFields(@NotNull GrTypeDefinition clazz, Collection collector) { - if (clazz.isInterface() && !clazz.isTrait()) return; + @NotNull + public List collectFields() { + if (myDefinition.isInterface() && !myDefinition.isTrait()) return Collections.emptyList(); - if (clazz.isTrait()) { - for (GrField field : clazz.getCodeFields()) { - collector.add(new GrTraitField(field, clazz, PsiSubstitutor.EMPTY)); + List result = ContainerUtil.newArrayList(); + + if (myDefinition.isTrait()) { + for (GrField field : myDefinition.getCodeFields()) { + result.add(new GrTraitField(field, myDefinition, PsiSubstitutor.EMPTY)); } } - GrImplementsClause clause = clazz.getImplementsClause(); - if (clause == null) return; - + GrImplementsClause clause = myDefinition.getImplementsClause(); + if (clause == null) return result; PsiClassType[] types = clause.getReferencedTypes(); List traits = getSuperTraitsByCorrectOrder(types); @@ -295,21 +303,17 @@ public class GrTypeDefinitionMembersCache { } }.getResult(); for (CandidateInfo candidateInfo : traitFields) { - collector.add(new GrTraitField(((PsiField)candidateInfo.getElement()), clazz, candidateInfo.getSubstitutor())); + result.add(new GrTraitField(((PsiField)candidateInfo.getElement()), myDefinition, candidateInfo.getSubstitutor())); } } - if (clazz.isTrait()) { - for (GrField field : clazz.getCodeFields()) { - collector.add(new GrTraitField(field, clazz, PsiSubstitutor.EMPTY)); - } - } + return result; } @NotNull - private static List getExpandingMethods(@NotNull GrTypeDefinition clazz, @NotNull CandidateInfo candidateInfo) { + private List getExpandingMethods(@NotNull CandidateInfo candidateInfo) { PsiMethod method = (PsiMethod)candidateInfo.getElement(); - GrLightMethodBuilder implementation = GrTraitMethod.create(method, candidateInfo.getSubstitutor()).setContainingClass(clazz); + GrLightMethodBuilder implementation = GrTraitMethod.create(method, candidateInfo.getSubstitutor()).setContainingClass(myDefinition); implementation.getModifierList().removeModifier(GrModifierFlags.ABSTRACT_MASK); GrReflectedMethod[] reflectedMethods = implementation.getReflectedMethods(); @@ -317,7 +321,7 @@ public class GrTypeDefinitionMembersCache { } @NotNull - private static List getSuperTraitsByCorrectOrder(@NotNull PsiClassType[] types) { + private List getSuperTraitsByCorrectOrder(@NotNull PsiClassType[] types) { List traits = ContainerUtil.newArrayList(); for (int i = types.length - 1; i >= 0; i--) { PsiClassType.ClassResolveResult resolveResult = types[i].resolveGenerics(); diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/GrClassImplUtil.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/GrClassImplUtil.java index 11dc762bade0..1777eaf686c1 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/GrClassImplUtil.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/GrClassImplUtil.java @@ -33,6 +33,7 @@ import com.intellij.util.ArrayUtil; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.hash.HashSet; +import gnu.trove.TObjectIntHashMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -50,7 +51,6 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGd import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrReflectedMethod; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; -import org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager; import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; @@ -58,6 +58,7 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.statements.typedef.GrTypeDefin import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrScriptField; import org.jetbrains.plugins.groovy.lang.resolve.CollectClassMembersUtil; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; +import org.jetbrains.plugins.groovy.lang.resolve.ast.AstTransformContributor; import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint; import java.util.*; @@ -136,63 +137,15 @@ public class GrClassImplUtil { @NotNull public static PsiClassType[] getImplementsListTypes(GrTypeDefinition grType) { - Set visited = new HashSet(); - List result = new ArrayList(); - getImplementListsInner(grType, result, visited); - return result.toArray(new PsiClassType[result.size()]); - } - - private static void getImplementListsInner(GrTypeDefinition grType, List result, Set visited) { - if (!visited.add(grType)) return; - + final Collection result = ContainerUtil.newLinkedHashSet(); final PsiClassType[] implementsTypes = getReferenceListTypes(grType.getImplementsClause()); - List fromDelegates = getImplementsFromDelegate(grType, visited); - if (fromDelegates != null) { - result.addAll(fromDelegates); - } + final PsiClassType[] extendsTypes = getReferenceListTypes(grType.getExtendsClause()); result.addAll(Arrays.asList(implementsTypes)); - - if (!grType.isInterface() && - !ContainerUtil.or(implementsTypes, IS_GROOVY_OBJECT) && - !ContainerUtil.or(getReferenceListTypes(grType.getExtendsClause()), IS_GROOVY_OBJECT)) { + result.addAll(AstTransformContributor.runContributors(grType).getImplementsTypes()); + if (!grType.isInterface() && !ContainerUtil.or(implementsTypes, IS_GROOVY_OBJECT) && !ContainerUtil.or(extendsTypes, IS_GROOVY_OBJECT)) { result.add(getGroovyObjectType(grType)); } - } - - @Nullable - private static List getImplementsFromDelegate(@NotNull final GrTypeDefinition grType, final Set visited) { - return RecursionManager.doPreventingRecursion(grType, true, new Computable>() { - @Override - public List compute() { - List result = new ArrayList(); - final GrField[] fields = grType.getCodeFields(); - for (GrField field : fields) { - final PsiAnnotation delegate = PsiImplUtil.getAnnotation(field, GroovyCommonClassNames.GROOVY_LANG_DELEGATE); - if (delegate == null) continue; - - final boolean shouldImplement = shouldImplementDelegatedInterfaces(delegate); - if (!shouldImplement) continue; - - final PsiType type = field.getDeclaredType(); - if (!(type instanceof PsiClassType)) continue; - - final PsiClass psiClass = ((PsiClassType)type).resolve(); - if (psiClass == null) continue; - - if (psiClass instanceof GrTypeDefinition) { - getImplementListsInner((GrTypeDefinition)psiClass, result, visited); - } - else { - result.addAll(Arrays.asList(psiClass.getImplementsListTypes())); - } - if (psiClass.isInterface()) { - result.add((PsiClassType)type); - } - } - return result; - - } - }); + return result.toArray(new PsiClassType[result.size()]); } public static PsiClassType getGroovyObjectType(@NotNull PsiElement context) { @@ -640,11 +593,6 @@ public class GrClassImplUtil { return PsiClassImplUtil.isClassEquivalentTo(definition, another); } - private static boolean shouldImplementDelegatedInterfaces(PsiAnnotation delegate) { - final Boolean result = GrAnnotationUtil.inferBooleanAttribute(delegate, "interfaces"); - return result == null || result.booleanValue(); - } - public static void addExpandingReflectedMethods(Collection result, PsiMethod method) { if (method instanceof GrMethod) { final GrReflectedMethod[] reflectedMethods = ((GrMethod)method).getReflectedMethods(); @@ -667,4 +615,25 @@ public class GrClassImplUtil { ContainerUtil.addIfNotNull(result, field.getSetter()); } } + + public static Collection filterOutAccessors(Collection result) { + final TObjectIntHashMap map = new TObjectIntHashMap(); + for (PsiMethod method : result) { + if (method instanceof GrAccessorMethod || GroovyPropertyUtils.isSimplePropertyAccessor(method)) { + final String methodName = method.getName(); + if (map.containsKey(methodName)) { + map.adjustValue(methodName, 1); + } + else { + map.put(methodName, 1); + } + } + } + return ContainerUtil.filter(result, new Condition() { + @Override + public boolean value(PsiMethod method) { + return !(method instanceof GrAccessorMethod) || map.get(method.getName()) <= 1; + } + }); + } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/AstTransformContributor.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/AstTransformContributor.java index 8ddf360e6932..73d985a36920 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/AstTransformContributor.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/AstTransformContributor.java @@ -18,78 +18,54 @@ package org.jetbrains.plugins.groovy.lang.resolve.ast; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.RecursionManager; -import com.intellij.psi.PsiClass; import com.intellij.psi.PsiMethod; -import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; -import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; -import java.util.List; /** * @author Max Medvedev */ public abstract class AstTransformContributor { + public static final ExtensionPointName EP_NAME = ExtensionPointName.create("org.intellij.groovy.astTransformContributor"); + @Deprecated public void collectMethods(@NotNull final GrTypeDefinition clazz, Collection collector) { } + @Deprecated public void collectFields(@NotNull final GrTypeDefinition clazz, Collection collector) { } - public void collectClasses(@NotNull final GrTypeDefinition clazz, Collection collector) { - + /** + * Subclasses should override this method. + */ + @NotNull + @SuppressWarnings("deprecation") + public Members collect(@NotNull final GrTypeDefinition clazz) { + final Members members = Members.create(); + collectMethods(clazz, members.getMethods()); + collectFields(clazz, members.getFields()); + return members; } @NotNull - public static Collection runContributorsForMethods(@NotNull final GrTypeDefinition clazz) { - Collection result = RecursionManager.doPreventingRecursion(clazz, true, new Computable>() { + public static Members runContributors(@NotNull final GrTypeDefinition clazz) { + Members result = RecursionManager.doPreventingRecursion(clazz, true, new Computable() { @Override - public Collection compute() { - Collection collector = new ArrayList(); + public Members compute() { + Members members = Members.create(); for (final AstTransformContributor contributor : EP_NAME.getExtensions()) { - contributor.collectMethods(clazz, collector); + members.addFrom(contributor.collect(clazz)); } - return collector; + return members; } }); - return result == null ? Collections.emptyList() : result; - } - - @NotNull - public static List runContributorsForFields(@NotNull final GrTypeDefinition clazz) { - List fields = RecursionManager.doPreventingRecursion(clazz, true, new Computable>() { - @Override - public List compute() { - List collector = new ArrayList(); - for (final AstTransformContributor contributor : EP_NAME.getExtensions()) { - contributor.collectFields(clazz, collector); - } - return collector; - } - }); - return fields != null ? fields : Collections.emptyList(); - } - - @NotNull - public static List runContributorsForClasses(@NotNull final GrTypeDefinition clazz) { - List fields = RecursionManager.doPreventingRecursion(clazz, true, new Computable>() { - @Override - public List compute() { - List collector = ContainerUtil.newArrayList(); - for (final AstTransformContributor contributor : EP_NAME.getExtensions()) { - contributor.collectClasses(clazz, collector); - } - return collector; - } - }); - return fields != null ? fields : Collections.emptyList(); + return result == null ? Members.EMPTY : result; } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/DelegatedMethodsContributor.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/DelegatedMethodsContributor.java index 6ff0c74a9f0c..0fd1a84f65b1 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/DelegatedMethodsContributor.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/DelegatedMethodsContributor.java @@ -49,12 +49,21 @@ import java.util.*; * @author Max Medvedev */ public class DelegatedMethodsContributor extends AstTransformContributor { + + @NotNull @Override - public void collectMethods(@NotNull final GrTypeDefinition clazz, @NotNull Collection collector) { + public Members collect(@NotNull GrTypeDefinition clazz) { + final Members result = Members.create(); + doCollectMethods(clazz, result.getMethods()); + doCollectImplementsTypes(clazz, result.getImplementsTypes()); + return result; + } + + private static void doCollectMethods(@NotNull final GrTypeDefinition clazz, @NotNull Collection collector) { Set processed = new HashSet(); if (!checkForDelegate(clazz)) return; - + Map signatures = new THashMap(MethodSignatureUtil.METHOD_PARAMETERS_ERASURE_EQUALITY); initializeSignatures(clazz, PsiSubstitutor.EMPTY, signatures, processed); @@ -334,4 +343,31 @@ public class DelegatedMethodsContributor extends AstTransformContributor { private static final Set OBJECT_METHODS = ContainerUtil.newHashSet("equals", "hashCode", "getClass", "clone", "toString", "notify", "notifyAll", "wait", "finalize"); private static final Set GROOVY_OBJECT_METHODS = ContainerUtil.newHashSet("invokeMethod", "getProperty", "setProperty", "getMetaClass", "setMetaClass"); + + private static void doCollectImplementsTypes(GrTypeDefinition grType, Collection result) { + final GrField[] fields = grType.getCodeFields(); + for (GrField field : fields) { + final PsiAnnotation delegate = PsiImplUtil.getAnnotation(field, GroovyCommonClassNames.GROOVY_LANG_DELEGATE); + if (delegate == null) continue; + + final boolean shouldImplement = shouldImplementDelegatedInterfaces(delegate); + if (!shouldImplement) continue; + + final PsiType type = field.getDeclaredType(); + if (!(type instanceof PsiClassType)) continue; + + final PsiClass psiClass = ((PsiClassType)type).resolve(); + if (psiClass == null) continue; + + result.addAll(Arrays.asList(psiClass.getImplementsListTypes())); + if (psiClass.isInterface()) { + result.add((PsiClassType)type); + } + } + } + + private static boolean shouldImplementDelegatedInterfaces(PsiAnnotation delegate) { + final Boolean result = GrAnnotationUtil.inferBooleanAttribute(delegate, "interfaces"); + return result == null || result.booleanValue(); + } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/Members.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/Members.java new file mode 100644 index 000000000000..95c5abf87ef7 --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/Members.java @@ -0,0 +1,118 @@ +/* + * Copyright 2000-2015 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 org.jetbrains.plugins.groovy.lang.resolve.ast; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiClassType; +import com.intellij.psi.PsiMethod; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; + +import java.util.Collection; +import java.util.Collections; + +public abstract class Members { + + @NotNull + public abstract Collection getMethods(); + + @NotNull + public abstract Collection getFields(); + + @NotNull + public abstract Collection getClasses(); + + @NotNull + public abstract Collection getImplementsTypes(); + + public abstract void addFrom(@NotNull Members other); + + public static final Members EMPTY = new Members() { + + @NotNull + @Override + public Collection getMethods() { + return Collections.emptyList(); + } + + @NotNull + @Override + public Collection getFields() { + return Collections.emptyList(); + } + + @NotNull + @Override + public Collection getClasses() { + return Collections.emptyList(); + } + + @NotNull + @Override + public Collection getImplementsTypes() { + return Collections.emptyList(); + } + + @Override + public void addFrom(@NotNull Members other) { + // do nothing + } + }; + + @NotNull + public static Members create() { + return new Members() { + + private final Collection methods = ContainerUtil.newArrayList(); + private final Collection fields = ContainerUtil.newArrayList(); + private final Collection classes = ContainerUtil.newArrayList(); + private final Collection implementsTypes = ContainerUtil.newArrayList(); + + @NotNull + @Override + public Collection getMethods() { + return methods; + } + + @NotNull + @Override + public Collection getFields() { + return fields; + } + + @NotNull + @Override + public Collection getClasses() { + return classes; + } + + @NotNull + @Override + public Collection getImplementsTypes() { + return implementsTypes; + } + + @Override + public void addFrom(@NotNull Members other) { + methods.addAll(other.getMethods()); + fields.addAll(other.getFields()); + classes.addAll(other.getClasses()); + implementsTypes.addAll(other.getImplementsTypes()); + } + }; + } +} diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/BuilderAnnotationContributor.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/BuilderAnnotationContributor.java index aed4f3617914..7be764a90db6 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/BuilderAnnotationContributor.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/BuilderAnnotationContributor.java @@ -15,48 +15,54 @@ */ package org.jetbrains.plugins.groovy.lang.resolve.ast.builder; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiMethod; -import com.intellij.psi.util.CachedValueProvider; -import com.intellij.psi.util.CachedValuesManager; -import com.intellij.psi.util.PsiModificationTracker; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; import org.jetbrains.plugins.groovy.lang.resolve.ast.AstTransformContributor; -import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.GrBuilderStrategySupport.Members; +import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy.DefaultBuilderStrategySupport; -import java.util.Collection; +public abstract class BuilderAnnotationContributor extends AstTransformContributor { -public class BuilderAnnotationContributor extends AstTransformContributor { + public static final String BUILDER_PACKAGE = "groovy.transform.builder"; + public static final String BUILDER_FQN = BUILDER_PACKAGE + ".Builder"; + public static final String ORIGIN_INFO = "via @Builder"; + public static final String STRATEGY_ATTRIBUTE = "builderStrategy"; - @Override - public void collectMethods(@NotNull GrTypeDefinition clazz, final Collection collector) { - collector.addAll(collectAll(clazz).methods); - } - - @Override - public void collectClasses(@NotNull GrTypeDefinition clazz, Collection collector) { - collector.addAll(collectAll(clazz).classes); - } - - @Override - public void collectFields(@NotNull GrTypeDefinition clazz, Collection collector) { - collector.addAll(collectAll(clazz).fields); - } - - private static Members collectAll(final GrTypeDefinition clazz) { - return CachedValuesManager.getCachedValue(clazz, new CachedValueProvider() { - @Nullable - @Override - public Result compute() { - final Members result = new Members(); - for (GrBuilderStrategySupport strategySupport : GrBuilderStrategySupport.EP.getExtensions()) { - result.addFrom(strategySupport.process(clazz)); - } - return Result.create(result, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT); + @Nullable + @Contract("null,_ -> null") + public static PsiClass getClassAttributeValue(@Nullable PsiAnnotation annotation, @NotNull String attributeName) { + if (annotation == null) return null; + final PsiAnnotationMemberValue value = annotation.findAttributeValue(attributeName); + if (value instanceof GrReferenceExpression) { + final PsiElement element = ((GrReferenceExpression)value).resolve(); + return element instanceof PsiClass ? (PsiClass)element : null; + } + else if (value instanceof PsiClassObjectAccessExpression) { + PsiType type = ((PsiClassObjectAccessExpression)value).getOperand().getType(); + if (type instanceof PsiClassType) { + return ((PsiClassType)type).resolve(); } - }); + } + return null; + } + + @Contract("null, _ -> false") + public static boolean isApplicable(@Nullable PsiAnnotation annotation, @NotNull String strategy) { + if (annotation == null) return false; + final PsiAnnotationMemberValue attributeValue = annotation.findDeclaredAttributeValue(STRATEGY_ATTRIBUTE); + if (attributeValue == null) { + return strategy == DefaultBuilderStrategySupport.DEFAULT_STRATEGY_NAME; + } + else { + final String value = attributeValue.getText(); + return strategy.equals(value) || StringUtil.getQualifiedName(BUILDER_PACKAGE, strategy).equals(value); + } + } + + public static PsiType createType(PsiClass clazz) { + return JavaPsiFacade.getElementFactory(clazz.getProject()).createType(clazz); } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/BuilderHelperLightPsiClass.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/BuilderHelperLightPsiClass.java index f2954a1d3476..0ef37cecb8e3 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/BuilderHelperLightPsiClass.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/BuilderHelperLightPsiClass.java @@ -16,6 +16,7 @@ package org.jetbrains.plugins.groovy.lang.resolve.ast.builder; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiModifier; import com.intellij.psi.impl.light.LightPsiClassBuilder; @@ -24,24 +25,28 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefini public class BuilderHelperLightPsiClass extends LightPsiClassBuilder { - private final @NotNull GrTypeDefinition myContainingClass; - public BuilderHelperLightPsiClass(@NotNull GrTypeDefinition containingClass, @NotNull String name) { super(containingClass, name); - myContainingClass = containingClass; - setScope(myContainingClass); - setOriginInfo(GrBuilderStrategySupport.ORIGIN_INFO); + setScope(containingClass); + setContainingClass(containingClass); + setOriginInfo(BuilderAnnotationContributor.ORIGIN_INFO); getModifierList().addModifier(PsiModifier.STATIC); } + @Override + public PsiElement getParent() { + return getContainingClass(); + } + @NotNull @Override public PsiClass getContainingClass() { - return myContainingClass; + //noinspection ConstantConditions + return super.getContainingClass(); } @Override public PsiFile getContainingFile() { - return myContainingClass.getContainingFile(); + return getContainingClass().getContainingFile(); } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/GrBuilderStrategySupport.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/GrBuilderStrategySupport.java deleted file mode 100644 index 082a6f20e2af..000000000000 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/GrBuilderStrategySupport.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2000-2015 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 org.jetbrains.plugins.groovy.lang.resolve.ast.builder; - -import com.intellij.openapi.extensions.ExtensionPointName; -import com.intellij.psi.*; -import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; -import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; -import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy.DefaultBuilderStrategySupport; - -import java.util.Collection; - -public abstract class GrBuilderStrategySupport { - - public static final ExtensionPointName EP = ExtensionPointName.create("org.intellij.groovy.builderStrategySupport"); - public static final String BUILDER_FQN = "groovy.transform.builder.Builder"; - public static final String ORIGIN_INFO = "by @Builder"; - - public static class Members { - public final Collection methods = ContainerUtil.newArrayList(); - public final Collection fields = ContainerUtil.newArrayList(); - public final Collection classes = ContainerUtil.newArrayList(); - public void addFrom(Members other) { - methods.addAll(other.methods); - fields.addAll(other.fields); - classes.addAll(other.classes); - } - } - - @NotNull - public abstract Members process(GrTypeDefinition typeDefinition); - - @Nullable - public static String getStrategy(PsiModifierListOwner annotatedMember) { - final PsiAnnotation annotation = PsiImplUtil.getAnnotation(annotatedMember, BUILDER_FQN); - if (annotation == null) return null; - - final PsiAnnotationMemberValue strategy = annotation.findDeclaredAttributeValue("builderStrategy"); - if (strategy instanceof GrReferenceExpression) { - final PsiElement element = ((GrReferenceExpression)strategy).resolve(); - return element instanceof PsiClass ? ((PsiClass)element).getQualifiedName() : null; - } - - return DefaultBuilderStrategySupport.DEFAULT_STRATEGY_FQN; - } -} diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/DefaultBuilderStrategySupport.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/DefaultBuilderStrategySupport.java index 7ed40938d27b..76ac329629c5 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/DefaultBuilderStrategySupport.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/DefaultBuilderStrategySupport.java @@ -26,15 +26,17 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; +import org.jetbrains.plugins.groovy.lang.resolve.ast.Members; +import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.BuilderAnnotationContributor; import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.BuilderHelperLightPsiClass; -import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.GrBuilderStrategySupport; -public class DefaultBuilderStrategySupport extends GrBuilderStrategySupport { +public class DefaultBuilderStrategySupport extends BuilderAnnotationContributor { - public static final String DEFAULT_STRATEGY_FQN = "groovy.transform.builder.DefaultStrategy"; + public static final String DEFAULT_STRATEGY_NAME = "DefaultStrategy"; @NotNull - public Members process(@NotNull final GrTypeDefinition typeDefinition) { + @Override + public Members collect(@NotNull final GrTypeDefinition typeDefinition) { return new DefaultBuilderStrategyHandler(typeDefinition).doProcess(); } @@ -47,7 +49,7 @@ public class DefaultBuilderStrategySupport extends GrBuilderStrategySupport { private DefaultBuilderStrategyHandler(@NotNull GrTypeDefinition typeDefinition) { myContainingClass = typeDefinition; myElementFactory = PsiElementFactory.SERVICE.getInstance(typeDefinition.getProject()); - myMembers = new Members(); + myMembers = Members.create(); } @NotNull @@ -64,11 +66,10 @@ public class DefaultBuilderStrategySupport extends GrBuilderStrategySupport { private void processTypeDefinition() { final PsiAnnotation builderAnno = PsiImplUtil.getAnnotation(myContainingClass, BUILDER_FQN); - if (builderAnno == null || !DEFAULT_STRATEGY_FQN.equals(getStrategy(myContainingClass))) return; + if (!isApplicable(builderAnno, DEFAULT_STRATEGY_NAME)) return; final PsiClass builderClass = createBuilderClass(builderAnno, myContainingClass.getCodeFields()); - final LightMethodBuilder builderMethod = createBuilderMethod(builderClass, builderAnno); - myMembers.classes.add(builderClass); - myMembers.methods.add(builderMethod); + myMembers.getMethods().add(createBuilderMethod(builderClass, builderAnno)); + myMembers.getClasses().add(builderClass); } @NotNull @@ -88,10 +89,9 @@ public class DefaultBuilderStrategySupport extends GrBuilderStrategySupport { builderClass.addMethod(createFieldSetter(builderClass, field, annotation)); } - final LightMethodBuilder buildMethod = new LightMethodBuilder(getManager(), getBuildMethodName(annotation)); - buildMethod.setContainingClass(builderClass); - buildMethod.setOriginInfo(ORIGIN_INFO); - buildMethod.setMethodReturnType(builtType == null ? myElementFactory.createType(myContainingClass) : builtType); + final LightMethodBuilder buildMethod = createBuildMethod( + annotation, builtType == null ? myElementFactory.createType(myContainingClass) : builtType, builderClass + ); return builderClass.addMethod(buildMethod); } @@ -114,7 +114,7 @@ public class DefaultBuilderStrategySupport extends GrBuilderStrategySupport { private void processMethod(@NotNull GrMethod method) { final PsiAnnotation annotation = PsiImplUtil.getAnnotation(method, BUILDER_FQN); - if (annotation == null || !DEFAULT_STRATEGY_FQN.equals(getStrategy(method))) return; + if (!isApplicable(annotation, DEFAULT_STRATEGY_NAME)) return; if (method.isConstructor()) { processConstructor(method, annotation); } @@ -125,52 +125,60 @@ public class DefaultBuilderStrategySupport extends GrBuilderStrategySupport { private void processConstructor(@NotNull GrMethod method, PsiAnnotation annotation) { PsiClass builderClass = createBuilderClass(annotation, method.getParameters()); - LightMethodBuilder builderMethod = createBuilderMethod(builderClass, annotation); - myMembers.methods.add(builderMethod); - myMembers.classes.add(builderClass); + myMembers.getMethods().add(createBuilderMethod(builderClass, annotation)); + myMembers.getClasses().add(builderClass); } private void processFactoryMethod(@NotNull GrMethod method, PsiAnnotation annotation) { PsiClass builderClass = createBuilderClass(annotation, method.getParameters(), method.getReturnType()); - LightMethodBuilder builderMethod = createBuilderMethod(builderClass, annotation); - myMembers.methods.add(builderMethod); - myMembers.classes.add(builderClass); - } - - @NotNull - private LightMethodBuilder createFieldSetter(@NotNull PsiClass builderClass, @NotNull GrVariable field, @NotNull PsiAnnotation annotation) { - final String name = field.getName(); - final LightMethodBuilder fieldSetter = new LightMethodBuilder(getManager(), getFieldMethodName(annotation, name)); - fieldSetter.addModifier(PsiModifier.PUBLIC); - fieldSetter.addParameter(name, field.getType(), false); - fieldSetter.setContainingClass(builderClass); - fieldSetter.setMethodReturnType(myElementFactory.createType(builderClass)); - fieldSetter.setNavigationElement(field); - return fieldSetter; + myMembers.getMethods().add(createBuilderMethod(builderClass, annotation)); + myMembers.getClasses().add(builderClass); } @NotNull private static String getBuilderMethodName(@NotNull PsiAnnotation annotation) { final String builderMethodName = AnnotationUtil.getDeclaredStringAttributeValue(annotation, "builderMethodName"); - return builderMethodName == null ? "builder" : builderMethodName; - } - - @NotNull - private static String getBuilderClassName(@NotNull PsiAnnotation annotation, @NotNull GrTypeDefinition clazz) { - final String builderClassName = AnnotationUtil.getDeclaredStringAttributeValue(annotation, "builderClassName"); - return builderClassName == null ? String.format("%s%s", clazz.getName(), "Builder") : builderClassName; - } - - @NotNull - private static String getFieldMethodName(@NotNull PsiAnnotation annotation, @NotNull String fieldName) { - final String prefix = AnnotationUtil.getDeclaredStringAttributeValue(annotation, "prefix"); - return prefix == null ? fieldName : String.format("%s%s", prefix, StringUtil.capitalize(fieldName)); - } - - @NotNull - private static String getBuildMethodName(@NotNull PsiAnnotation annotation) { - final String buildMethodName = AnnotationUtil.getDeclaredStringAttributeValue(annotation, "buildMethodName"); - return buildMethodName == null ? "build" : buildMethodName; + return StringUtil.isEmpty(builderMethodName) ? "builder" : builderMethodName; } } + + @NotNull + public static String getBuilderClassName(@NotNull PsiAnnotation annotation, @NotNull GrTypeDefinition clazz) { + final String builderClassName = AnnotationUtil.getDeclaredStringAttributeValue(annotation, "builderClassName"); + return builderClassName == null ? String.format("%s%s", clazz.getName(), "Builder") : builderClassName; + } + + @NotNull + public static LightMethodBuilder createBuildMethod(@NotNull PsiAnnotation annotation, @NotNull PsiType builtType, PsiClass builderClass) { + final LightMethodBuilder buildMethod = new LightMethodBuilder(annotation.getManager(), getBuildMethodName(annotation)); + buildMethod.setContainingClass(builderClass); + buildMethod.setOriginInfo(ORIGIN_INFO); + buildMethod.setMethodReturnType(builtType); + return buildMethod; + } + + @NotNull + public static LightMethodBuilder createFieldSetter(@NotNull PsiClass builderClass, @NotNull GrVariable field, @NotNull PsiAnnotation annotation) { + final String name = field.getName(); + final LightMethodBuilder fieldSetter = new LightMethodBuilder(builderClass.getManager(), getFieldMethodName(annotation, name)); + fieldSetter.addModifier(PsiModifier.PUBLIC); + fieldSetter.addParameter(name, field.getType()); + fieldSetter.setContainingClass(builderClass); + fieldSetter.setMethodReturnType(JavaPsiFacade.getElementFactory(builderClass.getProject()).createType(builderClass)); + fieldSetter.setNavigationElement(field); + fieldSetter.setOriginInfo(ORIGIN_INFO); + return fieldSetter; + } + + @NotNull + public static String getFieldMethodName(@NotNull PsiAnnotation annotation, @NotNull String fieldName) { + final String prefix = AnnotationUtil.getDeclaredStringAttributeValue(annotation, "prefix"); + return StringUtil.isEmpty(prefix) ? fieldName : String.format("%s%s", prefix, StringUtil.capitalize(fieldName)); + } + + @NotNull + private static String getBuildMethodName(@NotNull PsiAnnotation annotation) { + final String buildMethodName = AnnotationUtil.getDeclaredStringAttributeValue(annotation, "buildMethodName"); + return StringUtil.isEmpty(buildMethodName) ? "build" : buildMethodName; + } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/ExternalBuilderStrategySupport.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/ExternalBuilderStrategySupport.java new file mode 100644 index 000000000000..f001f78b8417 --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/ExternalBuilderStrategySupport.java @@ -0,0 +1,58 @@ +/* + * Copyright 2000-2015 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 org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy; + +import com.intellij.openapi.util.Pair; +import com.intellij.psi.PsiAnnotation; +import com.intellij.psi.PsiClass; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; +import org.jetbrains.plugins.groovy.lang.resolve.ast.Members; +import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.BuilderAnnotationContributor; + +import static org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy.DefaultBuilderStrategySupport.createBuildMethod; +import static org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy.DefaultBuilderStrategySupport.createFieldSetter; + +public class ExternalBuilderStrategySupport extends BuilderAnnotationContributor { + + public static final String EXTERNAL_STRATEGY_NAME = "ExternalStrategy"; + + @NotNull + @Override + public Members collect(@NotNull GrTypeDefinition builderClass) { + Pair definitionPair = getConstructedClass(builderClass); + if (definitionPair == null) return Members.EMPTY; + + final PsiAnnotation annotation = definitionPair.first; + final GrTypeDefinition typeDefinition = definitionPair.second; + final Members result = Members.create(); + for (GrField field : typeDefinition.getCodeFields()) { + result.getMethods().add(createFieldSetter(builderClass, field, annotation)); + } + result.getMethods().add(createBuildMethod(annotation, createType(typeDefinition), builderClass)); + return result; + } + + private static Pair getConstructedClass(GrTypeDefinition builderClass) { + final PsiAnnotation annotation = PsiImplUtil.getAnnotation(builderClass, BUILDER_FQN); + if (!isApplicable(annotation, EXTERNAL_STRATEGY_NAME)) return null; + final PsiClass constructedClass = getClassAttributeValue(annotation, "forClass"); + if (!(constructedClass instanceof GrTypeDefinition)) return null; + return Pair.create(annotation, (GrTypeDefinition)constructedClass); + } +} diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/InitializerBuilderStrategySupport.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/InitializerBuilderStrategySupport.java new file mode 100644 index 000000000000..39651af4c165 --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/InitializerBuilderStrategySupport.java @@ -0,0 +1,197 @@ +/* + * Copyright 2000-2015 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 org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy; + +import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.psi.impl.light.LightMethodBuilder; +import com.intellij.psi.impl.light.LightPsiClassBuilder; +import com.intellij.psi.impl.light.LightTypeParameterBuilder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; +import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; +import org.jetbrains.plugins.groovy.lang.resolve.ast.Members; +import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.BuilderAnnotationContributor; +import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.BuilderHelperLightPsiClass; + +import static org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy.DefaultBuilderStrategySupport.getBuilderClassName; +import static org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy.DefaultBuilderStrategySupport.getFieldMethodName; + +public class InitializerBuilderStrategySupport extends BuilderAnnotationContributor { + + public static final String INITIALIZER_STRATEGY_NAME = "InitializerStrategy"; + public static final String SET_FQN = "groovy.transform.builder.InitializerStrategy.SET"; + public static final String UNSET_FQN = "groovy.transform.builder.InitializerStrategy.UNSET"; + + @NotNull + @Override + public Members collect(@NotNull GrTypeDefinition clazz) { + return new InitializerBuilderStrategyHandler(clazz).doProcess(); + } + + private static class InitializerBuilderStrategyHandler { + + private final @NotNull GrTypeDefinition myContainingClass; + private final @NotNull PsiElementFactory myElementFactory; + private final Members myMembers; + + private InitializerBuilderStrategyHandler(@NotNull GrTypeDefinition typeDefinition) { + myContainingClass = typeDefinition; + myElementFactory = PsiElementFactory.SERVICE.getInstance(typeDefinition.getProject()); + myMembers = Members.create(); + } + + @NotNull + private PsiManager getManager() { + return myContainingClass.getManager(); + } + + @NotNull + public Members doProcess() { + processTypeDefinition(); + processConstructors(); + return myMembers; + } + + private void processTypeDefinition() { + final PsiAnnotation builderAnno = PsiImplUtil.getAnnotation(myContainingClass, BUILDER_FQN); + if (!isApplicable(builderAnno, INITIALIZER_STRATEGY_NAME)) return; + + final PsiClass builderClass = createBuilderClass(builderAnno, myContainingClass.getCodeFields()); + myMembers.getMethods().add(createBuilderMethod(builderClass, builderAnno)); + myMembers.getMethods().add(createBuilderConstructor(myContainingClass, builderClass, builderAnno)); + myMembers.getClasses().add(builderClass); + } + + @NotNull + private LightPsiClassBuilder createBuilderClass(@NotNull final PsiAnnotation annotation, + @NotNull GrVariable[] setters) { + final LightPsiClassBuilder builderClass = new BuilderHelperLightPsiClass( + myContainingClass, getBuilderClassName(annotation, myContainingClass) + ); + for (int i = 0; i < setters.length; i++) { + builderClass.getTypeParameterList().addParameter(new InitializerTypeParameter(builderClass, i)); + } + for (int i = 0; i < setters.length; i++) { + builderClass.addMethod(createFieldSetter(builderClass, setters[i], annotation, i)); + } + return builderClass.addMethod(createBuildMethod(annotation, builderClass)); + } + + @NotNull + private LightMethodBuilder createFieldSetter(@NotNull LightPsiClassBuilder builderClass, + @NotNull GrVariable field, + @NotNull PsiAnnotation annotation, + int currentField) { + final String name = field.getName(); + final LightMethodBuilder fieldSetter = new LightMethodBuilder(builderClass.getManager(), getFieldMethodName(annotation, name)); + final PsiSubstitutor substitutor = PsiSubstitutor.EMPTY.put( + builderClass.getTypeParameters()[currentField], + myElementFactory.createTypeByFQClassName(SET_FQN, annotation.getResolveScope()) + ); + fieldSetter.addModifier(PsiModifier.PUBLIC); + fieldSetter.addParameter(name, field.getType()); + fieldSetter.setContainingClass(builderClass); + fieldSetter.setMethodReturnType(myElementFactory.createType(builderClass, substitutor)); + fieldSetter.setNavigationElement(field); + fieldSetter.setOriginInfo(ORIGIN_INFO); + return fieldSetter; + } + + private LightMethodBuilder createBuildMethod(@NotNull PsiAnnotation annotation, @NotNull PsiClass builderClass) { + LightMethodBuilder buildMethod = new LightMethodBuilder(annotation.getManager(), builderClass.getLanguage(), getBuildMethodName(annotation)); + buildMethod.addModifier(PsiModifier.STATIC); + buildMethod.setContainingClass(builderClass); + buildMethod.setOriginInfo(ORIGIN_INFO); + buildMethod.setNavigationElement(annotation); + buildMethod.setMethodReturnType(createAllSetUnsetType(builderClass, false)); + return buildMethod; + } + + @NotNull + private LightMethodBuilder createBuilderMethod(@NotNull PsiClass builderClass, @NotNull PsiAnnotation annotation) { + final LightMethodBuilder builderMethod = new LightMethodBuilder(getManager(), getBuilderMethodName(annotation)); + builderMethod.addModifier(PsiModifier.STATIC); + builderMethod.setContainingClass(myContainingClass); + builderMethod.setOriginInfo(ORIGIN_INFO); + builderMethod.setNavigationElement(annotation); + builderMethod.setMethodReturnType(createAllSetUnsetType(builderClass, false)); + return builderMethod; + } + + @NotNull + private LightMethodBuilder createBuilderConstructor(@NotNull PsiClass constructedClass, @NotNull PsiClass builderClass, PsiAnnotation annotation) { + final LightMethodBuilder constructor = new LightMethodBuilder(constructedClass, constructedClass.getLanguage()).addParameter( + "builder", createAllSetUnsetType(builderClass, true) + ).setConstructor(true); + constructor.setNavigationElement(annotation); + constructor.setOriginInfo(ORIGIN_INFO); + return constructor; + } + + private void processConstructors() { + for (GrMethod method : myContainingClass.getCodeMethods()) { + final PsiAnnotation annotation = PsiImplUtil.getAnnotation(method, BUILDER_FQN); + if (!isApplicable(annotation, INITIALIZER_STRATEGY_NAME)) return; + if (method.isConstructor()) { + processConstructor(method, annotation); + } + } + } + + private void processConstructor(@NotNull GrMethod method, PsiAnnotation annotation) { + PsiClass builderClass = createBuilderClass(annotation, method.getParameters()); + myMembers.getMethods().add(createBuilderMethod(builderClass, annotation)); + myMembers.getMethods().add(createBuilderConstructor(myContainingClass, builderClass, annotation)); + myMembers.getClasses().add(builderClass); + } + + @NotNull + private static String getBuilderMethodName(@NotNull PsiAnnotation annotation) { + final String builderMethodName = AnnotationUtil.getDeclaredStringAttributeValue(annotation, "builderMethodName"); + return StringUtil.isEmpty(builderMethodName) ? "createInitializer" : builderMethodName; + } + + @NotNull + private static String getBuildMethodName(@NotNull PsiAnnotation annotation) { + final String builderMethodName = AnnotationUtil.getDeclaredStringAttributeValue(annotation, "buildMethodName"); + return StringUtil.isEmpty(builderMethodName) ? "create" : builderMethodName; + } + + @NotNull + private PsiType createAllSetUnsetType(@NotNull PsiClass builderClass, boolean setUnset) { + final PsiClassType type = myElementFactory.createTypeByFQClassName( + setUnset ? SET_FQN : UNSET_FQN, + builderClass.getResolveScope() + ); + final PsiType[] mappings = PsiType.createArray(builderClass.getTypeParameters().length); + for (int i = 0; i < mappings.length; i++) { + mappings[i] = type; + } + return myElementFactory.createType(builderClass, mappings); + } + } + + private static class InitializerTypeParameter extends LightTypeParameterBuilder { + + public InitializerTypeParameter(PsiTypeParameterListOwner owner, int index) { + super("T" + index, owner, index); + } + } +} diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/SimpleBuilderStrategySupport.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/SimpleBuilderStrategySupport.java new file mode 100644 index 000000000000..107fbf9d4dfb --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/SimpleBuilderStrategySupport.java @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2015 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 org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy; + +import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiAnnotation; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiModifier; +import com.intellij.psi.impl.light.LightMethodBuilder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; +import org.jetbrains.plugins.groovy.lang.resolve.ast.Members; +import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.BuilderAnnotationContributor; + +public class SimpleBuilderStrategySupport extends BuilderAnnotationContributor { + + public static final String SIMPLE_STRATEGY_NAME = "SimpleStrategy"; + + @NotNull + @Override + public Members collect(@NotNull GrTypeDefinition typeDefinition) { + final PsiAnnotation annotation = PsiImplUtil.getAnnotation(typeDefinition, BUILDER_FQN); + if (!isApplicable(annotation, SIMPLE_STRATEGY_NAME)) return Members.EMPTY; + final Members result = Members.create(); + for (GrField field : typeDefinition.getCodeFields()) { + result.getMethods().add(createFieldSetter(typeDefinition, field, annotation)); + } + return result; + } + + @NotNull + public static LightMethodBuilder createFieldSetter(@NotNull PsiClass builderClass, @NotNull GrVariable field, @NotNull PsiAnnotation annotation) { + final String name = field.getName(); + final LightMethodBuilder fieldSetter = new LightMethodBuilder(builderClass.getManager(), getFieldMethodName(annotation, name)); + fieldSetter.addModifier(PsiModifier.PUBLIC); + fieldSetter.addParameter(name, field.getType(), false); + fieldSetter.setContainingClass(builderClass); + fieldSetter.setMethodReturnType(createType(builderClass)); + fieldSetter.setNavigationElement(field); + fieldSetter.setOriginInfo(ORIGIN_INFO); + return fieldSetter; + } + + @NotNull + public static String getFieldMethodName(@NotNull PsiAnnotation annotation, @NotNull String fieldName) { + final String prefix = AnnotationUtil.getDeclaredStringAttributeValue(annotation, "prefix"); + return prefix == null ? "set" + StringUtil.capitalize(fieldName) + : prefix.isEmpty() ? fieldName + : prefix + StringUtil.capitalize(fieldName); + } +} diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index c7c251bf101b..7f448d6d6880 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -107,15 +107,16 @@ - - - + + + + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/control/CreateParameterForFieldIntention.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/control/CreateParameterForFieldIntention.java index 49865e8ce836..f14d383dbff0 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/control/CreateParameterForFieldIntention.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/control/CreateParameterForFieldIntention.java @@ -263,9 +263,9 @@ public class CreateParameterForFieldIntention extends Intention { return findCandidatesCached(constructor, (GrTypeDefinition)clazz); } - private static List findCandidates(GrMethod constructor, final GrTypeDefinition clazz) { + private static List findCandidates(PsiMethod constructor, final GrTypeDefinition clazz) { final List usedFields = new ArrayList(); - final GrOpenBlock block = constructor.getBlock(); + final GrOpenBlock block = constructor instanceof GrMethod ? ((GrMethod)constructor).getBlock() : null; if (block == null) { return usedFields; } @@ -308,7 +308,7 @@ public class CreateParameterForFieldIntention extends Intention { return fields; } - private static List findCandidatesCached(final GrMethod constructor, final GrTypeDefinition clazz) { + private static List findCandidatesCached(final PsiMethod constructor, final GrTypeDefinition clazz) { final CachedValue> value = constructor.getUserData(FIELD_CANDIDATES); if (value != null && value.getValue() != null) return value.getValue(); final CachedValue> cachedValue = @@ -335,7 +335,7 @@ public class CreateParameterForFieldIntention extends Intention { final PsiMethod[] constructors = psiClass.getConstructors(); final PsiManager manager = field.getManager(); for (PsiMethod constructor : constructors) { - final List fields = findCandidatesCached(((GrMethod)constructor), psiClass); + final List fields = findCandidatesCached(constructor, psiClass); if (ContainerUtil.find(fields, new Condition() { @Override public boolean value(GrField grField) { diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrBuilderTransformationCompletionTestBase.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrBuilderTransformationCompletionTestBase.groovy new file mode 100644 index 000000000000..f9778aa2374c --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrBuilderTransformationCompletionTestBase.groovy @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2015 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 org.jetbrains.plugins.groovy.completion.builder + +import com.intellij.testFramework.LightProjectDescriptor +import groovy.transform.CompileStatic +import org.jetbrains.annotations.NotNull +import org.jetbrains.plugins.groovy.GroovyLightProjectDescriptor +import org.jetbrains.plugins.groovy.completion.CompletionResult +import org.jetbrains.plugins.groovy.completion.GroovyCompletionTestBase + +import static com.intellij.codeInsight.completion.CompletionType.BASIC + +@CompileStatic +abstract class GrBuilderTransformationCompletionTestBase extends GroovyCompletionTestBase { + + @Override + @NotNull + protected LightProjectDescriptor getProjectDescriptor() { + return GroovyLightProjectDescriptor.GROOVY_2_3_9; + } + + void doVariantableTest(String text, String... args) { + doVariantableTest(text, CompletionResult.contain, args) + } + + void doVariantableTest(String text, CompletionResult cr, String... args) { + doVariantableTest(text, '', BASIC, cr, 1, args) + } +} diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrDefaultBuilderStrategyOnConstructorTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrDefaultBuilderStrategyOnConstructorTest.groovy new file mode 100644 index 000000000000..d32eae6f659a --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrDefaultBuilderStrategyOnConstructorTest.groovy @@ -0,0 +1,254 @@ +/* + * Copyright 2000-2015 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 org.jetbrains.plugins.groovy.completion.builder + +import groovy.transform.CompileStatic + +@CompileStatic +class GrDefaultBuilderStrategyOnConstructorTest extends GrBuilderTransformationCompletionTestBase { + + void 'test no builder method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class PojoConstructor { + @Builder(builderStrategy = DefaultStrategy) + PojoConstructor(def dynamic, int counter, String name) {} +} + +PojoConstructor. +''', 'builder' + } + + void 'test custom builder method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class PojoConstructor { + @Builder(builderStrategy = DefaultStrategy, builderMethodName = 'customBuilderMethod') + PojoConstructor(def dynamic, int counter, String name) {} +} + +PojoConstructor. +''', 'customBuilderMethod' + } + + /** + * The class even won't be compiled. + * Separate inspection highlights such code and offers to remove the parameter. + */ + void 'test null builder method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class PojoConstructor { + @Builder(builderStrategy = DefaultStrategy, builderMethodName = null) + PojoConstructor(def dynamic, int counter, String name) {} +} + +PojoConstructor. +''', 'builder' + } + + /** + * Class will be compiled but there will be runtime error. + */ + void 'test empty builder method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class PojoConstructor { + @Builder(builderStrategy = DefaultStrategy, builderMethodName = '') + PojoConstructor(def dynamic, int counter, String name) {} +} + +PojoConstructor. +''', 'builder' + } + + void 'test spaces builder method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class PojoConstructor { + @Builder(builderStrategy = DefaultStrategy, builderMethodName = ' ') + PojoConstructor(def dynamic, int counter, String name) {} +} + +PojoConstructor. +''', ' ' + } + + void 'test no prefix'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class PojoConstructor { + @Builder(builderStrategy = DefaultStrategy) + PojoConstructor(def dynamic, int counter, String name) {} +} + +PojoConstructor.builder(). +''', 'build', 'name', 'dynamic', 'counter' + } + + + void 'test empty prefix'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class PojoConstructor { + @Builder(builderStrategy = DefaultStrategy, prefix = '') + PojoConstructor(def dynamic, int counter, String name) {} +} + +PojoConstructor.builder(). +''', 'build', 'name', 'dynamic', 'counter' + } + + void 'test custom prefix'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class PojoConstructor { + @Builder(builderStrategy = DefaultStrategy, prefix = 'customPrefix') + PojoConstructor(def dynamic, int counter, String name) {} +} + +PojoConstructor.builder(). +''', 'build', 'customPrefixName', 'customPrefixDynamic', 'customPrefixCounter' + } + + void 'test spaces prefix'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class PojoConstructor { + @Builder(builderStrategy = DefaultStrategy, prefix = ' ') + PojoConstructor(def dynamic, int counter, String name) {} +} + +PojoConstructor.builder(). +''', 'build', ' Name', ' Dynamic', ' Counter' + } + + void 'test null prefix'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class PojoConstructor { + @Builder(builderStrategy = DefaultStrategy, prefix = null) + PojoConstructor(def dynamic, int counter, String name) {} +} + +PojoConstructor.builder(). +''', 'build', 'name', 'dynamic', 'counter' + } + + void 'test custom build method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class PojoConstructor { + @Builder(builderStrategy = DefaultStrategy, buildMethodName = 'customBuild') + PojoConstructor(def dynamic, int counter, String name) {} +} + +PojoConstructor.builder(). +''', 'customBuild', 'name', 'dynamic', 'counter' + } + + void 'test null build method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class PojoConstructor { + @Builder(builderStrategy = DefaultStrategy, buildMethodName = null) + PojoConstructor(def dynamic, int counter, String name) {} +} + +PojoConstructor.builder(). +''', 'build', 'name', 'dynamic', 'counter' + } + + void 'test empty build method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class PojoConstructor { + @Builder(builderStrategy = DefaultStrategy, buildMethodName = '') + PojoConstructor(def dynamic, int counter, String name) {} +} + +PojoConstructor.builder(). +''', 'build', 'name', 'dynamic', 'counter' + } + + void 'test spaces build method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class PojoConstructor { + @Builder(builderStrategy = DefaultStrategy, buildMethodName = ' ') + PojoConstructor(def dynamic, int counter, String name) {} +} + +PojoConstructor.builder(). +''', ' ', 'name', 'dynamic', 'counter' + } + + void 'test next setter'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class PojoConstructor { + @Builder(builderStrategy = DefaultStrategy) + PojoConstructor(def dynamic, int counter, String name) {} +} + +PojoConstructor.builder().counter(1). +''', 'build', 'name', 'dynamic', 'counter' + } + + void 'test one more setter further'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class PojoConstructor { + @Builder(builderStrategy = DefaultStrategy) + PojoConstructor(def dynamic, int counter, String name) {} +} + +PojoConstructor.builder().counter(1).name("Janet"). +''', 'build', 'name', 'dynamic', 'counter' + } +} diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrDefaultBuilderStrategyOnFactoryMethodTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrDefaultBuilderStrategyOnFactoryMethodTest.groovy new file mode 100644 index 000000000000..6c23c32ff650 --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrDefaultBuilderStrategyOnFactoryMethodTest.groovy @@ -0,0 +1,260 @@ +/* + * Copyright 2000-2015 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 org.jetbrains.plugins.groovy.completion.builder + +class GrDefaultBuilderStrategyOnFactoryMethodTest extends GrBuilderTransformationCompletionTestBase { + + void 'test no builder method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class Pojo { + @Builder(builderStrategy = DefaultStrategy) + static Pojo someLongMethodName(def dynamic, int counter, String name) {} +} + +Pojo. +''', 'builder' + } + + void 'test custom builder method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class Pojo { + @Builder(builderStrategy = DefaultStrategy, builderMethodName = 'customBuilderMethod') + static Pojo someLongMethodName(def dynamic, int counter, String name) {} +} + +Pojo. +''', 'customBuilderMethod' + } + + void 'test null builder method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class Pojo { + @Builder(builderStrategy = DefaultStrategy, builderMethodName = null) + static Pojo someLongMethodName(def dynamic, int counter, String name) {} +} + +Pojo. +''', 'builder' + } + + void 'test empty builder method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class Pojo { + @Builder(builderStrategy = DefaultStrategy, builderMethodName = '') + static Pojo someLongMethodName(def dynamic, int counter, String name) {} +} + +Pojo. +''', 'builder' + } + + void 'test spaces builder method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class Pojo { + @Builder(builderStrategy = DefaultStrategy, builderMethodName = ' ') + static Pojo someLongMethodName(def dynamic, int counter, String name) {} +} + +Pojo. +''', ' ' + } + + void 'test no prefix'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class Pojo { + @Builder(builderStrategy = DefaultStrategy) + static Pojo someLongMethodName(def dynamic, int counter, String name) {} +} + +Pojo.builder(). +''', 'build', 'name', 'dynamic', 'counter' + } + + + void 'test empty prefix'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class Pojo { + @Builder(builderStrategy = DefaultStrategy, prefix = '') + static Pojo someLongMethodName(def dynamic, int counter, String name) {} +} + +Pojo.builder(). +''', 'build', 'name', 'dynamic', 'counter' + } + + void 'test custom prefix'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class Pojo { + @Builder(builderStrategy = DefaultStrategy, prefix = 'customPrefix') + static Pojo someLongMethodName(def dynamic, int counter, String name) {} +} + +Pojo.builder(). +''', 'build', 'customPrefixName', 'customPrefixDynamic', 'customPrefixCounter' + } + + void 'test spaces prefix'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class Pojo { + @Builder(builderStrategy = DefaultStrategy, prefix = ' ') + static Pojo someLongMethodName(def dynamic, int counter, String name) {} +} + +Pojo.builder(). +''', 'build', ' Name', ' Dynamic', ' Counter' + } + + void 'test null prefix'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class Pojo { + @Builder(builderStrategy = DefaultStrategy, prefix = null) + static Pojo someLongMethodName(def dynamic, int counter, String name) {} +} + +Pojo.builder(). +''', 'build', 'name', 'dynamic', 'counter' + } + + void 'test custom build method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class Pojo { + @Builder(builderStrategy = DefaultStrategy, buildMethodName = 'customBuild') + static Pojo someLongMethodName(def dynamic, int counter, String name) {} +} + +Pojo.builder(). +''', 'customBuild', 'name', 'dynamic', 'counter' + } + + void 'test null build method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class Pojo { + @Builder(builderStrategy = DefaultStrategy, buildMethodName = null) + static Pojo someLongMethodName(def dynamic, int counter, String name) {} +} + +Pojo.builder(). +''', 'build', 'name', 'dynamic', 'counter' + } + + void 'test empty build method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class Pojo { + @Builder(builderStrategy = DefaultStrategy, buildMethodName = '') + static Pojo someLongMethodName(def dynamic, int counter, String name) {} +} + +Pojo.builder(). +''', 'build', 'name', 'dynamic', 'counter' + } + + void 'test spaces build method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class Pojo { + @Builder(builderStrategy = DefaultStrategy, buildMethodName = ' ') + static Pojo someLongMethodName(def dynamic, int counter, String name) {} +} + +Pojo.builder(). +''', ' ', 'name', 'dynamic', 'counter' + } + + void 'test next setter'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class Pojo { + @Builder(builderStrategy = DefaultStrategy) + static Pojo someLongMethodName(def dynamic, int counter, String name) {} +} + +Pojo.builder().counter(1). +''', 'build', 'name', 'dynamic', 'counter' + } + + void 'test one more setter further'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class Pojo { + @Builder(builderStrategy = DefaultStrategy) + static Pojo someLongMethodName(def dynamic, int counter, String name) {} +} + +Pojo.builder().counter(1).name("Janet"). +''', 'build', 'name', 'dynamic', 'counter' + } + + void 'test other return type'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.DefaultStrategy + +class OtherClass { def a,b,c } + +class Pojo { + @Builder(builderStrategy = DefaultStrategy) + static OtherClass someLongMethodName(def dynamic, int counter, String name) {} +} + +Pojo.builder().build(). +''', 'a', 'b', 'c' + } +} diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrDefaultBuilderStrategyTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrDefaultBuilderStrategyTest.groovy new file mode 100644 index 000000000000..bfbe8bdd88bf --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrDefaultBuilderStrategyTest.groovy @@ -0,0 +1,305 @@ +/* + * Copyright 2000-2015 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 org.jetbrains.plugins.groovy.completion.builder + +import groovy.transform.CompileStatic + +@CompileStatic +class GrDefaultBuilderStrategyTest extends GrBuilderTransformationCompletionTestBase { + + void "test no builder method"() { + doVariantableTest('''\ +import groovy.transform.builder.Builder + +@Builder +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo. +''', 'builder') + } + + void 'test custom builder method'() { + doVariantableTest('''\ +import groovy.transform.builder.Builder + +@Builder(builderMethodName = 'customBuilderMethod') +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo. +''', 'customBuilderMethod') + } + + /** + * The class even won't be compiled. + * Separate inspection highlights such code and offers to remove the parameter. + */ + void 'test null builder method'() { + doVariantableTest('''\ +import groovy.transform.builder.Builder + +@Builder(builderMethodName = null) +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo.b +''', 'builder') + } + + /** + * Class will be compiled but there will be runtime error. + */ + void 'test empty builder method'() { + doVariantableTest('''\ +import groovy.transform.builder.Builder + +@Builder(builderMethodName = '') +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo. +''', 'builder') + } + + /** + * I do not know what to say here! + */ + void 'test spaces builder method'() { + doVariantableTest('''\ +import groovy.transform.builder.Builder + +@Builder(builderMethodName = ' ') +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo. +''', ' ') + } + + void 'test no prefix'() { + doVariantableTest('''\ +import groovy.transform.builder.Builder + +@Builder +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo.builder(). +''', 'build', 'name', 'dynamic', 'counter') + } + + + void 'test empty prefix'() { + doVariantableTest('''\ +import groovy.transform.builder.Builder + +@Builder(prefix = '') +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo.builder(). +''', 'build', 'name', 'dynamic', 'counter') + } + + void 'test custom prefix'() { + doVariantableTest('''\ +import groovy.transform.builder.Builder + +@Builder(prefix = 'customPrefix') +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo.builder(). +''', 'build', 'customPrefixName', 'customPrefixDynamic', 'customPrefixCounter') + } + + void 'test spaces prefix'() { + doVariantableTest('''\ +import groovy.transform.builder.Builder + +@Builder(prefix = ' ') +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo.builder(). +''', 'build', ' Name', ' Dynamic', ' Counter') + } + + void 'test null prefix'() { + doVariantableTest('''\ +import groovy.transform.builder.Builder + +@Builder(prefix = null) +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo.builder(). +''', 'build', 'name', 'dynamic', 'counter') + } + + void 'test custom build method'() { + doVariantableTest('''\ +import groovy.transform.builder.Builder + +@Builder(buildMethodName = 'customBuild') +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo.builder(). +''', 'customBuild', 'name', 'dynamic', 'counter') + } + + void 'test null build method'() { + doVariantableTest('''\ +import groovy.transform.builder.Builder + +@Builder(buildMethodName = null) +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo.builder(). +''', 'build', 'name', 'dynamic', 'counter') + } + + void 'test empty build method'() { + doVariantableTest('''\ +import groovy.transform.builder.Builder + +@Builder(buildMethodName = '') +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo.builder(). +''', 'build', 'name', 'dynamic', 'counter') + } + + void 'test spaces build method'() { + doVariantableTest('''\ +import groovy.transform.builder.Builder + +@Builder(buildMethodName = ' ') +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo.builder(). +''', ' ', 'name', 'dynamic', 'counter') + } + + void 'test next setter'() { + doVariantableTest('''\ +import groovy.transform.builder.Builder + +@Builder +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo.builder().counter(1). +''', 'build', 'name', 'dynamic', 'counter') + } + + void 'test one more setter further'() { + doVariantableTest('''\ +import groovy.transform.builder.Builder + +@Builder +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo.builder().counter(1).name("Janet"). +''', 'build', 'name', 'dynamic', 'counter') + } +} diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrExternalBuilderStrategyTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrExternalBuilderStrategyTest.groovy new file mode 100644 index 000000000000..631002878fe7 --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrExternalBuilderStrategyTest.groovy @@ -0,0 +1,266 @@ +/* + * Copyright 2000-2015 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 org.jetbrains.plugins.groovy.completion.builder + +import groovy.transform.CompileStatic +import org.jetbrains.plugins.groovy.completion.CompletionResult + +@CompileStatic +class GrExternalBuilderStrategyTest extends GrBuilderTransformationCompletionTestBase { + + void 'test no class'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.ExternalStrategy + +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +@Builder(builderStrategy = ExternalStrategy) +class PojoBuilder {} + +new PojoBuilder(). +''', CompletionResult.notContain, 'build', 'name', 'dynamic', 'counter' + } + + void 'test no prefix'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.ExternalStrategy + +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +@Builder(builderStrategy = ExternalStrategy, forClass = Pojo) +class PojoBuilder {} + +new PojoBuilder(). +''', 'build', 'name', 'dynamic', 'counter' + } + + + void 'test empty prefix'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.ExternalStrategy + +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +@Builder(builderStrategy = ExternalStrategy, forClass = Pojo, prefix = '') +class PojoBuilder {} + +new PojoBuilder(). +''', 'build', 'name', 'dynamic', 'counter' + } + + void 'test custom prefix'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.ExternalStrategy + +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +@Builder(builderStrategy = ExternalStrategy, forClass = Pojo, prefix = 'customPrefix') +class PojoBuilder {} + +new PojoBuilder(). +''', 'build', 'customPrefixName', 'customPrefixDynamic', 'customPrefixCounter' + } + + void 'test spaces prefix'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.ExternalStrategy + +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +@Builder(builderStrategy = ExternalStrategy, forClass = Pojo, prefix = ' ') +class PojoBuilder {} + +new PojoBuilder(). +''', 'build', ' Name', ' Dynamic', ' Counter' + } + + void 'test null prefix'() { + doVariantableTest('''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.ExternalStrategy + +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +@Builder(builderStrategy = ExternalStrategy, forClass = Pojo, prefix = null) +class PojoBuilder {} + +new PojoBuilder(). +''', 'build', 'name', 'dynamic', 'counter') + } + + void 'test custom build method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.ExternalStrategy + +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +@Builder(builderStrategy = ExternalStrategy, forClass = Pojo, buildMethodName = 'customBuild') +class PojoBuilder {} + +new PojoBuilder(). +''', 'customBuild', 'name', 'dynamic', 'counter' + } + + void 'test null build method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.ExternalStrategy + +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +@Builder(builderStrategy = ExternalStrategy, forClass = Pojo, buildMethodName = null) +class PojoBuilder {} + +new PojoBuilder(). +''', 'build', 'name', 'dynamic', 'counter' + } + + void 'test empty build method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.ExternalStrategy + +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +@Builder(builderStrategy = ExternalStrategy, forClass = Pojo, buildMethodName = '') +class PojoBuilder {} + +new PojoBuilder(). +''', 'build', 'name', 'dynamic', 'counter' + } + + void 'test spaces build method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.ExternalStrategy + +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +@Builder(builderStrategy = ExternalStrategy, forClass = Pojo, buildMethodName = ' ') +class PojoBuilder {} + +new PojoBuilder(). +''', ' ', 'name', 'dynamic', 'counter' + } + + void 'test next setter'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.ExternalStrategy + +@Builder +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +@Builder(builderStrategy = ExternalStrategy, forClass = Pojo) +class PojoBuilder {} + +new PojoBuilder().counter(1). +''', 'build', 'name', 'dynamic', 'counter' + } + + void 'test one more setter further'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.ExternalStrategy + +@Builder +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +@Builder(builderStrategy = ExternalStrategy, forClass = Pojo) +class PojoBuilder {} + +new PojoBuilder().counter(1).name("Janet"). +''', 'build', 'name', 'dynamic', 'counter' + } +} diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrInitializerBuilderStrategyOnConstructorTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrInitializerBuilderStrategyOnConstructorTest.groovy new file mode 100644 index 000000000000..5c34e6d1d528 --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrInitializerBuilderStrategyOnConstructorTest.groovy @@ -0,0 +1,180 @@ +/* + * Copyright 2000-2015 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 org.jetbrains.plugins.groovy.completion.builder + +import groovy.transform.CompileStatic + +@CompileStatic +class GrInitializerBuilderStrategyOnConstructorTest extends GrBuilderTransformationCompletionTestBase { + + void 'test no builder method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.InitializerStrategy + +class Pojo { + @Builder(builderStrategy = InitializerStrategy) + Pojo(name, dynamic, counter) {} +} + +Pojo. +''', 'createInitializer' + } + + void 'test custom builder method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.InitializerStrategy + +class Pojo { + @Builder(builderStrategy = InitializerStrategy, builderMethodName = 'customBuilderMethod') + Pojo(name, dynamic, counter) {} +} + +Pojo. +''', 'customBuilderMethod' + } + + void 'test null builder method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.InitializerStrategy + +class Pojo { + @Builder(builderStrategy = InitializerStrategy, builderMethodName = null) + Pojo(name, dynamic, counter) {} +} + +Pojo. +''', 'createInitializer' + } + + void 'test empty builder method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.InitializerStrategy + +class Pojo { + @Builder(builderStrategy = InitializerStrategy, builderMethodName = '') + Pojo(name, dynamic, counter) {} +} + +Pojo. +''', 'createInitializer' + } + + void 'test spaces builder method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.InitializerStrategy + +class Pojo { + @Builder(builderStrategy = InitializerStrategy, builderMethodName = ' ') + Pojo(name, dynamic, counter) {} +} + +Pojo. +''', ' ' + } + + void 'test empty prefix'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.InitializerStrategy + +class Pojo { + @Builder(builderStrategy = InitializerStrategy, prefix = '') + Pojo(name, dynamic, counter) {} +} + +Pojo.createInitializer(). +''', 'name', 'dynamic', 'counter' + } + + void 'test custom prefix'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.InitializerStrategy + +class Pojo { + @Builder(builderStrategy = InitializerStrategy, prefix = 'customPrefix') + Pojo(name, dynamic, counter) {} +} + +Pojo.createInitializer(). +''', 'customPrefixName', 'customPrefixDynamic', 'customPrefixCounter' + } + + void 'test spaces prefix'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.InitializerStrategy + +class Pojo { + @Builder(builderStrategy = InitializerStrategy, prefix = ' ') + Pojo(name, dynamic, counter) {} +} + + +Pojo.createInitializer(). +''', ' Name', ' Dynamic', ' Counter' + } + + void 'test null prefix'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.InitializerStrategy + +class Pojo { + @Builder(builderStrategy = InitializerStrategy, prefix = null) + Pojo(name, dynamic, counter) {} +} + + +Pojo.createInitializer(). +''', 'name', 'dynamic', 'counter' + } + + void 'test next setter'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.InitializerStrategy + +class Pojo { + @Builder(builderStrategy = InitializerStrategy) + Pojo(name, dynamic, counter) {} +} + + +Pojo.createInitializer().counter(1). +''', 'name', 'dynamic', 'counter' + } + + void 'test one more setter further'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.InitializerStrategy + +class Pojo { + @Builder(builderStrategy = InitializerStrategy) + Pojo(name, dynamic, counter) {} +} + + +Pojo.createInitializer().counter(1).name("Janet"). +''', 'name', 'dynamic', 'counter' + } +} diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrInitializerBuilderStrategyTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrInitializerBuilderStrategyTest.groovy new file mode 100644 index 000000000000..fc1e9ed36613 --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrInitializerBuilderStrategyTest.groovy @@ -0,0 +1,220 @@ +/* + * Copyright 2000-2015 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 org.jetbrains.plugins.groovy.completion.builder + +import groovy.transform.CompileStatic + +@CompileStatic +class GrInitializerBuilderStrategyTest extends GrBuilderTransformationCompletionTestBase { + + void 'test no builder method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.InitializerStrategy + +@Builder(builderStrategy = InitializerStrategy) +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo. +''', 'createInitializer' + } + + void 'test custom builder method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.InitializerStrategy + +@Builder(builderStrategy = InitializerStrategy, builderMethodName = 'customBuilderMethod') +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo. +''', 'customBuilderMethod' + } + + void 'test null builder method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.InitializerStrategy + +@Builder(builderStrategy = InitializerStrategy, builderMethodName = null) +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo. +''', 'createInitializer' + } + + void 'test empty builder method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.InitializerStrategy + +@Builder(builderStrategy = InitializerStrategy, builderMethodName = '') +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo. +''', 'createInitializer' + } + + void 'test spaces builder method'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.InitializerStrategy + +@Builder(builderStrategy = InitializerStrategy, builderMethodName = ' ') +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo. +''', ' ' + } + + void 'test empty prefix'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.InitializerStrategy + +@Builder(builderStrategy = InitializerStrategy, prefix = '') +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo.createInitializer(). +''', 'name', 'dynamic', 'counter' + } + + void 'test custom prefix'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.InitializerStrategy + +@Builder(builderStrategy = InitializerStrategy, prefix = 'customPrefix') +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo.createInitializer(). +''', 'customPrefixName', 'customPrefixDynamic', 'customPrefixCounter' + } + + void 'test spaces prefix'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.InitializerStrategy + +@Builder(builderStrategy = InitializerStrategy, prefix = ' ') +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo.createInitializer(). +''', ' Name', ' Dynamic', ' Counter' + } + + void 'test null prefix'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.InitializerStrategy + +@Builder(builderStrategy = InitializerStrategy, prefix = null) +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo.createInitializer(). +''', 'name', 'dynamic', 'counter' + } + + void 'test next setter'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.InitializerStrategy + +@Builder(builderStrategy = InitializerStrategy) +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo.createInitializer().counter(1). +''', 'name', 'dynamic', 'counter' + } + + void 'test one more setter further'() { + doVariantableTest '''\ +import groovy.transform.builder.Builder +import groovy.transform.builder.InitializerStrategy + +@Builder(builderStrategy = InitializerStrategy) +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +Pojo.createInitializer().counter(1).name("Janet"). +''', 'name', 'dynamic', 'counter' + } +} diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrSimpleBuilderStrategyTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrSimpleBuilderStrategyTest.groovy new file mode 100644 index 000000000000..d06514aea49f --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrSimpleBuilderStrategyTest.groovy @@ -0,0 +1,201 @@ +/* + * Copyright 2000-2015 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 org.jetbrains.plugins.groovy.completion.builder + +import groovy.transform.CompileStatic +import org.jetbrains.plugins.groovy.completion.CompletionResult + +@CompileStatic +class GrSimpleBuilderStrategyTest extends GrBuilderTransformationCompletionTestBase { + + void 'test no prefix'() { + doVariantableTest(''' +import groovy.transform.builder.Builder +import groovy.transform.builder.SimpleStrategy + +@Builder(builderStrategy = SimpleStrategy) +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +new Pojo(). +''', 'setName', 'setDynamic', 'setCounter', 'method') + + doVariantableTest(''' +import groovy.transform.builder.Builder +import groovy.transform.builder.SimpleStrategy + +@Builder(builderStrategy = SimpleStrategy) +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +new Pojo(). +''', CompletionResult.notContain, 'build') + } + + void 'test empty prefix'() { + doVariantableTest(''' +import groovy.transform.builder.Builder +import groovy.transform.builder.SimpleStrategy + +@Builder(builderStrategy = SimpleStrategy, prefix = '') +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +new Pojo(). +''', 'name', 'dynamic', 'counter', 'method') + } + + void 'test custom prefix'() { + doVariantableTest ''' +import groovy.transform.builder.Builder +import groovy.transform.builder.SimpleStrategy + +@Builder(builderStrategy = SimpleStrategy, prefix = 'custom') +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +new Pojo(). +''', 'customName', 'customDynamic', 'customCounter', 'method' + } + + void 'test null prefix'() { + doVariantableTest ''' +import groovy.transform.builder.Builder +import groovy.transform.builder.SimpleStrategy + +@Builder(builderStrategy = SimpleStrategy, prefix = null) +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +new Pojo(). +''', 'setName', 'setDynamic', 'setCounter', 'method' + } + + void 'test spaces prefix'() { + doVariantableTest ''' +import groovy.transform.builder.Builder +import groovy.transform.builder.SimpleStrategy + +@Builder(builderStrategy = SimpleStrategy, prefix = ' ') +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +new Pojo(). +''', ' Name', ' Dynamic', ' Counter', 'method' + } + + void 'test next setter'() { + doVariantableTest ''' +import groovy.transform.builder.Builder +import groovy.transform.builder.SimpleStrategy + +@Builder(builderStrategy = SimpleStrategy) +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +new Pojo().setName("Janet"). +''', 'setName', 'setDynamic', 'setCounter', 'method' + } + + void 'test one more setter further'() { + doVariantableTest ''' +import groovy.transform.builder.Builder +import groovy.transform.builder.SimpleStrategy + +@Builder(builderStrategy = SimpleStrategy) +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +new Pojo().setName("Janet").setCounter(35). +''', 'setName', 'setDynamic', 'setCounter', 'method' + } + + void 'test next setter with prefix'() { + doVariantableTest ''' +import groovy.transform.builder.Builder +import groovy.transform.builder.SimpleStrategy + +@Builder(builderStrategy = SimpleStrategy, prefix = 'lol') +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +new Pojo().lolName("Janet"). +''', 'lolName', 'lolDynamic', 'lolCounter', 'method' + } + + void 'test one more setter further with prefix'() { + doVariantableTest ''' +import groovy.transform.builder.Builder +import groovy.transform.builder.SimpleStrategy + +@Builder(builderStrategy = SimpleStrategy, prefix = 'lol') +class Pojo { + String name + def dynamic + int counter + + def method() {} +} + +new Pojo().lolName("Janet").lolCounter(35). +''', 'lolName', 'lolDynamic', 'lolCounter', 'method' + } +} diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/TypeInferenceTestBase.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/TypeInferenceTestBase.groovy index fbbd630fcf8a..885b95539a7b 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/TypeInferenceTestBase.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/TypeInferenceTestBase.groovy @@ -36,7 +36,7 @@ abstract class TypeInferenceTestBase extends GroovyResolveTestCase { myFixture.addClass("package java.math; public class BigDecimal extends Number implements Comparable {}"); } - protected void doTest(@Language("Groovy") String text, @Nullable String type) { + protected void doTest(String text, @Nullable String type) { def file = myFixture.configureByText('_.groovy', text) def ref = file.findReferenceAt(myFixture.editor.caretModel.offset) as GrReferenceExpression def actual = ref.type diff --git a/python/src/com/jetbrains/python/codeInsight/PyGotoTargetRendererProvider.java b/python/src/com/jetbrains/python/codeInsight/PyGotoTargetRendererProvider.java index 09551a8549e9..50c41292cd0d 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyGotoTargetRendererProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/PyGotoTargetRendererProvider.java @@ -21,12 +21,13 @@ import com.intellij.ide.util.PsiElementListCellRenderer; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiNamedElement; import com.jetbrains.python.psi.PyElement; +import org.jetbrains.annotations.NotNull; /** * @author yole */ public class PyGotoTargetRendererProvider implements GotoTargetRendererProvider { - public PsiElementListCellRenderer getRenderer(final PsiElement element, GotoTargetHandler.GotoData gotoData) { + public PsiElementListCellRenderer getRenderer(@NotNull final PsiElement element, @NotNull GotoTargetHandler.GotoData gotoData) { if (element instanceof PyElement && element instanceof PsiNamedElement) return new PyElementListCellRenderer(); return null; } diff --git a/xml/impl/src/com/intellij/xml/impl/schema/GotoXmlSchemaTypeRendererProvider.java b/xml/impl/src/com/intellij/xml/impl/schema/GotoXmlSchemaTypeRendererProvider.java index 1017b9aca363..da6a67b46616 100644 --- a/xml/impl/src/com/intellij/xml/impl/schema/GotoXmlSchemaTypeRendererProvider.java +++ b/xml/impl/src/com/intellij/xml/impl/schema/GotoXmlSchemaTypeRendererProvider.java @@ -22,6 +22,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.impl.source.xml.XmlTagImpl; import com.intellij.psi.xml.XmlAttribute; +import org.jetbrains.annotations.NotNull; /** * Created with IntelliJ IDEA. @@ -31,7 +32,7 @@ import com.intellij.psi.xml.XmlAttribute; */ public class GotoXmlSchemaTypeRendererProvider implements GotoTargetRendererProvider { @Override - public PsiElementListCellRenderer getRenderer(PsiElement element, GotoTargetHandler.GotoData gotoData) { + public PsiElementListCellRenderer getRenderer(@NotNull PsiElement element, @NotNull GotoTargetHandler.GotoData gotoData) { if (element instanceof XmlTagImpl) { if (SchemaDefinitionsSearch.isTypeElement((XmlTagImpl)element)) { return new MyRenderer("");