From fe2e09c35d42101dbd8de6b88c9da3e04ef9df1f Mon Sep 17 00:00:00 2001 From: Alexander Lobas Date: Wed, 19 Aug 2015 14:32:23 +0300 Subject: [PATCH 01/52] OC-11982 Navigate from Objective-C to Swift derived methods and classes (review 2) --- .../codeInsight/navigation/JavaGotoTargetRendererProvider.java | 3 ++- .../com/intellij/codeInsight/navigation/GotoTargetHandler.java | 3 ++- .../codeInsight/navigation/GotoTargetRendererProvider.java | 3 ++- .../python/codeInsight/PyGotoTargetRendererProvider.java | 3 ++- .../xml/impl/schema/GotoXmlSchemaTypeRendererProvider.java | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) 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/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/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(""); From dd3976ced305ac0001821974721fbb331804a9c0 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 19 Aug 2015 13:47:48 +0200 Subject: [PATCH 02/52] don't ask for already cached p4 changelists in Repository/Committed view refresh (IDEA-137097) --- .../openapi/vcs/versionBrowser/ChangeBrowserSettings.java | 2 ++ .../openapi/vcs/changes/committed/CommittedChangesCache.java | 4 ++-- .../com/intellij/testFramework/vcs/AbstractVcsTestCase.java | 3 +++ 3 files changed, 7 insertions(+), 2 deletions(-) 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..9e4c7b21e2dd 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 @@ -54,6 +54,8 @@ public class ChangeBrowserSettings implements JDOMExternalizable { public String USER = ""; public boolean STOP_ON_COPY = false; + public transient 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) { From 5c564c25effce05e20da5d66f869cfadffc004dd Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 19 Aug 2015 13:57:02 +0200 Subject: [PATCH 03/52] don't inject invalid groovy (with ) in TypeInferenceTest --- .../plugins/groovy/lang/resolve/TypeInferenceTestBase.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 9c1d75b93ea88ba4cc90726c343323d35eb66e0c Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Wed, 19 Aug 2015 15:07:44 +0300 Subject: [PATCH 04/52] IDEA-142762 Documentation view does not preserve documentation for renamed method parameters --- .../javadoc/JavaDocInfoGenerator.java | 267 +++++++++--------- .../matchingParameterNameFromParent.html | 2 + .../matchingParameterNameFromParent.java | 13 + .../matchingTypeParameterNameFromParent.html | 2 + .../matchingTypeParameterNameFromParent.java | 13 + .../javadoc/JavaDocInfoGeneratorTest.java | 14 + 6 files changed, 183 insertions(+), 128 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/javadocIG/matchingParameterNameFromParent.html create mode 100644 java/java-tests/testData/codeInsight/javadocIG/matchingParameterNameFromParent.java create mode 100644 java/java-tests/testData/codeInsight/javadocIG/matchingTypeParameterNameFromParent.html create mode 100644 java/java-tests/testData/codeInsight/javadocIG/matchingTypeParameterNameFromParent.java 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-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/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() { From 295ea086400e068aa34832db6fd5730bc36d94ca Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Tue, 7 Jul 2015 13:05:08 +0300 Subject: [PATCH 05/52] [groovy] @Builder: simple builder strategy support (IDEA-125224) --- .../typedef/GrTypeDefinitionMembersCache.java | 9 ++- .../ast/builder/GrBuilderStrategySupport.java | 11 ++- .../SimpleBuilderStrategySupport.java | 67 +++++++++++++++++++ plugins/groovy/src/META-INF/plugin.xml | 1 + 4 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/SimpleBuilderStrategySupport.java diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java index 1e9638034652..7d98c6ec7816 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java @@ -154,10 +154,17 @@ public class GrTypeDefinitionMembersCache { return CachedValuesManager.getCachedValue(myDefinition, new CachedValueProvider() { @Override public Result compute() { - List result = ContainerUtil.newArrayList(); + final Collection result = ContainerUtil.newHashSet(); + GrClassImplUtil.collectMethodsFromBody(myDefinition, result); + final Map methodMap = new HashMap(); + for (PsiMethod method : result) { + methodMap.put(method.getSignature(PsiSubstitutor.EMPTY), method); + } + for (PsiMethod method : AstTransformContributor.runContributorsForMethods(myDefinition)) { + result.remove(methodMap.get(method.getSignature(PsiSubstitutor.EMPTY))); GrClassImplUtil.addExpandingReflectedMethods(result, method); } 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 index 082a6f20e2af..e04d5bdb0590 100644 --- 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 @@ -33,11 +33,20 @@ 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 final String STRATEGY_ATTRIBUTE = "builderStrategy"; public static class Members { + public static final Members EMPTY = new Members() { + @Override + public void addFrom(Members other) { + // do nothing + } + }; + 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); @@ -53,7 +62,7 @@ public abstract class GrBuilderStrategySupport { final PsiAnnotation annotation = PsiImplUtil.getAnnotation(annotatedMember, BUILDER_FQN); if (annotation == null) return null; - final PsiAnnotationMemberValue strategy = annotation.findDeclaredAttributeValue("builderStrategy"); + final PsiAnnotationMemberValue strategy = annotation.findDeclaredAttributeValue(STRATEGY_ATTRIBUTE); if (strategy instanceof GrReferenceExpression) { final PsiElement element = ((GrReferenceExpression)strategy).resolve(); return element instanceof PsiClass ? ((PsiClass)element).getQualifiedName() : null; 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..eb4b4719b535 --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/SimpleBuilderStrategySupport.java @@ -0,0 +1,67 @@ +/* + * 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.JavaPsiFacade; +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.builder.GrBuilderStrategySupport; + +public class SimpleBuilderStrategySupport extends GrBuilderStrategySupport { + + public static final String SIMPLE_STRATEGY_FQN = "groovy.transform.builder.SimpleStrategy"; + + @NotNull + @Override + public Members process(GrTypeDefinition typeDefinition) { + if (!SIMPLE_STRATEGY_FQN.equals(getStrategy(typeDefinition))) return Members.EMPTY; + final PsiAnnotation annotation = PsiImplUtil.getAnnotation(typeDefinition, BUILDER_FQN); + assert annotation != null; + final Members result = new Members(); + for (GrField field : typeDefinition.getCodeFields()) { + result.methods.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(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 String.format("%s%s", prefix == null ? "set" : prefix, StringUtil.capitalize(fieldName)); + } +} diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index c7c251bf101b..826a32fae624 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -116,6 +116,7 @@ + From 8af2b169b1e924032b4635444413ef3a78a2a3c4 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Tue, 7 Jul 2015 15:11:54 +0300 Subject: [PATCH 06/52] [groovy] @Builder: external builder strategy support (IDEA-125224) --- .../ast/builder/GrBuilderStrategySupport.java | 31 +++++++--- .../DefaultBuilderStrategySupport.java | 59 +++++++++++-------- .../ExternalBuilderStrategySupport.java | 57 ++++++++++++++++++ plugins/groovy/src/META-INF/plugin.xml | 1 + 4 files changed, 116 insertions(+), 32 deletions(-) create mode 100644 plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/ExternalBuilderStrategySupport.java 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 index e04d5bdb0590..05dee0b967eb 100644 --- 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 @@ -18,13 +18,13 @@ 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.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.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; @@ -62,12 +62,29 @@ public abstract class GrBuilderStrategySupport { final PsiAnnotation annotation = PsiImplUtil.getAnnotation(annotatedMember, BUILDER_FQN); if (annotation == null) return null; - final PsiAnnotationMemberValue strategy = annotation.findDeclaredAttributeValue(STRATEGY_ATTRIBUTE); - if (strategy instanceof GrReferenceExpression) { - final PsiElement element = ((GrReferenceExpression)strategy).resolve(); - return element instanceof PsiClass ? ((PsiClass)element).getQualifiedName() : null; - } + final PsiClass strategy = getDeclaredClassAttribute(annotation, STRATEGY_ATTRIBUTE); + return strategy == null ? null : strategy.getQualifiedName(); + } - return DefaultBuilderStrategySupport.DEFAULT_STRATEGY_FQN; + @Nullable + @Contract("null,_ -> null") + public static PsiClass getDeclaredClassAttribute(@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; + } + + 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/strategy/DefaultBuilderStrategySupport.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/DefaultBuilderStrategySupport.java index 7ed40938d27b..d17695290723 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 @@ -88,10 +88,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); } @@ -137,17 +136,6 @@ public class DefaultBuilderStrategySupport extends GrBuilderStrategySupport { 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; - } @NotNull private static String getBuilderMethodName(@NotNull PsiAnnotation annotation) { @@ -160,17 +148,38 @@ public class DefaultBuilderStrategySupport extends GrBuilderStrategySupport { 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 + 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 - private static String getBuildMethodName(@NotNull PsiAnnotation annotation) { - final String buildMethodName = AnnotationUtil.getDeclaredStringAttributeValue(annotation, "buildMethodName"); - return buildMethodName == null ? "build" : buildMethodName; - } + @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); + return fieldSetter; + } + + @NotNull + private static String getFieldMethodName(@NotNull PsiAnnotation annotation, @NotNull String fieldName) { + final String prefix = AnnotationUtil.getDeclaredStringAttributeValue(annotation, "prefix"); + return StringUtil.isEmptyOrSpaces(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 buildMethodName == null ? "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..fb6ac5dc349c --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/ExternalBuilderStrategySupport.java @@ -0,0 +1,57 @@ +/* + * 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.builder.GrBuilderStrategySupport; + +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 GrBuilderStrategySupport { + + public static final String EXTERNAL_STRATEGY_FQN = "groovy.transform.builder.ExternalStrategy"; + + @NotNull + @Override + public Members process(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 = new Members(); + for (GrField field : typeDefinition.getCodeFields()) { + result.methods.add(createFieldSetter(builderClass, field, annotation)); + } + result.methods.add(createBuildMethod(annotation, createType(typeDefinition), builderClass)); + return result; + } + + private static Pair getConstructedClass(GrTypeDefinition builderClass) { + if (!EXTERNAL_STRATEGY_FQN.equals(getStrategy(builderClass))) return null; + final PsiAnnotation annotation = PsiImplUtil.getAnnotation(builderClass, BUILDER_FQN); + final PsiClass constructedClass = getDeclaredClassAttribute(annotation, "forClass"); + if (!(constructedClass instanceof GrTypeDefinition)) return null; + return Pair.create(annotation, (GrTypeDefinition)constructedClass); + } +} diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index 826a32fae624..f573f143b8d7 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -117,6 +117,7 @@ + From 7aeb914c7bec59dd569804ca0c2fc5b5b064068e Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Wed, 8 Jul 2015 15:39:19 +0300 Subject: [PATCH 07/52] [groovy] method renamed getDeclaredClassAttribute() -> getClassAttributeValue() --- .../lang/resolve/ast/builder/GrBuilderStrategySupport.java | 4 ++-- .../ast/builder/strategy/ExternalBuilderStrategySupport.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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 index 05dee0b967eb..c68f60503c0b 100644 --- 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 @@ -62,13 +62,13 @@ public abstract class GrBuilderStrategySupport { final PsiAnnotation annotation = PsiImplUtil.getAnnotation(annotatedMember, BUILDER_FQN); if (annotation == null) return null; - final PsiClass strategy = getDeclaredClassAttribute(annotation, STRATEGY_ATTRIBUTE); + final PsiClass strategy = getClassAttributeValue(annotation, STRATEGY_ATTRIBUTE); return strategy == null ? null : strategy.getQualifiedName(); } @Nullable @Contract("null,_ -> null") - public static PsiClass getDeclaredClassAttribute(@Nullable PsiAnnotation annotation, @NotNull String attributeName) { + 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) { 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 index fb6ac5dc349c..f1dc128a6871 100644 --- 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 @@ -50,7 +50,7 @@ public class ExternalBuilderStrategySupport extends GrBuilderStrategySupport { private static Pair getConstructedClass(GrTypeDefinition builderClass) { if (!EXTERNAL_STRATEGY_FQN.equals(getStrategy(builderClass))) return null; final PsiAnnotation annotation = PsiImplUtil.getAnnotation(builderClass, BUILDER_FQN); - final PsiClass constructedClass = getDeclaredClassAttribute(annotation, "forClass"); + final PsiClass constructedClass = getClassAttributeValue(annotation, "forClass"); if (!(constructedClass instanceof GrTypeDefinition)) return null; return Pair.create(annotation, (GrTypeDefinition)constructedClass); } From 7448403b34d4c4ecc51763552a55cd9d8f703af7 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Wed, 8 Jul 2015 15:40:50 +0300 Subject: [PATCH 08/52] [groovy] do not add builder class to the list of inner classes & fix processing of @Builder annotation parameters (IDEA-125224) --- .../builder/strategy/DefaultBuilderStrategySupport.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) 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 d17695290723..c22f246bace1 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 @@ -67,7 +67,6 @@ public class DefaultBuilderStrategySupport extends GrBuilderStrategySupport { if (builderAnno == null || !DEFAULT_STRATEGY_FQN.equals(getStrategy(myContainingClass))) return; final PsiClass builderClass = createBuilderClass(builderAnno, myContainingClass.getCodeFields()); final LightMethodBuilder builderMethod = createBuilderMethod(builderClass, builderAnno); - myMembers.classes.add(builderClass); myMembers.methods.add(builderMethod); } @@ -126,21 +125,19 @@ public class DefaultBuilderStrategySupport extends GrBuilderStrategySupport { PsiClass builderClass = createBuilderClass(annotation, method.getParameters()); LightMethodBuilder builderMethod = createBuilderMethod(builderClass, annotation); myMembers.methods.add(builderMethod); - myMembers.classes.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 static String getBuilderMethodName(@NotNull PsiAnnotation annotation) { final String builderMethodName = AnnotationUtil.getDeclaredStringAttributeValue(annotation, "builderMethodName"); - return builderMethodName == null ? "builder" : builderMethodName; + return StringUtil.isEmpty(builderMethodName) ? "builder" : builderMethodName; } @NotNull @@ -174,12 +171,12 @@ public class DefaultBuilderStrategySupport extends GrBuilderStrategySupport { @NotNull private static String getFieldMethodName(@NotNull PsiAnnotation annotation, @NotNull String fieldName) { final String prefix = AnnotationUtil.getDeclaredStringAttributeValue(annotation, "prefix"); - return StringUtil.isEmptyOrSpaces(prefix) ? fieldName : String.format("%s%s", prefix, StringUtil.capitalize(fieldName)); + 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 buildMethodName == null ? "build" : buildMethodName; + return StringUtil.isEmpty(buildMethodName) ? "build" : buildMethodName; } } From 4430df58de6fba1e0687f95097249f886d94547b Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Wed, 8 Jul 2015 15:42:49 +0300 Subject: [PATCH 09/52] [groovy] default builder strategy completion tests (IDEA-125224) --- ...derTransformationCompletionTestBase.groovy | 39 +++ .../GrDefaultBuilderStrategyTest.groovy | 305 ++++++++++++++++++ 2 files changed, 344 insertions(+) create mode 100644 plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrBuilderTransformationCompletionTestBase.groovy create mode 100644 plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrDefaultBuilderStrategyTest.groovy 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..ce6f77965422 --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrBuilderTransformationCompletionTestBase.groovy @@ -0,0 +1,39 @@ +/* + * 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, CompletionResult cr = CompletionResult.contain, String... args) { + doVariantableTest(text, '', BASIC, cr, 1, args) + } +} 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') + } +} From 8f8188d8f4a341b1b33a3751897165e3a862bfa9 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Wed, 8 Jul 2015 21:26:54 +0300 Subject: [PATCH 10/52] [groovy] fix processing of @Builder attributes for SimpleStrategy --- .../ast/builder/strategy/SimpleBuilderStrategySupport.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 index eb4b4719b535..b137ec60880d 100644 --- 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 @@ -53,7 +53,7 @@ public class SimpleBuilderStrategySupport extends GrBuilderStrategySupport { fieldSetter.addModifier(PsiModifier.PUBLIC); fieldSetter.addParameter(name, field.getType(), false); fieldSetter.setContainingClass(builderClass); - fieldSetter.setMethodReturnType(JavaPsiFacade.getElementFactory(builderClass.getProject()).createType(builderClass)); + fieldSetter.setMethodReturnType(createType(builderClass)); fieldSetter.setNavigationElement(field); fieldSetter.setOriginInfo(ORIGIN_INFO); return fieldSetter; @@ -62,6 +62,8 @@ public class SimpleBuilderStrategySupport extends GrBuilderStrategySupport { @NotNull public static String getFieldMethodName(@NotNull PsiAnnotation annotation, @NotNull String fieldName) { final String prefix = AnnotationUtil.getDeclaredStringAttributeValue(annotation, "prefix"); - return String.format("%s%s", prefix == null ? "set" : prefix, StringUtil.capitalize(fieldName)); + return prefix == null ? "set" + StringUtil.capitalize(fieldName) + : prefix.isEmpty() ? fieldName + : prefix + StringUtil.capitalize(fieldName); } } From 107be6e261679fed624841ed7ff7e21a00c5562e Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Thu, 9 Jul 2015 12:41:03 +0300 Subject: [PATCH 11/52] [groovy] do not resolve @Builder annotation attributes values within ast transformations --- .../ast/builder/GrBuilderStrategySupport.java | 28 +++++++++++-------- .../DefaultBuilderStrategySupport.java | 6 ++-- .../ExternalBuilderStrategySupport.java | 4 +-- .../SimpleBuilderStrategySupport.java | 5 ++-- 4 files changed, 24 insertions(+), 19 deletions(-) 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 index c68f60503c0b..eed0654ae1f0 100644 --- 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 @@ -16,6 +16,7 @@ package org.jetbrains.plugins.groovy.lang.resolve.ast.builder; import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.Contract; @@ -24,14 +25,15 @@ 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 BUILDER_PACKAGE = "groovy.transform.builder"; + public static final String BUILDER_FQN = BUILDER_PACKAGE + ".Builder"; public static final String ORIGIN_INFO = "by @Builder"; public static final String STRATEGY_ATTRIBUTE = "builderStrategy"; @@ -57,15 +59,6 @@ public abstract class GrBuilderStrategySupport { @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 PsiClass strategy = getClassAttributeValue(annotation, STRATEGY_ATTRIBUTE); - return strategy == null ? null : strategy.getQualifiedName(); - } - @Nullable @Contract("null,_ -> null") public static PsiClass getClassAttributeValue(@Nullable PsiAnnotation annotation, @NotNull String attributeName) { @@ -84,6 +77,19 @@ public abstract class GrBuilderStrategySupport { 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/strategy/DefaultBuilderStrategySupport.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/DefaultBuilderStrategySupport.java index c22f246bace1..ead8b9128ce5 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 @@ -31,7 +31,7 @@ import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.GrBuilderStrategySu public class DefaultBuilderStrategySupport extends GrBuilderStrategySupport { - 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) { @@ -64,7 +64,7 @@ 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.methods.add(builderMethod); @@ -112,7 +112,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); } 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 index f1dc128a6871..a3d6b313d61e 100644 --- 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 @@ -29,7 +29,7 @@ import static org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy.Def public class ExternalBuilderStrategySupport extends GrBuilderStrategySupport { - public static final String EXTERNAL_STRATEGY_FQN = "groovy.transform.builder.ExternalStrategy"; + public static final String EXTERNAL_STRATEGY_NAME = "ExternalStrategy"; @NotNull @Override @@ -48,8 +48,8 @@ public class ExternalBuilderStrategySupport extends GrBuilderStrategySupport { } private static Pair getConstructedClass(GrTypeDefinition builderClass) { - if (!EXTERNAL_STRATEGY_FQN.equals(getStrategy(builderClass))) return null; 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/SimpleBuilderStrategySupport.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/SimpleBuilderStrategySupport.java index b137ec60880d..13e08d1f8688 100644 --- 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 @@ -31,14 +31,13 @@ import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.GrBuilderStrategySu public class SimpleBuilderStrategySupport extends GrBuilderStrategySupport { - public static final String SIMPLE_STRATEGY_FQN = "groovy.transform.builder.SimpleStrategy"; + public static final String SIMPLE_STRATEGY_NAME = "SimpleStrategy"; @NotNull @Override public Members process(GrTypeDefinition typeDefinition) { - if (!SIMPLE_STRATEGY_FQN.equals(getStrategy(typeDefinition))) return Members.EMPTY; final PsiAnnotation annotation = PsiImplUtil.getAnnotation(typeDefinition, BUILDER_FQN); - assert annotation != null; + if (!isApplicable(annotation, SIMPLE_STRATEGY_NAME)) return Members.EMPTY; final Members result = new Members(); for (GrField field : typeDefinition.getCodeFields()) { result.methods.add(createFieldSetter(typeDefinition, field, annotation)); From 9d6360bd0c659f3c4786b85c14316383fc64be46 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Thu, 9 Jul 2015 12:42:26 +0300 Subject: [PATCH 12/52] [groovy] completion tests for simple @Builder strategy support (IDEA-125224) --- .../GrSimpleBuilderStrategyTest.groovy | 201 ++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrSimpleBuilderStrategyTest.groovy 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' + } +} From b2b62001553760441345f5f8f7a4add03a693350 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Fri, 10 Jul 2015 20:04:00 +0300 Subject: [PATCH 13/52] [groovy] typo --- .../groovy/lang/resolve/ast/AstTransformContributor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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..c25c526744ca 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 @@ -80,7 +80,7 @@ public abstract class AstTransformContributor { @NotNull public static List runContributorsForClasses(@NotNull final GrTypeDefinition clazz) { - List fields = RecursionManager.doPreventingRecursion(clazz, true, new Computable>() { + List classes = RecursionManager.doPreventingRecursion(clazz, true, new Computable>() { @Override public List compute() { List collector = ContainerUtil.newArrayList(); @@ -90,6 +90,6 @@ public abstract class AstTransformContributor { return collector; } }); - return fields != null ? fields : Collections.emptyList(); + return classes != null ? classes : Collections.emptyList(); } } From adbde1f4007fb50637bbd154073d2372762d20df Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Sun, 12 Jul 2015 15:56:23 +0300 Subject: [PATCH 14/52] [groovy] move Members class to separate file --- .../groovy/lang/resolve/ast/Members.java | 43 +++++++++++++++++++ .../builder/BuilderAnnotationContributor.java | 2 +- .../ast/builder/GrBuilderStrategySupport.java | 24 +---------- .../DefaultBuilderStrategySupport.java | 1 + .../ExternalBuilderStrategySupport.java | 1 + .../SimpleBuilderStrategySupport.java | 2 +- 6 files changed, 48 insertions(+), 25 deletions(-) create mode 100644 plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/Members.java 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..88f3309801d2 --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/Members.java @@ -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.lang.resolve.ast; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; + +import java.util.Collection; + +public class Members { + + public static final Members EMPTY = new Members() { + @Override + public void addFrom(Members other) { + // do nothing + } + }; + + 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); + } +} 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..cb429ee08a03 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 @@ -25,7 +25,7 @@ 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.resolve.ast.AstTransformContributor; -import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.GrBuilderStrategySupport.Members; +import org.jetbrains.plugins.groovy.lang.resolve.ast.Members; import java.util.Collection; 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 index eed0654ae1f0..394ede5993f0 100644 --- 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 @@ -18,17 +18,14 @@ package org.jetbrains.plugins.groovy.lang.resolve.ast.builder; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; -import com.intellij.util.containers.ContainerUtil; 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.expressions.GrReferenceExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import org.jetbrains.plugins.groovy.lang.resolve.ast.Members; 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"); @@ -37,25 +34,6 @@ public abstract class GrBuilderStrategySupport { public static final String ORIGIN_INFO = "by @Builder"; public static final String STRATEGY_ATTRIBUTE = "builderStrategy"; - public static class Members { - public static final Members EMPTY = new Members() { - @Override - public void addFrom(Members other) { - // do nothing - } - }; - - 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); 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 ead8b9128ce5..85758c7dc760 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,6 +26,7 @@ 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.BuilderHelperLightPsiClass; import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.GrBuilderStrategySupport; 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 index a3d6b313d61e..3b8defdd7797 100644 --- 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 @@ -22,6 +22,7 @@ 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.GrBuilderStrategySupport; import static org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy.DefaultBuilderStrategySupport.createBuildMethod; 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 index 13e08d1f8688..2f52185c88b6 100644 --- 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 @@ -17,7 +17,6 @@ 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.JavaPsiFacade; import com.intellij.psi.PsiAnnotation; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiModifier; @@ -27,6 +26,7 @@ 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.GrBuilderStrategySupport; public class SimpleBuilderStrategySupport extends GrBuilderStrategySupport { From 2c49429338e3b63f74a261ab03d4de525987923b Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Sun, 12 Jul 2015 16:13:15 +0300 Subject: [PATCH 15/52] [groovy] use getters instead of direct access to fields of Members --- .../groovy/lang/resolve/ast/Members.java | 75 ++++++++++++++++--- .../builder/BuilderAnnotationContributor.java | 8 +- .../DefaultBuilderStrategySupport.java | 8 +- .../ExternalBuilderStrategySupport.java | 6 +- .../SimpleBuilderStrategySupport.java | 4 +- 5 files changed, 79 insertions(+), 22 deletions(-) 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 index 88f3309801d2..6fd2fb8cd099 100644 --- 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 @@ -18,26 +18,83 @@ package org.jetbrains.plugins.groovy.lang.resolve.ast; 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 java.util.Collection; +import java.util.Collections; -public class Members { +public abstract class Members { + + @NotNull + public abstract Collection getMethods(); + + @NotNull + public abstract Collection getFields(); + + @NotNull + public abstract Collection getClasses(); + + public abstract void addFrom(@NotNull Members other); public static final Members EMPTY = new Members() { + + @NotNull @Override - public void addFrom(Members other) { + public Collection getMethods() { + return Collections.emptyList(); + } + + @NotNull + @Override + public Collection getFields() { + return Collections.emptyList(); + } + + @NotNull + @Override + public Collection getClasses() { + return Collections.emptyList(); + } + + @Override + public void addFrom(@NotNull Members other) { // do nothing } }; - public final Collection methods = ContainerUtil.newArrayList(); - public final Collection fields = ContainerUtil.newArrayList(); - public final Collection classes = ContainerUtil.newArrayList(); + @NotNull + public static Members create() { + return new Members() { - public void addFrom(Members other) { - methods.addAll(other.methods); - fields.addAll(other.fields); - classes.addAll(other.classes); + private final Collection methods = ContainerUtil.newArrayList(); + private final Collection fields = ContainerUtil.newArrayList(); + private final Collection classes = ContainerUtil.newArrayList(); + + @NotNull + @Override + public Collection getMethods() { + return methods; + } + + @NotNull + @Override + public Collection getFields() { + return fields; + } + + @NotNull + @Override + public Collection getClasses() { + return classes; + } + + @Override + public void addFrom(@NotNull Members other) { + methods.addAll(other.getMethods()); + fields.addAll(other.getFields()); + classes.addAll(other.getClasses()); + } + }; } } 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 cb429ee08a03..ce3624b103e9 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 @@ -33,17 +33,17 @@ public class BuilderAnnotationContributor extends AstTransformContributor { @Override public void collectMethods(@NotNull GrTypeDefinition clazz, final Collection collector) { - collector.addAll(collectAll(clazz).methods); + collector.addAll(collectAll(clazz).getMethods()); } @Override public void collectClasses(@NotNull GrTypeDefinition clazz, Collection collector) { - collector.addAll(collectAll(clazz).classes); + collector.addAll(collectAll(clazz).getClasses()); } @Override public void collectFields(@NotNull GrTypeDefinition clazz, Collection collector) { - collector.addAll(collectAll(clazz).fields); + collector.addAll(collectAll(clazz).getFields()); } private static Members collectAll(final GrTypeDefinition clazz) { @@ -51,7 +51,7 @@ public class BuilderAnnotationContributor extends AstTransformContributor { @Nullable @Override public Result compute() { - final Members result = new Members(); + final Members result = Members.create(); for (GrBuilderStrategySupport strategySupport : GrBuilderStrategySupport.EP.getExtensions()) { result.addFrom(strategySupport.process(clazz)); } 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 85758c7dc760..5bd4735fe125 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 @@ -48,7 +48,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 @@ -68,7 +68,7 @@ public class DefaultBuilderStrategySupport extends GrBuilderStrategySupport { if (!isApplicable(builderAnno, DEFAULT_STRATEGY_NAME)) return; final PsiClass builderClass = createBuilderClass(builderAnno, myContainingClass.getCodeFields()); final LightMethodBuilder builderMethod = createBuilderMethod(builderClass, builderAnno); - myMembers.methods.add(builderMethod); + myMembers.getMethods().add(builderMethod); } @NotNull @@ -125,13 +125,13 @@ 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.getMethods().add(builderMethod); } 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.getMethods().add(builderMethod); } 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 index 3b8defdd7797..200f617e5038 100644 --- 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 @@ -40,11 +40,11 @@ public class ExternalBuilderStrategySupport extends GrBuilderStrategySupport { final PsiAnnotation annotation = definitionPair.first; final GrTypeDefinition typeDefinition = definitionPair.second; - final Members result = new Members(); + final Members result = Members.create(); for (GrField field : typeDefinition.getCodeFields()) { - result.methods.add(createFieldSetter(builderClass, field, annotation)); + result.getMethods().add(createFieldSetter(builderClass, field, annotation)); } - result.methods.add(createBuildMethod(annotation, createType(typeDefinition), builderClass)); + result.getMethods().add(createBuildMethod(annotation, createType(typeDefinition), builderClass)); return result; } 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 index 2f52185c88b6..d771ea387f82 100644 --- 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 @@ -38,9 +38,9 @@ public class SimpleBuilderStrategySupport extends GrBuilderStrategySupport { public Members process(GrTypeDefinition typeDefinition) { final PsiAnnotation annotation = PsiImplUtil.getAnnotation(typeDefinition, BUILDER_FQN); if (!isApplicable(annotation, SIMPLE_STRATEGY_NAME)) return Members.EMPTY; - final Members result = new Members(); + final Members result = Members.create(); for (GrField field : typeDefinition.getCodeFields()) { - result.methods.add(createFieldSetter(typeDefinition, field, annotation)); + result.getMethods().add(createFieldSetter(typeDefinition, field, annotation)); } return result; } From 247cecb953de1f0603043e2d2c81a36819be2ee6 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Sun, 12 Jul 2015 16:25:20 +0300 Subject: [PATCH 16/52] [groovy] collect all members from contributors simultaneously --- .../typedef/GrTypeDefinitionMembersCache.java | 12 ++--- .../resolve/ast/AstTransformContributor.java | 53 +++++-------------- 2 files changed, 19 insertions(+), 46 deletions(-) diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java index 7d98c6ec7816..396c921bbac8 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java @@ -116,7 +116,7 @@ public class GrTypeDefinitionMembersCache { final List 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); } }); @@ -139,12 +139,12 @@ public class GrTypeDefinitionMembersCache { 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); } }); @@ -163,7 +163,7 @@ public class GrTypeDefinitionMembersCache { methodMap.put(method.getSignature(PsiSubstitutor.EMPTY), method); } - for (PsiMethod method : AstTransformContributor.runContributorsForMethods(myDefinition)) { + for (PsiMethod method : AstTransformContributor.runContributors(myDefinition).getMethods()) { result.remove(methodMap.get(method.getSignature(PsiSubstitutor.EMPTY))); GrClassImplUtil.addExpandingReflectedMethods(result, method); } 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 c25c526744ca..c5f86bd8ac49 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 @@ -20,15 +20,11 @@ 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 @@ -48,48 +44,25 @@ public abstract class AstTransformContributor { } - @NotNull - public static Collection runContributorsForMethods(@NotNull final GrTypeDefinition clazz) { - Collection result = RecursionManager.doPreventingRecursion(clazz, true, new Computable>() { - @Override - public Collection compute() { - Collection collector = new ArrayList(); - for (final AstTransformContributor contributor : EP_NAME.getExtensions()) { - contributor.collectMethods(clazz, collector); - } - return collector; - } - }); - return result == null ? Collections.emptyList() : result; + public final Members collect(@NotNull final GrTypeDefinition clazz) { + final Members members = Members.create(); + collectMethods(clazz, members.getMethods()); + collectFields(clazz, members.getFields()); + collectClasses(clazz, members.getClasses()); + return members; } - @NotNull - public static List runContributorsForFields(@NotNull final GrTypeDefinition clazz) { - List fields = RecursionManager.doPreventingRecursion(clazz, true, new Computable>() { + public static Members runContributors(@NotNull final GrTypeDefinition clazz) { + Members result = RecursionManager.doPreventingRecursion(clazz, true, new Computable() { @Override - public List compute() { - List collector = new ArrayList(); + public Members compute() { + Members members = Members.create(); for (final AstTransformContributor contributor : EP_NAME.getExtensions()) { - contributor.collectFields(clazz, collector); + members.addFrom(contributor.collect(clazz)); } - return collector; + return members; } }); - return fields != null ? fields : Collections.emptyList(); - } - - @NotNull - public static List runContributorsForClasses(@NotNull final GrTypeDefinition clazz) { - List classes = 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 classes != null ? classes : Collections.emptyList(); + return result == null ? Members.EMPTY : result; } } From 0745e0ef96dd4e1774afe852d0e75593c7153276 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Sun, 12 Jul 2015 16:41:11 +0300 Subject: [PATCH 17/52] [groovy] merge GrBuilderStrategySupport.java to BuilderAnnotationContributor.java --- .../resolve/ast/AstTransformContributor.java | 4 +- .../builder/BuilderAnnotationContributor.java | 78 ++++++++++--------- .../builder/BuilderHelperLightPsiClass.java | 2 +- .../ast/builder/GrBuilderStrategySupport.java | 74 ------------------ .../DefaultBuilderStrategySupport.java | 7 +- .../ExternalBuilderStrategySupport.java | 6 +- .../SimpleBuilderStrategySupport.java | 6 +- plugins/groovy/src/META-INF/plugin.xml | 7 +- 8 files changed, 59 insertions(+), 125 deletions(-) delete mode 100644 plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/GrBuilderStrategySupport.java 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 c5f86bd8ac49..f7c27701ba93 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 @@ -44,7 +44,8 @@ public abstract class AstTransformContributor { } - public final Members collect(@NotNull final GrTypeDefinition clazz) { + @NotNull + public Members collect(@NotNull final GrTypeDefinition clazz) { final Members members = Members.create(); collectMethods(clazz, members.getMethods()); collectFields(clazz, members.getFields()); @@ -52,6 +53,7 @@ public abstract class AstTransformContributor { return members; } + @NotNull public static Members runContributors(@NotNull final GrTypeDefinition clazz) { Members result = RecursionManager.doPreventingRecursion(clazz, true, new Computable() { @Override 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 ce3624b103e9..a7dfc7bfd979 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.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 = "by @Builder"; + public static final String STRATEGY_ATTRIBUTE = "builderStrategy"; - @Override - public void collectMethods(@NotNull GrTypeDefinition clazz, final Collection collector) { - collector.addAll(collectAll(clazz).getMethods()); - } - - @Override - public void collectClasses(@NotNull GrTypeDefinition clazz, Collection collector) { - collector.addAll(collectAll(clazz).getClasses()); - } - - @Override - public void collectFields(@NotNull GrTypeDefinition clazz, Collection collector) { - collector.addAll(collectAll(clazz).getFields()); - } - - private static Members collectAll(final GrTypeDefinition clazz) { - return CachedValuesManager.getCachedValue(clazz, new CachedValueProvider() { - @Nullable - @Override - public Result compute() { - final Members result = Members.create(); - 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..d0757ee74b26 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 @@ -30,7 +30,7 @@ public class BuilderHelperLightPsiClass extends LightPsiClassBuilder { super(containingClass, name); myContainingClass = containingClass; setScope(myContainingClass); - setOriginInfo(GrBuilderStrategySupport.ORIGIN_INFO); + setOriginInfo(BuilderAnnotationContributor.ORIGIN_INFO); getModifierList().addModifier(PsiModifier.STATIC); } 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 394ede5993f0..000000000000 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/GrBuilderStrategySupport.java +++ /dev/null @@ -1,74 +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.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.expressions.GrReferenceExpression; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; -import org.jetbrains.plugins.groovy.lang.resolve.ast.Members; -import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy.DefaultBuilderStrategySupport; - -public abstract class GrBuilderStrategySupport { - - public static final ExtensionPointName EP = ExtensionPointName.create("org.intellij.groovy.builderStrategySupport"); - public static final String BUILDER_PACKAGE = "groovy.transform.builder"; - public static final String BUILDER_FQN = BUILDER_PACKAGE + ".Builder"; - public static final String ORIGIN_INFO = "by @Builder"; - public static final String STRATEGY_ATTRIBUTE = "builderStrategy"; - - @NotNull - public abstract Members process(GrTypeDefinition typeDefinition); - - @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/strategy/DefaultBuilderStrategySupport.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/DefaultBuilderStrategySupport.java index 5bd4735fe125..40ba00ac554e 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 @@ -27,15 +27,16 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefini 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_NAME = "DefaultStrategy"; @NotNull - public Members process(@NotNull final GrTypeDefinition typeDefinition) { + @Override + public Members collect(@NotNull final GrTypeDefinition typeDefinition) { return new DefaultBuilderStrategyHandler(typeDefinition).doProcess(); } 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 index 200f617e5038..f001f78b8417 100644 --- 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 @@ -23,18 +23,18 @@ 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.GrBuilderStrategySupport; +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 GrBuilderStrategySupport { +public class ExternalBuilderStrategySupport extends BuilderAnnotationContributor { public static final String EXTERNAL_STRATEGY_NAME = "ExternalStrategy"; @NotNull @Override - public Members process(GrTypeDefinition builderClass) { + public Members collect(@NotNull GrTypeDefinition builderClass) { Pair definitionPair = getConstructedClass(builderClass); if (definitionPair == null) return Members.EMPTY; 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 index d771ea387f82..107fbf9d4dfb 100644 --- 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 @@ -27,15 +27,15 @@ 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.GrBuilderStrategySupport; +import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.BuilderAnnotationContributor; -public class SimpleBuilderStrategySupport extends GrBuilderStrategySupport { +public class SimpleBuilderStrategySupport extends BuilderAnnotationContributor { public static final String SIMPLE_STRATEGY_NAME = "SimpleStrategy"; @NotNull @Override - public Members process(GrTypeDefinition typeDefinition) { + 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(); diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index f573f143b8d7..2cfd1e10e91d 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -114,10 +114,9 @@ - - - - + + + From a14103ad9b1184a5738b915c56365b7062e8b8f3 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Sun, 12 Jul 2015 16:44:22 +0300 Subject: [PATCH 18/52] [groovy] set origin info for setters & update origin info string --- .../lang/resolve/ast/builder/BuilderAnnotationContributor.java | 2 +- .../ast/builder/strategy/DefaultBuilderStrategySupport.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) 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 a7dfc7bfd979..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 @@ -28,7 +28,7 @@ public abstract class BuilderAnnotationContributor extends AstTransformContribut public static final String BUILDER_PACKAGE = "groovy.transform.builder"; public static final String BUILDER_FQN = BUILDER_PACKAGE + ".Builder"; - public static final String ORIGIN_INFO = "by @Builder"; + public static final String ORIGIN_INFO = "via @Builder"; public static final String STRATEGY_ATTRIBUTE = "builderStrategy"; @Nullable 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 40ba00ac554e..d69025ba9a6b 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 @@ -167,6 +167,7 @@ public class DefaultBuilderStrategySupport extends BuilderAnnotationContributor fieldSetter.setContainingClass(builderClass); fieldSetter.setMethodReturnType(JavaPsiFacade.getElementFactory(builderClass.getProject()).createType(builderClass)); fieldSetter.setNavigationElement(field); + fieldSetter.setOriginInfo(ORIGIN_INFO); return fieldSetter; } From d3f675030060d4f6c3624b151c7391a66afdb50e Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Sun, 12 Jul 2015 22:58:54 +0300 Subject: [PATCH 19/52] [groovy] filter out unwanted accessor methods if other suitable methods found --- .../typedef/GrTypeDefinitionMembersCache.java | 12 ++++------ .../groovy/lang/psi/util/GrClassImplUtil.java | 22 +++++++++++++++++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java index 396c921bbac8..53f0d859043f 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java @@ -154,17 +154,10 @@ public class GrTypeDefinitionMembersCache { return CachedValuesManager.getCachedValue(myDefinition, new CachedValueProvider() { @Override public Result compute() { - final Collection result = ContainerUtil.newHashSet(); + Collection result = ContainerUtil.newHashSet(); GrClassImplUtil.collectMethodsFromBody(myDefinition, result); - - final Map methodMap = new HashMap(); - for (PsiMethod method : result) { - methodMap.put(method.getSignature(PsiSubstitutor.EMPTY), method); - } - for (PsiMethod method : AstTransformContributor.runContributors(myDefinition).getMethods()) { - result.remove(methodMap.get(method.getSignature(PsiSubstitutor.EMPTY))); GrClassImplUtil.addExpandingReflectedMethods(result, method); } @@ -173,6 +166,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); } 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..03274831b944 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; @@ -667,4 +668,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; + } + }); + } } From cf94bf32bf2c6df4b7682dab39c28e2fe2bd0c66 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Sun, 12 Jul 2015 23:13:57 +0300 Subject: [PATCH 20/52] [groovy] test external builder strategy support --- .../GrExternalBuilderStrategyTest.groovy | 266 ++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrExternalBuilderStrategyTest.groovy 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' + } +} From 0778b81ef9af3dfdfe761152c0c6b387690a20d8 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Sun, 12 Jul 2015 23:27:06 +0300 Subject: [PATCH 21/52] [groovy] test default builder strategy on constructor support --- ...ultBuilderStrategyOnConstructorTest.groovy | 254 ++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrDefaultBuilderStrategyOnConstructorTest.groovy 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' + } +} From 437946e23e74cc6776f75b4ebc05be2087d97d33 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Sun, 12 Jul 2015 23:38:43 +0300 Subject: [PATCH 22/52] [groovy] test default builder strategy on factory method support --- ...tBuilderStrategyOnFactoryMethodTest.groovy | 260 ++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrDefaultBuilderStrategyOnFactoryMethodTest.groovy 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' + } +} From d5819c6a00c607867dd5bb9a9eb6be3723f60c5a Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Mon, 13 Jul 2015 15:44:08 +0300 Subject: [PATCH 23/52] [java] implementation of light psi type parameter that doesn't depend on some delegate --- .../impl/light/LightTypeParameterBuilder.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 java/java-psi-impl/src/com/intellij/psi/impl/light/LightTypeParameterBuilder.java 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); + } +} From 4ee944637ebf18cd96feb2f4f60854eccfadbfba Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Mon, 13 Jul 2015 15:49:58 +0300 Subject: [PATCH 24/52] [groovy] initializer @Builder strategy support on type definition --- .../DefaultBuilderStrategySupport.java | 12 +- .../InitializerBuilderStrategySupport.java | 179 ++++++++++++++++++ plugins/groovy/src/META-INF/plugin.xml | 1 + 3 files changed, 186 insertions(+), 6 deletions(-) create mode 100644 plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/InitializerBuilderStrategySupport.java 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 d69025ba9a6b..cb732a263f95 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 @@ -141,12 +141,12 @@ public class DefaultBuilderStrategySupport extends BuilderAnnotationContributor final String builderMethodName = AnnotationUtil.getDeclaredStringAttributeValue(annotation, "builderMethodName"); return StringUtil.isEmpty(builderMethodName) ? "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 + 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 @@ -172,7 +172,7 @@ public class DefaultBuilderStrategySupport extends BuilderAnnotationContributor } @NotNull - private static String getFieldMethodName(@NotNull PsiAnnotation annotation, @NotNull String fieldName) { + 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)); } 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..36a206cb897a --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/InitializerBuilderStrategySupport.java @@ -0,0 +1,179 @@ +/* + * 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)); + } + + @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; + } + + @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; + } + + @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()); + LightMethodBuilder builderMethod = createBuilderMethod(builderClass, annotation); + myMembers.getMethods().add(builderMethod); + } + + @NotNull + private static String getBuilderMethodName(@NotNull PsiAnnotation annotation) { + final String builderMethodName = AnnotationUtil.getDeclaredStringAttributeValue(annotation, "builderMethodName"); + return StringUtil.isEmpty(builderMethodName) ? "createInitializer" : 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/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index 2cfd1e10e91d..32c268851592 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -117,6 +117,7 @@ + From 2c9b32e1d1c40ff233dbe0de8cf19c4bea9a92e6 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Mon, 13 Jul 2015 16:45:10 +0300 Subject: [PATCH 25/52] [groovy] CCE fix --- .../groovy/findUsages/GroovyConstructorUsagesSearcher.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 Date: Mon, 13 Jul 2015 16:46:25 +0300 Subject: [PATCH 26/52] [groovy] initializer @Builder strategy support on constructors --- .../builder/strategy/InitializerBuilderStrategySupport.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 index 36a206cb897a..7b16d323fa8c 100644 --- 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 @@ -146,8 +146,8 @@ public class InitializerBuilderStrategySupport extends BuilderAnnotationContribu private void processConstructor(@NotNull GrMethod method, PsiAnnotation annotation) { PsiClass builderClass = createBuilderClass(annotation, method.getParameters()); - LightMethodBuilder builderMethod = createBuilderMethod(builderClass, annotation); - myMembers.getMethods().add(builderMethod); + myMembers.getMethods().add(createBuilderMethod(builderClass, annotation)); + myMembers.getMethods().add(createBuilderConstructor(myContainingClass, builderClass, annotation)); } @NotNull From f37373c17d9ec0c037f8fc7d620cb2d4bc534c36 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Mon, 13 Jul 2015 17:24:25 +0300 Subject: [PATCH 27/52] [groovy] completion tests for initializer @Builder strategy support on type definition --- .../GrInitializerBuilderStrategyTest.groovy | 220 ++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrInitializerBuilderStrategyTest.groovy 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' + } +} From b1991c1546abe899e8bff100d0e6eca44ec7ab1d Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Mon, 13 Jul 2015 17:37:23 +0300 Subject: [PATCH 28/52] [groovy] completion tests for initializer @Builder strategy support on constructor --- ...zerBuilderStrategyOnConstructorTest.groovy | 180 ++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrInitializerBuilderStrategyOnConstructorTest.groovy 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' + } +} From 0abda35e8da9d1af5516507a76296101ceee574e Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Mon, 13 Jul 2015 18:19:16 +0300 Subject: [PATCH 29/52] [groovy] add @Builder helper class to inner classes --- .../strategy/DefaultBuilderStrategySupport.java | 13 ++++++------- .../strategy/InitializerBuilderStrategySupport.java | 2 ++ 2 files changed, 8 insertions(+), 7 deletions(-) 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 cb732a263f95..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 @@ -68,8 +68,8 @@ public class DefaultBuilderStrategySupport extends BuilderAnnotationContributor final PsiAnnotation builderAnno = PsiImplUtil.getAnnotation(myContainingClass, BUILDER_FQN); if (!isApplicable(builderAnno, DEFAULT_STRATEGY_NAME)) return; final PsiClass builderClass = createBuilderClass(builderAnno, myContainingClass.getCodeFields()); - final LightMethodBuilder builderMethod = createBuilderMethod(builderClass, builderAnno); - myMembers.getMethods().add(builderMethod); + myMembers.getMethods().add(createBuilderMethod(builderClass, builderAnno)); + myMembers.getClasses().add(builderClass); } @NotNull @@ -125,17 +125,16 @@ public class DefaultBuilderStrategySupport extends BuilderAnnotationContributor private void processConstructor(@NotNull GrMethod method, PsiAnnotation annotation) { PsiClass builderClass = createBuilderClass(annotation, method.getParameters()); - LightMethodBuilder builderMethod = createBuilderMethod(builderClass, annotation); - myMembers.getMethods().add(builderMethod); + 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.getMethods().add(builderMethod); + 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"); 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 index 7b16d323fa8c..d8a7599ce61e 100644 --- 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 @@ -76,6 +76,7 @@ public class InitializerBuilderStrategySupport extends BuilderAnnotationContribu 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 @@ -148,6 +149,7 @@ public class InitializerBuilderStrategySupport extends BuilderAnnotationContribu PsiClass builderClass = createBuilderClass(annotation, method.getParameters()); myMembers.getMethods().add(createBuilderMethod(builderClass, annotation)); myMembers.getMethods().add(createBuilderConstructor(myContainingClass, builderClass, annotation)); + myMembers.getClasses().add(builderClass); } @NotNull From a2fb27139e3a74625beebcf1c5c8d23c470cc4d1 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Mon, 13 Jul 2015 19:00:34 +0300 Subject: [PATCH 30/52] [groovy] add build method for initializer strategy @Builder helper class --- .../InitializerBuilderStrategySupport.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) 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 index d8a7599ce61e..39651af4c165 100644 --- 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 @@ -91,7 +91,7 @@ public class InitializerBuilderStrategySupport extends BuilderAnnotationContribu for (int i = 0; i < setters.length; i++) { builderClass.addMethod(createFieldSetter(builderClass, setters[i], annotation, i)); } - return builderClass; + return builderClass.addMethod(createBuildMethod(annotation, builderClass)); } @NotNull @@ -114,6 +114,16 @@ public class InitializerBuilderStrategySupport extends BuilderAnnotationContribu 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)); @@ -158,6 +168,12 @@ public class InitializerBuilderStrategySupport extends BuilderAnnotationContribu 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( From 2f1cb0dc15f1294fb813300baee87b506aadb3ec Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Thu, 13 Aug 2015 20:05:04 +0300 Subject: [PATCH 31/52] [groovy] CCE fixed, cast removed because type definition methods can be light methods that don't implement GrMethod interface --- .../control/CreateParameterForFieldIntention.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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) { From 6fd66660b85c0bcfda5164ae86caa641688c4bbb Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Thu, 13 Aug 2015 20:06:00 +0300 Subject: [PATCH 32/52] [groovy] test compilation fixed, one can not use varargs with default arguments in Groovy --- .../GrBuilderTransformationCompletionTestBase.groovy | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 index ce6f77965422..f9778aa2374c 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrBuilderTransformationCompletionTestBase.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/builder/GrBuilderTransformationCompletionTestBase.groovy @@ -33,7 +33,11 @@ abstract class GrBuilderTransformationCompletionTestBase extends GroovyCompletio return GroovyLightProjectDescriptor.GROOVY_2_3_9; } - void doVariantableTest(String text, CompletionResult cr = CompletionResult.contain, String... args) { + 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) } } From a12a8da9e8a49e5d31f9105af1611f04c91be6a0 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Thu, 13 Aug 2015 21:58:43 +0300 Subject: [PATCH 33/52] [groovy] provide parent of builder helper class (for proper FQN) & use containingClass field from parent LightPsiClassBuilder --- .../ast/builder/BuilderHelperLightPsiClass.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) 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 d0757ee74b26..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); + 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(); } } From 7c5cff325cf44b8b55536da8b601e0e16677f72b Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Thu, 13 Aug 2015 22:19:12 +0300 Subject: [PATCH 34/52] [java] fix psi reference list role for myImplementsList in LightPsiClassBuilder --- .../com/intellij/psi/impl/light/LightPsiClassBuilder.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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..46b356392f66 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 @@ -22,11 +22,14 @@ 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; From 41196000b1cc48964e606f21effa78b60bc16364 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Thu, 13 Aug 2015 22:17:25 +0300 Subject: [PATCH 35/52] [java] properly compare light psi classes --- .../com/intellij/psi/impl/light/LightPsiClassBuilder.java | 6 ++++++ 1 file changed, 6 insertions(+) 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 46b356392f66..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,6 +16,7 @@ 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; @@ -107,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; From 7696c81b256fd8507c71ea111e170a761dbd82aa Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Fri, 14 Aug 2015 16:17:32 +0300 Subject: [PATCH 36/52] [groovy] preserve order of methods in class by using linked hash set --- .../impl/statements/typedef/GrTypeDefinitionMembersCache.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java index 53f0d859043f..eaa66189af48 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java @@ -154,7 +154,7 @@ public class GrTypeDefinitionMembersCache { return CachedValuesManager.getCachedValue(myDefinition, new CachedValueProvider() { @Override public Result compute() { - Collection result = ContainerUtil.newHashSet(); + Collection result = ContainerUtil.newLinkedHashSet(); GrClassImplUtil.collectMethodsFromBody(myDefinition, result); for (PsiMethod method : AstTransformContributor.runContributors(myDefinition).getMethods()) { From 94d53572d5afd323e07d19904a4398354869b249 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Mon, 17 Aug 2015 18:42:00 +0300 Subject: [PATCH 37/52] [groovy] consistent AST transformation processing & ability to contribute implements types --- .../resolve/ast/AstTransformContributor.java | 13 +++++++------ .../groovy/lang/resolve/ast/Members.java | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) 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 f7c27701ba93..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,7 +18,6 @@ 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 org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; @@ -30,26 +29,28 @@ import java.util.Collection; * @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()); - collectClasses(clazz, members.getClasses()); return members; } 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 index 6fd2fb8cd099..95c5abf87ef7 100644 --- 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 @@ -16,6 +16,7 @@ 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; @@ -35,6 +36,9 @@ public abstract class Members { @NotNull public abstract Collection getClasses(); + @NotNull + public abstract Collection getImplementsTypes(); + public abstract void addFrom(@NotNull Members other); public static final Members EMPTY = new Members() { @@ -57,6 +61,12 @@ public abstract class Members { return Collections.emptyList(); } + @NotNull + @Override + public Collection getImplementsTypes() { + return Collections.emptyList(); + } + @Override public void addFrom(@NotNull Members other) { // do nothing @@ -70,6 +80,7 @@ public abstract class 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 @@ -89,11 +100,18 @@ public abstract class Members { 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()); } }; } From 0fbb1674fa1986331f1d30a512dce714222195d0 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Mon, 17 Aug 2015 18:58:41 +0300 Subject: [PATCH 38/52] [groovy] contribute to implements list while processing @Delegate transformation (simultaneously with contributing to methods) --- .../groovy/lang/psi/util/GrClassImplUtil.java | 65 ++----------------- .../ast/DelegatedMethodsContributor.java | 40 +++++++++++- 2 files changed, 44 insertions(+), 61 deletions(-) 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 03274831b944..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 @@ -51,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; @@ -59,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.*; @@ -137,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) { @@ -641,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(); 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(); + } } From 6b7acf113ffaaf6daaffdf3af38aa1c79121658c Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Wed, 19 Aug 2015 14:47:58 +0300 Subject: [PATCH 39/52] [groovy] collect traits methods independently of AstTransformations --- .../typedef/GrTypeDefinitionMembersCache.java | 59 ++++++++++--------- plugins/groovy/src/META-INF/plugin.xml | 1 - 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java index eaa66189af48..f68d3229e762 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java @@ -135,6 +135,7 @@ public class GrTypeDefinitionMembersCache { private List getFieldsImpl() { List fields = ContainerUtil.newArrayList(myDefinition.getCodeFields()); + fields.addAll(new TraitCollector().collectFields()); fields.addAll(getSyntheticFields()); return fields; } @@ -157,6 +158,7 @@ public class GrTypeDefinitionMembersCache { Collection result = ContainerUtil.newLinkedHashSet(); GrClassImplUtil.collectMethodsFromBody(myDefinition, result); + result.addAll(new TraitCollector().collectMethods(result)); for (PsiMethod method : AstTransformContributor.runContributors(myDefinition).getMethods()) { GrClassImplUtil.addExpandingReflectedMethods(result, method); } @@ -179,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(); @@ -216,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) { @@ -235,6 +236,8 @@ public class GrTypeDefinitionMembersCache { } })); + List result = ContainerUtil.newArrayList(); + for (PsiClassType.ClassResolveResult resolveResult : traits) { GrTypeDefinition trait = (GrTypeDefinition)resolveResult.getElement(); LOG.assertTrue(trait != null); @@ -260,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); @@ -298,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(); @@ -320,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/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index 32c268851592..7f448d6d6880 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -107,7 +107,6 @@ - From ab53036a8f0dd92238eb4377eb8a82905ad01c48 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Wed, 19 Aug 2015 16:17:48 +0300 Subject: [PATCH 40/52] remove listener on dispose: reference to myDialog may remain inside swing caches, but we do not want any other hard refs --- .../src/com/intellij/openapi/ui/DialogWrapper.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) 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()); From d0a267bfa97dcedaf4cb8b628629eae5bd9749b5 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Wed, 19 Aug 2015 16:19:35 +0300 Subject: [PATCH 41/52] fixing debugger memory leaks --- .../intellij/xdebugger/impl/ui/tree/XDebuggerTreePanel.java | 6 ++++++ 1 file changed, 6 insertions(+) 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() { From 9271c199fe21e0d3ecd426270a8efd8853c12947 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 19 Aug 2015 15:29:07 +0200 Subject: [PATCH 42/52] ChangeBrowserSettings: use @Transient annotation instead of modifier (IDEA-CR-4440) --- .../openapi/vcs/versionBrowser/ChangeBrowserSettings.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 9e4c7b21e2dd..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,7 +55,7 @@ public class ChangeBrowserSettings implements JDOMExternalizable { public String USER = ""; public boolean STOP_ON_COPY = false; - public transient boolean STRICTLY_AFTER = false; + @Transient public boolean STRICTLY_AFTER = false; public void readExternal(Element element) throws InvalidDataException { DefaultJDOMExternalizer.readExternal(this, element); From 18e61eb34c0225c09d2d23170e5af37be6d90e05 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Wed, 19 Aug 2015 17:16:43 +0300 Subject: [PATCH 43/52] fix test --- .../codeInsight/completion/NormalCompletionOrderingTest.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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() { From 419b4697bd1e6cac5227ba0e8c9749ba10a035a3 Mon Sep 17 00:00:00 2001 From: irengrig Date: Wed, 19 Aug 2015 16:22:35 +0200 Subject: [PATCH 44/52] started project startup tasks. --- .../startup/ProjectStartupConfigurable.java | 167 ++++++++++++++++++ .../startup/ProjectStartupConfiguration.java | 107 +++++++++++ .../ProjectStartupConfigurationBase.java | 106 +++++++++++ .../ProjectStartupLocalConfiguration.java | 32 ++++ .../ProjectStartupSharedConfiguration.java | 34 ++++ .../startup/ProjectStartupTasksTreeModel.java | 92 ++++++++++ 6 files changed, 538 insertions(+) create mode 100644 platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupConfigurable.java create mode 100644 platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupConfiguration.java create mode 100644 platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupConfigurationBase.java create mode 100644 platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupLocalConfiguration.java create mode 100644 platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupSharedConfiguration.java create mode 100644 platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupTasksTreeModel.java 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..40aad31d005e --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupConfigurable.java @@ -0,0 +1,167 @@ +/* + * 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.options.Configurable; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.options.SearchableConfigurable; +import com.intellij.openapi.project.Project; +import com.intellij.ui.AnActionButton; +import com.intellij.ui.AnActionButtonRunnable; +import com.intellij.ui.ColoredTreeCellRenderer; +import com.intellij.ui.ToolbarDecorator; +import com.intellij.ui.treeStructure.Tree; +import com.intellij.util.ui.FormBuilder; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +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; + + 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(); + final JPanel tasksPanel = ToolbarDecorator.createDecorator(myTree) + .setAddAction(new AnActionButtonRunnable() { + @Override + public void run(AnActionButton button) { + //todo temp + final RunnerAndConfigurationSettings load = ((RunManagerEx)RunManager.getInstance(myProject)).findConfigurationByName("Load"); + if (load != null) { + final ProjectStartupTasksTreeModel oldModel = (ProjectStartupTasksTreeModel)myTree.getModel(); + final List configurations = oldModel.getConfigurations(); + if (! configurations.contains(load)) { + configurations.add(load); + } + Collections.sort(configurations, new Comparator() { + @Override + public int compare(RunnerAndConfigurationSettings o1, RunnerAndConfigurationSettings o2) { + return o1.getName().compareToIgnoreCase(o2.getName()); + } + }); + setModel(new ProjectStartupTasksTreeModel(configurations)); + } + } + }) + .setEditAction(new AnActionButtonRunnable() { + @Override + public void run(AnActionButton button) { + //todo + } + }) + .setRemoveAction(new AnActionButtonRunnable() { + @Override + public void run(AnActionButton button) { + //todo + } + }) + .createPanel(); + return FormBuilder.createFormBuilder() // todo bundle + .addLabeledComponentFillVertically("Tasks to be executed right after opening the project.", tasksPanel) + .getPanel(); + } + + @Override + public boolean isModified() { + return false; + } + + @Override + public void apply() throws ConfigurationException { + + } + + @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) { + // todo correct renderer + final String name = ((RunnerAndConfigurationSettings)value).getName(); + append(name); + } + } + }); + } +} 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/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; + } +} From 3502a2a7580fc397f09291a3411d801e56618902 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Wed, 19 Aug 2015 17:46:32 +0300 Subject: [PATCH 45/52] [vcs] Provide a utility method to find the PushSupport for the vcs Requested by a 3-party plugin author. Reuse the method from the PushController. --- .../src/com/intellij/dvcs/DvcsUtil.java | 12 +++++ .../intellij/dvcs/push/PushController.java | 54 +++++++++---------- 2 files changed, 39 insertions(+), 27 deletions(-) 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) { From 1ef12e7765846129b3888b3e33a763ed49450733 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 19 Aug 2015 17:10:17 +0200 Subject: [PATCH 46/52] [platform] memory allocation optimization for non-blocking process readers --- .../intellij/util/io/BaseOutputReader.java | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) 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; From f59ddb43fa4f8aa3e57128d1471c83b6f43dea93 Mon Sep 17 00:00:00 2001 From: irengrig Date: Wed, 19 Aug 2015 17:14:08 +0200 Subject: [PATCH 47/52] project startup tasks, add run configuration step (show popup with edit action) --- .../startup/ProjectStartupConfigurable.java | 152 ++++++++++++++---- 1 file changed, 125 insertions(+), 27 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupConfigurable.java b/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupConfigurable.java index 40aad31d005e..4ce967006f6e 100644 --- a/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupConfigurable.java +++ b/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupConfigurable.java @@ -15,24 +15,37 @@ */ package com.intellij.execution.startup; +import com.intellij.execution.Executor; +import com.intellij.execution.ExecutorRegistry; import com.intellij.execution.RunManager; -import com.intellij.execution.RunManagerEx; 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.icons.AllIcons; +import com.intellij.idea.ActionsBundle; +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.ui.AnActionButton; -import com.intellij.ui.AnActionButtonRunnable; -import com.intellij.ui.ColoredTreeCellRenderer; -import com.intellij.ui.ToolbarDecorator; +import com.intellij.openapi.ui.popup.JBPopup; +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 java.awt.*; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -44,6 +57,7 @@ public class ProjectStartupConfigurable implements SearchableConfigurable, Confi private final Project myProject; private Tree myTree; private ProjectStartupConfiguration myConfiguration; + private ToolbarDecorator myDecorator; public ProjectStartupConfigurable(Project project) { myProject = project; @@ -79,26 +93,11 @@ public class ProjectStartupConfigurable implements SearchableConfigurable, Confi public JComponent createComponent() { myTree = new Tree(); installRenderer(); - final JPanel tasksPanel = ToolbarDecorator.createDecorator(myTree) + myDecorator = ToolbarDecorator.createDecorator(myTree) .setAddAction(new AnActionButtonRunnable() { @Override public void run(AnActionButton button) { - //todo temp - final RunnerAndConfigurationSettings load = ((RunManagerEx)RunManager.getInstance(myProject)).findConfigurationByName("Load"); - if (load != null) { - final ProjectStartupTasksTreeModel oldModel = (ProjectStartupTasksTreeModel)myTree.getModel(); - final List configurations = oldModel.getConfigurations(); - if (! configurations.contains(load)) { - configurations.add(load); - } - Collections.sort(configurations, new Comparator() { - @Override - public int compare(RunnerAndConfigurationSettings o1, RunnerAndConfigurationSettings o2) { - return o1.getName().compareToIgnoreCase(o2.getName()); - } - }); - setModel(new ProjectStartupTasksTreeModel(configurations)); - } + selectAndAddConfiguration(button); } }) .setEditAction(new AnActionButtonRunnable() { @@ -112,13 +111,112 @@ public class ProjectStartupConfigurable implements SearchableConfigurable, Confi public void run(AnActionButton button) { //todo } - }) - .createPanel(); + }); + final JPanel tasksPanel = myDecorator.createPanel(); return FormBuilder.createFormBuilder() // todo bundle .addLabeledComponentFillVertically("Tasks to be executed right after opening the project.", tasksPanel) .getPanel(); } + 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() { return false; @@ -157,9 +255,9 @@ public class ProjectStartupConfigurable implements SearchableConfigurable, Confi int row, boolean hasFocus) { if (value instanceof RunnerAndConfigurationSettings) { - // todo correct renderer - final String name = ((RunnerAndConfigurationSettings)value).getName(); - append(name); + final RunnerAndConfigurationSettings settings = (RunnerAndConfigurationSettings)value; + setIcon(settings.getConfiguration().getIcon()); + append(settings.getName()); } } }); From 2338173c5c1365b2d3a2e03ca8c46dfb3cc6aa61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yann=20C=C3=A9bron?= Date: Wed, 19 Aug 2015 17:52:57 +0200 Subject: [PATCH 48/52] [devkit] DOM: fix @Stubbed for Extensions --- plugins/devkit/src/dom/IdeaPlugin.java | 3 ++- .../devkit/src/dom/impl/PluginXmlDomFileDescription.java | 4 ++-- .../devkit/testData/pluginXmlDomStubs/pluginXmlStubs.xml | 2 ++ .../testSources/codeInsight/PluginXmlDomStubsTest.java | 7 +++++-- 4 files changed, 11 insertions(+), 5 deletions(-) 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 { From fdf2a1ee24a9cfcdb55a7e668cf8473cb9b697d3 Mon Sep 17 00:00:00 2001 From: irengrig Date: Wed, 19 Aug 2015 18:03:00 +0200 Subject: [PATCH 49/52] project startup tasks, delete/edit --- .../startup/ProjectStartupConfigurable.java | 54 +++++++++++++++++-- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupConfigurable.java b/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupConfigurable.java index 4ce967006f6e..aa95294d78e6 100644 --- a/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupConfigurable.java +++ b/platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupConfigurable.java @@ -22,8 +22,10 @@ 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; @@ -31,6 +33,7 @@ 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; @@ -44,6 +47,8 @@ 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; @@ -103,21 +108,60 @@ public class ProjectStartupConfigurable implements SearchableConfigurable, Confi .setEditAction(new AnActionButtonRunnable() { @Override public void run(AnActionButton button) { - //todo + 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) { - //todo + 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 @@ -219,12 +263,14 @@ public class ProjectStartupConfigurable implements SearchableConfigurable, Confi @Override public boolean isModified() { - return false; + 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 From a3df77e8f97228092ffaec9d2cba68a464676f73 Mon Sep 17 00:00:00 2001 From: irengrig Date: Wed, 19 Aug 2015 18:20:03 +0200 Subject: [PATCH 50/52] project startup tasks, startup manager code --- .../startup/ProjectStartupRunner.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 platform/lang-impl/src/com/intellij/execution/startup/ProjectStartupRunner.java 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()); + } + } +} From 26c69a8a4927a19dcde98db07e6a20a42adc6e00 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Tue, 18 Aug 2015 16:08:21 +0300 Subject: [PATCH 51/52] re-enabled non-recursive duplicate calculation --- .../impl/source/tree/RecursiveLighterASTNodeWalkingVisitor.java | 1 - 1 file changed, 1 deletion(-) 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); } From 24999cf7c56a8b9af6a20e291b14a42eebf4869c Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 19 Aug 2015 19:09:52 +0300 Subject: [PATCH 52/52] reverted. For some reason clsElement.getProject() can be default project --- .../intellij/psi/impl/compiled/ClsRepositoryPsiElement.java | 6 ------ 1 file changed, 6 deletions(-) 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();