Commits

Tim Vernum committed 1c0b0f3

Infer class parameters from lambda method arguments

  • Participants
  • Parent commits d78f959

Comments (0)

Files changed (9)

File convert/source/java/main/org/adjective/syntactic/convert/j7to8/ASTMethodInfo.java

     private final ASTMethodDeclaration _method;
     private final TypeParameterLookup _parameterLookup;
 
-    public ASTMethodInfo(final ASTMethodDeclaration method, final TypeParameterLookup parameterLookup)
+    public ASTMethodInfo(final ASTMethodDeclaration method, final TypeParameterLookup lookup)
     {
         _method = method;
-        _parameterLookup = parameterLookup;
+        _parameterLookup = lookup;
     }
 
     private JavaType getType(final JavaType type)
         }
     }
 
+    private JavaType[] getTypes(final JavaType[] in)
+    {
+        JavaType[] out = new JavaType[in.length];
+        for (int i = 0; i < in.length; i++)
+        {
+            out[i] = getType(in[i]);
+        }
+        return out;
+    }
+
     @Override
     public JavaType getReturnType()
     {
     @Override
     public JavaType[] getParameterTypes()
     {
-        return _method.getParameters().getParameterTypes();
+        return getTypes(_method.getParameters().getParameterTypes());
     }
 
     @Override
     }
 
     @Override
+    public Integer[] getClassTypeParameters()
+    {
+        final JavaType[] types = _method.getParameters().getParameterTypes();
+        final Integer[] vals = new Integer[types.length];
+
+        for (int i = 0; i < vals.length; i++)
+        {
+            final int lookup = _parameterLookup.findParameterIndex(types[i].getTypeName());
+            if (lookup != -1)
+            {
+                vals[i] = lookup;
+            }
+        }
+
+        return vals;
+    }
+
+    @Override
     public boolean isVarArgs()
     {
         return _method.getParameters().isVarArgs();

File convert/source/java/main/org/adjective/syntactic/convert/j7to8/ASTTypeInfo.java

     @Override
     public JavaType findParameter(final String name)
     {
+        Integer index = findParameterIndex(name);
+        if (index == null)
+        {
+            return null;
+        }
+        else
+        {
+            return _parameters[index].getType();
+        }
+    }
+
+    @Override
+    public Integer findParameterIndex(final String name)
+    {
         int i = 0;
         for (ASTTypeParameter parameter : _declaration.getTypeParameters().getParameters())
         {
             if (parameter.getIdentifier().getIdentifier().equals(name))
             {
-                return _parameters[i].getType();
+                return i;
             }
             i++;
         }
         return null;
     }
+
 }

File convert/source/java/main/org/adjective/syntactic/convert/j7to8/ClassInfo.java

     @Override
     public JavaType findParameter(String name)
     {
+        final Integer index = findParameterIndex(name);
+        return index == null ? null : _parameters[index].getType();
+    }
+
+    @Override
+    public Integer findParameterIndex(final String name)
+    {
         final TypeVariable<? extends Class<?>>[] typeParameters = _cls.getTypeParameters();
         for (int i = 0; i < typeParameters.length; i++)
         {
             TypeVariable<? extends Class<?>> typeParameter = typeParameters[i];
             if (typeParameter.getName().equals(name))
             {
-                return _parameters[i].getType();
+                return i;
             }
         }
         return null;

File convert/source/java/main/org/adjective/syntactic/convert/j7to8/ClassMethodInfo.java

     }
 
     @Override
