Add API to specify fixed value reference for named argument.
authorSergey Evdokimov <sergey.evdokimov@jetbrains.com>
Fri, 16 Aug 2013 11:23:31 +0000 (15:23 +0400)
committerSergey Evdokimov <sergey.evdokimov@jetbrains.com>
Fri, 16 Aug 2013 11:24:10 +0000 (15:24 +0400)
plugins/groovy/src/org/jetbrains/plugins/groovy/extensions/GroovyMethodDescriptor.java
plugins/groovy/src/org/jetbrains/plugins/groovy/extensions/GroovyMethodInfo.java
plugins/groovy/src/org/jetbrains/plugins/groovy/util/FixedValuesReferenceProvider.java [new file with mode: 0644]

index 9d7f9a5dfd925b6e26a1aeb829d564167d48f37b..4f06c151e11ddbf1c84d993870b2d36451974d94 100644 (file)
@@ -71,6 +71,9 @@ public class GroovyMethodDescriptor {
     @Attribute("referenceProvider")
     public String referenceProvider;
 
+    @Attribute("values")
+    public String values;
+
     protected Iterable<String> getNames() {
       assert !StringUtil.isEmptyOrSpaces(name);
       return StringUtil.tokenize(name, ATTR_NAMES_DELIMITER);
@@ -100,23 +103,6 @@ public class GroovyMethodDescriptor {
     }
   }
 
-  public Map<String, String> getNamedArgumentsReferenceProviders() {
-    if (myArguments == null) return Collections.emptyMap();
-
-    Map<String, String> res = new HashMap<String, String>();
-
-    for (NamedArgument argument : myArguments) {
-      if (argument.referenceProvider != null) {
-        for (String name : argument.getNames()) {
-          Object oldValue = res.put(name, argument.referenceProvider);
-          assert oldValue == null;
-        }
-      }
-    }
-
-    return res;
-  }
-
   @Nullable
   public Map<String, NamedArgumentDescriptor> getArgumentsMap() {
     if (myArguments == null && namedArgs == null) {
index 655e6cbd7d9b6a207b996719023b368061f4a3ef..ae2972ea1433864c28425c7000c46b2d05056eec 100644 (file)
@@ -12,6 +12,7 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUt
 import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightMethodBuilder;
 import org.jetbrains.plugins.groovy.refactoring.GroovyNamesUtil;
 import org.jetbrains.plugins.groovy.util.ClassInstanceCache;
+import org.jetbrains.plugins.groovy.util.FixedValuesReferenceProvider;
 
 import java.lang.reflect.Modifier;
 import java.util.*;
@@ -37,7 +38,7 @@ public class GroovyMethodInfo {
   private final String myNamedArgProviderClassName;
   private GroovyNamedArgumentProvider myNamedArgProviderInstance;
 
-  private Map<String, String> myNamedArgReferenceProviderClassNames;
+  private Map<String, NamedArgumentReference> myNamedArgReferenceProviders;
 
   private static void ensureInit() {
     if (METHOD_INFOS != null) return;
@@ -136,9 +137,9 @@ public class GroovyMethodInfo {
     myNamedArguments = method.getArgumentsMap();
     myNamedArgProviderClassName = method.namedArgsProvider;
 
-    myNamedArgReferenceProviderClassNames = method.getNamedArgumentsReferenceProviders();
+    myNamedArgReferenceProviders = getNamedArgumentsReferenceProviders(method);
 
-    myAllSupportedNamedArguments.addAll(myNamedArgReferenceProviderClassNames.keySet());
+    myAllSupportedNamedArguments.addAll(myNamedArgReferenceProviders.keySet());
 
     if (ApplicationManager.getApplication().isInternal()) {
       // Check classes to avoid typo.
@@ -147,8 +148,8 @@ public class GroovyMethodInfo {
 
       assertClassExists(myReturnTypeCalculatorClassName, PairFunction.class);
 
-      for (String className : myNamedArgReferenceProviderClassNames.values()) {
-        assertClassExists(className, PsiReferenceProvider.class, GroovyNamedArgumentReferenceProvider.class);
+      for (NamedArgumentReference r : myNamedArgReferenceProviders.values()) {
+        assertClassExists(r.myProviderClassName, PsiReferenceProvider.class, GroovyNamedArgumentReferenceProvider.class);
       }
     }
   }
@@ -171,6 +172,39 @@ public class GroovyMethodInfo {
     }
   }
 
+  private static Map<String, NamedArgumentReference> getNamedArgumentsReferenceProviders(GroovyMethodDescriptor methodDescriptor) {
+    if (methodDescriptor.myArguments == null) return Collections.emptyMap();
+
+    Map<String, NamedArgumentReference> res = new HashMap<String, NamedArgumentReference>();
+
+    for (GroovyMethodDescriptor.NamedArgument argument : methodDescriptor.myArguments) {
+      NamedArgumentReference r;
+
+      if (argument.referenceProvider != null) {
+        assert argument.values == null;
+        r = new NamedArgumentReference(argument.referenceProvider);
+      }
+      else if (argument.values != null) {
+        List<String> values = new ArrayList<String>();
+        for (StringTokenizer st = new StringTokenizer(argument.values, " ,;"); st.hasMoreTokens(); ) {
+          values.add(st.nextToken());
+        }
+
+        r = new NamedArgumentReference(values.toArray(new String[values.size()]));
+      }
+      else {
+        continue;
+      }
+
+      for (String name : argument.getNames()) {
+        Object oldValue = res.put(name, r);
+        assert oldValue == null;
+      }
+    }
+
+    return res;
+  }
+
   private static void addMethodDescriptor(Map<String, Map<String, List<GroovyMethodInfo>>> res,
                                           GroovyMethodDescriptor method,
                                           @NotNull ClassLoader classLoader,
@@ -235,10 +269,10 @@ public class GroovyMethodInfo {
    */
   @Nullable
   public Object getNamedArgReferenceProvider(String namedArgumentName) {
-    String className = myNamedArgReferenceProviderClassNames.get(namedArgumentName);
-    if (className == null) return null;
+    NamedArgumentReference r = myNamedArgReferenceProviders.get(namedArgumentName);
+    if (r == null) return null;
 
-    return ClassInstanceCache.getInstance(className, myClassLoader);
+    return r.getProvider(myClassLoader);
   }
 
   @Nullable
@@ -275,4 +309,40 @@ public class GroovyMethodInfo {
 
     return true;
   }
+
+  private static class NamedArgumentReference {
+    private final String myProviderClassName;
+    private final String[] myValues;
+
+    private volatile Object myProvider;
+
+    public NamedArgumentReference(String providerClassName) {
+      myProviderClassName = providerClassName;
+      myValues = null;
+    }
+
+    public NamedArgumentReference(String[] values) {
+      myValues = values;
+      myProviderClassName = null;
+    }
+
+    private Object doGetProvider(ClassLoader classLoader) {
+      if (myProviderClassName != null) {
+        return ClassInstanceCache.getInstance(myProviderClassName, classLoader);
+      }
+
+      return new FixedValuesReferenceProvider(myValues);
+    }
+
+    // @return instance of PsiReferenceProvider or GroovyNamedArgumentReferenceProvider or null.
+    public Object getProvider(ClassLoader classLoader) {
+      Object res = myProvider;
+      if (res == null) {
+        res = doGetProvider(classLoader);
+        myProvider = res;
+      }
+
+      return res;
+    }
+  }
 }
diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/util/FixedValuesReferenceProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/util/FixedValuesReferenceProvider.java
new file mode 100644 (file)
index 0000000..5ccdd81
--- /dev/null
@@ -0,0 +1,48 @@
+package org.jetbrains.plugins.groovy.util;
+
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.PsiReferenceBase;
+import com.intellij.psi.PsiReferenceProvider;
+import com.intellij.util.ProcessingContext;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Sergey Evdokimov
+ */
+public class FixedValuesReferenceProvider extends PsiReferenceProvider {
+
+  private final String[] myValues;
+
+  private boolean mySoft;
+
+  public FixedValuesReferenceProvider(@NotNull String[] values) {
+    this(values, false);
+  }
+
+  public FixedValuesReferenceProvider(@NotNull String[] values, boolean soft) {
+    myValues = values;
+    mySoft = soft;
+  }
+
+  @NotNull
+  @Override
+  public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
+    return new PsiReference[]{
+      new PsiReferenceBase<PsiElement>(element, mySoft) {
+        @Nullable
+        @Override
+        public PsiElement resolve() {
+          return null;
+        }
+
+        @NotNull
+        @Override
+        public Object[] getVariants() {
+          return myValues;
+        }
+      }
+    };
+  }
+}