diff --git a/codegen/src/main/java/org/web3j/codegen/AbiTypesGenerator.java b/codegen/src/main/java/org/web3j/codegen/AbiTypesGenerator.java index 7b3f55218..bece6ff70 100644 --- a/codegen/src/main/java/org/web3j/codegen/AbiTypesGenerator.java +++ b/codegen/src/main/java/org/web3j/codegen/AbiTypesGenerator.java @@ -49,8 +49,8 @@ private void generate(String destinationDir) throws IOException { // generateFixedTypes(Fixed.class, destinationDir); // generateFixedTypes(Ufixed.class, destinationDir); - generateBytesTypes(Bytes.class, destinationDir); - generateStaticArrayTypes(StaticArray.class, destinationDir); + generateBytesTypes(destinationDir); + generateStaticArrayTypes(destinationDir); } private void generateIntTypes( @@ -145,9 +145,8 @@ private void generateFixedTypes( } } - private void generateBytesTypes( - Class superclass, String path) throws IOException { - String packageName = createPackageName(superclass); + private void generateBytesTypes(String path) throws IOException { + String packageName = createPackageName(Bytes.class); ClassName className; for (int byteSize = 1; byteSize <= 32; byteSize++) { @@ -158,7 +157,7 @@ private void generateBytesTypes( .addStatement("super($L, $N)", byteSize, "value") .build(); - className = ClassName.get(packageName, superclass.getSimpleName() + byteSize); + className = ClassName.get(packageName, Bytes.class.getSimpleName() + byteSize); FieldSpec defaultFieldSpec = FieldSpec .builder(className, DEFAULT, Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) @@ -168,7 +167,7 @@ private void generateBytesTypes( TypeSpec bytesType = TypeSpec .classBuilder(className.simpleName()) .addJavadoc(CODEGEN_WARNING) - .superclass(superclass) + .superclass(Bytes.class) .addModifiers(Modifier.PUBLIC) .addField(defaultFieldSpec) .addMethod(constructorSpec) @@ -178,23 +177,24 @@ private void generateBytesTypes( } } - private void generateStaticArrayTypes( - Class superclass, String path) throws IOException { - String packageName = createPackageName(superclass); + private void generateStaticArrayTypes(String path) throws IOException { + String packageName = createPackageName(StaticArray.class); ClassName className; for (int length = 1; length <= StaticArray.MAX_SIZE_OF_STATIC_ARRAY; length++) { TypeVariableName typeVariableName = TypeVariableName.get("T").withBounds(Type.class); - MethodSpec constructorSpec = MethodSpec.constructorBuilder() + MethodSpec oldConstructorSpec = MethodSpec.constructorBuilder() + .addAnnotation(Deprecated.class) .addModifiers(Modifier.PUBLIC) .addParameter(ParameterizedTypeName.get(ClassName.get(List.class), typeVariableName), "values") .addStatement("super($L, $N)", length, "values") .build(); - MethodSpec arrayOverloadConstructorSpec = MethodSpec.constructorBuilder() + MethodSpec oldArrayOverloadConstructorSpec = MethodSpec.constructorBuilder() + .addAnnotation(Deprecated.class) .addAnnotation(SafeVarargs.class) .addModifiers(Modifier.PUBLIC) .addParameter(ArrayTypeName.of(typeVariableName), "values") @@ -202,15 +202,35 @@ private void generateStaticArrayTypes( .addStatement("super($L, $N)", length, "values") .build(); - className = ClassName.get(packageName, superclass.getSimpleName() + length); + MethodSpec constructorSpec = MethodSpec.constructorBuilder() + .addModifiers(Modifier.PUBLIC) + .addParameter(ParameterizedTypeName.get(ClassName.get(Class.class), + typeVariableName), "type") + .addParameter(ParameterizedTypeName.get(ClassName.get(List.class), + typeVariableName), "values") + .addStatement("super(type, $L, values)", length) + .build(); + + MethodSpec arrayOverloadConstructorSpec = MethodSpec.constructorBuilder() + .addAnnotation(SafeVarargs.class) + .addModifiers(Modifier.PUBLIC) + .addParameter(ParameterizedTypeName.get(ClassName.get(Class.class), + typeVariableName), "type") + .addParameter(ArrayTypeName.of(typeVariableName), "values") + .varargs() + .addStatement("super(type, $L, values)", length) + .build(); + + className = ClassName.get(packageName, StaticArray.class.getSimpleName() + length); TypeSpec bytesType = TypeSpec .classBuilder(className.simpleName()) .addTypeVariable(typeVariableName) .addJavadoc(CODEGEN_WARNING) - .superclass(ParameterizedTypeName.get(ClassName.get(superclass), + .superclass(ParameterizedTypeName.get(ClassName.get(StaticArray.class), typeVariableName)) .addModifiers(Modifier.PUBLIC) + .addMethods(Arrays.asList(oldConstructorSpec, oldArrayOverloadConstructorSpec)) .addMethods(Arrays.asList(constructorSpec, arrayOverloadConstructorSpec)) .build(); diff --git a/codegen/src/main/java/org/web3j/codegen/AbiTypesMapperGenerator.java b/codegen/src/main/java/org/web3j/codegen/AbiTypesMapperGenerator.java index ed8583b4c..b80580757 100644 --- a/codegen/src/main/java/org/web3j/codegen/AbiTypesMapperGenerator.java +++ b/codegen/src/main/java/org/web3j/codegen/AbiTypesMapperGenerator.java @@ -1,6 +1,7 @@ package org.web3j.codegen; import java.io.IOException; + import javax.lang.model.element.Modifier; import com.squareup.javapoet.ClassName; @@ -42,12 +43,35 @@ private void generate(String destinationDir) throws IOException { String typesPackageName = "org.web3j.abi.datatypes"; String autoGeneratedTypesPackageName = typesPackageName + ".generated"; + MethodSpec getTypeSpec = getTypeMethodSpec(typesPackageName, autoGeneratedTypesPackageName); + MethodSpec getTypeAsStringSpec = getTypeAsStringMethodSpec(); + + MethodSpec constructorSpec = MethodSpec.constructorBuilder() + .addModifiers(Modifier.PRIVATE) + .build(); + + TypeSpec typeSpec = TypeSpec + .classBuilder("AbiTypes") + .addJavadoc(buildWarning(AbiTypesMapperGenerator.class)) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addMethod(constructorSpec) + .addMethod(getTypeSpec) + .addMethod(getTypeAsStringSpec) + .build(); + + write(autoGeneratedTypesPackageName, typeSpec, destinationDir); + } + + private MethodSpec getTypeMethodSpec( + final String typesPackageName, + final String autoGeneratedTypesPackageName) { + MethodSpec.Builder builder = MethodSpec.methodBuilder("getType") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addParameter(String.class, TYPE) .returns( ParameterizedTypeName.get(ClassName.get(Class.class), - WildcardTypeName.subtypeOf(Object.class)) + WildcardTypeName.subtypeOf(Type.class)) ) .beginControlFlow("switch (type)"); @@ -58,21 +82,7 @@ private void generate(String destinationDir) throws IOException { "Unsupported type encountered: ", TYPE); builder.endControlFlow(); - MethodSpec methodSpec = builder.build(); - - MethodSpec constructorSpec = MethodSpec.constructorBuilder() - .addModifiers(Modifier.PRIVATE) - .build(); - - TypeSpec typeSpec = TypeSpec - .classBuilder("AbiTypes") - .addJavadoc(buildWarning(AbiTypesMapperGenerator.class)) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addMethod(constructorSpec) - .addMethod(methodSpec) - .build(); - - write(autoGeneratedTypesPackageName, typeSpec, destinationDir); + return builder.build(); } private MethodSpec.Builder addTypes(MethodSpec.Builder builder, String packageName) { @@ -146,4 +156,24 @@ private MethodSpec.Builder addStatement(MethodSpec.Builder builder, String packa "case \"$L\":\nreturn $T.class", typeName, ClassName.get(packageName, className)); } + private MethodSpec getTypeAsStringMethodSpec() { + + String controlCondition = "if ($T.class.equals(" + TYPE + "))"; + + MethodSpec.Builder builder = MethodSpec.methodBuilder("getTypeAString") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .addParameter(ParameterizedTypeName.get(ClassName.get(Class.class), + WildcardTypeName.subtypeOf(Type.class)), TYPE) + .returns(String.class) + .beginControlFlow(controlCondition, Utf8String.class) + .addStatement("return \"string\"") + .nextControlFlow("else " + controlCondition, DynamicBytes.class) + .addStatement("return \"bytes\"") + .nextControlFlow("else") + .addStatement("return type.getSimpleName().toLowerCase()") + .endControlFlow(); + + return builder.build(); + } + } diff --git a/codegen/src/main/java/org/web3j/codegen/SolidityFunctionWrapper.java b/codegen/src/main/java/org/web3j/codegen/SolidityFunctionWrapper.java index 6d36d3c96..98479e614 100644 --- a/codegen/src/main/java/org/web3j/codegen/SolidityFunctionWrapper.java +++ b/codegen/src/main/java/org/web3j/codegen/SolidityFunctionWrapper.java @@ -540,6 +540,7 @@ private String createMappedParameterTypes(ParameterSpec parameterSpec) { String parameterSpecType = parameterSpec.type.toString(); TypeName typeName = typeNames.get(0); String typeMapInput = typeName + ".class"; + String componentType = typeName.toString(); if (typeName instanceof ParameterizedTypeName) { List typeArguments = ((ParameterizedTypeName) typeName).typeArguments; if (typeArguments.size() != 1) { @@ -547,12 +548,13 @@ private String createMappedParameterTypes(ParameterSpec parameterSpec) { "Only a single parameterized type is supported"); } TypeName innerTypeName = typeArguments.get(0); - parameterSpecType = ((ParameterizedTypeName) parameterSpec.type) - .rawType.toString(); - typeMapInput = ((ParameterizedTypeName) typeName).rawType + ".class, " - + innerTypeName + ".class"; + componentType = ((ParameterizedTypeName) typeName).rawType.toString(); + parameterSpecType = ((ParameterizedTypeName) parameterSpec.type).rawType + + "<" + componentType + ">"; + typeMapInput = componentType + ".class,\n" + innerTypeName + ".class"; } return "new " + parameterSpecType + "(\n" + + " " + componentType + ".class,\n" + " org.web3j.abi.Utils.typeMap(" + parameterSpec.name + ", " + typeMapInput + "))"; } @@ -868,7 +870,7 @@ MethodSpec buildEventFlowableFunction( Strings.lowercaseFirstLetter(functionName) + "EventFlowable"; ParameterizedTypeName parameterizedTypeName = ParameterizedTypeName.get(ClassName.get(Flowable.class), - ClassName.get("", responseClassName)); + ClassName.get("", responseClassName)); MethodSpec.Builder flowableMethodBuilder = MethodSpec.methodBuilder(generatedFunctionName) @@ -912,7 +914,7 @@ MethodSpec buildDefaultEventFlowableFunction( Strings.lowercaseFirstLetter(functionName) + "EventFlowable"; ParameterizedTypeName parameterizedTypeName = ParameterizedTypeName.get(ClassName.get(Flowable.class), - ClassName.get("", responseClassName)); + ClassName.get("", responseClassName)); MethodSpec.Builder flowableMethodBuilder = MethodSpec.methodBuilder(generatedFunctionName) diff --git a/codegen/src/test/java/org/web3j/codegen/SolidityFunctionWrapperTest.java b/codegen/src/test/java/org/web3j/codegen/SolidityFunctionWrapperTest.java index e54fdc1c6..7b9134089 100644 --- a/codegen/src/test/java/org/web3j/codegen/SolidityFunctionWrapperTest.java +++ b/codegen/src/test/java/org/web3j/codegen/SolidityFunctionWrapperTest.java @@ -31,7 +31,6 @@ import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsEqual.equalTo; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -254,7 +253,7 @@ public void testBuildFunctionConstantSingleValueRawListReturn() throws Exception AbiDefinition functionDefinition = new AbiDefinition( true, Arrays.asList( - new AbiDefinition.NamedType("param", "uint8")), + new AbiDefinition.NamedType("param", "uint8")), "functionName", Arrays.asList( new AbiDefinition.NamedType("result", "address[]")), @@ -266,19 +265,94 @@ public void testBuildFunctionConstantSingleValueRawListReturn() throws Exception //CHECKSTYLE:OFF String expected = "public org.web3j.protocol.core.RemoteCall functionName(java.math.BigInteger param) {\n" - + " final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_FUNCTIONNAME, \n" - + " java.util.Arrays.asList(new org.web3j.abi.datatypes.generated.Uint8(param)), \n" - + " java.util.Arrays.>asList(new org.web3j.abi.TypeReference>() {}));\n" - + " return new org.web3j.protocol.core.RemoteCall(\n" - + " new java.util.concurrent.Callable() {\n" - + " @java.lang.Override\n" - + " @java.lang.SuppressWarnings(\"unchecked\")\n" - + " public java.util.List call() throws java.lang.Exception {\n" - + " java.util.List result = (java.util.List) executeCallSingleValueReturn(function, java.util.List.class);\n" - + " return convertToNative(result);\n" - + " }\n" - + " });\n" - + "}\n"; + + " final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_FUNCTIONNAME, \n" + + " java.util.Arrays.asList(new org.web3j.abi.datatypes.generated.Uint8(param)), \n" + + " java.util.Arrays.>asList(new org.web3j.abi.TypeReference>() {}));\n" + + " return new org.web3j.protocol.core.RemoteCall(\n" + + " new java.util.concurrent.Callable() {\n" + + " @java.lang.Override\n" + + " @java.lang.SuppressWarnings(\"unchecked\")\n" + + " public java.util.List call() throws java.lang.Exception {\n" + + " java.util.List result = (java.util.List) executeCallSingleValueReturn(function, java.util.List.class);\n" + + " return convertToNative(result);\n" + + " }\n" + + " });\n" + + "}\n"; + //CHECKSTYLE:ON + + assertThat(methodSpec.toString(), is(expected)); + } + + @Test + public void testBuildFunctionConstantDynamicArrayRawListReturn() throws Exception { + AbiDefinition functionDefinition = new AbiDefinition( + true, + Arrays.asList( + new AbiDefinition.NamedType("param", "uint8[]")), + "functionName", + Arrays.asList( + new AbiDefinition.NamedType("result", "address[]")), + "type", + false); + + MethodSpec methodSpec = solidityFunctionWrapper.buildFunction(functionDefinition); + + //CHECKSTYLE:OFF + String expected = + "public org.web3j.protocol.core.RemoteCall functionName(java.util.List param) {\n" + + " final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_FUNCTIONNAME, \n" + + " java.util.Arrays.asList(new org.web3j.abi.datatypes.DynamicArray(\n" + + " org.web3j.abi.datatypes.generated.Uint8.class,\n" + + " org.web3j.abi.Utils.typeMap(param, org.web3j.abi.datatypes.generated.Uint8.class))), \n" + + " java.util.Arrays.>asList(new org.web3j.abi.TypeReference>() {}));\n" + + " return new org.web3j.protocol.core.RemoteCall(\n" + + " new java.util.concurrent.Callable() {\n" + + " @java.lang.Override\n" + + " @java.lang.SuppressWarnings(\"unchecked\")\n" + + " public java.util.List call() throws java.lang.Exception {\n" + + " java.util.List result = (java.util.List) executeCallSingleValueReturn(function, java.util.List.class);\n" + + " return convertToNative(result);\n" + + " }\n" + + " });\n" + + "}\n"; + //CHECKSTYLE:ON + + assertThat(methodSpec.toString(), is(expected)); + } + + @Test + public void testBuildFunctionConstantMultiDynamicArrayRawListReturn() throws Exception { + AbiDefinition functionDefinition = new AbiDefinition( + true, + Arrays.asList( + new AbiDefinition.NamedType("param", "uint8[][]")), + "functionName", + Arrays.asList( + new AbiDefinition.NamedType("result", "address[]")), + "type", + false); + + MethodSpec methodSpec = solidityFunctionWrapper.buildFunction(functionDefinition); + + //CHECKSTYLE:OFF + String expected = + "public org.web3j.protocol.core.RemoteCall functionName(java.util.List> param) {\n" + + " final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_FUNCTIONNAME, \n" + + " java.util.Arrays.asList(new org.web3j.abi.datatypes.DynamicArray(\n" + + " org.web3j.abi.datatypes.DynamicArray.class,\n" + + " org.web3j.abi.Utils.typeMap(param, org.web3j.abi.datatypes.DynamicArray.class,\n" + + " org.web3j.abi.datatypes.generated.Uint8.class))), \n" + + " java.util.Arrays.>asList(new org.web3j.abi.TypeReference>() {}));\n" + + " return new org.web3j.protocol.core.RemoteCall(\n" + + " new java.util.concurrent.Callable() {\n" + + " @java.lang.Override\n" + + " @java.lang.SuppressWarnings(\"unchecked\")\n" + + " public java.util.List call() throws java.lang.Exception {\n" + + " java.util.List result = (java.util.List) executeCallSingleValueReturn(function, java.util.List.class);\n" + + " return convertToNative(result);\n" + + " }\n" + + " });\n" + + "}\n"; //CHECKSTYLE:ON assertThat(methodSpec.toString(), is(expected)); @@ -300,8 +374,8 @@ public void testBuildFunctionConstantInvalid() throws Exception { //CHECKSTYLE:OFF String expected = "public void functionName(java.math.BigInteger param) {\n" - + " throw new RuntimeException(\"cannot call constant function with void return type\");\n" - + "}\n"; + + " throw new RuntimeException(\"cannot call constant function with void return type\");\n" + + "}\n"; //CHECKSTYLE:ON assertThat(methodSpec.toString(), is(expected)); @@ -447,7 +521,6 @@ public void testBuildFuncNameConstants() throws Exception { builder.addFields(solidityFunctionWrapper .buildFuncNameConstants(Collections.singletonList(functionDefinition))); - //CHECKSTYLE:OFF String expected = "class testClass {\n" +