mapping of parameters to arguments of call for Groovy cidr/96.98 idea/96.99 pycharm/96.100
authorMaxim Medvedev <maxim.medvedev@jetbrains.com>
Sat, 17 Apr 2010 10:13:11 +0000 (14:13 +0400)
committerMaxim Medvedev <maxim.medvedev@jetbrains.com>
Sat, 17 Apr 2010 10:13:11 +0000 (14:13 +0400)
plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrClosureSignatureUtil.java

index e074fcbe7d3e0ec5ec8e1172d94ee28058f184a5..79510b570cd913fa26867744304283a78fab9968 100644 (file)
@@ -17,11 +17,21 @@ package org.jetbrains.plugins.groovy.lang.psi.impl;
 
 import com.intellij.psi.*;
 import com.intellij.psi.search.GlobalSearchScope;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument;
 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
 import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureParameter;
 import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureSignature;
 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
 import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureImpl;
+import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * @author Maxim.Medvedev
@@ -60,9 +70,9 @@ public class GrClosureSignatureUtil {
   }
 
   private static boolean isApplicable(GrClosureSignature signature, PsiType[] args, PsiManager manager, GlobalSearchScope scope) {
-     GrClosureParameter[] params = signature.getParameters();
+   GrClosureParameter[] params = signature.getParameters();
     if (args.length > params.length && !signature.isVarargs()) return false;
-    int optional = getOptionalParamCount(signature);
+    int optional = getOptionalParamCount(signature, false);
     int notOptional = params.length - optional;
     if (signature.isVarargs()) notOptional--;
     if (notOptional > args.length) return false;
@@ -82,7 +92,7 @@ public class GrClosureSignatureUtil {
                                       int argCount,
                                       PsiManager manager,
                                       GlobalSearchScope scope) {
-    int optional = getOptionalParamCount(params);
+    int optional = getOptionalParamCount(params, false);
     int notOptional = paramCount - optional;
     int optionalArgs = argCount - notOptional;
     int cur = 0;
@@ -128,10 +138,10 @@ public class GrClosureSignatureUtil {
 
       while (curArg < args.length) {
         if (skipOptionals) {
-          while (params[curParam].isOptional()) curParam++;
+          while (curParam < paramLength && params[curParam].isOptional()) curParam++;
         }
 
-        if (curParam >= paramLength) break;
+        if (curParam == paramLength) break;
 
         if (params[curParam].isOptional()) {
           if (TypesUtil.isAssignable(params[curParam].getType(), args[curArg], manager, scope) &&
@@ -156,16 +166,182 @@ public class GrClosureSignatureUtil {
   }
 
 
-  public static int getOptionalParamCount(GrClosureSignature signature) {
-    return getOptionalParamCount(signature.getParameters());
+  public static int getOptionalParamCount(GrClosureSignature signature, boolean hasNamedArgs) {
+    return getOptionalParamCount(signature.getParameters(), hasNamedArgs);
   }
 
-  public static int getOptionalParamCount(GrClosureParameter[] parameters) {
-
+  public static int getOptionalParamCount(GrClosureParameter[] parameters, boolean hasNamedArgs) {
     int count = 0;
-    for (GrClosureParameter parameter : parameters) {
+    int i = 0;
+    if (hasNamedArgs) i++;
+    for (; i < parameters.length; i++) {
+      GrClosureParameter parameter = parameters[i];
       if (parameter.isOptional()) count++;
     }
     return count;
   }
+
+  /**
+   * Returns array of lists which contain psiElements mapped to parameters
+   *
+   * @param signature
+   * @param list
+   * @return null if signature can not be applied to this argumentList
+   */
+  @Nullable
+  public static List<PsiElement>[] mapParametersToArguments(GrClosureSignature signature,
+                                                            GrArgumentList list,
+                                                            PsiManager manager,
+                                                            GlobalSearchScope scope) {
+
+    List<PsiElement>[] map = map(signature, list, manager, scope);
+    if (map != null) return map;
+
+    if (signature.isVarargs()) {
+      return new ApplicabilityMapperForVararg(manager, scope, list, signature).map();
+    }
+    return null;
+  }
+
+  @Nullable
+  private static List<PsiElement>[] map(GrClosureSignature signature, GrArgumentList list, PsiManager manager, GlobalSearchScope scope) {
+    final GrExpression[] args = list.getExpressionArguments();
+    final GrNamedArgument[] namedArgs = list.getNamedArguments();
+    boolean hasNamedArgs = namedArgs.length > 0;
+
+
+    GrClosureParameter[] params = signature.getParameters();
+
+    List<PsiElement>[] map = new List[params.length];
+
+    int paramLength = params.length;
+    if (hasNamedArgs) {
+      if (paramLength == 0) return null;
+      PsiType type = params[0].getType();
+      PsiClassType mapType = PsiUtil.createMapType(manager, scope);
+      if (TypesUtil.isAssignable(mapType, type, manager, scope)) {
+        paramLength--;
+        map[0] = Arrays.<PsiElement>asList(namedArgs);
+      }
+      else {
+        return null;
+      }
+    }
+
+    if (args.length > paramLength && !signature.isVarargs()) return null;
+    int optional = getOptionalParamCount(signature, hasNamedArgs);
+    int notOptional = paramLength - optional;
+    if (signature.isVarargs()) notOptional--;
+    if (notOptional > args.length) return null;
+
+    int curParam = 0;
+    optional = args.length - notOptional;
+    if (hasNamedArgs) curParam++;
+    for (int curArg = 0; curArg < args.length; curArg++, curParam++) {
+      while (optional == 0 && curParam < paramLength && params[curParam].isOptional()) {
+        curParam++;
+      }
+      if (curParam == paramLength) return null;
+      if (params[curParam].isOptional()) optional--;
+      if (TypesUtil.isAssignableByMethodCallConversion(params[curParam].getType(), args[curArg].getType(), manager, scope)) {
+        map[curParam] = Collections.<PsiElement>singletonList(args[curArg]);
+      }
+      else {
+        return null;
+      }
+    }
+    return map;
+  }
+
+  private static class ApplicabilityMapperForVararg {
+    private PsiManager manager;
+    private GlobalSearchScope scope;
+    private GrExpression[] args;
+    private PsiType[] types;
+    private GrNamedArgument[] namedArgs;
+    private int paramLength;
+    private GrClosureParameter[] params;
+    private PsiType vararg;
+
+    public ApplicabilityMapperForVararg(PsiManager manager, GlobalSearchScope scope, GrArgumentList list, GrClosureSignature signature) {
+      this.manager = manager;
+      this.scope = scope;
+      args = list.getExpressionArguments();
+      namedArgs = list.getNamedArguments();
+      params = signature.getParameters();
+      paramLength = params.length - 1;
+      vararg = ((PsiArrayType)params[paramLength].getType()).getComponentType();
+
+      types = new PsiType[args.length];
+      for (int i = 0; i < args.length; i++) {
+        types[i] = args[i].getType();
+      }
+    }
+
+    @Nullable
+    public List<PsiElement>[] map() {
+      boolean hasNamedArgs = namedArgs.length > 0;
+      List<PsiElement>[] map = new List[params.length];
+      if (hasNamedArgs) {
+        if (params.length == 0) return null;
+        PsiType type = params[0].getType();
+        PsiClassType mapType = PsiUtil.createMapType(manager, scope);
+        if (TypesUtil.isAssignable(mapType, type, manager, scope)) {
+          map[0] = Arrays.<PsiElement>asList(namedArgs);
+        }
+        else {
+          return null;
+        }
+      }
+      int start = hasNamedArgs ? 1 : 0;
+      int notOptionals = 0;
+      for (int i = start; i < paramLength; i++) {
+        if (!params[i].isOptional()) notOptionals++;
+      }
+      return mapInternal(start, 0, false, notOptionals, map);
+    }
+
+    @Nullable
+    private List<PsiElement>[] mapInternal(int curParam, int curArg, boolean skipOptionals, int notOptional, List<PsiElement>[] map) {
+      if (notOptional > args.length - curArg) return null;
+      if (notOptional == args.length - curArg) skipOptionals = true;
+
+      while (curArg < args.length) {
+        if (skipOptionals) {
+          while (curParam < paramLength && params[curParam].isOptional()) curParam++;
+        }
+
+        if (curParam == paramLength) break;
+
+        if (params[curParam].isOptional()) {
+          if (TypesUtil.isAssignable(params[curParam].getType(), types[curArg], manager, scope)) {
+            List<PsiElement>[] copy = mapInternal(curParam + 1, curArg + 1, false, notOptional, copyMap(map));
+            if (copy != null) return copy;
+          }
+          skipOptionals = true;
+        }
+        else {
+          if (!TypesUtil.isAssignableByMethodCallConversion(params[curParam].getType(), types[curArg], manager, scope)) return null;
+          map[curParam] = Collections.<PsiElement>singletonList(args[curArg]);
+          notOptional--;
+          curArg++;
+          curParam++;
+        }
+      }
+
+      map[paramLength] = new ArrayList<PsiElement>(args.length - curArg);
+
+      for (; curArg < args.length; curArg++) {
+        if (!TypesUtil.isAssignableByMethodCallConversion(vararg, types[curArg], manager, scope)) return null;
+        map[paramLength].add(args[curArg]);
+      }
+      return map;
+    }
+
+    private static List<PsiElement>[] copyMap(List<PsiElement>[] map) {
+      List<PsiElement>[] copy = new List[map.length];
+      System.arraycopy(map, 0, copy, 0, map.length);
+      return copy;
+    }
+  }
 }