+    public Integer[] getClassTypeParameters()
+    {
+        final Type[] genericTypes = _method.getGenericParameterTypes();
+        final Integer[] index = new Integer[genericTypes.length];
+        for (int i = 0; i < index.length; i++)
+        {
+            Type parameter = genericTypes[i];
+            if (parameter instanceof TypeVariable)
+            {
+                final TypeVariable var = (TypeVariable) parameter;
+                index[i] = _parameterLookup.findParameterIndex(var.getName());
+            }
+        }
+        return index;
+    }
+
+    @Override
     public boolean isVarArgs()
     {
         return _method.isVarArgs();

File convert/source/java/main/org/adjective/syntactic/convert/j7to8/Convert7To8Visitor.java

 
     private ExpressionNode convertLambda(final ASTLambdaExpression lambda, final JavaType forType)
     {
-        final ModifierSet set = new ModifierSet();
-        set.set(ModifierSet.Modifier.PUBLIC);
+        final ModifierSet set = new ModifierSet(ModifierSet.Modifier.PUBLIC);
         final ASTModifiers modifiers = new ASTModifiers(set);
-        final ASTMethodDeclaration method = makeMethodDeclaration(forType, lambda.getParameters(), lambda.getBody());
-        final ASTMember member = new ASTMember(modifiers, method);
+        final TypeInfo typeInfo = getTypeInfo(forType);
+        final MethodInfo methodInfo = getFunctionalMethod(typeInfo);
+        final ASTMethodDeclaration astMethod = makeMethodDeclaration(methodInfo,
+                                                                     lambda.getParameters(),
+                                                                     lambda.getBody());
+        final ASTMember member = new ASTMember(modifiers, astMethod);
         ASTClassOrInterfaceBody body = new ASTClassOrInterfaceBody(new ASTClassOrInterfaceBodyElement(member));
-        return new ASTAllocationExpression(withoutWildcard(forType), new ASTArguments(), body);
+        final JavaType astType = substituteType(astMethod.getParameters(), methodInfo, forType);
+        return new ASTAllocationExpression(withoutWildcard(astType), new ASTArguments(), body);
+    }
+
+    private JavaType substituteType(final ASTFormalParameters parameters, final MethodInfo method, final JavaType type)
+    {
+        final Integer[] typeParameters = method.getClassTypeParameters();
+        if (isNull(typeParameters))
+        {
+            return type;
+        }
+        final ParameterizedName lastName = last(type.getParameterizedTypeName());
+        TypeParameter[] classParameters = Arrays.copyOf(lastName.getParameters(), lastName.getParameters().length);
+        for (int i = 0; i < typeParameters.length; i++)
+        {
+            final Integer classIndex = typeParameters[i];
+            if (classIndex != null)
+            {
+                classParameters[classIndex] = new SimpleTypeParameter(TypeParameter.Kind.EXACT,
+                                                                     parameters.getParameterTypes()[i]);
+            }
+        }
+
+        ParameterizedName[] name = new ParameterizedName[type.getParameterizedTypeName().length];
+        System.arraycopy(type.getParameterizedTypeName(), 0, name, 0, name.length - 1);
+        name[name.length - 1] = new SimpleParameterizedName(lastName.getName(), classParameters);
+        return new SimpleJavaType(name, type.getArrayDepth());
+    }
+
+    private boolean isNull(final Object[] array)
+    {
+        for (Object o : array)
+        {
+            if(o != null) {
+                return false;
+            }
+        }
+        return true;
     }
 
     private JavaType withoutWildcard(final JavaType forType)
         return false;
     }
 
-    private ASTMethodDeclaration makeMethodDeclaration(final JavaType forType, final ASTLambdaParameters parameters,
-                                                       final ASTLambdaBody lambdaBody)
+    private ASTMethodDeclaration makeMethodDeclaration(final MethodInfo method, final ASTLambdaParameters parameters, final ASTLambdaBody lambdaBody)
+    {
+        final ASTMethodBody body = getMethodBody(lambdaBody);
+        final ASTNameList exceptions = method.getExceptionTypes().length == 0 ? null
+                : new ASTNameList(method.getExceptionTypes());
+        return new ASTMethodDeclaration(method.getReturnType(),
+                                        new ASTIdentifier(method.getName()),
+                                        makeFormalParameters(method, parameters),
+                                        new ASTArraySuffixList(),
+                                        exceptions,
+                                        body);
+    }
+
+    private MethodInfo getFunctionalMethod(final TypeInfo typeInfo)
+    {
+        final Collection<MethodInfo> methods = filterMethods(typeInfo.getMethods());
+        if (methods.size() != 1)
+        {
+            throw new ConversionException("Class '" + typeInfo.getName() + "' should have exactly 1 method but has " + methods
+                    .size());
+        }
+
+        return methods.iterator().next();
+    }
+
+    private TypeInfo getTypeInfo(final JavaType forType)
     {
         String name = forType.getTypeName();
         if (forType.getParameterizedTypeName().length == 1)
         {
             throw new ConversionException("Cannot find '" + name + "'");
         }
-        final Collection<MethodInfo> methods = filterMethods(typeInfo.getMethods());
-        if (methods.size() != 1)
-        {
-            throw new ConversionException("Class '" + typeInfo.getName() + "' should have exactly 1 method but has " + methods
-                    .size());
-        }
-
-        MethodInfo method = methods.iterator().next();
-
-        final ASTMethodBody body = getMethodBody(lambdaBody);
-        return makeMethodDeclaration(method, parameters, body);
+        return typeInfo;
     }
 
     private ASTMethodBody getMethodBody(final ASTLambdaBody lambdaBody)
         return cls.getName().equals(type.getTypeName());
     }
 
-    private ASTMethodDeclaration makeMethodDeclaration(final MethodInfo method, final ASTLambdaParameters parameters, final ASTMethodBody body)
-    {
-        final ASTNameList exceptions = method.getExceptionTypes().length == 0 ? null : new ASTNameList(method.getExceptionTypes());
-        return new ASTMethodDeclaration(method.getReturnType(),
-                                        new ASTIdentifier(method.getName()),
-                                        makeFormalParameters(method, parameters),
-                                        new ASTArraySuffixList(),
-                                        exceptions,
-                                        body);
-    }
-
     private ASTFormalParameters makeFormalParameters(final MethodInfo method, final ASTLambdaParameters parameters)
     {
         final JavaType[] parameterTypes;

File convert/source/java/main/org/adjective/syntactic/convert/j7to8/MethodInfo.java

     public String getName();
     public JavaType[] getParameterTypes();
     public String[] getParameterNames();
+
+    /**
+     * @return An array of integers representing, for each of the parameters of this method, the zero-based index of the enclosing class's type parameters are represented by that method parameter (or null if the method parameter is not a class type parameter)
+     * e.g. For the declaration
+     * <pre>interface C&lt;S,T&gt; {
+     *   void f( String s, T t );
+     * }</pre>
+     * This method would return <code>{ null, 1 }</code>
+     */
+    public Integer[] getClassTypeParameters();
     public boolean isVarArgs();
     public JavaType[] getExceptionTypes();
 }

File convert/source/java/main/org/adjective/syntactic/convert/j7to8/TypeParameterLookup.java

 
 public interface TypeParameterLookup {
     JavaType findParameter(String name);
+    Integer findParameterIndex(String name);
 }

File convert/source/java/samples/sample/GenericLambda.java

     public static void main(String[] args)
     {
         final Transformer<? super Long, CharSequence> transformer = (Number n) -> n.toString();
-        transform(Collections.singletonList(99L),
-                  transformer
-        );
+        transform(Collections.singletonList(99L), transformer);
     }
 
 }

File parser/source/java/parser/org/adjective/syntactic/parser/util/ModifierSet.java

  */
 package org.adjective.syntactic.parser.util;
 
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.EnumSet;
 
 public class ModifierSet
         PUBLIC, PROTECTED, PRIVATE, ABSTRACT, STATIC, FINAL, SYNCHRONIZED, NATIVE, TRANSIENT, VOLATILE, STRICTFP;
     }
 
-    private EnumSet<Modifier> _modifiers;
+    private final EnumSet<Modifier> _modifiers;
+
+    public ModifierSet()
+    {
+        _modifiers = EnumSet.noneOf(Modifier.class);
+    }
 
     public ModifierSet(ModifierSet modifiers)
     {
         _modifiers = EnumSet.copyOf(modifiers._modifiers);
     }
 
-    public ModifierSet()
+    public ModifierSet(final Modifier... modifiers)
     {
-        _modifiers = EnumSet.noneOf(Modifier.class);
+        this(Arrays.asList(modifiers));
+    }
+
+    public ModifierSet(Collection<Modifier> modifiers)
+    {
+        _modifiers = EnumSet.copyOf(modifiers);
     }
 
     public void set(Modifier modifier)