From 25869d139c15116ab38ab05af1569a895385a471 Mon Sep 17 00:00:00 2001 From: Ivan Savytskyi Date: Sat, 4 Feb 2017 01:57:59 -0500 Subject: [PATCH 1/5] Refactor Mapper to use factory --- .../android/api/graphql/Field.java | 127 +++- .../api/graphql/ResponseFieldMapper.java | 2 +- .../FragmentsResponseMapperBuilder.kt | 86 +++ .../compiler/ResponseFieldMapperBuilder.kt | 336 ---------- .../SchemaTypeResponseMapperBuilder.kt | 402 +++++++++++ .../android/compiler/SchemaTypeSpecBuilder.kt | 45 +- .../apollographql/android/compiler/Util.kt | 63 +- .../com/example/all_planets/TestQuery.json | 115 ++++ .../TestQueryExpected.java | 2 + .../TestQueryExpected.java | 2 + .../TestQueryExpected.java | 8 + .../TestQueryExpected.java | 156 +++-- .../HeroDetailsExpected.java | 566 +++++++++------- .../TestQueryExpected.java | 196 +++--- .../products_with_dates/TestQuery.json | 68 ++ .../com/example/scalar_types/TestQuery.json | 7 - .../scalar_types/TestQueryExpected.java | 5 +- .../simple_fragment/TestQueryExpected.java | 2 + .../unique_type_name/TestQueryExpected.java | 2 + .../android/compiler/CodegenTest.kt | 4 + .../pojo/ApolloConverterFactory.java | 25 +- .../pojo/ApolloResponseBodyConverter.java | 35 +- .../pojo/BufferedResponseReader.java | 56 +- .../pojo/ResponseJsonStreamReader.java | 31 +- .../android/converter/pojo/AllPlanets.java | 624 ++++++++++++++---- .../android/converter/pojo/FilmFragment.java | 65 -- .../converter/pojo/IntegrationTest.java | 3 + .../converter/pojo/PlanetFragment.java | 74 --- .../converter/pojo/ProductsWithDate.java | 407 +++++++++--- .../converter/pojo/fragment/FilmFragment.java | 106 +++ .../pojo/fragment/PlanetFragment.java | 125 ++++ .../converter/pojo/type/CustomType.java | 14 + .../ApolloPluginBasicAndroidTest.groovy | 4 +- 33 files changed, 2494 insertions(+), 1269 deletions(-) create mode 100644 apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/FragmentsResponseMapperBuilder.kt delete mode 100644 apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/ResponseFieldMapperBuilder.kt create mode 100644 apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/SchemaTypeResponseMapperBuilder.kt create mode 100644 apollo-compiler/src/test/graphql/com/example/all_planets/TestQuery.json create mode 100644 apollo-compiler/src/test/graphql/com/example/products_with_dates/TestQuery.json delete mode 100644 apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/FilmFragment.java delete mode 100644 apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/PlanetFragment.java create mode 100644 apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/fragment/FilmFragment.java create mode 100644 apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/fragment/PlanetFragment.java create mode 100644 apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/type/CustomType.java diff --git a/apollo-api/src/main/java/com/apollographql/android/api/graphql/Field.java b/apollo-api/src/main/java/com/apollographql/android/api/graphql/Field.java index 1904eeb4110..aec8ada1698 100644 --- a/apollo-api/src/main/java/com/apollographql/android/api/graphql/Field.java +++ b/apollo-api/src/main/java/com/apollographql/android/api/graphql/Field.java @@ -4,72 +4,70 @@ import java.util.Collections; import java.util.Map; -/** TODO */ -public final class Field { +/** TODO add javadocs */ +public class Field { private final Type type; private final String responseName; private final String fieldName; private final Map arguments; - private final ObjectReader objectReader; - private final ListReader listReader; private final boolean optional; - private final ScalarType scalarType; public static Field forString(String responseName, String fieldName, Map arguments, boolean optional) { - return new Field(Type.STRING, responseName, fieldName, arguments, null, null, optional, null); + return new Field(Type.STRING, responseName, fieldName, arguments, optional); } public static Field forInt(String responseName, String fieldName, Map arguments, boolean optional) { - return new Field(Type.INT, responseName, fieldName, arguments, null, null, optional, null); + return new Field(Type.INT, responseName, fieldName, arguments, optional); } public static Field forLong(String responseName, String fieldName, Map arguments, boolean optional) { - return new Field(Type.LONG, responseName, fieldName, arguments, null, null, optional, null); + return new Field(Type.LONG, responseName, fieldName, arguments, optional); } public static Field forDouble(String responseName, String fieldName, Map arguments, boolean optional) { - return new Field(Type.DOUBLE, responseName, fieldName, arguments, null, null, optional, null); + return new Field(Type.DOUBLE, responseName, fieldName, arguments, optional); } public static Field forBoolean(String responseName, String fieldName, Map arguments, boolean optional) { - return new Field(Type.BOOLEAN, responseName, fieldName, arguments, null, null, optional, null); + return new Field(Type.BOOLEAN, responseName, fieldName, arguments, optional); } public static Field forObject(String responseName, String fieldName, Map arguments, boolean optional, ObjectReader objectReader) { - return new Field(Type.OBJECT, responseName, fieldName, arguments, objectReader, null, optional, null); + return new ObjectField(responseName, fieldName, arguments, optional, objectReader); } public static Field forList(String responseName, String fieldName, Map arguments, boolean optional, ListReader listReader) { - return new Field(Type.LIST, responseName, fieldName, arguments, null, listReader, optional, null); + return new ScalarListField(responseName, fieldName, arguments, optional, listReader); } public static Field forList(String responseName, String fieldName, Map arguments, boolean optional, ObjectReader objectReader) { - return new Field(Type.LIST, responseName, fieldName, arguments, objectReader, null, optional, null); + return new ObjectListField(responseName, fieldName, arguments, optional, objectReader); } public static Field forCustomType(String responseName, String fieldName, Map arguments, boolean optional, ScalarType scalarType) { - return new Field(Type.CUSTOM, responseName, fieldName, arguments, null, null, optional, scalarType); + return new CustomTypeField(responseName, fieldName, arguments, optional, scalarType); } - private Field(Type type, String responseName, String fieldName, Map arguments, - ObjectReader objectReader, ListReader listReader, boolean optional, ScalarType scalarType) { + public static Field forConditionalType(String responseName, String fieldName, + ConditionalTypeReader conditionalTypeReader) { + return new ConditionalTypeField(responseName, fieldName, conditionalTypeReader); + } + + private Field(Type type, String responseName, String fieldName, Map arguments, boolean optional) { this.type = type; this.responseName = responseName; this.fieldName = fieldName; this.arguments = arguments == null ? Collections.emptyMap() : Collections.unmodifiableMap(arguments); - this.objectReader = objectReader; - this.listReader = listReader; this.optional = optional; - this.scalarType = scalarType; } public Type type() { @@ -88,22 +86,10 @@ public Map arguments() { return arguments; } - public ObjectReader objectReader() { - return objectReader; - } - public boolean optional() { return optional; } - public ListReader listReader() { - return listReader; - } - - public ScalarType scalarType() { - return scalarType; - } - public static enum Type { STRING, INT, @@ -111,8 +97,10 @@ public static enum Type { DOUBLE, BOOLEAN, OBJECT, - LIST, - CUSTOM + SCALAR_LIST, + OBJECT_LIST, + CUSTOM, + CONDITIONAL } public interface ObjectReader { @@ -123,6 +111,10 @@ public interface ListReader { T read(ListItemReader reader) throws IOException; } + public interface ConditionalTypeReader { + T read(String conditionalType, ResponseReader reader) throws IOException; + } + public interface ListItemReader { String readString() throws IOException; @@ -137,4 +129,73 @@ public interface ListItemReader { T readCustomType(ScalarType scalarType) throws IOException; } + + public static final class ObjectField extends Field { + private final ObjectReader objectReader; + + ObjectField(String responseName, String fieldName, Map arguments, boolean optional, + ObjectReader objectReader) { + super(Type.OBJECT, responseName, fieldName, arguments, optional); + this.objectReader = objectReader; + } + + public ObjectReader objectReader() { + return objectReader; + } + } + + public static final class ScalarListField extends Field { + private final ListReader listReader; + + ScalarListField(String responseName, String fieldName, Map arguments, boolean optional, + ListReader listReader) { + super(Type.SCALAR_LIST, responseName, fieldName, arguments, optional); + this.listReader = listReader; + } + + public ListReader listReader() { + return listReader; + } + } + + public static final class ObjectListField extends Field { + private final ObjectReader objectReader; + + ObjectListField(String responseName, String fieldName, Map arguments, boolean optional, + ObjectReader objectReader) { + super(Type.OBJECT_LIST, responseName, fieldName, arguments, optional); + this.objectReader = objectReader; + } + + public ObjectReader objectReader() { + return objectReader; + } + } + + public static final class CustomTypeField extends Field { + private final ScalarType scalarType; + + CustomTypeField(String responseName, String fieldName, Map arguments, boolean optional, + ScalarType scalarType) { + super(Type.CUSTOM, responseName, fieldName, arguments, optional); + this.scalarType = scalarType; + } + + public ScalarType scalarType() { + return scalarType; + } + } + + public static final class ConditionalTypeField extends Field { + private final ConditionalTypeReader conditionalTypeReader; + + ConditionalTypeField(String responseName, String fieldName, ConditionalTypeReader conditionalTypeReader) { + super(Type.CONDITIONAL, responseName, fieldName, null, false); + this.conditionalTypeReader = conditionalTypeReader; + } + + public ConditionalTypeReader conditionalTypeReader() { + return conditionalTypeReader; + } + } } diff --git a/apollo-api/src/main/java/com/apollographql/android/api/graphql/ResponseFieldMapper.java b/apollo-api/src/main/java/com/apollographql/android/api/graphql/ResponseFieldMapper.java index e4841c0c3af..6006a365aed 100644 --- a/apollo-api/src/main/java/com/apollographql/android/api/graphql/ResponseFieldMapper.java +++ b/apollo-api/src/main/java/com/apollographql/android/api/graphql/ResponseFieldMapper.java @@ -4,5 +4,5 @@ /** TODO */ public interface ResponseFieldMapper { - void map(final ResponseReader responseReader, final T instance) throws IOException; + T map(final ResponseReader responseReader) throws IOException; } diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/FragmentsResponseMapperBuilder.kt b/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/FragmentsResponseMapperBuilder.kt new file mode 100644 index 00000000000..d73c7d7e957 --- /dev/null +++ b/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/FragmentsResponseMapperBuilder.kt @@ -0,0 +1,86 @@ +package com.apollographql.android.compiler + +import com.apollographql.android.api.graphql.ResponseFieldMapper +import com.apollographql.android.api.graphql.ResponseReader +import com.apollographql.android.compiler.ir.CodeGenerationContext +import com.squareup.javapoet.* +import java.io.IOException +import javax.annotation.Nonnull +import javax.lang.model.element.Modifier + +class FragmentsResponseMapperBuilder( + val fragments: List, + val codeGenerationContext: CodeGenerationContext +) { + fun build(): TypeSpec { + val contentValueFields = fragments.map { + FieldSpec.builder(ClassName.get(codeGenerationContext.fragmentsPackage, it.capitalize()), it.toLowerCase()) + .build() + } + return TypeSpec.classBuilder("Mapper") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + .addSuperinterface(RESPONSE_FIELD_MAPPER_TYPE) + .addMethod(constructor()) + .addField(FACTORY_FIELD) + .addField(FieldSpec.builder(CONDITIONAL_TYPE_PARAM.type, CONDITIONAL_TYPE_PARAM.name).build()) + .addMethod(mapMethod(contentValueFields)) + .build() + } + + private fun constructor(): MethodSpec = + MethodSpec.constructorBuilder() + .addModifiers(Modifier.PUBLIC) + .addParameter(FACTORY_PARAM) + .addParameter(CONDITIONAL_TYPE_PARAM) + .addStatement("this.\$L = \$L", FACTORY_PARAM.name, FACTORY_PARAM.name) + .addStatement("this.\$L = \$L", CONDITIONAL_TYPE_PARAM.name, CONDITIONAL_TYPE_PARAM.name) + .build() + + private fun mapMethod(contentValueFields: List) = + MethodSpec.methodBuilder("map") + .addModifiers(Modifier.PUBLIC) + .addAnnotation(Override::class.java) + .addParameter(READER_PARAM) + .addException(IOException::class.java) + .returns(SchemaTypeSpecBuilder.FRAGMENTS_TYPE) + .addCode(mapMethodCode(contentValueFields)) + .build() + + private fun mapMethodCode(contentValueFields: List) = + CodeBlock.builder() + .add(contentValueFields + .fold(CodeBlock.builder()) { builder, field -> builder.addStatement("\$T \$N = null", field.type, field) } + .build()) + .add(contentValueFields + .fold(CodeBlock.builder()) { builder, field -> + builder + .beginControlFlow("if (\$L.equals(\$T.TYPE_CONDITION))", CONDITIONAL_TYPE_VAR, field.type) + .addStatement("\$N = new \$T.Mapper(\$N.\$L\$L()).map(\$N)", field, field.type, FACTORY_PARAM, + (field.type as ClassName).simpleName().decapitalize(), Util.FACTORY_TYPE_NAME, + READER_PARAM) + .endControlFlow() + }.build()) + .add("return \$L.\$L().\$L(", FACTORY_VAR, Util.FACTORY_CREATOR_ACCESS_METHOD_NAME, + Util.CREATOR_CREATE_METHOD_NAME) + .add(contentValueFields + .mapIndexed { i, fieldSpec -> CodeBlock.of("\$L\$L", if (i > 0) ", " else "", fieldSpec.name) } + .fold(CodeBlock.builder(), CodeBlock.Builder::add) + .build()) + .add(");\n") + .build() + + companion object { + private val API_RESPONSE_FIELD_MAPPER_TYPE = ClassName.get(ResponseFieldMapper::class.java) + private val RESPONSE_FIELD_MAPPER_TYPE = ParameterizedTypeName.get(API_RESPONSE_FIELD_MAPPER_TYPE, + SchemaTypeSpecBuilder.FRAGMENTS_TYPE) + private val FACTORY_VAR = Util.FACTORY_TYPE_NAME.decapitalize() + private val FACTORY_PARAM = ParameterSpec.builder(Util.FACTORY_INTERFACE_TYPE, FACTORY_VAR) + .addAnnotation(Nonnull::class.java).build() + private val CONDITIONAL_TYPE_VAR = "conditionalType" + private val CONDITIONAL_TYPE_PARAM = ParameterSpec.builder(String::class.java, CONDITIONAL_TYPE_VAR) + .addAnnotation(Nonnull::class.java).build() + private val FACTORY_FIELD = FieldSpec.builder(Util.FACTORY_INTERFACE_TYPE, Util.FACTORY_TYPE_NAME.decapitalize(), + Modifier.FINAL).build() + private val READER_PARAM = ParameterSpec.builder(ResponseReader::class.java, "reader").build() + } +} \ No newline at end of file diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/ResponseFieldMapperBuilder.kt b/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/ResponseFieldMapperBuilder.kt deleted file mode 100644 index 8040b0ec2ab..00000000000 --- a/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/ResponseFieldMapperBuilder.kt +++ /dev/null @@ -1,336 +0,0 @@ -package com.apollographql.android.compiler - -import com.apollographql.android.api.graphql.ResponseFieldMapper -import com.apollographql.android.api.graphql.ResponseReader -import com.apollographql.android.compiler.ir.CodeGenerationContext -import com.apollographql.android.compiler.ir.Field -import com.apollographql.android.compiler.ir.InlineFragment -import com.apollographql.android.compiler.ir.TypeDeclaration -import com.squareup.javapoet.* -import java.io.IOException -import javax.lang.model.element.Modifier - -class ResponseFieldMapperBuilder( - typeName: String, - val fields: List, - val fragmentSpreads: List, - val inlineFragments: List, - val typeOverrideMap: Map, - val context: CodeGenerationContext -) { - private val typeClassName = ClassName.get("", typeName) - private val hasFragments = inlineFragments.isNotEmpty() || fragmentSpreads.isNotEmpty() - private val responseFieldMapperType = ParameterizedTypeName.get(API_RESPONSE_FIELD_MAPPER, typeClassName) - - fun build(): FieldSpec = FieldSpec - .builder(responseFieldMapperType, "MAPPER") - .addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL) - .initializer("\$L", mapperType()) - .build() - - private fun mapperType() = TypeSpec - .anonymousClassBuilder("") - .superclass(responseFieldMapperType) - .addField(fieldArrayField()) - .addMethod(mapMethod()) - .build() - - private fun fieldArrayField() = FieldSpec - .builder(Array::class.java, "FIELDS", Modifier.PRIVATE, - Modifier.FINAL) - .initializer(CodeBlock - .builder() - .add("{\n") - .indent() - .add(responseFieldFactoryStatements()) - .unindent() - .add("\n}") - .build() - ) - .build() - - private fun mapMethod() = MethodSpec - .methodBuilder("map") - .addModifiers(Modifier.PUBLIC) - .addAnnotation(Override::class.java) - .addParameter(PARAM_SPEC_READER) - .addParameter(ParameterSpec.builder(typeClassName, PARAM_INSTANCE, Modifier.FINAL).build()) - .addException(IOException::class.java) - .addCode(mapMethodCode()) - .build() - - private fun mapMethodCode() = CodeBlock - .builder() - .add("\$L.read(", PARAM_READER) - .add("\$L", valueHandlerType()) - .add(", FIELDS);\n") - .build() - - private fun valueHandlerType() = TypeSpec - .anonymousClassBuilder("") - .superclass(ResponseReader.ValueHandler::class.java) - .addMethod(valueHandlerHandleMethod()) - .build() - - private fun valueHandlerHandleMethod() = MethodSpec - .methodBuilder("handle") - .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC) - .addParameter(TypeName.INT, PARAM_FIELD_INDEX, Modifier.FINAL) - .addParameter(TypeName.OBJECT, PARAM_VALUE, Modifier.FINAL) - .addException(IOException::class.java) - .addCode(CodeBlock - .builder() - .beginControlFlow("switch (\$L)", PARAM_FIELD_INDEX) - .add(fields - .mapIndexed { i, field -> fieldValueCaseStatement(field, i) } - .fold(CodeBlock.builder(), CodeBlock.Builder::add) - .build()) - .add(valueHandlerFragmentsCaseStatement(fields.size)) - .endControlFlow() - .build()) - .build() - - - private fun fieldValueCaseStatement(field: Field, index: Int): CodeBlock { - val fieldSpec = field.fieldSpec(context.customTypeMap) - val fieldRawType = fieldSpec.type.withoutAnnotations().overrideTypeName(typeOverrideMap) - return CodeBlock.builder() - .beginControlFlow("case $index:") - .add( - if (fieldRawType.isEnum()) { - CodeBlock - .builder() - .beginControlFlow("if (\$L != null)", PARAM_VALUE) - .addStatement("\$L.\$L = \$T.valueOf(\$L)", PARAM_INSTANCE, fieldSpec.name, fieldRawType, PARAM_VALUE) - .endControlFlow() - .build() - } else { - CodeBlock.of("\$L.\$L = (\$T) \$L;\n", PARAM_INSTANCE, fieldSpec.name, fieldRawType, PARAM_VALUE) - }) - .addStatement("break") - .endControlFlow() - .build() - } - - private fun valueHandlerFragmentsCaseStatement(index: Int): CodeBlock { - if (hasFragments) { - return CodeBlock - .builder() - .beginControlFlow("case $index:") - .addStatement("\$T \$L = (\$T) \$L", String::class.java, PARAM_TYPE_NAME, String::class.java, PARAM_VALUE) - .add(inlineFragments - .map { inlineFragmentInitStatement(it) } - .fold(CodeBlock.builder(), CodeBlock.Builder::add) - .build()) - .add(fragmentsInitStatement()) - .addStatement("break") - .endControlFlow() - .build() - } else { - return CodeBlock.of("") - } - } - - private fun inlineFragmentInitStatement(fragment: InlineFragment): CodeBlock { - val fieldSpec = fragment.fieldSpec() - val fieldRawType = fieldSpec.type.withoutAnnotations() - return CodeBlock.builder() - .beginControlFlow("if (\$L.equals(\$S))", PARAM_TYPE_NAME, fragment.typeCondition) - .addStatement("\$L.\$L = new \$T(\$L)", PARAM_INSTANCE, fieldSpec.name, fieldRawType, PARAM_READER) - .endControlFlow() - .build() - } - - private fun fragmentsInitStatement(): CodeBlock { - if (fragmentSpreads.isNotEmpty()) { - return CodeBlock.builder() - .addStatement("\$L.\$L = new \$L(\$L, \$L)", PARAM_INSTANCE, - SchemaTypeSpecBuilder.FRAGMENTS_INTERFACE_NAME.decapitalize(), - SchemaTypeSpecBuilder.FRAGMENTS_INTERFACE_NAME, PARAM_READER, PARAM_TYPE_NAME) - .build() - } else { - return CodeBlock.of("") - } - } - - private fun responseFieldFactoryStatements(): CodeBlock { - val typeNameFieldStatement = if (hasFragments) { - CodeBlock.of("\$T.forString(\$S, \$S, null, false)", com.apollographql.android.api.graphql.Field::class.java, - "__typename", - "__typename") - } else { - CodeBlock.of("") - } - return fields - .map { it.responseFieldFactoryStatement() } - .plus(typeNameFieldStatement) - .filter { !it.isEmpty } - .foldIndexed(CodeBlock.builder()) { i, builder, code -> - builder - .let { if (i > 0) it.add(",\n") else it } - .add(code) - } - .build() - } - - private fun Field.responseFieldFactoryStatement(): CodeBlock { - val fieldTypeName = fieldSpec(context.customTypeMap).type.withoutAnnotations() - if (fieldTypeName.isScalar() || fieldTypeName.isCustomScalarType()) { - return scalarResponseFieldFactoryStatement(fieldTypeName) - } else if (fieldTypeName.isList()) { - return listResponseFieldFactoryStatement(fieldTypeName) - } else { - return objectResponseFieldFactoryStatement("forObject", fieldTypeName) - } - } - - private fun fieldFactoryMethod(type: TypeName) = when (type) { - ClassNames.STRING -> "forString" - TypeName.INT, TypeName.INT.box() -> "forInt" - TypeName.LONG, TypeName.LONG.box() -> "forLong" - TypeName.DOUBLE, TypeName.DOUBLE.box() -> "forDouble" - TypeName.BOOLEAN, TypeName.BOOLEAN.box() -> "forBoolean" - else -> { - if (type.isEnum()) { - "forString" - } else if (type.isList()) { - "forList" - } else { - "forObject" - } - } - } - - private fun TypeName.isList() = - (this is ParameterizedTypeName && rawType == ClassNames.LIST) - - private fun TypeName.isEnum() = - ((this is ClassName) && context.typeDeclarations.count { it.kind == "EnumType" && it.name == simpleName() } > 0) - - private fun TypeName.isCustomScalarType() = - context.customTypeMap.containsValue(toString()) - - private fun TypeName.isScalar() = (SCALAR_TYPES.contains(this) || isEnum()) - - private fun TypeName.listParamType() = - (this as ParameterizedTypeName) - .typeArguments - .first() - .let { if (it is WildcardTypeName) it.upperBounds.first() else it } - - private fun Field.scalarResponseFieldFactoryStatement(type: TypeName): CodeBlock { - if (type.isCustomScalarType()) { - val customScalarEnum = CustomEnumTypeSpecBuilder.className(context) - val customScalarEnumConst = normalizeGraphQlType(this.type).toUpperCase() - return CodeBlock.of("\$T.forCustomType(\$S, \$S, null, \$L, \$T.\$L)", API_RESPONSE_FIELD, responseName, - fieldName, isOptional(), customScalarEnum, customScalarEnumConst) - } else { - val factoryMethod = fieldFactoryMethod(type) - return CodeBlock.of("\$T.\$L(\$S, \$S, null, \$L)", API_RESPONSE_FIELD, factoryMethod, responseName, - fieldName, isOptional()) - } - } - - private fun Field.objectResponseFieldFactoryStatement(factoryMethod: String, type: TypeName) = CodeBlock - .builder() - .add("\$T.$factoryMethod(\$S, \$S, null, \$L, new \$T() {\n", API_RESPONSE_FIELD, responseName, - fieldName, isOptional(), apiResponseFieldReaderTypeName(type.overrideTypeName(typeOverrideMap))) - .indent() - .beginControlFlow("@Override public \$T read(final \$T \$L) throws \$T", type.overrideTypeName(typeOverrideMap), - ClassNames.API_RESPONSE_READER, PARAM_READER, IOException::class.java) - .add(CodeBlock.of("return new \$T(\$L);\n", type.overrideTypeName(typeOverrideMap), PARAM_READER)) - .endControlFlow() - .unindent() - .add("})") - .build() - - private fun Field.listResponseFieldFactoryStatement(type: TypeName): CodeBlock { - val rawFieldType = type.let { if (it.isList()) it.listParamType() else it } - return CodeBlock - .builder() - .add( - if (rawFieldType.isCustomScalarType()) { - readCustomListItemStatement(rawFieldType) - } else if (rawFieldType.isScalar()) { - readScalarListItemStatement(rawFieldType) - } else { - objectResponseFieldFactoryStatement("forList", rawFieldType) - }) - .build() - } - - private fun apiResponseFieldReaderTypeName(type: TypeName) = - ParameterizedTypeName.get(API_RESPONSE_FIELD_OBJECT_READER, type.overrideTypeName(typeOverrideMap)) - - private fun apiResponseFieldListItemReaderTypeName(type: TypeName) = - ParameterizedTypeName.get(API_RESPONSE_FIELD_LIST_READER, type.overrideTypeName(typeOverrideMap)) - - private fun Field.readScalarListItemStatement(type: TypeName): CodeBlock { - val readMethod = when (type) { - ClassNames.STRING -> "readString()" - TypeName.INT, TypeName.INT.box() -> "readInt()" - TypeName.LONG, TypeName.LONG.box() -> "readLong()" - TypeName.DOUBLE, TypeName.DOUBLE.box() -> "readDouble()" - TypeName.BOOLEAN, TypeName.BOOLEAN.box() -> "readBoolean()" - else -> "readString()" - } - - val readStatement = if (type.isEnum()) - CodeBlock.of("return \$T.valueOf(\$L.\$L);\n", type.overrideTypeName(typeOverrideMap), PARAM_READER, readMethod) - else - CodeBlock.of("return \$L.\$L;\n", PARAM_READER, readMethod) - - return CodeBlock - .builder() - .add("\$T.forList(\$S, \$S, null, \$L, new \$T() {\n", API_RESPONSE_FIELD, responseName, fieldName, - isOptional(), apiResponseFieldListItemReaderTypeName(type.overrideTypeName(typeOverrideMap))) - .indent() - .beginControlFlow("@Override public \$T read(final \$T \$L) throws \$T", type.overrideTypeName(typeOverrideMap), - API_RESPONSE_FIELD_LIST_ITEM_READER, PARAM_READER, IOException::class.java) - .add(readStatement) - .endControlFlow() - .unindent() - .add("})") - .build() - } - - private fun Field.readCustomListItemStatement(type: TypeName): CodeBlock { - val customScalarEnum = CustomEnumTypeSpecBuilder.className(context) - val customScalarEnumConst = normalizeGraphQlType(this.type).toUpperCase() - return CodeBlock - .builder() - .add("\$T.forList(\$S, \$S, null, \$L, new \$T() {\n", API_RESPONSE_FIELD, responseName, fieldName, - isOptional(), apiResponseFieldListItemReaderTypeName(type.overrideTypeName(typeOverrideMap))) - .indent() - .beginControlFlow("@Override public \$T read(final \$T \$L) throws \$T", type.overrideTypeName(typeOverrideMap), - API_RESPONSE_FIELD_LIST_ITEM_READER, PARAM_READER, IOException::class.java) - .add(CodeBlock.of("return \$L.readCustomType(\$T.\$L);\n", PARAM_READER, customScalarEnum, customScalarEnumConst)) - .endControlFlow() - .unindent() - .add("})") - .build() - } - - companion object { - private val PARAM_READER = "reader" - private val PARAM_INSTANCE = "instance" - private val PARAM_TYPE_NAME = "typename" - private val PARAM_FIELD_INDEX = "fieldIndex" - private val PARAM_VALUE = "value" - private val PARAM_SPEC_READER = ParameterSpec.builder(ResponseReader::class.java, PARAM_READER, - Modifier.FINAL).build() - private val SCALAR_TYPES = listOf(ClassNames.STRING, TypeName.INT, TypeName.INT.box(), TypeName.LONG, - TypeName.LONG.box(), TypeName.DOUBLE, TypeName.DOUBLE.box(), TypeName.BOOLEAN, TypeName.BOOLEAN.box()) - private val API_RESPONSE_FIELD = ClassName.get(com.apollographql.android.api.graphql.Field::class.java) - private val API_RESPONSE_FIELD_OBJECT_READER = ClassName.get( - com.apollographql.android.api.graphql.Field.ObjectReader::class.java) - private val API_RESPONSE_FIELD_LIST_READER = ClassName.get( - com.apollographql.android.api.graphql.Field.ListReader::class.java) - private val API_RESPONSE_FIELD_LIST_ITEM_READER = ClassName.get( - com.apollographql.android.api.graphql.Field.ListItemReader::class.java) - private val API_RESPONSE_FIELD_MAPPER = ClassName.get(ResponseFieldMapper::class.java) - private fun normalizeGraphQlType(type: String) = - type.removeSuffix("!").removeSurrounding(prefix = "[", suffix = "]").removeSuffix("!") - } -} diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/SchemaTypeResponseMapperBuilder.kt b/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/SchemaTypeResponseMapperBuilder.kt new file mode 100644 index 00000000000..218ac51d127 --- /dev/null +++ b/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/SchemaTypeResponseMapperBuilder.kt @@ -0,0 +1,402 @@ +package com.apollographql.android.compiler + +import com.apollographql.android.api.graphql.ResponseFieldMapper +import com.apollographql.android.api.graphql.ResponseReader +import com.apollographql.android.compiler.ir.CodeGenerationContext +import com.apollographql.android.compiler.ir.Field +import com.apollographql.android.compiler.ir.InlineFragment +import com.squareup.javapoet.* +import java.io.IOException +import javax.annotation.Nonnull +import javax.lang.model.element.Modifier + +class SchemaTypeResponseMapperBuilder( + typeName: String, + val fields: List, + val fragmentSpreads: List, + val inlineFragments: List, + val typeOverrideMap: Map, + val context: CodeGenerationContext +) { + private val typeClassName = ClassName.get("", typeName) + private val hasFragments = inlineFragments.isNotEmpty() || fragmentSpreads.isNotEmpty() + private val responseFieldMapperType = ParameterizedTypeName.get(API_RESPONSE_FIELD_MAPPER_TYPE, typeClassName) + + fun build(): TypeSpec { + val contentValueFields = fields + .map { it.fieldSpec(context.customTypeMap) } + .map { FieldSpec.builder(it.type.overrideTypeName(typeOverrideMap), it.name).build() } + .plus(inlineFragments + .map { it.fieldSpec() } + .map { FieldSpec.builder(it.type.overrideTypeName(typeOverrideMap), it.name).build() }) + .let { + if (fragmentSpreads.isNotEmpty()) { + it.plus(FRAGMENTS_FIELD) + } else { + it + } + } + return TypeSpec.classBuilder("Mapper") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + .addSuperinterface(responseFieldMapperType) + .addMethod(constructor()) + .addType(contentValuesType(contentValueFields)) + .addField(factoryField()) + .addField(fieldArrayField()) + .addMethod(mapMethod(contentValueFields)) + .build() + } + + private fun factoryField() = + FieldSpec.builder(Util.FACTORY_INTERFACE_TYPE, FACTORY_VAR, Modifier.FINAL).build() + + private fun constructor(): MethodSpec = + MethodSpec.constructorBuilder() + .addModifiers(Modifier.PUBLIC) + .addParameter( + ParameterSpec.builder(Util.FACTORY_INTERFACE_TYPE, "factory") + .addAnnotation(Nonnull::class.java) + .build()) + .addStatement("this.\$L = \$L", FACTORY_VAR, "factory") + .build() + + private fun contentValuesType(contentValueFields: List) = + TypeSpec.classBuilder(CONTENT_VALUES_TYPE) + .addModifiers(Modifier.STATIC, Modifier.FINAL) + .addFields(contentValueFields) + .build() + + private fun fieldArrayField() = + FieldSpec.builder(Array::class.java, FIELDS_VAR) + .addModifiers(Modifier.FINAL) + .initializer(CodeBlock.builder() + .add("{\n") + .indent() + .add(fieldFactoryStatements() + .filter { !it.isEmpty } + .foldIndexed(CodeBlock.builder()) { i, builder, code -> + builder.add(if (i > 0) ",\n" else "").add(code) + } + .build()) + .unindent() + .add("\n}") + .build() + ) + .build() + + private fun mapMethod(contentValueFields: List) = + MethodSpec.methodBuilder("map") + .addModifiers(Modifier.PUBLIC) + .addAnnotation(Override::class.java) + .addParameter(READER_PARAM) + .addException(IOException::class.java) + .returns(typeClassName) + .addCode(mapMethodCode(contentValueFields)) + .build() + + private fun mapMethodCode(contentValueFields: List) = + CodeBlock.builder() + .addStatement("final \$T \$L = new \$T()", CONTENT_VALUES_TYPE, CONTENT_VALUES_VAR, CONTENT_VALUES_TYPE) + .add("\$L\$L.read(", READER_PARAM.name, if (hasFragments) ".toBufferedReader()" else "") + .add("\$L", TypeSpec.anonymousClassBuilder("") + .superclass(ResponseReader.ValueHandler::class.java) + .addMethod(valueHandleMethod(contentValueFields)) + .build()) + .add(", \$L);\n", FIELDS_VAR) + .add("return \$L.\$L().\$L(", FACTORY_VAR, Util.FACTORY_CREATOR_ACCESS_METHOD_NAME, + Util.CREATOR_CREATE_METHOD_NAME) + .add(contentValueFields + .mapIndexed { i, fieldSpec -> + CodeBlock.of("\$L\$L.\$L", if (i > 0) ", " else "", CONTENT_VALUES_VAR, fieldSpec.name) + } + .fold(CodeBlock.builder(), CodeBlock.Builder::add) + .build()) + .add(");\n") + .build() + + private fun valueHandleMethod(contentValueFields: List) = + MethodSpec.methodBuilder("handle") + .addAnnotation(Override::class.java) + .addModifiers(Modifier.PUBLIC) + .addParameter(TypeName.INT, FIELD_INDEX_PARAM, Modifier.FINAL) + .addParameter(TypeName.OBJECT, VALUE_PARAM, Modifier.FINAL) + .addException(IOException::class.java) + .addCode(valueHandleSwitchCode(contentValueFields)) + .build() + + private fun valueHandleSwitchCode(contentValueFields: List) = + CodeBlock.builder() + .beginControlFlow("switch (\$L)", FIELD_INDEX_PARAM) + .add(contentValueFields + .mapIndexed { i, field -> fieldValueCaseStatement(field, i) } + .fold(CodeBlock.builder(), CodeBlock.Builder::add) + .build()) + .endControlFlow() + .build() + + private fun fieldValueCaseStatement(fieldSpec: FieldSpec, index: Int): CodeBlock { + val fieldRawType = fieldSpec.type.withoutAnnotations() + val setContentValueCode = if (fieldSpec.type.isEnum()) { + CodeBlock.builder() + .beginControlFlow("if (\$L != null)", VALUE_PARAM) + .addStatement("\$L.\$L = \$T.valueOf(\$L)", CONTENT_VALUES_VAR, fieldSpec.name, fieldRawType, VALUE_PARAM) + .endControlFlow() + .build() + } else { + CodeBlock.of("\$L.\$L = (\$T) \$L;\n", CONTENT_VALUES_VAR, fieldSpec.name, fieldRawType, VALUE_PARAM) + } + return CodeBlock.builder() + .beginControlFlow("case $index:") + .add(setContentValueCode) + .addStatement("break") + .endControlFlow() + .build() + } + + private fun fieldFactoryStatements() = + fields.map { it.responseFieldFactoryStatement() } + .plus(inlineFragments.map { it.fieldFactoryStatement(it.fieldSpec().type.withoutAnnotations()) }) + .plus(if (fragmentSpreads.isNotEmpty()) fragmentsFieldFactory() else CodeBlock.of("")) + + private fun Field.responseFieldFactoryStatement(): CodeBlock { + val fieldTypeName = fieldSpec(context.customTypeMap).type.withoutAnnotations() + if (fieldTypeName.isScalar() || fieldTypeName.isCustomScalarType()) { + return scalarResponseFieldFactoryStatement(fieldTypeName) + } else if (fieldTypeName.isList()) { + return listResponseFieldFactoryStatement(fieldTypeName) + } else { + return objectResponseFieldFactoryStatement("forObject", fieldTypeName.overrideTypeName(typeOverrideMap)) + } + } + + private fun fieldFactoryMethod(type: TypeName) = when (type) { + ClassNames.STRING -> "forString" + TypeName.INT, TypeName.INT.box() -> "forInt" + TypeName.LONG, TypeName.LONG.box() -> "forLong" + TypeName.DOUBLE, TypeName.DOUBLE.box() -> "forDouble" + TypeName.BOOLEAN, TypeName.BOOLEAN.box() -> "forBoolean" + else -> { + if (type.isEnum()) { + "forString" + } else if (type.isList()) { + "forList" + } else { + "forObject" + } + } + } + + private fun TypeName.isList() = + (this is ParameterizedTypeName && rawType == ClassNames.LIST) + + private fun TypeName.isEnum() = + ((this is ClassName) && context.typeDeclarations.count { it.kind == "EnumType" && it.name == simpleName() } > 0) + + private fun TypeName.isCustomScalarType() = + context.customTypeMap.containsValue(toString()) + + private fun TypeName.isScalar() = (SCALAR_TYPES.contains(this) || isEnum()) + + private fun TypeName.listParamType() = + (this as ParameterizedTypeName) + .typeArguments + .first() + .let { if (it is WildcardTypeName) it.upperBounds.first() else it } + + private fun Field.scalarResponseFieldFactoryStatement(type: TypeName): CodeBlock { + if (type.isCustomScalarType()) { + val customScalarEnum = CustomEnumTypeSpecBuilder.className(context) + val customScalarEnumConst = normalizeGraphQlType(this.type).toUpperCase() + return CodeBlock.of("\$T.forCustomType(\$S, \$S, null, \$L, \$T.\$L)", API_RESPONSE_FIELD_TYPE, responseName, + fieldName, isOptional(), customScalarEnum, customScalarEnumConst) + } else { + val factoryMethod = fieldFactoryMethod(type) + return CodeBlock.of("\$T.\$L(\$S, \$S, null, \$L)", API_RESPONSE_FIELD_TYPE, factoryMethod, responseName, + fieldName, isOptional()) + } + } + + private fun Field.objectResponseFieldFactoryStatement(factoryMethod: String, type: TypeName): CodeBlock { + val typeFactoryMethod = if (type is ParameterizedTypeName) { + val typeArgument = type.typeArguments[0] + if (typeArgument is WildcardTypeName) { + (typeArgument.upperBounds[0] as ClassName).simpleName().decapitalize() + } else { + (typeArgument as ClassName).simpleName().decapitalize() + } + } else { + (type as ClassName).simpleName().decapitalize() + } + "Factory" + return CodeBlock.builder() + .add("\$T.$factoryMethod(\$S, \$S, null, \$L, new \$T() {\n", API_RESPONSE_FIELD_TYPE, responseName, + fieldName, isOptional(), apiResponseFieldObjectReaderTypeName(type)) + .indent() + .beginControlFlow("@Override public \$T read(final \$T \$L) throws \$T", type, + ClassNames.API_RESPONSE_READER, READER_PARAM.name, IOException::class.java) + .add(CodeBlock.of("return new \$T.Mapper(\$L.\$L()).map(\$L);\n", type, FACTORY_VAR, typeFactoryMethod, + READER_PARAM.name)) + .endControlFlow() + .unindent() + .add("})") + .build() + } + + private fun Field.listResponseFieldFactoryStatement(type: TypeName): CodeBlock { + val rawFieldType = type.let { if (it.isList()) it.listParamType() else it } + return CodeBlock + .builder() + .add( + if (rawFieldType.isCustomScalarType()) { + readCustomListItemStatement(rawFieldType) + } else if (rawFieldType.isScalar()) { + readScalarListItemStatement(rawFieldType) + } else { + objectResponseFieldFactoryStatement("forList", rawFieldType.overrideTypeName(typeOverrideMap)) + }) + .build() + } + + private fun InlineFragment.fieldFactoryStatement(type: TypeName): CodeBlock { + fun readCodeBlock(): CodeBlock { + return CodeBlock.builder() + .beginControlFlow("if (\$L.equals(\$S))", CONDITIONAL_TYPE_VAR, typeCondition) + .add(CodeBlock.of("return new \$T.Mapper(\$L.\$LFactory()).map(\$L);\n", type, + FACTORY_VAR, (type as ClassName).simpleName().decapitalize(), READER_PARAM.name)) + .nextControlFlow("else") + .addStatement("return null") + .endControlFlow() + .build() + } + + fun conditionalFieldReaderType() = + TypeSpec.anonymousClassBuilder("") + .superclass(apiConditionalFieldReaderTypeName(type)) + .addMethod(MethodSpec + .methodBuilder("read") + .addModifiers(Modifier.PUBLIC) + .addAnnotation(Override::class.java) + .addException(IOException::class.java) + .returns(type) + .addParameter(ParameterSpec.builder(String::class.java, CONDITIONAL_TYPE_VAR).build()) + .addParameter(READER_PARAM) + .addCode(readCodeBlock()) + .build()) + .build() + + return CodeBlock.of("\$T.forConditionalType(\$S, \$S, \$L)", API_RESPONSE_FIELD_TYPE, "__typename", "__typename", + conditionalFieldReaderType()) + } + + private fun fragmentsFieldFactory(): CodeBlock { + fun readCodeBlock(): CodeBlock { + return CodeBlock.builder() + .add(CodeBlock.of("return new Fragments.Mapper(\$L.fragmentsFactory(), \$L).map(\$L);\n", FACTORY_VAR, + CONDITIONAL_TYPE_VAR, READER_PARAM.name)) + .build() + } + + fun conditionalFieldReaderType() = + TypeSpec.anonymousClassBuilder("") + .superclass(apiConditionalFieldReaderTypeName(ClassName.get("", "Fragments"))) + .addMethod(MethodSpec + .methodBuilder("read") + .addModifiers(Modifier.PUBLIC) + .addAnnotation(Override::class.java) + .addException(IOException::class.java) + .returns(ClassName.get("", "Fragments")) + .addParameter(ParameterSpec.builder(String::class.java, CONDITIONAL_TYPE_VAR).build()) + .addParameter(READER_PARAM) + .addCode(readCodeBlock()) + .build()) + .build() + + return CodeBlock.of("\$T.forConditionalType(\$S, \$S, \$L)", API_RESPONSE_FIELD_TYPE, "__typename", "__typename", + conditionalFieldReaderType()) + } + + private fun apiResponseFieldObjectReaderTypeName(type: TypeName) = + ParameterizedTypeName.get(API_RESPONSE_FIELD_OBJECT_READER_TYPE, type) + + private fun apiResponseFieldListItemReaderTypeName(type: TypeName) = + ParameterizedTypeName.get(API_RESPONSE_FIELD_LIST_READER_TYPE, type) + + private fun apiConditionalFieldReaderTypeName(type: TypeName) = + ParameterizedTypeName.get(API_RESPONSE_FIELD_CONDITIONAL_TYPE_READER_TYPE, type) + + + private fun Field.readScalarListItemStatement(type: TypeName): CodeBlock { + val readMethod = when (type) { + ClassNames.STRING -> "readString()" + TypeName.INT, TypeName.INT.box() -> "readInt()" + TypeName.LONG, TypeName.LONG.box() -> "readLong()" + TypeName.DOUBLE, TypeName.DOUBLE.box() -> "readDouble()" + TypeName.BOOLEAN, TypeName.BOOLEAN.box() -> "readBoolean()" + else -> "readString()" + } + + val readStatement = if (type.isEnum()) + CodeBlock.of("return \$T.valueOf(\$L.\$L);\n", type.overrideTypeName(typeOverrideMap), READER_PARAM.name, + readMethod) + else + CodeBlock.of("return \$L.\$L;\n", READER_PARAM.name, readMethod) + + return CodeBlock + .builder() + .add("\$T.forList(\$S, \$S, null, \$L, new \$T() {\n", API_RESPONSE_FIELD_TYPE, responseName, fieldName, + isOptional(), apiResponseFieldListItemReaderTypeName(type.overrideTypeName(typeOverrideMap))) + .indent() + .beginControlFlow("@Override public \$T read(final \$T \$L) throws \$T", type.overrideTypeName(typeOverrideMap), + API_RESPONSE_FIELD_LIST_ITEM_READER_TYPE, READER_PARAM.name, IOException::class.java) + .add(readStatement) + .endControlFlow() + .unindent() + .add("})") + .build() + } + + private fun Field.readCustomListItemStatement(type: TypeName): CodeBlock { + val customScalarEnum = CustomEnumTypeSpecBuilder.className(context) + val customScalarEnumConst = normalizeGraphQlType(this.type).toUpperCase() + return CodeBlock + .builder() + .add("\$T.forList(\$S, \$S, null, \$L, new \$T() {\n", API_RESPONSE_FIELD_TYPE, responseName, fieldName, + isOptional(), apiResponseFieldListItemReaderTypeName(type.overrideTypeName(typeOverrideMap))) + .indent() + .beginControlFlow("@Override public \$T read(final \$T \$L) throws \$T", type.overrideTypeName(typeOverrideMap), + API_RESPONSE_FIELD_LIST_ITEM_READER_TYPE, READER_PARAM.name, IOException::class.java) + .add(CodeBlock.of("return \$L.readCustomType(\$T.\$L);\n", READER_PARAM.name, customScalarEnum, + customScalarEnumConst)) + .endControlFlow() + .unindent() + .add("})") + .build() + } + + companion object { + private val CONTENT_VALUES_TYPE = ClassName.get("", "__ContentValues") + private val CONTENT_VALUES_VAR = "contentValues" + private val FACTORY_VAR = Util.FACTORY_TYPE_NAME.decapitalize() + private val FIELDS_VAR = "fields" + private val CONDITIONAL_TYPE_VAR = "conditionalType" + private val FIELD_INDEX_PARAM = "fieldIndex" + private val VALUE_PARAM = "value" + private val READER_PARAM = ParameterSpec.builder(ResponseReader::class.java, "reader").build() + private val SCALAR_TYPES = listOf(ClassNames.STRING, TypeName.INT, TypeName.INT.box(), TypeName.LONG, + TypeName.LONG.box(), TypeName.DOUBLE, TypeName.DOUBLE.box(), TypeName.BOOLEAN, TypeName.BOOLEAN.box()) + private val API_RESPONSE_FIELD_TYPE = ClassName.get(com.apollographql.android.api.graphql.Field::class.java) + private val API_RESPONSE_FIELD_OBJECT_READER_TYPE = ClassName.get( + com.apollographql.android.api.graphql.Field.ObjectReader::class.java) + private val API_RESPONSE_FIELD_LIST_READER_TYPE = ClassName.get( + com.apollographql.android.api.graphql.Field.ListReader::class.java) + private val API_RESPONSE_FIELD_LIST_ITEM_READER_TYPE = ClassName.get( + com.apollographql.android.api.graphql.Field.ListItemReader::class.java) + private val API_RESPONSE_FIELD_MAPPER_TYPE = ClassName.get(ResponseFieldMapper::class.java) + private val API_RESPONSE_FIELD_CONDITIONAL_TYPE_READER_TYPE = ClassName.get( + com.apollographql.android.api.graphql.Field.ConditionalTypeReader::class.java) + private val FRAGMENTS_FIELD = FieldSpec.builder(ClassName.get("", SchemaTypeSpecBuilder.FRAGMENTS_TYPE_NAME), + SchemaTypeSpecBuilder.FRAGMENTS_TYPE_NAME.decapitalize()).build() + + private fun normalizeGraphQlType(type: String) = + type.removeSuffix("!").removeSurrounding(prefix = "[", suffix = "]").removeSuffix("!") + } +} diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/SchemaTypeSpecBuilder.kt b/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/SchemaTypeSpecBuilder.kt index 02d641ace40..b021517019b 100644 --- a/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/SchemaTypeSpecBuilder.kt +++ b/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/SchemaTypeSpecBuilder.kt @@ -4,7 +4,6 @@ import com.apollographql.android.compiler.ir.CodeGenerationContext import com.apollographql.android.compiler.ir.Field import com.apollographql.android.compiler.ir.InlineFragment import com.squareup.javapoet.* -import java.io.IOException import javax.lang.model.element.Modifier class SchemaTypeSpecBuilder( @@ -16,25 +15,14 @@ class SchemaTypeSpecBuilder( ) { private val uniqueTypeName = formatUniqueTypeName(typeName, context.reservedTypeNames) private val innerTypeNameOverrideMap = buildUniqueTypeNameMap(context.reservedTypeNames + typeName) - private val hasFragments = inlineFragments.isNotEmpty() || fragmentSpreads.isNotEmpty() fun build(vararg modifiers: Modifier): TypeSpec { + val mapper = SchemaTypeResponseMapperBuilder(uniqueTypeName, fields, fragmentSpreads, inlineFragments, + innerTypeNameOverrideMap, context).build() val typeSpecBuilder = if (context.abstractType) { TypeSpec.interfaceBuilder(uniqueTypeName) } else { - val mapperField = ResponseFieldMapperBuilder(uniqueTypeName, fields, fragmentSpreads, inlineFragments, - innerTypeNameOverrideMap, context).build() TypeSpec.classBuilder(uniqueTypeName) - .addField(mapperField) - .addMethod(MethodSpec - .constructorBuilder() - .addModifiers(Modifier.PUBLIC) - .addParameter(PARAM_SPEC_READER) - .addException(IOException::class.java) - .addStatement("\$L.map(\$L, this)", mapperField.name, - if (hasFragments) "$PARAM_READER.toBufferedReader()" else PARAM_READER) - .build() - ) } return typeSpecBuilder .addModifiers(*modifiers) @@ -53,6 +41,9 @@ class SchemaTypeSpecBuilder( .withValueInitConstructor() .withCreatorImplementation() .withFactoryImplementation() + .toBuilder() + .addType(mapper) + .build() } } @@ -102,18 +93,18 @@ class SchemaTypeSpecBuilder( private fun fragmentsAccessorMethodSpec(abstract: Boolean): MethodSpec { val methodSpecBuilder = MethodSpec - .methodBuilder(FRAGMENTS_INTERFACE_NAME.decapitalize()) - .returns(ClassName.get("", FRAGMENTS_INTERFACE_NAME)) + .methodBuilder(FRAGMENTS_TYPE_NAME.decapitalize()) + .returns(ClassName.get("", FRAGMENTS_TYPE_NAME)) .addModifiers(Modifier.PUBLIC) .addModifiers(if (abstract) listOf(Modifier.ABSTRACT) else emptyList()) if (!abstract) { - methodSpecBuilder.addCode(CodeBlock.of("return this.${FRAGMENTS_INTERFACE_NAME.toLowerCase()};\n")) + methodSpecBuilder.addCode(CodeBlock.of("return this.${FRAGMENTS_TYPE_NAME.toLowerCase()};\n")) } return methodSpecBuilder.build() } private fun fragmentsFieldSpec(): FieldSpec = FieldSpec - .builder(ClassName.get("", FRAGMENTS_INTERFACE_NAME.capitalize()), FRAGMENTS_INTERFACE_NAME.decapitalize()) + .builder(ClassName.get("", FRAGMENTS_TYPE_NAME.capitalize()), FRAGMENTS_TYPE_NAME.decapitalize()) .addModifiers(Modifier.PRIVATE) .build() @@ -146,18 +137,18 @@ class SchemaTypeSpecBuilder( }) } + val mapper = FragmentsResponseMapperBuilder(fragments, context).build() val typeSpecBuilder = if (abstractClass) { - TypeSpec.interfaceBuilder(FRAGMENTS_INTERFACE_NAME) + TypeSpec.interfaceBuilder(FRAGMENTS_TYPE_NAME) } else { - TypeSpec.classBuilder(FRAGMENTS_INTERFACE_NAME) - .addMethod(SchemaFragmentsConstructorBuilder(fragmentSpreads).build()) + TypeSpec.classBuilder(FRAGMENTS_TYPE_NAME) } return typeSpecBuilder .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addFragmentFields() .addFragmentAccessorMethods() .build() - .withFactory() + .withFactory(fragments) .withCreator() .let { if (context.abstractType) @@ -166,7 +157,10 @@ class SchemaTypeSpecBuilder( it .withValueInitConstructor() .withCreatorImplementation() - .withFactoryImplementation() + .withFactoryImplementation(fragments) + .toBuilder() + .addType(mapper) + .build() } } @@ -181,8 +175,7 @@ class SchemaTypeSpecBuilder( } companion object { - val FRAGMENTS_INTERFACE_NAME: String = "Fragments" - private val PARAM_READER = "reader" - private val PARAM_SPEC_READER = ParameterSpec.builder(ClassNames.API_RESPONSE_READER, PARAM_READER).build() + val FRAGMENTS_TYPE_NAME: String = "Fragments" + val FRAGMENTS_TYPE: ClassName = ClassName.get("", SchemaTypeSpecBuilder.FRAGMENTS_TYPE_NAME) } } diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/Util.kt b/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/Util.kt index 2a3c2e770ef..c8732b740bf 100644 --- a/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/Util.kt +++ b/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/Util.kt @@ -32,10 +32,10 @@ fun MethodSpec.overrideReturnType(typeNameOverrideMap: Map): Met fun TypeSpec.withCreator(): TypeSpec { return toBuilder() - .addType(TypeSpec.interfaceBuilder(Util.CREATOR_INTERFACE_NAME) + .addType(TypeSpec.interfaceBuilder(Util.CREATOR_TYPE_NAME) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addMethod(MethodSpec - .methodBuilder(Util.CREATOR_METHOD_CREATE) + .methodBuilder(Util.CREATOR_CREATE_METHOD_NAME) .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .addParameters( methodSpecs.filter { !it.isConstructor }.map { @@ -70,9 +70,9 @@ fun TypeSpec.withCreatorImplementation(): TypeSpec { fun creatorInitializer(constructorClassName: String, fieldSpecs: List) = TypeSpec.anonymousClassBuilder("") - .superclass(ClassName.get("", Util.CREATOR_INTERFACE_NAME)) + .superclass(ClassName.get("", Util.CREATOR_TYPE_NAME)) .addMethod(MethodSpec - .methodBuilder(Util.CREATOR_METHOD_CREATE) + .methodBuilder(Util.CREATOR_CREATE_METHOD_NAME) .addModifiers(Modifier.PUBLIC) .addAnnotation(Override::class.java) .addParameters(methodSpecs.toParameterSpecs()) @@ -83,64 +83,67 @@ fun TypeSpec.withCreatorImplementation(): TypeSpec { return toBuilder() .addField(FieldSpec - .builder(ClassName.get("", Util.CREATOR_INTERFACE_NAME), Util.CREATOR_INTERFACE_NAME.toUpperCase()) + .builder(ClassName.get("", Util.CREATOR_TYPE_NAME), Util.CREATOR_TYPE_NAME.toUpperCase()) .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .initializer("\$L", creatorInitializer(name, fieldSpecs)) .build()) .build() } -fun TypeSpec.withFactory(): TypeSpec { +fun TypeSpec.withFactory(plusFactories: List = emptyList()): TypeSpec { return toBuilder() - .addType(TypeSpec.interfaceBuilder(Util.FACTORY_INTERFACE_NAME) + .addType(TypeSpec.interfaceBuilder(Util.FACTORY_TYPE_NAME) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .addMethod(MethodSpec - .methodBuilder(Util.CREATOR_INTERFACE_NAME.decapitalize()) - .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) - .returns(ClassName.get("", Util.CREATOR_INTERFACE_NAME)) - .build()) + .addMethod( + MethodSpec.methodBuilder(Util.FACTORY_CREATOR_ACCESS_METHOD_NAME) + .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) + .returns(ClassName.get("", Util.CREATOR_TYPE_NAME)) + .build()) .addMethods(typeSpecs - .filter { it.name != Util.CREATOR_INTERFACE_NAME } + .map { it.name } + .filter { it != Util.CREATOR_TYPE_NAME } + .plus(plusFactories) .map { - MethodSpec - .methodBuilder("${it.name.decapitalize()}${Util.FACTORY_INTERFACE_NAME}") + MethodSpec.methodBuilder("${it.decapitalize()}${Util.FACTORY_TYPE_NAME}") .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) - .returns(ClassName.get("", "${it.name}.${Util.FACTORY_INTERFACE_NAME}")) + .returns(ClassName.get("", "$it.${Util.FACTORY_TYPE_NAME}")) .build() }) .build()) .build() } -fun TypeSpec.withFactoryImplementation(): TypeSpec { +fun TypeSpec.withFactoryImplementation(plusFactories: List = emptyList()): TypeSpec { fun factoryInitializer(typeSpecs: List) = TypeSpec.anonymousClassBuilder("") - .superclass(ClassName.get("", Util.FACTORY_INTERFACE_NAME)) + .superclass(Util.FACTORY_INTERFACE_TYPE) .addMethod(MethodSpec - .methodBuilder(Util.CREATOR_INTERFACE_NAME.decapitalize()) + .methodBuilder(Util.FACTORY_CREATOR_ACCESS_METHOD_NAME) .addModifiers(Modifier.PUBLIC) .addAnnotation(Override::class.java) - .returns(ClassName.get("", Util.CREATOR_INTERFACE_NAME)) - .addStatement("return \$L", Util.CREATOR_INTERFACE_NAME.toUpperCase()) + .returns(ClassName.get("", Util.CREATOR_TYPE_NAME)) + .addStatement("return \$L", Util.CREATOR_TYPE_NAME.toUpperCase()) .build()) .addMethods(typeSpecs + .map { it.name } + .plus(plusFactories) .map { MethodSpec - .methodBuilder("${it.name.decapitalize()}${Util.FACTORY_INTERFACE_NAME}") + .methodBuilder("${it.decapitalize()}${Util.FACTORY_TYPE_NAME}") .addModifiers(Modifier.PUBLIC) .addAnnotation(Override::class.java) - .returns(ClassName.get("", "${it.name}.${Util.FACTORY_INTERFACE_NAME}")) - .addStatement("return \$L.\$L", it.name, Util.FACTORY_INTERFACE_NAME.toUpperCase()) + .returns(ClassName.get("", "$it.${Util.FACTORY_TYPE_NAME}")) + .addStatement("return \$L.\$L", it, Util.FACTORY_TYPE_NAME.toUpperCase()) .build() }) .build() return toBuilder() .addField(FieldSpec - .builder(ClassName.get("", Util.FACTORY_INTERFACE_NAME), Util.FACTORY_INTERFACE_NAME.toUpperCase()) + .builder(Util.FACTORY_INTERFACE_TYPE, Util.FACTORY_TYPE_NAME.toUpperCase()) .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .initializer("\$L", factoryInitializer( - typeSpecs.filter { it.name != Util.CREATOR_INTERFACE_NAME && it.name != Util.FACTORY_INTERFACE_NAME })) + typeSpecs.filter { it.name != Util.CREATOR_TYPE_NAME && it.name != Util.FACTORY_TYPE_NAME })) .build()) .build() } @@ -162,7 +165,9 @@ fun TypeSpec.withValueInitConstructor(): TypeSpec { } object Util { - const val CREATOR_INTERFACE_NAME: String = "Creator" - const val CREATOR_METHOD_CREATE: String = "create" - const val FACTORY_INTERFACE_NAME: String = "Factory" + const val CREATOR_TYPE_NAME: String = "Creator" + const val CREATOR_CREATE_METHOD_NAME: String = "create" + const val FACTORY_CREATOR_ACCESS_METHOD_NAME: String = "creator" + const val FACTORY_TYPE_NAME: String = "Factory" + val FACTORY_INTERFACE_TYPE: ClassName = ClassName.get("", FACTORY_TYPE_NAME) } \ No newline at end of file diff --git a/apollo-compiler/src/test/graphql/com/example/all_planets/TestQuery.json b/apollo-compiler/src/test/graphql/com/example/all_planets/TestQuery.json new file mode 100644 index 00000000000..47eefd37976 --- /dev/null +++ b/apollo-compiler/src/test/graphql/com/example/all_planets/TestQuery.json @@ -0,0 +1,115 @@ +{ + "operations": [ + { + "operationName": "TestQuery", + "operationType": "query", + "variables": [], + "source": "query TestQuery {\n allPlanets(first: 300) {\n planets {\n ...PlanetFargment\n filmConnection {\n totalCount\n films {\n title\n ...FilmFragment\n }\n }\n }\n }\n}", + "fields": [ + { + "responseName": "allPlanets", + "fieldName": "allPlanets", + "type": "PlanetsConnection", + "fields": [ + { + "responseName": "planets", + "fieldName": "planets", + "type": "[Planet]", + "fields": [ + { + "responseName": "filmConnection", + "fieldName": "filmConnection", + "type": "PlanetFilmsConnection", + "fields": [ + { + "responseName": "totalCount", + "fieldName": "totalCount", + "type": "Int" + }, + { + "responseName": "films", + "fieldName": "films", + "type": "[Film]", + "fields": [ + { + "responseName": "title", + "fieldName": "title", + "type": "String" + } + ], + "fragmentSpreads": [ + "FilmFragment" + ], + "inlineFragments": [] + } + ], + "fragmentSpreads": [], + "inlineFragments": [] + } + ], + "fragmentSpreads": [ + "PlanetFargment" + ], + "inlineFragments": [] + } + ], + "fragmentSpreads": [], + "inlineFragments": [] + } + ], + "fragmentsReferenced": [ + "FilmFragment", + "PlanetFargment" + ], + "filePath": "src/test/graphql/com/example/all_planets/TestQuery.json" + } + ], + "fragments": [ + { + "fragmentName": "PlanetFargment", + "source": "fragment PlanetFargment on Planet {\n name\n climates\n surfaceWater\n}", + "typeCondition": "Planet", + "fields": [ + { + "responseName": "name", + "fieldName": "name", + "type": "String" + }, + { + "responseName": "climates", + "fieldName": "climates", + "type": "[String]" + }, + { + "responseName": "surfaceWater", + "fieldName": "surfaceWater", + "type": "Float" + } + ], + "fragmentSpreads": [], + "inlineFragments": [], + "fragmentsReferenced": [] + }, + { + "fragmentName": "FilmFragment", + "source": "fragment FilmFragment on Film {\n title\n producers\n}", + "typeCondition": "Film", + "fields": [ + { + "responseName": "title", + "fieldName": "title", + "type": "String" + }, + { + "responseName": "producers", + "fieldName": "producers", + "type": "[String]" + } + ], + "fragmentSpreads": [], + "inlineFragments": [], + "fragmentsReferenced": [] + } + ], + "typesUsed": [] +} \ No newline at end of file diff --git a/apollo-compiler/src/test/graphql/com/example/fragment_friends_connection/TestQueryExpected.java b/apollo-compiler/src/test/graphql/com/example/fragment_friends_connection/TestQueryExpected.java index 5d2699a3b82..c9b1246efa5 100644 --- a/apollo-compiler/src/test/graphql/com/example/fragment_friends_connection/TestQueryExpected.java +++ b/apollo-compiler/src/test/graphql/com/example/fragment_friends_connection/TestQueryExpected.java @@ -47,6 +47,8 @@ interface Fragments { interface Factory { Creator creator(); + + HeroDetails.Factory heroDetailsFactory(); } interface Creator { diff --git a/apollo-compiler/src/test/graphql/com/example/fragment_with_inline_fragment/TestQueryExpected.java b/apollo-compiler/src/test/graphql/com/example/fragment_with_inline_fragment/TestQueryExpected.java index 947576b6697..c424a21dd3f 100644 --- a/apollo-compiler/src/test/graphql/com/example/fragment_with_inline_fragment/TestQueryExpected.java +++ b/apollo-compiler/src/test/graphql/com/example/fragment_with_inline_fragment/TestQueryExpected.java @@ -56,6 +56,8 @@ interface Fragments { interface Factory { Creator creator(); + + HeroDetails.Factory heroDetailsFactory(); } interface Creator { diff --git a/apollo-compiler/src/test/graphql/com/example/fragments_with_type_condition/TestQueryExpected.java b/apollo-compiler/src/test/graphql/com/example/fragments_with_type_condition/TestQueryExpected.java index 96dfe03f36c..82f1ae312f2 100644 --- a/apollo-compiler/src/test/graphql/com/example/fragments_with_type_condition/TestQueryExpected.java +++ b/apollo-compiler/src/test/graphql/com/example/fragments_with_type_condition/TestQueryExpected.java @@ -59,6 +59,10 @@ interface Fragments { interface Factory { Creator creator(); + + HumanDetails.Factory humanDetailsFactory(); + + DroidDetails.Factory droidDetailsFactory(); } interface Creator { @@ -87,6 +91,10 @@ interface Fragments { interface Factory { Creator creator(); + + HumanDetails.Factory humanDetailsFactory(); + + DroidDetails.Factory droidDetailsFactory(); } interface Creator { diff --git a/apollo-compiler/src/test/graphql/com/example/pojo_custom_scalar_type/TestQueryExpected.java b/apollo-compiler/src/test/graphql/com/example/pojo_custom_scalar_type/TestQueryExpected.java index eb22e976785..296906b1426 100644 --- a/apollo-compiler/src/test/graphql/com/example/pojo_custom_scalar_type/TestQueryExpected.java +++ b/apollo-compiler/src/test/graphql/com/example/pojo_custom_scalar_type/TestQueryExpected.java @@ -46,31 +46,6 @@ public Operation.Variables variables() { } public static class Data implements Operation.Data { - private static final ResponseFieldMapper MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { - Field.forObject("hero", "hero", null, true, new Field.ObjectReader() { - @Override public Hero read(final ResponseReader reader) throws IOException { - return new Hero(reader); - } - }) - }; - - @Override - public void map(final ResponseReader reader, final Data instance) throws IOException { - reader.read(new ResponseReader.ValueHandler() { - @Override - public void handle(final int fieldIndex, final Object value) throws IOException { - switch (fieldIndex) { - case 0: { - instance.hero = (Hero) value; - break; - } - } - } - }, FIELDS); - } - }; - public static final Creator CREATOR = new Creator() { @Override public Data create(@Nullable Hero hero) { @@ -92,10 +67,6 @@ public Hero.Factory heroFactory() { private @Nullable Hero hero; - public Data(ResponseReader reader) throws IOException { - MAPPER.map(reader, this); - } - public Data(@Nullable Hero hero) { this.hero = hero; } @@ -105,41 +76,6 @@ public Data(@Nullable Hero hero) { } public static class Hero { - private static final ResponseFieldMapper MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { - Field.forString("name", "name", null, false), - Field.forCustomType("birthDate", "birthDate", null, false, CustomType.DATE), - Field.forList("appearanceDates", "appearanceDates", null, false, new Field.ListReader() { - @Override public Date read(final Field.ListItemReader reader) throws IOException { - return reader.readCustomType(CustomType.DATE); - } - }) - }; - - @Override - public void map(final ResponseReader reader, final Hero instance) throws IOException { - reader.read(new ResponseReader.ValueHandler() { - @Override - public void handle(final int fieldIndex, final Object value) throws IOException { - switch (fieldIndex) { - case 0: { - instance.name = (String) value; - break; - } - case 1: { - instance.birthDate = (Date) value; - break; - } - case 2: { - instance.appearanceDates = (List) value; - break; - } - } - } - }, FIELDS); - } - }; - public static final Creator CREATOR = new Creator() { @Override public Hero create(@Nonnull String name, @Nonnull Date birthDate, @@ -161,10 +97,6 @@ public Creator creator() { private @Nonnull List appearanceDates; - public Hero(ResponseReader reader) throws IOException { - MAPPER.map(reader, this); - } - public Hero(@Nonnull String name, @Nonnull Date birthDate, @Nonnull List appearanceDates) { this.name = name; @@ -192,6 +124,57 @@ public interface Creator { Hero create(@Nonnull String name, @Nonnull Date birthDate, @Nonnull List appearanceDates); } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { + Field.forString("name", "name", null, false), + Field.forCustomType("birthDate", "birthDate", null, false, CustomType.DATE), + Field.forList("appearanceDates", "appearanceDates", null, false, new Field.ListReader() { + @Override public Date read(final Field.ListItemReader reader) throws IOException { + return reader.readCustomType(CustomType.DATE); + } + }) + }; + + public Mapper(@Nonnull Factory factory) { + this.factory = factory; + } + + @Override + public Hero map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); + reader.read(new ResponseReader.ValueHandler() { + @Override + public void handle(final int fieldIndex, final Object value) throws IOException { + switch (fieldIndex) { + case 0: { + contentValues.name = (String) value; + break; + } + case 1: { + contentValues.birthDate = (Date) value; + break; + } + case 2: { + contentValues.appearanceDates = (List) value; + break; + } + } + } + }, fields); + return factory.creator().create(contentValues.name, contentValues.birthDate, contentValues.appearanceDates); + } + + static final class __ContentValues { + String name; + + Date birthDate; + + List appearanceDates; + } + } } public interface Factory { @@ -203,5 +186,42 @@ public interface Factory { public interface Creator { Data create(@Nullable Hero hero); } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { + Field.forObject("hero", "hero", null, true, new Field.ObjectReader() { + @Override public Hero read(final ResponseReader reader) throws IOException { + return new Hero.Mapper(factory.heroFactory()).map(reader); + } + }) + }; + + public Mapper(@Nonnull Factory factory) { + this.factory = factory; + } + + @Override + public Data map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); + reader.read(new ResponseReader.ValueHandler() { + @Override + public void handle(final int fieldIndex, final Object value) throws IOException { + switch (fieldIndex) { + case 0: { + contentValues.hero = (Hero) value; + break; + } + } + } + }, fields); + return factory.creator().create(contentValues.hero); + } + + static final class __ContentValues { + Hero hero; + } + } } } diff --git a/apollo-compiler/src/test/graphql/com/example/pojo_fragment_with_inline_fragment/HeroDetailsExpected.java b/apollo-compiler/src/test/graphql/com/example/pojo_fragment_with_inline_fragment/HeroDetailsExpected.java index 88d08f73552..c1a16611deb 100644 --- a/apollo-compiler/src/test/graphql/com/example/pojo_fragment_with_inline_fragment/HeroDetailsExpected.java +++ b/apollo-compiler/src/test/graphql/com/example/pojo_fragment_with_inline_fragment/HeroDetailsExpected.java @@ -15,44 +15,6 @@ @Generated("Apollo GraphQL") public class HeroDetails { - private static final ResponseFieldMapper MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { - Field.forString("name", "name", null, false), - Field.forObject("friendsConnection", "friendsConnection", null, false, new Field.ObjectReader() { - @Override public FriendsConnection read(final ResponseReader reader) throws IOException { - return new FriendsConnection(reader); - } - }), - Field.forString("__typename", "__typename", null, false) - }; - - @Override - public void map(final ResponseReader reader, final HeroDetails instance) throws IOException { - reader.read(new ResponseReader.ValueHandler() { - @Override - public void handle(final int fieldIndex, final Object value) throws IOException { - switch (fieldIndex) { - case 0: { - instance.name = (String) value; - break; - } - case 1: { - instance.friendsConnection = (FriendsConnection) value; - break; - } - case 2: { - String typename = (String) value; - if (typename.equals("Droid")) { - instance.asDroid = new AsDroid(reader); - } - break; - } - } - } - }, FIELDS); - } - }; - public static final Creator CREATOR = new Creator() { @Override public HeroDetails create(@Nonnull String name, @Nonnull FriendsConnection friendsConnection, @@ -104,10 +66,6 @@ public AsDroid.Factory asDroidFactory() { private @Nullable AsDroid asDroid; - public HeroDetails(ResponseReader reader) throws IOException { - MAPPER.map(reader.toBufferedReader(), this); - } - public HeroDetails(@Nonnull String name, @Nonnull FriendsConnection friendsConnection, @Nullable AsDroid asDroid) { this.name = name; @@ -128,37 +86,6 @@ public HeroDetails(@Nonnull String name, @Nonnull FriendsConnection friendsConne } public static class FriendsConnection { - private static final ResponseFieldMapper MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { - Field.forInt("totalCount", "totalCount", null, true), - Field.forList("edges", "edges", null, true, new Field.ObjectReader() { - @Override public Edge read(final ResponseReader reader) throws IOException { - return new Edge(reader); - } - }) - }; - - @Override - public void map(final ResponseReader reader, final FriendsConnection instance) throws - IOException { - reader.read(new ResponseReader.ValueHandler() { - @Override - public void handle(final int fieldIndex, final Object value) throws IOException { - switch (fieldIndex) { - case 0: { - instance.totalCount = (Integer) value; - break; - } - case 1: { - instance.edges = (List) value; - break; - } - } - } - }, FIELDS); - } - }; - public static final Creator CREATOR = new Creator() { @Override public FriendsConnection create(@Nullable Integer totalCount, @@ -183,10 +110,6 @@ public Edge.Factory edgeFactory() { private @Nullable List edges; - public FriendsConnection(ResponseReader reader) throws IOException { - MAPPER.map(reader, this); - } - public FriendsConnection(@Nullable Integer totalCount, @Nullable List edges) { this.totalCount = totalCount; this.edges = edges; @@ -201,31 +124,6 @@ public FriendsConnection(@Nullable Integer totalCount, @Nullable List MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { - Field.forObject("node", "node", null, true, new Field.ObjectReader() { - @Override public Node read(final ResponseReader reader) throws IOException { - return new Node(reader); - } - }) - }; - - @Override - public void map(final ResponseReader reader, final Edge instance) throws IOException { - reader.read(new ResponseReader.ValueHandler() { - @Override - public void handle(final int fieldIndex, final Object value) throws IOException { - switch (fieldIndex) { - case 0: { - instance.node = (Node) value; - break; - } - } - } - }, FIELDS); - } - }; - public static final Creator CREATOR = new Creator() { @Override public Edge create(@Nullable Node node) { @@ -247,10 +145,6 @@ public Node.Factory nodeFactory() { private @Nullable Node node; - public Edge(ResponseReader reader) throws IOException { - MAPPER.map(reader, this); - } - public Edge(@Nullable Node node) { this.node = node; } @@ -260,27 +154,6 @@ public Edge(@Nullable Node node) { } public static class Node { - private static final ResponseFieldMapper MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { - Field.forString("name", "name", null, false) - }; - - @Override - public void map(final ResponseReader reader, final Node instance) throws IOException { - reader.read(new ResponseReader.ValueHandler() { - @Override - public void handle(final int fieldIndex, final Object value) throws IOException { - switch (fieldIndex) { - case 0: { - instance.name = (String) value; - break; - } - } - } - }, FIELDS); - } - }; - public static final Creator CREATOR = new Creator() { @Override public Node create(@Nonnull String name) { @@ -297,10 +170,6 @@ public Creator creator() { private @Nonnull String name; - public Node(ResponseReader reader) throws IOException { - MAPPER.map(reader, this); - } - public Node(@Nonnull String name) { this.name = name; } @@ -316,6 +185,39 @@ public interface Factory { public interface Creator { Node create(@Nonnull String name); } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { + Field.forString("name", "name", null, false) + }; + + public Mapper(@Nonnull Factory factory) { + this.factory = factory; + } + + @Override + public Node map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); + reader.read(new ResponseReader.ValueHandler() { + @Override + public void handle(final int fieldIndex, final Object value) throws IOException { + switch (fieldIndex) { + case 0: { + contentValues.name = (String) value; + break; + } + } + } + }, fields); + return factory.creator().create(contentValues.name); + } + + static final class __ContentValues { + String name; + } + } } public interface Factory { @@ -327,6 +229,43 @@ public interface Factory { public interface Creator { Edge create(@Nullable Node node); } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { + Field.forObject("node", "node", null, true, new Field.ObjectReader() { + @Override public Node read(final ResponseReader reader) throws IOException { + return new Node.Mapper(factory.nodeFactory()).map(reader); + } + }) + }; + + public Mapper(@Nonnull Factory factory) { + this.factory = factory; + } + + @Override + public Edge map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); + reader.read(new ResponseReader.ValueHandler() { + @Override + public void handle(final int fieldIndex, final Object value) throws IOException { + switch (fieldIndex) { + case 0: { + contentValues.node = (Node) value; + break; + } + } + } + }, fields); + return factory.creator().create(contentValues.node); + } + + static final class __ContentValues { + Node node; + } + } } public interface Factory { @@ -338,44 +277,53 @@ public interface Factory { public interface Creator { FriendsConnection create(@Nullable Integer totalCount, @Nullable List edges); } - } - public static class AsDroid { - private static final ResponseFieldMapper MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { - Field.forString("name", "name", null, false), - Field.forObject("friendsConnection", "friendsConnection", null, false, new Field.ObjectReader() { - @Override public FriendsConnection$ read(final ResponseReader reader) throws IOException { - return new FriendsConnection$(reader); + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { + Field.forInt("totalCount", "totalCount", null, true), + Field.forList("edges", "edges", null, true, new Field.ObjectReader() { + @Override public Edge read(final ResponseReader reader) throws IOException { + return new Edge.Mapper(factory.edgeFactory()).map(reader); } - }), - Field.forString("primaryFunction", "primaryFunction", null, true) + }) }; + public Mapper(@Nonnull Factory factory) { + this.factory = factory; + } + @Override - public void map(final ResponseReader reader, final AsDroid instance) throws IOException { + public FriendsConnection map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); reader.read(new ResponseReader.ValueHandler() { @Override public void handle(final int fieldIndex, final Object value) throws IOException { switch (fieldIndex) { case 0: { - instance.name = (String) value; + contentValues.totalCount = (Integer) value; break; } case 1: { - instance.friendsConnection = (FriendsConnection$) value; - break; - } - case 2: { - instance.primaryFunction = (String) value; + contentValues.edges = (List) value; break; } } } - }, FIELDS); + }, fields); + return factory.creator().create(contentValues.totalCount, contentValues.edges); } - }; + static final class __ContentValues { + Integer totalCount; + + List edges; + } + } + } + + public static class AsDroid { public static final Creator CREATOR = new Creator() { @Override public AsDroid create(@Nonnull String name, @Nonnull FriendsConnection$ friendsConnection, @@ -402,10 +350,6 @@ public Creator creator() { private @Nullable String primaryFunction; - public AsDroid(ResponseReader reader) throws IOException { - MAPPER.map(reader, this); - } - public AsDroid(@Nonnull String name, @Nonnull FriendsConnection$ friendsConnection, @Nullable String primaryFunction) { this.name = name; @@ -426,37 +370,6 @@ public AsDroid(@Nonnull String name, @Nonnull FriendsConnection$ friendsConnecti } public static class FriendsConnection$ { - private static final ResponseFieldMapper MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { - Field.forInt("totalCount", "totalCount", null, true), - Field.forList("edges", "edges", null, true, new Field.ObjectReader() { - @Override public Edge read(final ResponseReader reader) throws IOException { - return new Edge(reader); - } - }) - }; - - @Override - public void map(final ResponseReader reader, final FriendsConnection$ instance) throws - IOException { - reader.read(new ResponseReader.ValueHandler() { - @Override - public void handle(final int fieldIndex, final Object value) throws IOException { - switch (fieldIndex) { - case 0: { - instance.totalCount = (Integer) value; - break; - } - case 1: { - instance.edges = (List) value; - break; - } - } - } - }, FIELDS); - } - }; - public static final Creator CREATOR = new Creator() { @Override public FriendsConnection$ create(@Nullable Integer totalCount, @@ -481,10 +394,6 @@ public Edge.Factory edgeFactory() { private @Nullable List edges; - public FriendsConnection$(ResponseReader reader) throws IOException { - MAPPER.map(reader, this); - } - public FriendsConnection$(@Nullable Integer totalCount, @Nullable List edges) { this.totalCount = totalCount; @@ -500,31 +409,6 @@ public Edge.Factory edgeFactory() { } public static class Edge { - private static final ResponseFieldMapper MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { - Field.forObject("node", "node", null, true, new Field.ObjectReader() { - @Override public Node read(final ResponseReader reader) throws IOException { - return new Node(reader); - } - }) - }; - - @Override - public void map(final ResponseReader reader, final Edge instance) throws IOException { - reader.read(new ResponseReader.ValueHandler() { - @Override - public void handle(final int fieldIndex, final Object value) throws IOException { - switch (fieldIndex) { - case 0: { - instance.node = (Node) value; - break; - } - } - } - }, FIELDS); - } - }; - public static final Creator CREATOR = new Creator() { @Override public Edge create(@Nullable Node node) { @@ -546,10 +430,6 @@ public Node.Factory nodeFactory() { private @Nullable Node node; - public Edge(ResponseReader reader) throws IOException { - MAPPER.map(reader, this); - } - public Edge(@Nullable Node node) { this.node = node; } @@ -559,27 +439,6 @@ public Edge(@Nullable Node node) { } public static class Node { - private static final ResponseFieldMapper MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { - Field.forString("name", "name", null, false) - }; - - @Override - public void map(final ResponseReader reader, final Node instance) throws IOException { - reader.read(new ResponseReader.ValueHandler() { - @Override - public void handle(final int fieldIndex, final Object value) throws IOException { - switch (fieldIndex) { - case 0: { - instance.name = (String) value; - break; - } - } - } - }, FIELDS); - } - }; - public static final Creator CREATOR = new Creator() { @Override public Node create(@Nonnull String name) { @@ -596,10 +455,6 @@ public Creator creator() { private @Nonnull String name; - public Node(ResponseReader reader) throws IOException { - MAPPER.map(reader, this); - } - public Node(@Nonnull String name) { this.name = name; } @@ -615,6 +470,39 @@ public interface Factory { public interface Creator { Node create(@Nonnull String name); } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { + Field.forString("name", "name", null, false) + }; + + public Mapper(@Nonnull Factory factory) { + this.factory = factory; + } + + @Override + public Node map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); + reader.read(new ResponseReader.ValueHandler() { + @Override + public void handle(final int fieldIndex, final Object value) throws IOException { + switch (fieldIndex) { + case 0: { + contentValues.name = (String) value; + break; + } + } + } + }, fields); + return factory.creator().create(contentValues.name); + } + + static final class __ContentValues { + String name; + } + } } public interface Factory { @@ -626,6 +514,43 @@ public interface Factory { public interface Creator { Edge create(@Nullable Node node); } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { + Field.forObject("node", "node", null, true, new Field.ObjectReader() { + @Override public Node read(final ResponseReader reader) throws IOException { + return new Node.Mapper(factory.nodeFactory()).map(reader); + } + }) + }; + + public Mapper(@Nonnull Factory factory) { + this.factory = factory; + } + + @Override + public Edge map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); + reader.read(new ResponseReader.ValueHandler() { + @Override + public void handle(final int fieldIndex, final Object value) throws IOException { + switch (fieldIndex) { + case 0: { + contentValues.node = (Node) value; + break; + } + } + } + }, fields); + return factory.creator().create(contentValues.node); + } + + static final class __ContentValues { + Node node; + } + } } public interface Factory { @@ -638,6 +563,50 @@ public interface Creator { FriendsConnection$ create(@Nullable Integer totalCount, @Nullable List edges); } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { + Field.forInt("totalCount", "totalCount", null, true), + Field.forList("edges", "edges", null, true, new Field.ObjectReader() { + @Override public Edge read(final ResponseReader reader) throws IOException { + return new Edge.Mapper(factory.edgeFactory()).map(reader); + } + }) + }; + + public Mapper(@Nonnull Factory factory) { + this.factory = factory; + } + + @Override + public FriendsConnection$ map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); + reader.read(new ResponseReader.ValueHandler() { + @Override + public void handle(final int fieldIndex, final Object value) throws IOException { + switch (fieldIndex) { + case 0: { + contentValues.totalCount = (Integer) value; + break; + } + case 1: { + contentValues.edges = (List) value; + break; + } + } + } + }, fields); + return factory.creator().create(contentValues.totalCount, contentValues.edges); + } + + static final class __ContentValues { + Integer totalCount; + + List edges; + } + } } public interface Factory { @@ -650,6 +619,57 @@ public interface Creator { AsDroid create(@Nonnull String name, @Nonnull FriendsConnection$ friendsConnection, @Nullable String primaryFunction); } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { + Field.forString("name", "name", null, false), + Field.forObject("friendsConnection", "friendsConnection", null, false, new Field.ObjectReader() { + @Override public FriendsConnection$ read(final ResponseReader reader) throws IOException { + return new FriendsConnection$.Mapper(factory.friendsConnection$Factory()).map(reader); + } + }), + Field.forString("primaryFunction", "primaryFunction", null, true) + }; + + public Mapper(@Nonnull Factory factory) { + this.factory = factory; + } + + @Override + public AsDroid map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); + reader.read(new ResponseReader.ValueHandler() { + @Override + public void handle(final int fieldIndex, final Object value) throws IOException { + switch (fieldIndex) { + case 0: { + contentValues.name = (String) value; + break; + } + case 1: { + contentValues.friendsConnection = (FriendsConnection$) value; + break; + } + case 2: { + contentValues.primaryFunction = (String) value; + break; + } + } + } + }, fields); + return factory.creator().create(contentValues.name, contentValues.friendsConnection, contentValues.primaryFunction); + } + + static final class __ContentValues { + String name; + + FriendsConnection$ friendsConnection; + + String primaryFunction; + } + } } public interface Factory { @@ -664,4 +684,64 @@ public interface Creator { HeroDetails create(@Nonnull String name, @Nonnull FriendsConnection friendsConnection, @Nullable AsDroid asDroid); } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { + Field.forString("name", "name", null, false), + Field.forObject("friendsConnection", "friendsConnection", null, false, new Field.ObjectReader() { + @Override public FriendsConnection read(final ResponseReader reader) throws IOException { + return new FriendsConnection.Mapper(factory.friendsConnectionFactory()).map(reader); + } + }), + Field.forConditionalType("__typename", "__typename", new Field.ConditionalTypeReader() { + @Override + public AsDroid read(String conditionalType, ResponseReader reader) throws IOException { + if (conditionalType.equals("Droid")) { + return new AsDroid.Mapper(factory.asDroidFactory()).map(reader); + } else { + return null; + } + } + }) + }; + + public Mapper(@Nonnull Factory factory) { + this.factory = factory; + } + + @Override + public HeroDetails map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); + reader.toBufferedReader().read(new ResponseReader.ValueHandler() { + @Override + public void handle(final int fieldIndex, final Object value) throws IOException { + switch (fieldIndex) { + case 0: { + contentValues.name = (String) value; + break; + } + case 1: { + contentValues.friendsConnection = (FriendsConnection) value; + break; + } + case 2: { + contentValues.asDroid = (AsDroid) value; + break; + } + } + } + }, fields); + return factory.creator().create(contentValues.name, contentValues.friendsConnection, contentValues.asDroid); + } + + static final class __ContentValues { + String name; + + FriendsConnection friendsConnection; + + AsDroid asDroid; + } + } } diff --git a/apollo-compiler/src/test/graphql/com/example/pojo_fragment_with_inline_fragment/TestQueryExpected.java b/apollo-compiler/src/test/graphql/com/example/pojo_fragment_with_inline_fragment/TestQueryExpected.java index cd602b5015d..b57c83c8cb8 100644 --- a/apollo-compiler/src/test/graphql/com/example/pojo_fragment_with_inline_fragment/TestQueryExpected.java +++ b/apollo-compiler/src/test/graphql/com/example/pojo_fragment_with_inline_fragment/TestQueryExpected.java @@ -47,31 +47,6 @@ public Operation.Variables variables() { } public static class Data implements Operation.Data { - private static final ResponseFieldMapper MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { - Field.forObject("hero", "hero", null, true, new Field.ObjectReader() { - @Override public Hero read(final ResponseReader reader) throws IOException { - return new Hero(reader); - } - }) - }; - - @Override - public void map(final ResponseReader reader, final Data instance) throws IOException { - reader.read(new ResponseReader.ValueHandler() { - @Override - public void handle(final int fieldIndex, final Object value) throws IOException { - switch (fieldIndex) { - case 0: { - instance.hero = (Hero) value; - break; - } - } - } - }, FIELDS); - } - }; - public static final Creator CREATOR = new Creator() { @Override public Data create(@Nullable Hero hero) { @@ -93,10 +68,6 @@ public Hero.Factory heroFactory() { private @Nullable Hero hero; - public Data(ResponseReader reader) throws IOException { - MAPPER.map(reader, this); - } - public Data(@Nullable Hero hero) { this.hero = hero; } @@ -106,42 +77,6 @@ public Data(@Nullable Hero hero) { } public static class Hero { - private static final ResponseFieldMapper MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { - Field.forString("name", "name", null, false), - Field.forList("appearsIn", "appearsIn", null, false, new Field.ListReader() { - @Override public Episode read(final Field.ListItemReader reader) throws IOException { - return Episode.valueOf(reader.readString()); - } - }), - Field.forString("__typename", "__typename", null, false) - }; - - @Override - public void map(final ResponseReader reader, final Hero instance) throws IOException { - reader.read(new ResponseReader.ValueHandler() { - @Override - public void handle(final int fieldIndex, final Object value) throws IOException { - switch (fieldIndex) { - case 0: { - instance.name = (String) value; - break; - } - case 1: { - instance.appearsIn = (List) value; - break; - } - case 2: { - String typename = (String) value; - instance.fragments = new Fragments(reader, typename); - break; - } - } - } - }, FIELDS); - } - }; - public static final Creator CREATOR = new Creator() { @Override public Hero create(@Nonnull String name, @Nonnull List appearsIn, @@ -168,10 +103,6 @@ public Fragments.Factory fragmentsFactory() { private Fragments fragments; - public Hero(ResponseReader reader) throws IOException { - MAPPER.map(reader.toBufferedReader(), this); - } - public Hero(@Nonnull String name, @Nonnull List appearsIn, Fragments fragments) { this.name = name; @@ -204,16 +135,15 @@ public Fragments create(HeroDetails heroDetails) { public Creator creator() { return CREATOR; } + + @Override + public HeroDetails.Factory heroDetailsFactory() { + return HeroDetails.FACTORY; + } }; private HeroDetails heroDetails; - Fragments(ResponseReader reader, String typename) throws IOException { - if (typename.equals(HeroDetails.TYPE_CONDITION)) { - this.heroDetails = new HeroDetails(reader); - } - } - public Fragments(HeroDetails heroDetails) { this.heroDetails = heroDetails; } @@ -224,11 +154,33 @@ public HeroDetails heroDetails() { public interface Factory { Creator creator(); + + HeroDetails.Factory heroDetailsFactory(); } public interface Creator { Fragments create(HeroDetails heroDetails); } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + String conditionalType; + + public Mapper(@Nonnull Factory factory, @Nonnull String conditionalType) { + this.factory = factory; + this.conditionalType = conditionalType; + } + + @Override + public Fragments map(ResponseReader reader) throws IOException { + HeroDetails herodetails = null; + if (conditionalType.equals(HeroDetails.TYPE_CONDITION)) { + herodetails = new HeroDetails.Mapper(factory.heroDetailsFactory()).map(reader); + } + return factory.creator().create(herodetails); + } + } } public interface Factory { @@ -241,6 +193,63 @@ public interface Creator { Hero create(@Nonnull String name, @Nonnull List appearsIn, Fragments fragments); } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { + Field.forString("name", "name", null, false), + Field.forList("appearsIn", "appearsIn", null, false, new Field.ListReader() { + @Override public Episode read(final Field.ListItemReader reader) throws IOException { + return Episode.valueOf(reader.readString()); + } + }), + Field.forConditionalType("__typename", "__typename", new Field.ConditionalTypeReader() { + @Override + public Fragments read(String conditionalType, ResponseReader reader) throws + IOException { + return new Fragments.Mapper(factory.fragmentsFactory(), conditionalType).map(reader); + } + }) + }; + + public Mapper(@Nonnull Factory factory) { + this.factory = factory; + } + + @Override + public Hero map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); + reader.toBufferedReader().read(new ResponseReader.ValueHandler() { + @Override + public void handle(final int fieldIndex, final Object value) throws IOException { + switch (fieldIndex) { + case 0: { + contentValues.name = (String) value; + break; + } + case 1: { + contentValues.appearsIn = (List) value; + break; + } + case 2: { + contentValues.fragments = (Fragments) value; + break; + } + } + } + }, fields); + return factory.creator().create(contentValues.name, contentValues.appearsIn, contentValues.fragments); + } + + static final class __ContentValues { + String name; + + List appearsIn; + + Fragments fragments; + } + } } public interface Factory { @@ -252,5 +261,42 @@ public interface Factory { public interface Creator { Data create(@Nullable Hero hero); } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { + Field.forObject("hero", "hero", null, true, new Field.ObjectReader() { + @Override public Hero read(final ResponseReader reader) throws IOException { + return new Hero.Mapper(factory.heroFactory()).map(reader); + } + }) + }; + + public Mapper(@Nonnull Factory factory) { + this.factory = factory; + } + + @Override + public Data map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); + reader.read(new ResponseReader.ValueHandler() { + @Override + public void handle(final int fieldIndex, final Object value) throws IOException { + switch (fieldIndex) { + case 0: { + contentValues.hero = (Hero) value; + break; + } + } + } + }, fields); + return factory.creator().create(contentValues.hero); + } + + static final class __ContentValues { + Hero hero; + } + } } } diff --git a/apollo-compiler/src/test/graphql/com/example/products_with_dates/TestQuery.json b/apollo-compiler/src/test/graphql/com/example/products_with_dates/TestQuery.json new file mode 100644 index 00000000000..adf90558165 --- /dev/null +++ b/apollo-compiler/src/test/graphql/com/example/products_with_dates/TestQuery.json @@ -0,0 +1,68 @@ +{ + "operations": [ + { + "operationName": "TestQuery", + "operationType": "query", + "variables": [], + "source": "query TestQuery {\n shop {\n products(first: 50) {\n edges {\n node {\n title\n createdAt\n }\n }\n }\n }\n}", + "fields": [ + { + "responseName": "shop", + "fieldName": "shop", + "type": "Shop!", + "fields": [ + { + "responseName": "products", + "fieldName": "products", + "type": "ProductConnection!", + "fields": [ + { + "responseName": "edges", + "fieldName": "edges", + "type": "[ProductEdge!]!", + "fields": [ + { + "responseName": "node", + "fieldName": "node", + "type": "Product!", + "fields": [ + { + "responseName": "title", + "fieldName": "title", + "type": "String!" + }, + { + "responseName": "createdAt", + "fieldName": "createdAt", + "type": "DateTime!" + } + ], + "fragmentSpreads": [], + "inlineFragments": [] + } + ], + "fragmentSpreads": [], + "inlineFragments": [] + } + ], + "fragmentSpreads": [], + "inlineFragments": [] + } + ], + "fragmentSpreads": [], + "inlineFragments": [] + } + ], + "fragmentsReferenced": [], + "filePath": "src/test/graphql/com/example/products_with_dates/TestQuery.json" + } + ], + "fragments": [], + "typesUsed": [ + { + "kind": "ScalarType", + "name": "DateTime", + "description": null + } + ] +} \ No newline at end of file diff --git a/apollo-compiler/src/test/graphql/com/example/scalar_types/TestQuery.json b/apollo-compiler/src/test/graphql/com/example/scalar_types/TestQuery.json index 203a8abb236..59b8475c178 100644 --- a/apollo-compiler/src/test/graphql/com/example/scalar_types/TestQuery.json +++ b/apollo-compiler/src/test/graphql/com/example/scalar_types/TestQuery.json @@ -91,13 +91,6 @@ ], "fragmentSpreads": [], "inlineFragments": [] - }, - { - "responseName": "graphQlNestedList", - "fieldName": "graphQlNestedList", - "type": "[[Int]]", - "fragmentSpreads": [], - "inlineFragments": [] } ], "fragmentsReferenced": [], diff --git a/apollo-compiler/src/test/graphql/com/example/scalar_types/TestQueryExpected.java b/apollo-compiler/src/test/graphql/com/example/scalar_types/TestQueryExpected.java index a46c1122c89..d27e0e32807 100644 --- a/apollo-compiler/src/test/graphql/com/example/scalar_types/TestQueryExpected.java +++ b/apollo-compiler/src/test/graphql/com/example/scalar_types/TestQueryExpected.java @@ -57,8 +57,6 @@ public interface Data extends Operation.Data { @Nullable List graphQlListOfObjects(); - @Nullable List> graphQlNestedList(); - interface GraphQlListOfObject { int someField(); @@ -83,8 +81,7 @@ Data create(@Nullable String graphQlString, @Nullable String graphQlIdNullable, int graphQlIntNonNullable, @Nullable Double graphQlFloatNullable, double graphQlFloatNonNullable, @Nullable Boolean graphQlBooleanNullable, boolean graphQlBooleanNonNullable, @Nullable List graphQlListOfInt, - @Nullable List graphQlListOfObjects, - @Nullable List> graphQlNestedList); + @Nullable List graphQlListOfObjects); } } } diff --git a/apollo-compiler/src/test/graphql/com/example/simple_fragment/TestQueryExpected.java b/apollo-compiler/src/test/graphql/com/example/simple_fragment/TestQueryExpected.java index a774d4c73e7..d38f3b3409a 100644 --- a/apollo-compiler/src/test/graphql/com/example/simple_fragment/TestQueryExpected.java +++ b/apollo-compiler/src/test/graphql/com/example/simple_fragment/TestQueryExpected.java @@ -47,6 +47,8 @@ interface Fragments { interface Factory { Creator creator(); + + HeroDetails.Factory heroDetailsFactory(); } interface Creator { diff --git a/apollo-compiler/src/test/graphql/com/example/unique_type_name/TestQueryExpected.java b/apollo-compiler/src/test/graphql/com/example/unique_type_name/TestQueryExpected.java index 7eed3f3b2ef..823b3c87cb8 100644 --- a/apollo-compiler/src/test/graphql/com/example/unique_type_name/TestQueryExpected.java +++ b/apollo-compiler/src/test/graphql/com/example/unique_type_name/TestQueryExpected.java @@ -99,6 +99,8 @@ interface Fragments { interface Factory { Creator creator(); + + HeroDetails.Factory heroDetailsFactory(); } interface Creator { diff --git a/apollo-compiler/src/test/kotlin/com/apollographql/android/compiler/CodegenTest.kt b/apollo-compiler/src/test/kotlin/com/apollographql/android/compiler/CodegenTest.kt index 8b0a89563ae..6ae9f7c964e 100644 --- a/apollo-compiler/src/test/kotlin/com/apollographql/android/compiler/CodegenTest.kt +++ b/apollo-compiler/src/test/kotlin/com/apollographql/android/compiler/CodegenTest.kt @@ -74,6 +74,10 @@ class CodeGenTest(val testDir: File, val pkgName: String, val generatePOJO: Bool arrayOf(it, it.name, false, mapOf("Date" to "java.util.Date", "UnsupportedType" to "java.lang.Object")) } else if (it.name == "pojo_custom_scalar_type") { arrayOf(it, it.name, true, mapOf("Date" to "java.util.Date", "UnsupportedType" to "java.lang.Object")) +// } else if (it.name == "scalar_types") { +// arrayOf(it, it.name, true, emptyMap()) +// } else if (it.name == "products_with_dates") { +// arrayOf(it, it.name, true, mapOf("DateTime" to "java.util.Date")) } else { arrayOf(it, it.name, it.name.startsWith("pojo"), emptyMap()) } diff --git a/apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/ApolloConverterFactory.java b/apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/ApolloConverterFactory.java index 2a93d33f4a3..cd032e135e4 100644 --- a/apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/ApolloConverterFactory.java +++ b/apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/ApolloConverterFactory.java @@ -2,12 +2,13 @@ import com.apollographql.android.api.graphql.Operation; import com.apollographql.android.api.graphql.Response; +import com.apollographql.android.api.graphql.ResponseFieldMapper; import com.apollographql.android.api.graphql.ScalarType; import java.lang.annotation.Annotation; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import javax.annotation.Nonnull; @@ -18,9 +19,12 @@ /** TODO */ public final class ApolloConverterFactory extends Converter.Factory { + private final Map responseFieldMappers; private final Map customTypeAdapters; - private ApolloConverterFactory(Map customTypeAdapters) { + public ApolloConverterFactory(Map responseFieldMappers, + Map customTypeAdapters) { + this.responseFieldMappers = responseFieldMappers; this.customTypeAdapters = customTypeAdapters; } @@ -29,14 +33,20 @@ private ApolloConverterFactory(Map customTypeAdap if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; if (Response.class.isAssignableFrom((Class) parameterizedType.getRawType())) { - return new ApolloResponseBodyConverter(parameterizedType.getActualTypeArguments()[0], customTypeAdapters); + ResponseFieldMapper responseMapper = responseFieldMappers.get(parameterizedType.getActualTypeArguments()[0]); + if (responseMapper == null) { + throw new RuntimeException("failed to resolve response field mapper for: " + type + ". Did you forget to " + + "register one"); + } + return new ApolloResponseBodyConverter(responseMapper, customTypeAdapters); } } return null; } public static class Builder { - private final Map customTypeAdapters = new HashMap<>(); + private final Map responseFieldMappers = new LinkedHashMap<>(); + private final Map customTypeAdapters = new LinkedHashMap<>(); public Builder withCustomTypeAdapter(@Nonnull ScalarType scalarType, @Nonnull CustomTypeAdapter customTypeAdapter) { @@ -44,8 +54,13 @@ public Builder withCustomTypeAdapter(@Nonnull ScalarType scalarType, return this; } + public Builder withResponseFieldMapper(@Nonnull Type type, @Nonnull ResponseFieldMapper responseFieldMapper) { + responseFieldMappers.put(type, responseFieldMapper); + return this; + } + public ApolloConverterFactory build() { - return new ApolloConverterFactory(customTypeAdapters); + return new ApolloConverterFactory(responseFieldMappers, customTypeAdapters); } } } diff --git a/apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/ApolloResponseBodyConverter.java b/apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/ApolloResponseBodyConverter.java index 37cce564eda..8d78de1d2da 100644 --- a/apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/ApolloResponseBodyConverter.java +++ b/apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/ApolloResponseBodyConverter.java @@ -4,12 +4,11 @@ import com.apollographql.android.api.graphql.Field; import com.apollographql.android.api.graphql.Operation; import com.apollographql.android.api.graphql.Response; +import com.apollographql.android.api.graphql.ResponseFieldMapper; import com.apollographql.android.api.graphql.ResponseReader; import com.apollographql.android.api.graphql.ScalarType; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Type; import java.util.List; import java.util.Map; @@ -17,11 +16,12 @@ import retrofit2.Converter; class ApolloResponseBodyConverter implements Converter> { - private final Type type; + private final ResponseFieldMapper responseFieldMapper; private final Map customTypeAdapters; - ApolloResponseBodyConverter(Type type, Map customTypeAdapters) { - this.type = type; + ApolloResponseBodyConverter(ResponseFieldMapper responseFieldMapper, + Map customTypeAdapters) { + this.responseFieldMapper = responseFieldMapper; this.customTypeAdapters = customTypeAdapters; } @@ -35,7 +35,11 @@ class ApolloResponseBodyConverter implements Converter() { + @Override public Object read(ResponseReader reader) throws IOException { + return responseFieldMapper.map(reader); + } + }); } else if ("errors".equals(name)) { errors = readResponseErrors(responseStreamReader); } else { @@ -47,25 +51,6 @@ class ApolloResponseBodyConverter implements Converter(data, errors); } - private Operation.Data readResponseData(ResponseJsonStreamReader reader) throws IOException { - return reader.nextObject(true, new Field.ObjectReader() { - @Override public Operation.Data read(ResponseReader reader) throws IOException { - //noinspection TryWithIdenticalCatches - try { - return (Operation.Data) ((Class) type).getConstructor(ResponseReader.class).newInstance(reader); - } catch (InstantiationException e) { - throw new RuntimeException("Can't instantiate " + type, e); - } catch (IllegalAccessException e) { - throw new RuntimeException("Can't instantiate " + type, e); - } catch (InvocationTargetException e) { - throw new RuntimeException("Can't instantiate " + type, e); - } catch (NoSuchMethodException e) { - throw new RuntimeException("Can't instantiate " + type, e); - } - } - }); - } - private List readResponseErrors(ResponseJsonStreamReader reader) throws IOException { return reader.nextList(true, new Field.ObjectReader() { @Override public Error read(ResponseReader reader) throws IOException { diff --git a/apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/BufferedResponseReader.java b/apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/BufferedResponseReader.java index e0563180765..badc4c67a53 100644 --- a/apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/BufferedResponseReader.java +++ b/apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/BufferedResponseReader.java @@ -43,13 +43,19 @@ handler.handle(fieldIndex, readBoolean(field)); break; case OBJECT: - handler.handle(fieldIndex, readObject(field)); + handler.handle(fieldIndex, readObject((Field.ObjectField) field)); break; - case LIST: - handler.handle(fieldIndex, readList(field)); + case SCALAR_LIST: + handler.handle(fieldIndex, readScalarList((Field.ScalarListField) field)); + break; + case OBJECT_LIST: + handler.handle(fieldIndex, readObjectList((Field.ObjectListField) field)); break; case CUSTOM: - handler.handle(fieldIndex, readCustomType(field)); + handler.handle(fieldIndex, readCustomType((Field.CustomTypeField) field)); + break; + case CONDITIONAL: + handler.handle(fieldIndex, readConditional((Field.ConditionalTypeField) field)); break; default: throw new IllegalArgumentException("Unsupported field type"); @@ -108,7 +114,7 @@ Boolean readBoolean(Field field) throws IOException { } } - @SuppressWarnings("unchecked") T readObject(Field field) throws IOException { + @SuppressWarnings("unchecked") T readObject(Field.ObjectField field) throws IOException { Map value = (Map) buffer.get(field.responseName()); checkValue(value, field.optional()); if (value == null) { @@ -118,7 +124,22 @@ Boolean readBoolean(Field field) throws IOException { } } - @SuppressWarnings("unchecked") List readList(Field field) throws IOException { + @SuppressWarnings("unchecked") List readScalarList(Field.ScalarListField field) throws IOException { + List values = (List) buffer.get(field.responseName()); + checkValue(values, field.optional()); + if (values == null) { + return null; + } else { + List result = new ArrayList<>(); + for (Object value : values) { + T item = (T) field.listReader().read(new BufferedListItemReader(value, customTypeAdapters)); + result.add(item); + } + return result; + } + } + + @SuppressWarnings("unchecked") List readObjectList(Field.ObjectListField field) throws IOException { List values = (List) buffer.get(field.responseName()); checkValue(values, field.optional()); if (values == null) { @@ -126,20 +147,15 @@ Boolean readBoolean(Field field) throws IOException { } else { List result = new ArrayList<>(); for (Object value : values) { - T item; - if (value instanceof Map) { - item = (T) field.objectReader().read(new BufferedResponseReader((Map) value, - customTypeAdapters)); - } else { - item = (T) field.listReader().read(new BufferedListItemReader(value, customTypeAdapters)); - } + T item = (T) field.objectReader().read(new BufferedResponseReader((Map) value, + customTypeAdapters)); result.add(item); } return result; } } - @SuppressWarnings("unchecked") private T readCustomType(Field field) { + @SuppressWarnings("unchecked") private T readCustomType(Field.CustomTypeField field) { Object value = buffer.get(field.responseName()); checkValue(value, field.optional()); if (value == null) { @@ -147,12 +163,22 @@ Boolean readBoolean(Field field) throws IOException { } else { CustomTypeAdapter typeAdapter = customTypeAdapters.get(field.scalarType()); if (typeAdapter == null) { - throw new RuntimeException("Can't resolve custom type adapter for " + field.scalarType().typeName()); + throw new RuntimeException("Can't resolve custom type adapter for " + + field.scalarType().typeName()); } return typeAdapter.decode(value.toString()); } } + @SuppressWarnings("unchecked") private T readConditional(Field.ConditionalTypeField field) throws IOException { + String value = (String) buffer.get(field.responseName()); + checkValue(value, field.optional()); + if (value == null) { + return null; + } else { + return (T) field.conditionalTypeReader().read(value, this); + } + } private void checkValue(Object value, boolean optional) { if (!optional && value == null) { diff --git a/apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/ResponseJsonStreamReader.java b/apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/ResponseJsonStreamReader.java index 680fceaf3b3..aadf968fb49 100644 --- a/apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/ResponseJsonStreamReader.java +++ b/apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/ResponseJsonStreamReader.java @@ -57,13 +57,16 @@ handler.handle(fieldIndex, readBoolean(field)); break; case OBJECT: - handler.handle(fieldIndex, readObject(field)); + handler.handle(fieldIndex, readObject((Field.ObjectField) field)); break; - case LIST: - handler.handle(fieldIndex, readList(field)); + case SCALAR_LIST: + handler.handle(fieldIndex, readScalarList((Field.ScalarListField) field)); + break; + case OBJECT_LIST: + handler.handle(fieldIndex, readObjectList((Field.ObjectListField) field)); break; case CUSTOM: - handler.handle(fieldIndex, readCustomType(field)); + handler.handle(fieldIndex, readCustomType((Field.CustomTypeField) field)); break; default: throw new IllegalArgumentException("Unsupported field type"); @@ -220,19 +223,19 @@ private Boolean readBoolean(Field field) throws IOException { return nextBoolean(field.optional()); } - @SuppressWarnings("unchecked") private T readObject(Field field) throws IOException { + @SuppressWarnings("unchecked") private T readObject(Field.ObjectField field) throws IOException { return (T) nextObject(field.optional(), field.objectReader()); } - @SuppressWarnings("unchecked") private List readList(Field field) throws IOException { - if (field.objectReader() != null) { - return nextList(field.optional(), field.objectReader()); - } else { - return nextList(field.optional(), field.listReader()); - } + @SuppressWarnings("unchecked") private List readScalarList(Field.ScalarListField field) throws IOException { + return nextList(field.optional(), field.listReader()); + } + + @SuppressWarnings("unchecked") private List readObjectList(Field.ObjectListField field) throws IOException { + return nextList(field.optional(), field.objectReader()); } - private T readCustomType(Field field) throws IOException { + private T readCustomType(Field.CustomTypeField field) throws IOException { return nextCustomType(field.optional(), field.scalarType()); } @@ -275,7 +278,7 @@ private static Map toMap(ResponseJsonStreamReader streamReader) } else if (streamReader.isNextObject()) { result.put(name, readObject(streamReader)); } else if (streamReader.isNextList()) { - result.put(name, readList(streamReader)); + result.put(name, readScalarList(streamReader)); } else { result.put(name, readScalar(streamReader)); } @@ -291,7 +294,7 @@ private static Map readObject(final ResponseJsonStreamReader str }); } - private static List readList(final ResponseJsonStreamReader streamReader) throws IOException { + private static List readScalarList(final ResponseJsonStreamReader streamReader) throws IOException { return streamReader.nextList(false, new Field.ListReader() { @Override public Object read(Field.ListItemReader reader) throws IOException { if (streamReader.isNextObject()) { diff --git a/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/AllPlanets.java b/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/AllPlanets.java index 75907e35e66..30ed2d188d2 100644 --- a/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/AllPlanets.java +++ b/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/AllPlanets.java @@ -5,12 +5,20 @@ import com.apollographql.android.api.graphql.Query; import com.apollographql.android.api.graphql.ResponseFieldMapper; import com.apollographql.android.api.graphql.ResponseReader; +import com.apollographql.android.converter.pojo.fragment.FilmFragment; +import com.apollographql.android.converter.pojo.fragment.PlanetFragment; import java.io.IOException; +import java.lang.Integer; +import java.lang.Object; +import java.lang.Override; +import java.lang.String; import java.util.List; - +import javax.annotation.Generated; +import javax.annotation.Nonnull; import javax.annotation.Nullable; +@Generated("Apollo GraphQL") public final class AllPlanets implements Query { public static final String OPERATION_DEFINITION = "query TestQuery {\n" + " allPlanets(first: 300) {\n" @@ -48,118 +56,100 @@ public Variables variables() { } public static class Data implements Operation.Data { - private static final ResponseFieldMapper MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { - Field.forObject("allPlanets", "allPlanets", null, true, new Field.ObjectReader() { - @Override public AllPlanet read(final ResponseReader reader) throws IOException { - return new AllPlanet(reader); - } - }) - }; + public static final Creator CREATOR = new Creator() { + @Override + public Data create(@Nullable AllPlanet allPlanets) { + return new Data(allPlanets); + } + }; + public static final Factory FACTORY = new Factory() { @Override - public void map(final ResponseReader reader, final Data instance) throws IOException { - reader.read(new ResponseReader.ValueHandler() { - @Override - public void handle(final int fieldIndex, final Object value) throws IOException { - switch (fieldIndex) { - case 0: { - instance.allPlanets = (AllPlanet) value; - break; - } - } - } - }, FIELDS); + public Creator creator() { + return CREATOR; + } + + @Override + public AllPlanet.Factory allPlanetFactory() { + return AllPlanet.FACTORY; } }; - @Nullable private AllPlanet allPlanets; + private @Nullable AllPlanet allPlanets; - public Data(ResponseReader reader) throws IOException { - MAPPER.map(reader, this); + public Data(@Nullable AllPlanet allPlanets) { + this.allPlanets = allPlanets; } - @Nullable public AllPlanet allPlanets() { + public @Nullable AllPlanet allPlanets() { return this.allPlanets; } public static class AllPlanet { - private static final ResponseFieldMapper MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { - Field.forList("planets", "planets", null, true, new Field.ObjectReader() { - @Override public Planet read(final ResponseReader reader) throws IOException { - return new Planet(reader); - } - }) - }; + public static final Creator CREATOR = new Creator() { + @Override + public AllPlanet create(@Nullable List planets) { + return new AllPlanet(planets); + } + }; + public static final Factory FACTORY = new Factory() { @Override - public void map(final ResponseReader reader,final AllPlanet instance) throws IOException { - reader.read(new ResponseReader.ValueHandler() { - @Override - public void handle(final int fieldIndex, final Object value) throws IOException { - switch (fieldIndex) { - case 0: { - instance.planets = (List) value; - break; - } - } - } - }, FIELDS); + public Creator creator() { + return CREATOR; + } + + @Override + public Planet.Factory planetFactory() { + return Planet.FACTORY; } }; - @Nullable private List planets; + private @Nullable List planets; - public AllPlanet(ResponseReader reader) throws IOException { - MAPPER.map(reader, this); + public AllPlanet(@Nullable List planets) { + this.planets = planets; } - @Nullable public List planets() { + public @Nullable List planets() { return this.planets; } public static class Planet { - private static final ResponseFieldMapper MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { - Field.forObject("filmConnection", "filmConnection", null, true, new Field.ObjectReader() { - @Override public FilmConnection read(final ResponseReader reader) throws IOException { - return new FilmConnection(reader); - } - }), - Field.forString("__typename", "__typename", null, false) - }; + public static final Creator CREATOR = new Creator() { + @Override + public Planet create(@Nullable FilmConnection filmConnection, Fragments fragments) { + return new Planet(filmConnection, fragments); + } + }; + public static final Factory FACTORY = new Factory() { @Override - public void map(final ResponseReader reader, final Planet instance) throws IOException { - reader.read(new ResponseReader.ValueHandler() { - @Override - public void handle(final int fieldIndex, final Object value) throws IOException { - switch (fieldIndex) { - case 0: { - instance.filmConnection = (FilmConnection) value; - break; - } - case 1: { - String typename = (String) value; - instance.fragments = new Fragments(reader, typename); - break; - } - } - } - }, FIELDS); + public Creator creator() { + return CREATOR; + } + + @Override + public FilmConnection.Factory filmConnectionFactory() { + return FilmConnection.FACTORY; + } + + @Override + public Fragments.Factory fragmentsFactory() { + return Fragments.FACTORY; } }; - @Nullable private FilmConnection filmConnection; + private @Nullable FilmConnection filmConnection; private Fragments fragments; - public Planet(ResponseReader reader) throws IOException { - MAPPER.map(reader.toBufferedReader(), this); + public Planet(@Nullable FilmConnection filmConnection, Fragments fragments) { + this.filmConnection = filmConnection; + this.fragments = fragments; } - @Nullable public FilmConnection filmConnection() { + public @Nullable FilmConnection filmConnection() { return this.filmConnection; } @@ -168,126 +158,472 @@ public Fragments fragments() { } public static class FilmConnection { - private static final ResponseFieldMapper MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { - Field.forInt("totalCount", "totalCount", null, true), - Field.forList("films", "films", null, true, new Field.ObjectReader() { - @Override public Film read(final ResponseReader reader) throws IOException { - return new Film(reader); - } - }) - }; + public static final Creator CREATOR = new Creator() { + @Override + public FilmConnection create(@Nullable Integer totalCount, + @Nullable List films) { + return new FilmConnection(totalCount, films); + } + }; + public static final Factory FACTORY = new Factory() { @Override - public void map(final ResponseReader reader, final FilmConnection instance) throws IOException { - reader.read(new ResponseReader.ValueHandler() { - @Override - public void handle(final int fieldIndex, final Object value) throws IOException { - switch (fieldIndex) { - case 0: { - instance.totalCount = (Integer) value; - break; - } - case 1: { - instance.films = (List) value; - break; - } - } - } - }, FIELDS); + public Creator creator() { + return CREATOR; + } + + @Override + public Film.Factory filmFactory() { + return Film.FACTORY; } }; - @Nullable private Integer totalCount; + private @Nullable Integer totalCount; - @Nullable private List films; + private @Nullable List films; - public FilmConnection(ResponseReader reader) throws IOException { - MAPPER.map(reader, this); + public FilmConnection(@Nullable Integer totalCount, + @Nullable List films) { + this.totalCount = totalCount; + this.films = films; } - @Nullable public Integer totalCount() { + public @Nullable Integer totalCount() { return this.totalCount; } - @Nullable public List films() { + public @Nullable List films() { return this.films; } public static class Film { - private static final ResponseFieldMapper MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { + public static final Creator CREATOR = new Creator() { + @Override + public Film create(@Nullable String title, Fragments fragments) { + return new Film(title, fragments); + } + }; + + public static final Factory FACTORY = new Factory() { + @Override + public Creator creator() { + return CREATOR; + } + + @Override + public Fragments.Factory fragmentsFactory() { + return Fragments.FACTORY; + } + }; + + private @Nullable String title; + + private Fragments fragments; + + public Film(@Nullable String title, Fragments fragments) { + this.title = title; + this.fragments = fragments; + } + + public @Nullable String title() { + return this.title; + } + + public Fragments fragments() { + return this.fragments; + } + + public static class Fragments { + public static final Creator CREATOR = new Creator() { + @Override + public Fragments create(FilmFragment filmFragment) { + return new Fragments(filmFragment); + } + }; + + public static final Factory FACTORY = new Factory() { + @Override + public Creator creator() { + return CREATOR; + } + + @Override + public FilmFragment.Factory filmFragmentFactory() { + return FilmFragment.FACTORY; + } + }; + + private FilmFragment filmFragment; + + public Fragments(FilmFragment filmFragment) { + this.filmFragment = filmFragment; + } + + public FilmFragment filmFragment() { + return this.filmFragment; + } + + public interface Factory { + Creator creator(); + + FilmFragment.Factory filmFragmentFactory(); + } + + public interface Creator { + Fragments create(FilmFragment filmFragment); + } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + String conditionalType; + + public Mapper(@Nonnull Factory factory, @Nonnull String conditionalType) { + this.factory = factory; + this.conditionalType = conditionalType; + } + + @Override + public Fragments map(ResponseReader reader) throws IOException { + FilmFragment filmfragment = null; + if (conditionalType.equals(FilmFragment.TYPE_CONDITION)) { + filmfragment = new FilmFragment.Mapper(factory.filmFragmentFactory()).map(reader); + } + return factory.creator().create(filmfragment); + } + } + } + + public interface Factory { + Creator creator(); + + Fragments.Factory fragmentsFactory(); + } + + public interface Creator { + Film create(@Nullable String title, Fragments fragments); + } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { Field.forString("title", "title", null, true), - Field.forString("__typename", "__typename", null, false) + Field.forConditionalType("__typename", "__typename", new Field.ConditionalTypeReader() { + @Override + public Fragments read(String conditionalType, ResponseReader reader) throws + IOException { + return new Fragments.Mapper(factory.fragmentsFactory(), conditionalType).map(reader); + } + }) }; + public Mapper(@Nonnull Factory factory) { + this.factory = factory; + } + @Override - public void map(final ResponseReader reader, final Film instance) throws IOException { - reader.read(new ResponseReader.ValueHandler() { + public Film map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); + reader.toBufferedReader().read(new ResponseReader.ValueHandler() { @Override public void handle(final int fieldIndex, final Object value) throws IOException { switch (fieldIndex) { case 0: { - instance.title = (String) value; + contentValues.title = (String) value; break; } case 1: { - String typename = (String) value; - instance.fragments = new Fragments(reader, typename); + contentValues.fragments = (Fragments) value; break; } } } - }, FIELDS); + }, fields); + return factory.creator().create(contentValues.title, contentValues.fragments); } + + static final class __ContentValues { + String title; + + Fragments fragments; + } + } + } + + public interface Factory { + Creator creator(); + + Film.Factory filmFactory(); + } + + public interface Creator { + FilmConnection create(@Nullable Integer totalCount, + @Nullable List films); + } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { + Field.forInt("totalCount", "totalCount", null, true), + Field.forList("films", "films", null, true, new Field.ObjectReader() { + @Override public Film read(final ResponseReader reader) throws IOException { + return new Film.Mapper(factory.filmFactory()).map(reader); + } + }) }; - @Nullable private String title; + public Mapper(@Nonnull Factory factory) { + this.factory = factory; + } - private Fragments fragments; + @Override + public FilmConnection map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); + reader.read(new ResponseReader.ValueHandler() { + @Override + public void handle(final int fieldIndex, final Object value) throws IOException { + switch (fieldIndex) { + case 0: { + contentValues.totalCount = (Integer) value; + break; + } + case 1: { + contentValues.films = (List) value; + break; + } + } + } + }, fields); + return factory.creator().create(contentValues.totalCount, contentValues.films); + } - public Film(ResponseReader reader) throws IOException { - MAPPER.map(reader.toBufferedReader(), this); + static final class __ContentValues { + Integer totalCount; + + List films; } + } + } - @Nullable public String title() { - return this.title; + public static class Fragments { + public static final Creator CREATOR = new Creator() { + @Override + public Fragments create(PlanetFragment planetFargment) { + return new Fragments(planetFargment); } + }; - public Fragments fragments() { - return this.fragments; + public static final Factory FACTORY = new Factory() { + @Override + public Creator creator() { + return CREATOR; } - public static class Fragments { - private FilmFragment filmFragment; + @Override + public PlanetFragment.Factory planetFargmentFactory() { + return PlanetFragment.FACTORY; + } + }; - Fragments(ResponseReader reader, String __typename) throws IOException { - if (__typename.equals(FilmFragment.TYPE_CONDITION)) { - this.filmFragment = new FilmFragment(reader); - } - } + private PlanetFragment planetFargment; - public FilmFragment filmFragment() { - return this.filmFragment; + public Fragments(PlanetFragment planetFargment) { + this.planetFargment = planetFargment; + } + + public PlanetFragment planetFargment() { + return this.planetFargment; + } + + public interface Factory { + Creator creator(); + + PlanetFragment.Factory planetFargmentFactory(); + } + + public interface Creator { + Fragments create(PlanetFragment planetFargment); + } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + String conditionalType; + + public Mapper(@Nonnull Factory factory, @Nonnull String conditionalType) { + this.factory = factory; + this.conditionalType = conditionalType; + } + + @Override + public Fragments map(ResponseReader reader) throws IOException { + PlanetFragment planetfargment = null; + if (conditionalType.equals(PlanetFragment.TYPE_CONDITION)) { + planetfargment = new PlanetFragment.Mapper(factory.planetFargmentFactory()).map(reader); } + return factory.creator().create(planetfargment); } } } - public static class Fragments { - private PlanetFragment planetFragment; + public interface Factory { + Creator creator(); - Fragments(ResponseReader reader, String __typename) throws IOException { - if (__typename.equals(PlanetFragment.TYPE_CONDITION)) { - this.planetFragment = new PlanetFragment(reader); - } + FilmConnection.Factory filmConnectionFactory(); + + Fragments.Factory fragmentsFactory(); + } + + public interface Creator { + Planet create(@Nullable FilmConnection filmConnection, Fragments fragments); + } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { + Field.forObject("filmConnection", "filmConnection", null, true, new Field.ObjectReader() { + @Override public FilmConnection read(final ResponseReader reader) throws IOException { + return new FilmConnection.Mapper(factory.filmConnectionFactory()).map(reader); + } + }), + Field.forConditionalType("__typename", "__typename", new Field.ConditionalTypeReader() { + @Override + public Fragments read(String conditionalType, ResponseReader reader) throws + IOException { + return new Fragments.Mapper(factory.fragmentsFactory(), conditionalType).map(reader); + } + }) + }; + + public Mapper(@Nonnull Factory factory) { + this.factory = factory; } - public PlanetFragment planetFargment() { - return this.planetFragment; + @Override + public Planet map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); + reader.toBufferedReader().read(new ResponseReader.ValueHandler() { + @Override + public void handle(final int fieldIndex, final Object value) throws IOException { + switch (fieldIndex) { + case 0: { + contentValues.filmConnection = (FilmConnection) value; + break; + } + case 1: { + contentValues.fragments = (Fragments) value; + break; + } + } + } + }, fields); + return factory.creator().create(contentValues.filmConnection, contentValues.fragments); + } + + static final class __ContentValues { + FilmConnection filmConnection; + + Fragments fragments; } } } + + public interface Factory { + Creator creator(); + + Planet.Factory planetFactory(); + } + + public interface Creator { + AllPlanet create(@Nullable List planets); + } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { + Field.forList("planets", "planets", null, true, new Field.ObjectReader() { + @Override public Planet read(final ResponseReader reader) throws IOException { + return new Planet.Mapper(factory.planetFactory()).map(reader); + } + }) + }; + + public Mapper(@Nonnull Factory factory) { + this.factory = factory; + } + + @Override + public AllPlanet map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); + reader.read(new ResponseReader.ValueHandler() { + @Override + public void handle(final int fieldIndex, final Object value) throws IOException { + switch (fieldIndex) { + case 0: { + contentValues.planets = (List) value; + break; + } + } + } + }, fields); + return factory.creator().create(contentValues.planets); + } + + static final class __ContentValues { + List planets; + } + } + } + + public interface Factory { + Creator creator(); + + AllPlanet.Factory allPlanetFactory(); + } + + public interface Creator { + Data create(@Nullable AllPlanet allPlanets); + } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { + Field.forObject("allPlanets", "allPlanets", null, true, new Field.ObjectReader() { + @Override public AllPlanet read(final ResponseReader reader) throws IOException { + return new AllPlanet.Mapper(factory.allPlanetFactory()).map(reader); + } + }) + }; + + public Mapper(@Nonnull Factory factory) { + this.factory = factory; + } + + @Override + public Data map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); + reader.read(new ResponseReader.ValueHandler() { + @Override + public void handle(final int fieldIndex, final Object value) throws IOException { + switch (fieldIndex) { + case 0: { + contentValues.allPlanets = (AllPlanet) value; + break; + } + } + } + }, fields); + return factory.creator().create(contentValues.allPlanets); + } + + static final class __ContentValues { + AllPlanet allPlanets; + } } } } diff --git a/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/FilmFragment.java b/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/FilmFragment.java deleted file mode 100644 index 0736d7dd004..00000000000 --- a/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/FilmFragment.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.apollographql.android.converter.pojo; - -import com.apollographql.android.api.graphql.Field; -import com.apollographql.android.api.graphql.ResponseFieldMapper; -import com.apollographql.android.api.graphql.ResponseReader; -import java.io.IOException; -import java.lang.Object; -import java.lang.Override; -import java.lang.String; -import java.util.List; -import javax.annotation.Nullable; - -public class FilmFragment { - private static final ResponseFieldMapper MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { - Field.forString("title", "title", null, true), - Field.forList("producers", "producers", null, true, new Field.ListReader() { - @Override public String read(final Field.ListItemReader reader) throws IOException { - return reader.readString(); - } - }) - }; - - @Override public void map(final ResponseReader reader, final FilmFragment instance) throws IOException { - reader.read(new ResponseReader.ValueHandler() { - @Override - public void handle(final int fieldIndex, final Object value) throws IOException { - switch (fieldIndex) { - case 0: { - instance.title = (String) value; - break; - } - case 1: { - instance.producers = (List) value; - break; - } - } - } - }, FIELDS); - } - }; - - public static final String FRAGMENT_DEFINITION = "fragment FilmFragment on Film {\n" - + " title\n" - + " producers\n" - + "}"; - - public static final String TYPE_CONDITION = "Film"; - - @Nullable private String title; - - @Nullable private List producers; - - public FilmFragment(ResponseReader reader) throws IOException { - MAPPER.map(reader, this); - } - - @Nullable public String title() { - return this.title; - } - - @Nullable public List producers() { - return this.producers; - } -} diff --git a/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/IntegrationTest.java b/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/IntegrationTest.java index a137f8cd710..b8c504f0769 100644 --- a/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/IntegrationTest.java +++ b/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/IntegrationTest.java @@ -67,6 +67,9 @@ Call> productsWithDate(@Body OperationRequest MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { - Field.forString("name", "name", null, true), - Field.forList("climates", "climates", null, true, new Field.ListReader() { - @Override public String read(final Field.ListItemReader reader) throws IOException { - return reader.readString(); - } - }), - Field.forDouble("surfaceWater", "surfaceWater", null, true) - }; - - @Override public void map(final ResponseReader reader, final PlanetFragment instance) throws IOException { - reader.read(new ResponseReader.ValueHandler() { - @Override - public void handle(final int fieldIndex, final Object value) throws IOException { - switch (fieldIndex) { - case 0: { - instance.name = (String) value; - break; - } - case 1: { - instance.climates = (List) value; - break; - } - case 2: { - instance.surfaceWater = (Double) value; - break; - } - } - } - }, FIELDS); - } - }; - - public static final String FRAGMENT_DEFINITION = "fragment PlanetFragment on Planet {\n" - + " name\n" - + " climates\n" - + " surfaceWater\n" - + "}"; - - public static final String TYPE_CONDITION = "Planet"; - - @Nullable private String name; - - @Nullable private List climates; - - @Nullable private Double surfaceWater; - - public PlanetFragment(ResponseReader reader) throws IOException { - MAPPER.map(reader, this); - } - - @Nullable public String name() { - return this.name; - } - - @Nullable public List climates() { - return this.climates; - } - - @Nullable public Double surfaceWater() { - return this.surfaceWater; - } -} diff --git a/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/ProductsWithDate.java b/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/ProductsWithDate.java index 1eedb3dc28a..fe9fe3974ca 100644 --- a/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/ProductsWithDate.java +++ b/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/ProductsWithDate.java @@ -48,35 +48,29 @@ public Variables variables() { } public static class Data implements Operation.Data { - private static final ResponseFieldMapper MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { - Field.forObject("shop", "shop", null, false, new Field.ObjectReader() { - @Override public Shop read(final ResponseReader reader) throws IOException { - return new Shop(reader); - } - }) - }; + public static final Creator CREATOR = new Creator() { + @Override + public Data create(@Nonnull Shop shop) { + return new Data(shop); + } + }; + public static final Factory FACTORY = new Factory() { @Override - public void map(final ResponseReader reader, final Data instance) throws IOException { - reader.read(new ResponseReader.ValueHandler() { - @Override - public void handle(final int fieldIndex, final Object value) throws IOException { - switch (fieldIndex) { - case 0: { - instance.shop = (Shop) value; - break; - } - } - } - }, FIELDS); + public Creator creator() { + return CREATOR; + } + + @Override + public Shop.Factory shopFactory() { + return Shop.FACTORY; } }; private @Nonnull Shop shop; - public Data(ResponseReader reader) throws IOException { - MAPPER.map(reader, this); + public Data(@Nonnull Shop shop) { + this.shop = shop; } public @Nonnull Shop shop() { @@ -84,35 +78,29 @@ public Data(ResponseReader reader) throws IOException { } public static class Shop { - private static final ResponseFieldMapper MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { - Field.forObject("products", "products", null, false, new Field.ObjectReader() { - @Override public Product read(final ResponseReader reader) throws IOException { - return new Product(reader); - } - }) - }; + public static final Creator CREATOR = new Creator() { + @Override + public Shop create(@Nonnull Product products) { + return new Shop(products); + } + }; + public static final Factory FACTORY = new Factory() { @Override - public void map(final ResponseReader reader, final Shop instance) throws IOException { - reader.read(new ResponseReader.ValueHandler() { - @Override - public void handle(final int fieldIndex, final Object value) throws IOException { - switch (fieldIndex) { - case 0: { - instance.products = (Product) value; - break; - } - } - } - }, FIELDS); + public Creator creator() { + return CREATOR; + } + + @Override + public Product.Factory productFactory() { + return Product.FACTORY; } }; private @Nonnull Product products; - public Shop(ResponseReader reader) throws IOException { - MAPPER.map(reader, this); + public Shop(@Nonnull Product products) { + this.products = products; } public @Nonnull Product products() { @@ -120,35 +108,29 @@ public Shop(ResponseReader reader) throws IOException { } public static class Product { - private static final ResponseFieldMapper MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { - Field.forList("edges", "edges", null, false, new Field.ObjectReader() { - @Override public Edge read(final ResponseReader reader) throws IOException { - return new Edge(reader); - } - }) - }; + public static final Creator CREATOR = new Creator() { + @Override + public Product create(@Nonnull List edges) { + return new Product(edges); + } + }; + public static final Factory FACTORY = new Factory() { @Override - public void map(final ResponseReader reader, final Product instance) throws IOException { - reader.read(new ResponseReader.ValueHandler() { - @Override - public void handle(final int fieldIndex, final Object value) throws IOException { - switch (fieldIndex) { - case 0: { - instance.edges = (List) value; - break; - } - } - } - }, FIELDS); + public Creator creator() { + return CREATOR; + } + + @Override + public Edge.Factory edgeFactory() { + return Edge.FACTORY; } }; private @Nonnull List edges; - public Product(ResponseReader reader) throws IOException { - MAPPER.map(reader, this); + public Product(@Nonnull List edges) { + this.edges = edges; } public @Nonnull List edges() { @@ -156,35 +138,29 @@ public Product(ResponseReader reader) throws IOException { } public static class Edge { - private static final ResponseFieldMapper MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { - Field.forObject("node", "node", null, false, new Field.ObjectReader() { - @Override public Node read(final ResponseReader reader) throws IOException { - return new Node(reader); - } - }) - }; + public static final Creator CREATOR = new Creator() { + @Override + public Edge create(@Nonnull Node node) { + return new Edge(node); + } + }; + public static final Factory FACTORY = new Factory() { @Override - public void map(final ResponseReader reader, final Edge instance) throws IOException { - reader.read(new ResponseReader.ValueHandler() { - @Override - public void handle(final int fieldIndex, final Object value) throws IOException { - switch (fieldIndex) { - case 0: { - instance.node = (Node) value; - break; - } - } - } - }, FIELDS); + public Creator creator() { + return CREATOR; + } + + @Override + public Node.Factory nodeFactory() { + return Node.FACTORY; } }; private @Nonnull Node node; - public Edge(ResponseReader reader) throws IOException { - MAPPER.map(reader, this); + public Edge(@Nonnull Node node) { + this.node = node; } public @Nonnull Node node() { @@ -192,49 +168,274 @@ public Edge(ResponseReader reader) throws IOException { } public static class Node { - private static final ResponseFieldMapper MAPPER = new ResponseFieldMapper() { - private final Field[] FIELDS = { + public static final Creator CREATOR = new Creator() { + @Override + public Node create(@Nonnull String title, @Nonnull Date createdAt) { + return new Node(title, createdAt); + } + }; + + public static final Factory FACTORY = new Factory() { + @Override + public Creator creator() { + return CREATOR; + } + }; + + private @Nonnull String title; + + private @Nonnull Date createdAt; + + public Node(@Nonnull String title, @Nonnull Date createdAt) { + this.title = title; + this.createdAt = createdAt; + } + + public @Nonnull String title() { + return this.title; + } + + public @Nonnull Date createdAt() { + return this.createdAt; + } + + public interface Factory { + Creator creator(); + } + + public interface Creator { + Node create(@Nonnull String title, @Nonnull Date createdAt); + } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { Field.forString("title", "title", null, false), Field.forCustomType("createdAt", "createdAt", null, false, CustomType.DATETIME) }; + public Mapper(@Nonnull Factory factory) { + this.factory = factory; + } + @Override - public void map(final ResponseReader reader, final Node instance) throws IOException { + public Node map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); reader.read(new ResponseReader.ValueHandler() { @Override public void handle(final int fieldIndex, final Object value) throws IOException { switch (fieldIndex) { case 0: { - instance.title = (String) value; + contentValues.title = (String) value; break; } case 1: { - instance.createdAt = (Date) value; + contentValues.createdAt = (Date) value; break; } } } - }, FIELDS); + }, fields); + return factory.creator().create(contentValues.title, contentValues.createdAt); } - }; - private @Nonnull String title; + static final class __ContentValues { + String title; - private @Nonnull Date createdAt; + Date createdAt; + } + } + } + + public interface Factory { + Creator creator(); + + Node.Factory nodeFactory(); + } - public Node(ResponseReader reader) throws IOException { - MAPPER.map(reader, this); + public interface Creator { + Edge create(@Nonnull Node node); + } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { + Field.forObject("node", "node", null, false, new Field.ObjectReader() { + @Override public Node read(final ResponseReader reader) throws IOException { + return new Node.Mapper(factory.nodeFactory()).map(reader); + } + }) + }; + + public Mapper(@Nonnull Factory factory) { + this.factory = factory; } - public @Nonnull String title() { - return this.title; + @Override + public Edge map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); + reader.read(new ResponseReader.ValueHandler() { + @Override + public void handle(final int fieldIndex, final Object value) throws IOException { + switch (fieldIndex) { + case 0: { + contentValues.node = (Node) value; + break; + } + } + } + }, fields); + return factory.creator().create(contentValues.node); } - public @Nonnull Date createdAt() { - return this.createdAt; + static final class __ContentValues { + Node node; } } } + + public interface Factory { + Creator creator(); + + Edge.Factory edgeFactory(); + } + + public interface Creator { + Product create(@Nonnull List edges); + } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { + Field.forList("edges", "edges", null, false, new Field.ObjectReader() { + @Override public Edge read(final ResponseReader reader) throws IOException { + return new Edge.Mapper(factory.edgeFactory()).map(reader); + } + }) + }; + + public Mapper(@Nonnull Factory factory) { + this.factory = factory; + } + + @Override + public Product map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); + reader.read(new ResponseReader.ValueHandler() { + @Override + public void handle(final int fieldIndex, final Object value) throws IOException { + switch (fieldIndex) { + case 0: { + contentValues.edges = (List) value; + break; + } + } + } + }, fields); + return factory.creator().create(contentValues.edges); + } + + static final class __ContentValues { + List edges; + } + } + } + + public interface Factory { + Creator creator(); + + Product.Factory productFactory(); + } + + public interface Creator { + Shop create(@Nonnull Product products); + } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { + Field.forObject("products", "products", null, false, new Field.ObjectReader() { + @Override public Product read(final ResponseReader reader) throws IOException { + return new Product.Mapper(factory.productFactory()).map(reader); + } + }) + }; + + public Mapper(@Nonnull Factory factory) { + this.factory = factory; + } + + @Override + public Shop map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); + reader.read(new ResponseReader.ValueHandler() { + @Override + public void handle(final int fieldIndex, final Object value) throws IOException { + switch (fieldIndex) { + case 0: { + contentValues.products = (Product) value; + break; + } + } + } + }, fields); + return factory.creator().create(contentValues.products); + } + + static final class __ContentValues { + Product products; + } + } + } + + public interface Factory { + Creator creator(); + + Shop.Factory shopFactory(); + } + + public interface Creator { + Data create(@Nonnull Shop shop); + } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { + Field.forObject("shop", "shop", null, false, new Field.ObjectReader() { + @Override public Shop read(final ResponseReader reader) throws IOException { + return new Shop.Mapper(factory.shopFactory()).map(reader); + } + }) + }; + + public Mapper(@Nonnull Factory factory) { + this.factory = factory; + } + + @Override + public Data map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); + reader.read(new ResponseReader.ValueHandler() { + @Override + public void handle(final int fieldIndex, final Object value) throws IOException { + switch (fieldIndex) { + case 0: { + contentValues.shop = (Shop) value; + break; + } + } + } + }, fields); + return factory.creator().create(contentValues.shop); + } + + static final class __ContentValues { + Shop shop; } } } diff --git a/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/fragment/FilmFragment.java b/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/fragment/FilmFragment.java new file mode 100644 index 00000000000..b0bce4c1ecd --- /dev/null +++ b/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/fragment/FilmFragment.java @@ -0,0 +1,106 @@ +package com.apollographql.android.converter.pojo.fragment; + +import com.apollographql.android.api.graphql.Field; +import com.apollographql.android.api.graphql.ResponseFieldMapper; +import com.apollographql.android.api.graphql.ResponseReader; +import java.io.IOException; +import java.lang.Object; +import java.lang.Override; +import java.lang.String; +import java.util.List; +import javax.annotation.Generated; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +@Generated("Apollo GraphQL") +public class FilmFragment { + public static final Creator CREATOR = new Creator() { + @Override + public FilmFragment create(@Nullable String title, @Nullable List producers) { + return new FilmFragment(title, producers); + } + }; + + public static final Factory FACTORY = new Factory() { + @Override + public Creator creator() { + return CREATOR; + } + }; + + public static final String FRAGMENT_DEFINITION = "fragment FilmFragment on Film {\n" + + " title\n" + + " producers\n" + + "}"; + + public static final String TYPE_CONDITION = "Film"; + + private @Nullable String title; + + private @Nullable List producers; + + public FilmFragment(@Nullable String title, @Nullable List producers) { + this.title = title; + this.producers = producers; + } + + public @Nullable String title() { + return this.title; + } + + public @Nullable List producers() { + return this.producers; + } + + public interface Factory { + Creator creator(); + } + + public interface Creator { + FilmFragment create(@Nullable String title, @Nullable List producers); + } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { + Field.forString("title", "title", null, true), + Field.forList("producers", "producers", null, true, new Field.ListReader() { + @Override public String read(final Field.ListItemReader reader) throws IOException { + return reader.readString(); + } + }) + }; + + public Mapper(@Nonnull Factory factory) { + this.factory = factory; + } + + @Override + public FilmFragment map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); + reader.read(new ResponseReader.ValueHandler() { + @Override + public void handle(final int fieldIndex, final Object value) throws IOException { + switch (fieldIndex) { + case 0: { + contentValues.title = (String) value; + break; + } + case 1: { + contentValues.producers = (List) value; + break; + } + } + } + }, fields); + return factory.creator().create(contentValues.title, contentValues.producers); + } + + static final class __ContentValues { + String title; + + List producers; + } + } +} diff --git a/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/fragment/PlanetFragment.java b/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/fragment/PlanetFragment.java new file mode 100644 index 00000000000..da0785f7676 --- /dev/null +++ b/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/fragment/PlanetFragment.java @@ -0,0 +1,125 @@ +package com.apollographql.android.converter.pojo.fragment; + +import com.apollographql.android.api.graphql.Field; +import com.apollographql.android.api.graphql.ResponseFieldMapper; +import com.apollographql.android.api.graphql.ResponseReader; +import java.io.IOException; +import java.lang.Double; +import java.lang.Object; +import java.lang.Override; +import java.lang.String; +import java.util.List; +import javax.annotation.Generated; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +@Generated("Apollo GraphQL") +public class PlanetFragment { + public static final Creator CREATOR = new Creator() { + @Override + public PlanetFragment create(@Nullable String name, @Nullable List climates, + @Nullable Double surfaceWater) { + return new PlanetFragment(name, climates, surfaceWater); + } + }; + + public static final Factory FACTORY = new Factory() { + @Override + public Creator creator() { + return CREATOR; + } + }; + + public static final String FRAGMENT_DEFINITION = "fragment PlanetFragment on Planet {\n" + + " name\n" + + " climates\n" + + " surfaceWater\n" + + "}"; + + public static final String TYPE_CONDITION = "Planet"; + + private @Nullable String name; + + private @Nullable List climates; + + private @Nullable Double surfaceWater; + + public PlanetFragment(@Nullable String name, @Nullable List climates, + @Nullable Double surfaceWater) { + this.name = name; + this.climates = climates; + this.surfaceWater = surfaceWater; + } + + public @Nullable String name() { + return this.name; + } + + public @Nullable List climates() { + return this.climates; + } + + public @Nullable Double surfaceWater() { + return this.surfaceWater; + } + + public interface Factory { + Creator creator(); + } + + public interface Creator { + PlanetFragment create(@Nullable String name, @Nullable List climates, + @Nullable Double surfaceWater); + } + + public static final class Mapper implements ResponseFieldMapper { + final Factory factory; + + final Field[] fields = { + Field.forString("name", "name", null, true), + Field.forList("climates", "climates", null, true, new Field.ListReader() { + @Override public String read(final Field.ListItemReader reader) throws IOException { + return reader.readString(); + } + }), + Field.forDouble("surfaceWater", "surfaceWater", null, true) + }; + + public Mapper(@Nonnull Factory factory) { + this.factory = factory; + } + + @Override + public PlanetFragment map(ResponseReader reader) throws IOException { + final __ContentValues contentValues = new __ContentValues(); + reader.read(new ResponseReader.ValueHandler() { + @Override + public void handle(final int fieldIndex, final Object value) throws IOException { + switch (fieldIndex) { + case 0: { + contentValues.name = (String) value; + break; + } + case 1: { + contentValues.climates = (List) value; + break; + } + case 2: { + contentValues.surfaceWater = (Double) value; + break; + } + } + } + }, fields); + return factory.creator().create(contentValues.name, contentValues.climates, contentValues.surfaceWater); + } + + static final class __ContentValues { + String name; + + List climates; + + Double surfaceWater; + } + } +} diff --git a/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/type/CustomType.java b/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/type/CustomType.java new file mode 100644 index 00000000000..1db4df03b8d --- /dev/null +++ b/apollo-converters/pojo/src/test/java/com/apollographql/android/converter/pojo/type/CustomType.java @@ -0,0 +1,14 @@ +package com.apollographql.android.converter.pojo.type; + +import com.apollographql.android.api.graphql.ScalarType; +import java.lang.String; +import javax.annotation.Generated; + +@Generated("Apollo GraphQL") +public enum CustomType implements ScalarType { + DATETIME { + public String typeName() { + return "DateTime"; + } + } +} diff --git a/apollo-gradle-plugin/src/test/groovy/com/apollographql/android/gradle/integration/ApolloPluginBasicAndroidTest.groovy b/apollo-gradle-plugin/src/test/groovy/com/apollographql/android/gradle/integration/ApolloPluginBasicAndroidTest.groovy index 4fe054510fb..db153ea7812 100644 --- a/apollo-gradle-plugin/src/test/groovy/com/apollographql/android/gradle/integration/ApolloPluginBasicAndroidTest.groovy +++ b/apollo-gradle-plugin/src/test/groovy/com/apollographql/android/gradle/integration/ApolloPluginBasicAndroidTest.groovy @@ -22,11 +22,11 @@ class ApolloPluginBasicAndroidTest extends Specification { when: def result = GradleRunner.create().withProjectDir(testProjectDir) .withPluginClasspath() - .withArguments("build") + .withArguments("generateApolloClasses") .forwardStdError(new OutputStreamWriter(System.err)).build() then: - result.task(":build").outcome == TaskOutcome.SUCCESS + result.task(":generateApolloClasses").outcome == TaskOutcome.SUCCESS // IR Files generated successfully assert new File(testProjectDir, "build/generated/source/apollo/generatedIR/src/main/graphql/ReleaseAPI.json").isFile() From 59b6d4bebeb28dae65a4b157cec69487298424a2 Mon Sep 17 00:00:00 2001 From: Ivan Savytskyi Date: Sat, 4 Feb 2017 01:58:42 -0500 Subject: [PATCH 2/5] Remove garbage --- .../com/example/all_planets/TestQuery.json | 115 ------------------ .../products_with_dates/TestQuery.json | 68 ----------- .../android/compiler/CodegenTest.kt | 4 - 3 files changed, 187 deletions(-) delete mode 100644 apollo-compiler/src/test/graphql/com/example/all_planets/TestQuery.json delete mode 100644 apollo-compiler/src/test/graphql/com/example/products_with_dates/TestQuery.json diff --git a/apollo-compiler/src/test/graphql/com/example/all_planets/TestQuery.json b/apollo-compiler/src/test/graphql/com/example/all_planets/TestQuery.json deleted file mode 100644 index 47eefd37976..00000000000 --- a/apollo-compiler/src/test/graphql/com/example/all_planets/TestQuery.json +++ /dev/null @@ -1,115 +0,0 @@ -{ - "operations": [ - { - "operationName": "TestQuery", - "operationType": "query", - "variables": [], - "source": "query TestQuery {\n allPlanets(first: 300) {\n planets {\n ...PlanetFargment\n filmConnection {\n totalCount\n films {\n title\n ...FilmFragment\n }\n }\n }\n }\n}", - "fields": [ - { - "responseName": "allPlanets", - "fieldName": "allPlanets", - "type": "PlanetsConnection", - "fields": [ - { - "responseName": "planets", - "fieldName": "planets", - "type": "[Planet]", - "fields": [ - { - "responseName": "filmConnection", - "fieldName": "filmConnection", - "type": "PlanetFilmsConnection", - "fields": [ - { - "responseName": "totalCount", - "fieldName": "totalCount", - "type": "Int" - }, - { - "responseName": "films", - "fieldName": "films", - "type": "[Film]", - "fields": [ - { - "responseName": "title", - "fieldName": "title", - "type": "String" - } - ], - "fragmentSpreads": [ - "FilmFragment" - ], - "inlineFragments": [] - } - ], - "fragmentSpreads": [], - "inlineFragments": [] - } - ], - "fragmentSpreads": [ - "PlanetFargment" - ], - "inlineFragments": [] - } - ], - "fragmentSpreads": [], - "inlineFragments": [] - } - ], - "fragmentsReferenced": [ - "FilmFragment", - "PlanetFargment" - ], - "filePath": "src/test/graphql/com/example/all_planets/TestQuery.json" - } - ], - "fragments": [ - { - "fragmentName": "PlanetFargment", - "source": "fragment PlanetFargment on Planet {\n name\n climates\n surfaceWater\n}", - "typeCondition": "Planet", - "fields": [ - { - "responseName": "name", - "fieldName": "name", - "type": "String" - }, - { - "responseName": "climates", - "fieldName": "climates", - "type": "[String]" - }, - { - "responseName": "surfaceWater", - "fieldName": "surfaceWater", - "type": "Float" - } - ], - "fragmentSpreads": [], - "inlineFragments": [], - "fragmentsReferenced": [] - }, - { - "fragmentName": "FilmFragment", - "source": "fragment FilmFragment on Film {\n title\n producers\n}", - "typeCondition": "Film", - "fields": [ - { - "responseName": "title", - "fieldName": "title", - "type": "String" - }, - { - "responseName": "producers", - "fieldName": "producers", - "type": "[String]" - } - ], - "fragmentSpreads": [], - "inlineFragments": [], - "fragmentsReferenced": [] - } - ], - "typesUsed": [] -} \ No newline at end of file diff --git a/apollo-compiler/src/test/graphql/com/example/products_with_dates/TestQuery.json b/apollo-compiler/src/test/graphql/com/example/products_with_dates/TestQuery.json deleted file mode 100644 index adf90558165..00000000000 --- a/apollo-compiler/src/test/graphql/com/example/products_with_dates/TestQuery.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "operations": [ - { - "operationName": "TestQuery", - "operationType": "query", - "variables": [], - "source": "query TestQuery {\n shop {\n products(first: 50) {\n edges {\n node {\n title\n createdAt\n }\n }\n }\n }\n}", - "fields": [ - { - "responseName": "shop", - "fieldName": "shop", - "type": "Shop!", - "fields": [ - { - "responseName": "products", - "fieldName": "products", - "type": "ProductConnection!", - "fields": [ - { - "responseName": "edges", - "fieldName": "edges", - "type": "[ProductEdge!]!", - "fields": [ - { - "responseName": "node", - "fieldName": "node", - "type": "Product!", - "fields": [ - { - "responseName": "title", - "fieldName": "title", - "type": "String!" - }, - { - "responseName": "createdAt", - "fieldName": "createdAt", - "type": "DateTime!" - } - ], - "fragmentSpreads": [], - "inlineFragments": [] - } - ], - "fragmentSpreads": [], - "inlineFragments": [] - } - ], - "fragmentSpreads": [], - "inlineFragments": [] - } - ], - "fragmentSpreads": [], - "inlineFragments": [] - } - ], - "fragmentsReferenced": [], - "filePath": "src/test/graphql/com/example/products_with_dates/TestQuery.json" - } - ], - "fragments": [], - "typesUsed": [ - { - "kind": "ScalarType", - "name": "DateTime", - "description": null - } - ] -} \ No newline at end of file diff --git a/apollo-compiler/src/test/kotlin/com/apollographql/android/compiler/CodegenTest.kt b/apollo-compiler/src/test/kotlin/com/apollographql/android/compiler/CodegenTest.kt index 6ae9f7c964e..8b0a89563ae 100644 --- a/apollo-compiler/src/test/kotlin/com/apollographql/android/compiler/CodegenTest.kt +++ b/apollo-compiler/src/test/kotlin/com/apollographql/android/compiler/CodegenTest.kt @@ -74,10 +74,6 @@ class CodeGenTest(val testDir: File, val pkgName: String, val generatePOJO: Bool arrayOf(it, it.name, false, mapOf("Date" to "java.util.Date", "UnsupportedType" to "java.lang.Object")) } else if (it.name == "pojo_custom_scalar_type") { arrayOf(it, it.name, true, mapOf("Date" to "java.util.Date", "UnsupportedType" to "java.lang.Object")) -// } else if (it.name == "scalar_types") { -// arrayOf(it, it.name, true, emptyMap()) -// } else if (it.name == "products_with_dates") { -// arrayOf(it, it.name, true, mapOf("DateTime" to "java.util.Date")) } else { arrayOf(it, it.name, it.name.startsWith("pojo"), emptyMap()) } From 71e1cc69d9b4d8340e8842c354a392e1e7796366 Mon Sep 17 00:00:00 2001 From: Ivan Savytskyi Date: Sat, 4 Feb 2017 16:48:49 -0500 Subject: [PATCH 3/5] Refactoring --- .../FragmentsResponseMapperBuilder.kt | 95 ++-- .../SchemaTypeResponseMapperBuilder.kt | 417 +++++++++++------- .../TestQueryExpected.java | 6 +- 3 files changed, 319 insertions(+), 199 deletions(-) diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/FragmentsResponseMapperBuilder.kt b/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/FragmentsResponseMapperBuilder.kt index d73c7d7e957..9bd1b22ee97 100644 --- a/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/FragmentsResponseMapperBuilder.kt +++ b/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/FragmentsResponseMapperBuilder.kt @@ -3,70 +3,114 @@ package com.apollographql.android.compiler import com.apollographql.android.api.graphql.ResponseFieldMapper import com.apollographql.android.api.graphql.ResponseReader import com.apollographql.android.compiler.ir.CodeGenerationContext +import com.apollographql.android.compiler.ir.Fragment import com.squareup.javapoet.* import java.io.IOException import javax.annotation.Nonnull import javax.lang.model.element.Modifier +/** + * Responsible for [Fragments.Mapper] class generation + * + * Example of generated class: + * + * ``` + * public static final class Mapper implements ResponseFieldMapper { + * final Factory factory; + * + * String conditionalType; + * + * public Mapper(@Nonnull Factory factory, @Nonnull String conditionalType) { + * this.factory = factory; + * this.conditionalType = conditionalType; + * } + * + * @Override + * public Fragments map(ResponseReader reader) throws IOException { + * HeroDetails heroDetails = null; + * if (conditionalType.equals(HeroDetails.TYPE_CONDITION)) { + * heroDetails = new HeroDetails.Mapper(factory.heroDetailsFactory()).map(reader); + * } + * return factory.creator().create(heroDetails); + * } + * } + * + *``` + */ class FragmentsResponseMapperBuilder( val fragments: List, val codeGenerationContext: CodeGenerationContext ) { fun build(): TypeSpec { - val contentValueFields = fragments.map { - FieldSpec.builder(ClassName.get(codeGenerationContext.fragmentsPackage, it.capitalize()), it.toLowerCase()) - .build() - } + val fragmentFields = fragments.map { FieldSpec.builder(fragmentType(it), it.decapitalize()).build() } return TypeSpec.classBuilder("Mapper") .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .addSuperinterface(RESPONSE_FIELD_MAPPER_TYPE) .addMethod(constructor()) .addField(FACTORY_FIELD) .addField(FieldSpec.builder(CONDITIONAL_TYPE_PARAM.type, CONDITIONAL_TYPE_PARAM.name).build()) - .addMethod(mapMethod(contentValueFields)) + .addMethod(mapMethod(fragmentFields)) .build() } + private fun fragmentType(fragmentName: String) = + ClassName.get(codeGenerationContext.fragmentsPackage, fragmentName.capitalize()) + private fun constructor(): MethodSpec = MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) .addParameter(FACTORY_PARAM) .addParameter(CONDITIONAL_TYPE_PARAM) - .addStatement("this.\$L = \$L", FACTORY_PARAM.name, FACTORY_PARAM.name) - .addStatement("this.\$L = \$L", CONDITIONAL_TYPE_PARAM.name, CONDITIONAL_TYPE_PARAM.name) + .addStatement("this.$FACTORY_VAR = $FACTORY_VAR") + .addStatement("this.$CONDITIONAL_TYPE_VAR = $CONDITIONAL_TYPE_VAR") .build() - private fun mapMethod(contentValueFields: List) = + private fun mapMethod(fragmentFields: List) = MethodSpec.methodBuilder("map") .addModifiers(Modifier.PUBLIC) .addAnnotation(Override::class.java) .addParameter(READER_PARAM) .addException(IOException::class.java) .returns(SchemaTypeSpecBuilder.FRAGMENTS_TYPE) - .addCode(mapMethodCode(contentValueFields)) + .addCode(mapMethodCode(fragmentFields)) .build() - private fun mapMethodCode(contentValueFields: List) = + private fun mapMethodCode(fragmentFields: List) = CodeBlock.builder() - .add(contentValueFields + .add(initFragmentsCode(fragmentFields)) + .add(createFragmentsCode(fragmentFields)) + .add(");\n") + .build() + + private fun initFragmentsCode(fragmentFields: List) = + CodeBlock.builder() + .add(fragmentFields .fold(CodeBlock.builder()) { builder, field -> builder.addStatement("\$T \$N = null", field.type, field) } .build()) - .add(contentValueFields - .fold(CodeBlock.builder()) { builder, field -> - builder - .beginControlFlow("if (\$L.equals(\$T.TYPE_CONDITION))", CONDITIONAL_TYPE_VAR, field.type) - .addStatement("\$N = new \$T.Mapper(\$N.\$L\$L()).map(\$N)", field, field.type, FACTORY_PARAM, - (field.type as ClassName).simpleName().decapitalize(), Util.FACTORY_TYPE_NAME, - READER_PARAM) - .endControlFlow() - }.build()) - .add("return \$L.\$L().\$L(", FACTORY_VAR, Util.FACTORY_CREATOR_ACCESS_METHOD_NAME, - Util.CREATOR_CREATE_METHOD_NAME) - .add(contentValueFields + .add(fragmentFields + .fold(CodeBlock.builder()) { builder, field -> builder.add(initFragmentCode(field)) } + .build()) + .build() + + private fun initFragmentCode(fragmentField: FieldSpec): CodeBlock { + val fragmentFieldTypeName = (fragmentField.type as ClassName).simpleName() + val factoryAccessorMethodName = fragmentFieldTypeName.decapitalize() + Util.FACTORY_TYPE_NAME + return CodeBlock.builder() + .beginControlFlow("if ($CONDITIONAL_TYPE_VAR.equals(\$T.${Fragment.TYPE_CONDITION_FIELD_NAME}))", + fragmentField.type) + .addStatement("\$N = new \$T.Mapper($FACTORY_VAR.$factoryAccessorMethodName()).map($READER_VAR)", fragmentField, + fragmentField.type) + .endControlFlow() + .build() + } + + private fun createFragmentsCode(fragmentFields: List) = + CodeBlock.builder() + .add("return $FACTORY_VAR.${Util.FACTORY_CREATOR_ACCESS_METHOD_NAME}().${Util.CREATOR_CREATE_METHOD_NAME}(") + .add(fragmentFields .mapIndexed { i, fieldSpec -> CodeBlock.of("\$L\$L", if (i > 0) ", " else "", fieldSpec.name) } .fold(CodeBlock.builder(), CodeBlock.Builder::add) .build()) - .add(");\n") .build() companion object { @@ -81,6 +125,7 @@ class FragmentsResponseMapperBuilder( .addAnnotation(Nonnull::class.java).build() private val FACTORY_FIELD = FieldSpec.builder(Util.FACTORY_INTERFACE_TYPE, Util.FACTORY_TYPE_NAME.decapitalize(), Modifier.FINAL).build() - private val READER_PARAM = ParameterSpec.builder(ResponseReader::class.java, "reader").build() + private val READER_VAR = "reader" + private val READER_PARAM = ParameterSpec.builder(ResponseReader::class.java, READER_VAR).build() } } \ No newline at end of file diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/SchemaTypeResponseMapperBuilder.kt b/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/SchemaTypeResponseMapperBuilder.kt index 218ac51d127..10466398ffa 100644 --- a/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/SchemaTypeResponseMapperBuilder.kt +++ b/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/SchemaTypeResponseMapperBuilder.kt @@ -10,6 +10,89 @@ import java.io.IOException import javax.annotation.Nonnull import javax.lang.model.element.Modifier +/** + * Responsible for any schema type [Mapper] class generation + * + * Example of generated class: + * + * ``` + *public static final class Mapper implements ResponseFieldMapper { + * final Factory factory; + * + * final Field[] fields = { + * Field.forString("name", "name", null, false), + * Field.forCustomType("birthDate", "birthDate", null, false, CustomType.DATE), + * Field.forList("appearanceDates", "appearanceDates", null, false, new Field.ListReader() { + * @Override public Date read(final Field.ListItemReader reader) throws IOException { + * return reader.readCustomType(CustomType.DATE); + * } + * }), + * Field.forConditionalType("__typename", "__typename", new Field.ConditionalTypeReader() { + * @Override + * public Fragments read(String conditionalType, ResponseReader reader) throws IOException { + * return new Fragments.Mapper(factory.fragmentsFactory(), conditionalType).map(reader); + * } + * }), + * Field.forObject("film", "film", null, true, new Field.ObjectReader() { + * @Override public Film read(final ResponseReader reader) throws IOException { + * return new Film.Mapper(factory.filmFactory()).map(reader); + * } + * }) + * }; + * + * public Mapper(@Nonnull Factory factory) { + * this.factory = factory; + * } + * + * @Override + * public Hero map(ResponseReader reader) throws IOException { + * final __ContentValues contentValues = new __ContentValues(); + * reader.read(new ResponseReader.ValueHandler() { + * @Override + * public void handle(final int fieldIndex, final Object value) throws IOException { + * switch (fieldIndex) { + * case 0: { + * contentValues.name = (String) value; + * break; + * } + * case 1: { + * contentValues.birthDate = (Date) value; + * break; + * } + * case 2: { + * contentValues.appearanceDates = (List) value; + * break; + * } + * case 3: { + * contentValues.fragments = (Fragments) value; + * break; + * } + * case 4: { + * contentValues.film = (Film) value; + * break; + * } + * } + * } + * }, fields); + * return factory.creator().create(contentValues.name, contentValues.birthDate, contentValues.appearanceDates, + * fragments, film); + * } + * + * static final class __ContentValues { + * String name; + * + * Date birthDate; + * + * List appearanceDates; + * + * Fragments fragments; + * + * Film film; + * } + *} + * + * ``` + */ class SchemaTypeResponseMapperBuilder( typeName: String, val fields: List, @@ -29,35 +112,24 @@ class SchemaTypeResponseMapperBuilder( .plus(inlineFragments .map { it.fieldSpec() } .map { FieldSpec.builder(it.type.overrideTypeName(typeOverrideMap), it.name).build() }) - .let { - if (fragmentSpreads.isNotEmpty()) { - it.plus(FRAGMENTS_FIELD) - } else { - it - } - } + .let { if (fragmentSpreads.isNotEmpty()) it.plus(FRAGMENTS_FIELD) else it } + return TypeSpec.classBuilder("Mapper") .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .addSuperinterface(responseFieldMapperType) .addMethod(constructor()) .addType(contentValuesType(contentValueFields)) - .addField(factoryField()) - .addField(fieldArrayField()) + .addField(FACTORY_FIELD) + .addField(fieldArray(fields)) .addMethod(mapMethod(contentValueFields)) .build() } - private fun factoryField() = - FieldSpec.builder(Util.FACTORY_INTERFACE_TYPE, FACTORY_VAR, Modifier.FINAL).build() - - private fun constructor(): MethodSpec = + private fun constructor() = MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) - .addParameter( - ParameterSpec.builder(Util.FACTORY_INTERFACE_TYPE, "factory") - .addAnnotation(Nonnull::class.java) - .build()) - .addStatement("this.\$L = \$L", FACTORY_VAR, "factory") + .addParameter(FACTORY_PARAM) + .addStatement("this.$FACTORY_VAR = $FACTORY_VAR") .build() private fun contentValuesType(contentValueFields: List) = @@ -66,24 +138,159 @@ class SchemaTypeResponseMapperBuilder( .addFields(contentValueFields) .build() - private fun fieldArrayField() = + private fun fieldArray(fields: List) = FieldSpec.builder(Array::class.java, FIELDS_VAR) .addModifiers(Modifier.FINAL) .initializer(CodeBlock.builder() .add("{\n") .indent() - .add(fieldFactoryStatements() - .filter { !it.isEmpty } - .foldIndexed(CodeBlock.builder()) { i, builder, code -> - builder.add(if (i > 0) ",\n" else "").add(code) - } - .build()) + .add(fieldFactoriesCode(fields)) .unindent() .add("\n}") .build() ) .build() + private fun fieldFactoriesCode(fields: List) = + fields.map { fieldFactoryCode(it) } + .plus(inlineFragments.map { inlineFragmentFieldFactoryCode(it) }) + .plus(if (fragmentSpreads.isNotEmpty()) fragmentsFieldFactoryCode() else CodeBlock.of("")) + .filter { !it.isEmpty } + .foldIndexed(CodeBlock.builder()) { i, builder, code -> + builder.add(if (i > 0) ",\n" else "").add(code) + } + .build() + + private fun fieldFactoryCode(field: Field): CodeBlock { + val fieldTypeName = field.fieldSpec(context.customTypeMap).type.withoutAnnotations() + if (fieldTypeName.isScalar() || fieldTypeName.isCustomScalarType()) { + return scalarFieldFactoryCode(field, fieldTypeName) + } else if (fieldTypeName.isList()) { + return listFieldFactoryCode(field, fieldTypeName) + } else { + return objectFieldFactoryCode(field, "forObject", fieldTypeName.overrideTypeName(typeOverrideMap)) + } + } + + private fun scalarFieldFactoryCode(field: Field, type: TypeName): CodeBlock { + if (type.isCustomScalarType()) { + val customScalarEnum = CustomEnumTypeSpecBuilder.className(context) + val customScalarEnumConst = normalizeGraphQlType(field.type).toUpperCase() + return CodeBlock.of("\$T.forCustomType(\$S, \$S, null, \$L, \$T.\$L)", API_RESPONSE_FIELD_TYPE, + field.responseName, field.fieldName, field.isOptional(), customScalarEnum, customScalarEnumConst) + } else { + val factoryMethod = scalarFieldFactoryMethod(type) + return CodeBlock.of("\$T.\$L(\$S, \$S, null, \$L)", API_RESPONSE_FIELD_TYPE, factoryMethod, field.responseName, + field.fieldName, field.isOptional()) + } + } + + private fun scalarFieldFactoryMethod(type: TypeName) = when (type) { + ClassNames.STRING -> "forString" + TypeName.INT, TypeName.INT.box() -> "forInt" + TypeName.LONG, TypeName.LONG.box() -> "forLong" + TypeName.DOUBLE, TypeName.DOUBLE.box() -> "forDouble" + TypeName.BOOLEAN, TypeName.BOOLEAN.box() -> "forBoolean" + else -> { + if (type.isEnum()) { + "forString" + } else { + throw RuntimeException("unsupported scalar type $type") + } + } + } + + private fun listFieldFactoryCode(field: Field, type: TypeName): CodeBlock { + val rawFieldType = type.let { if (it.isList()) it.listParamType() else it } + return CodeBlock.builder() + .add( + if (rawFieldType.isCustomScalarType()) { + customTypeListFieldFactoryCode(field, rawFieldType) + } else if (rawFieldType.isScalar()) { + scalarListFieldFactoryCode(field, rawFieldType) + } else { + objectFieldFactoryCode(field, "forList", rawFieldType.overrideTypeName(typeOverrideMap)) + }) + .build() + } + + private fun customTypeListFieldFactoryCode(field: Field, type: TypeName): CodeBlock { + val customScalarEnum = CustomEnumTypeSpecBuilder.className(context) + val customScalarEnumConst = normalizeGraphQlType(field.type).toUpperCase() + return CodeBlock + .builder() + .add("\$T.forList(\$S, \$S, null, \$L, new \$T() {\n", API_RESPONSE_FIELD_TYPE, field.responseName, + field.fieldName, field.isOptional(), + apiResponseFieldListItemReaderType(type.overrideTypeName(typeOverrideMap))) + .indent() + .beginControlFlow("@Override public \$T read(final \$T \$L) throws \$T", type.overrideTypeName(typeOverrideMap), + API_RESPONSE_FIELD_LIST_ITEM_READER_TYPE, READER_VAR, IOException::class.java) + .add(CodeBlock.of("return \$L.readCustomType(\$T.\$L);\n", READER_VAR, customScalarEnum, + customScalarEnumConst)) + .endControlFlow() + .unindent() + .add("})") + .build() + } + + private fun apiResponseFieldListItemReaderType(type: TypeName) = + ParameterizedTypeName.get(API_RESPONSE_FIELD_LIST_READER_TYPE, type) + + private fun scalarListFieldFactoryCode(field: Field, type: TypeName): CodeBlock { + val readMethod = when (type) { + ClassNames.STRING -> "readString()" + TypeName.INT, TypeName.INT.box() -> "readInt()" + TypeName.LONG, TypeName.LONG.box() -> "readLong()" + TypeName.DOUBLE, TypeName.DOUBLE.box() -> "readDouble()" + TypeName.BOOLEAN, TypeName.BOOLEAN.box() -> "readBoolean()" + else -> "readString()" + } + + val readStatement = if (type.isEnum()) + CodeBlock.of("return \$T.valueOf(\$L.\$L);\n", type.overrideTypeName(typeOverrideMap), READER_VAR, readMethod) + else + CodeBlock.of("return \$L.\$L;\n", READER_VAR, readMethod) + + return CodeBlock + .builder() + .add("\$T.forList(\$S, \$S, null, \$L, new \$T() {\n", API_RESPONSE_FIELD_TYPE, field.responseName, + field.fieldName, field.isOptional(), + apiResponseFieldListItemReaderType(type.overrideTypeName(typeOverrideMap))) + .indent() + .beginControlFlow("@Override public \$T read(final \$T \$L) throws \$T", type.overrideTypeName(typeOverrideMap), + API_RESPONSE_FIELD_LIST_ITEM_READER_TYPE, READER_VAR, IOException::class.java) + .add(readStatement) + .endControlFlow() + .unindent() + .add("})") + .build() + } + + private fun objectFieldFactoryCode(field: Field, factoryMethod: String, type: TypeName): CodeBlock { + val typeFactoryMethod = if (type is ParameterizedTypeName) { + val typeArgument = type.typeArguments[0] + if (typeArgument is WildcardTypeName) { + (typeArgument.upperBounds[0] as ClassName).simpleName().decapitalize() + } else { + (typeArgument as ClassName).simpleName().decapitalize() + } + } else { + (type as ClassName).simpleName().decapitalize() + } + "Factory" + return CodeBlock.builder() + .add("\$T.$factoryMethod(\$S, \$S, null, \$L, new \$T() {\n", API_RESPONSE_FIELD_TYPE, field.responseName, + field.fieldName, field.isOptional(), apiResponseFieldObjectReaderTypeName(type)) + .indent() + .beginControlFlow("@Override public \$T read(final \$T \$L) throws \$T", type, + ClassNames.API_RESPONSE_READER, READER_VAR, IOException::class.java) + .add(CodeBlock.of("return new \$T.Mapper(\$L.\$L()).map(\$L);\n", type, FACTORY_VAR, typeFactoryMethod, + READER_VAR)) + .endControlFlow() + .unindent() + .add("})") + .build() + } + private fun mapMethod(contentValueFields: List) = MethodSpec.methodBuilder("map") .addModifiers(Modifier.PUBLIC) @@ -96,8 +303,8 @@ class SchemaTypeResponseMapperBuilder( private fun mapMethodCode(contentValueFields: List) = CodeBlock.builder() - .addStatement("final \$T \$L = new \$T()", CONTENT_VALUES_TYPE, CONTENT_VALUES_VAR, CONTENT_VALUES_TYPE) - .add("\$L\$L.read(", READER_PARAM.name, if (hasFragments) ".toBufferedReader()" else "") + .addStatement("final \$T $CONTENT_VALUES_VAR = new \$T()", CONTENT_VALUES_TYPE, CONTENT_VALUES_TYPE) + .add("${READER_PARAM.name}\$L.read(", if (hasFragments) ".toBufferedReader()" else "") .add("\$L", TypeSpec.anonymousClassBuilder("") .superclass(ResponseReader.ValueHandler::class.java) .addMethod(valueHandleMethod(contentValueFields)) @@ -153,39 +360,6 @@ class SchemaTypeResponseMapperBuilder( .build() } - private fun fieldFactoryStatements() = - fields.map { it.responseFieldFactoryStatement() } - .plus(inlineFragments.map { it.fieldFactoryStatement(it.fieldSpec().type.withoutAnnotations()) }) - .plus(if (fragmentSpreads.isNotEmpty()) fragmentsFieldFactory() else CodeBlock.of("")) - - private fun Field.responseFieldFactoryStatement(): CodeBlock { - val fieldTypeName = fieldSpec(context.customTypeMap).type.withoutAnnotations() - if (fieldTypeName.isScalar() || fieldTypeName.isCustomScalarType()) { - return scalarResponseFieldFactoryStatement(fieldTypeName) - } else if (fieldTypeName.isList()) { - return listResponseFieldFactoryStatement(fieldTypeName) - } else { - return objectResponseFieldFactoryStatement("forObject", fieldTypeName.overrideTypeName(typeOverrideMap)) - } - } - - private fun fieldFactoryMethod(type: TypeName) = when (type) { - ClassNames.STRING -> "forString" - TypeName.INT, TypeName.INT.box() -> "forInt" - TypeName.LONG, TypeName.LONG.box() -> "forLong" - TypeName.DOUBLE, TypeName.DOUBLE.box() -> "forDouble" - TypeName.BOOLEAN, TypeName.BOOLEAN.box() -> "forBoolean" - else -> { - if (type.isEnum()) { - "forString" - } else if (type.isList()) { - "forList" - } else { - "forObject" - } - } - } - private fun TypeName.isList() = (this is ParameterizedTypeName && rawType == ClassNames.LIST) @@ -203,63 +377,11 @@ class SchemaTypeResponseMapperBuilder( .first() .let { if (it is WildcardTypeName) it.upperBounds.first() else it } - private fun Field.scalarResponseFieldFactoryStatement(type: TypeName): CodeBlock { - if (type.isCustomScalarType()) { - val customScalarEnum = CustomEnumTypeSpecBuilder.className(context) - val customScalarEnumConst = normalizeGraphQlType(this.type).toUpperCase() - return CodeBlock.of("\$T.forCustomType(\$S, \$S, null, \$L, \$T.\$L)", API_RESPONSE_FIELD_TYPE, responseName, - fieldName, isOptional(), customScalarEnum, customScalarEnumConst) - } else { - val factoryMethod = fieldFactoryMethod(type) - return CodeBlock.of("\$T.\$L(\$S, \$S, null, \$L)", API_RESPONSE_FIELD_TYPE, factoryMethod, responseName, - fieldName, isOptional()) - } - } - - private fun Field.objectResponseFieldFactoryStatement(factoryMethod: String, type: TypeName): CodeBlock { - val typeFactoryMethod = if (type is ParameterizedTypeName) { - val typeArgument = type.typeArguments[0] - if (typeArgument is WildcardTypeName) { - (typeArgument.upperBounds[0] as ClassName).simpleName().decapitalize() - } else { - (typeArgument as ClassName).simpleName().decapitalize() - } - } else { - (type as ClassName).simpleName().decapitalize() - } + "Factory" - return CodeBlock.builder() - .add("\$T.$factoryMethod(\$S, \$S, null, \$L, new \$T() {\n", API_RESPONSE_FIELD_TYPE, responseName, - fieldName, isOptional(), apiResponseFieldObjectReaderTypeName(type)) - .indent() - .beginControlFlow("@Override public \$T read(final \$T \$L) throws \$T", type, - ClassNames.API_RESPONSE_READER, READER_PARAM.name, IOException::class.java) - .add(CodeBlock.of("return new \$T.Mapper(\$L.\$L()).map(\$L);\n", type, FACTORY_VAR, typeFactoryMethod, - READER_PARAM.name)) - .endControlFlow() - .unindent() - .add("})") - .build() - } - - private fun Field.listResponseFieldFactoryStatement(type: TypeName): CodeBlock { - val rawFieldType = type.let { if (it.isList()) it.listParamType() else it } - return CodeBlock - .builder() - .add( - if (rawFieldType.isCustomScalarType()) { - readCustomListItemStatement(rawFieldType) - } else if (rawFieldType.isScalar()) { - readScalarListItemStatement(rawFieldType) - } else { - objectResponseFieldFactoryStatement("forList", rawFieldType.overrideTypeName(typeOverrideMap)) - }) - .build() - } - - private fun InlineFragment.fieldFactoryStatement(type: TypeName): CodeBlock { + private fun inlineFragmentFieldFactoryCode(fragment: InlineFragment): CodeBlock { + val type = fragment.fieldSpec().type.withoutAnnotations() fun readCodeBlock(): CodeBlock { return CodeBlock.builder() - .beginControlFlow("if (\$L.equals(\$S))", CONDITIONAL_TYPE_VAR, typeCondition) + .beginControlFlow("if (\$L.equals(\$S))", CONDITIONAL_TYPE_VAR, fragment.typeCondition) .add(CodeBlock.of("return new \$T.Mapper(\$L.\$LFactory()).map(\$L);\n", type, FACTORY_VAR, (type as ClassName).simpleName().decapitalize(), READER_PARAM.name)) .nextControlFlow("else") @@ -287,7 +409,7 @@ class SchemaTypeResponseMapperBuilder( conditionalFieldReaderType()) } - private fun fragmentsFieldFactory(): CodeBlock { + private fun fragmentsFieldFactoryCode(): CodeBlock { fun readCodeBlock(): CodeBlock { return CodeBlock.builder() .add(CodeBlock.of("return new Fragments.Mapper(\$L.fragmentsFactory(), \$L).map(\$L);\n", FACTORY_VAR, @@ -317,70 +439,25 @@ class SchemaTypeResponseMapperBuilder( private fun apiResponseFieldObjectReaderTypeName(type: TypeName) = ParameterizedTypeName.get(API_RESPONSE_FIELD_OBJECT_READER_TYPE, type) - private fun apiResponseFieldListItemReaderTypeName(type: TypeName) = - ParameterizedTypeName.get(API_RESPONSE_FIELD_LIST_READER_TYPE, type) - private fun apiConditionalFieldReaderTypeName(type: TypeName) = ParameterizedTypeName.get(API_RESPONSE_FIELD_CONDITIONAL_TYPE_READER_TYPE, type) - - private fun Field.readScalarListItemStatement(type: TypeName): CodeBlock { - val readMethod = when (type) { - ClassNames.STRING -> "readString()" - TypeName.INT, TypeName.INT.box() -> "readInt()" - TypeName.LONG, TypeName.LONG.box() -> "readLong()" - TypeName.DOUBLE, TypeName.DOUBLE.box() -> "readDouble()" - TypeName.BOOLEAN, TypeName.BOOLEAN.box() -> "readBoolean()" - else -> "readString()" - } - - val readStatement = if (type.isEnum()) - CodeBlock.of("return \$T.valueOf(\$L.\$L);\n", type.overrideTypeName(typeOverrideMap), READER_PARAM.name, - readMethod) - else - CodeBlock.of("return \$L.\$L;\n", READER_PARAM.name, readMethod) - - return CodeBlock - .builder() - .add("\$T.forList(\$S, \$S, null, \$L, new \$T() {\n", API_RESPONSE_FIELD_TYPE, responseName, fieldName, - isOptional(), apiResponseFieldListItemReaderTypeName(type.overrideTypeName(typeOverrideMap))) - .indent() - .beginControlFlow("@Override public \$T read(final \$T \$L) throws \$T", type.overrideTypeName(typeOverrideMap), - API_RESPONSE_FIELD_LIST_ITEM_READER_TYPE, READER_PARAM.name, IOException::class.java) - .add(readStatement) - .endControlFlow() - .unindent() - .add("})") - .build() - } - - private fun Field.readCustomListItemStatement(type: TypeName): CodeBlock { - val customScalarEnum = CustomEnumTypeSpecBuilder.className(context) - val customScalarEnumConst = normalizeGraphQlType(this.type).toUpperCase() - return CodeBlock - .builder() - .add("\$T.forList(\$S, \$S, null, \$L, new \$T() {\n", API_RESPONSE_FIELD_TYPE, responseName, fieldName, - isOptional(), apiResponseFieldListItemReaderTypeName(type.overrideTypeName(typeOverrideMap))) - .indent() - .beginControlFlow("@Override public \$T read(final \$T \$L) throws \$T", type.overrideTypeName(typeOverrideMap), - API_RESPONSE_FIELD_LIST_ITEM_READER_TYPE, READER_PARAM.name, IOException::class.java) - .add(CodeBlock.of("return \$L.readCustomType(\$T.\$L);\n", READER_PARAM.name, customScalarEnum, - customScalarEnumConst)) - .endControlFlow() - .unindent() - .add("})") - .build() - } - companion object { private val CONTENT_VALUES_TYPE = ClassName.get("", "__ContentValues") private val CONTENT_VALUES_VAR = "contentValues" private val FACTORY_VAR = Util.FACTORY_TYPE_NAME.decapitalize() + private val FACTORY_PARAM = ParameterSpec.builder(Util.FACTORY_INTERFACE_TYPE, FACTORY_VAR) + .addAnnotation(Nonnull::class.java).build() + private val FACTORY_FIELD = + FieldSpec.builder(FACTORY_PARAM.type, FACTORY_PARAM.name, Modifier.FINAL).build() + private val FRAGMENTS_FIELD = FieldSpec.builder(ClassName.get("", SchemaTypeSpecBuilder.FRAGMENTS_TYPE_NAME), + SchemaTypeSpecBuilder.FRAGMENTS_TYPE_NAME.decapitalize()).build() private val FIELDS_VAR = "fields" private val CONDITIONAL_TYPE_VAR = "conditionalType" private val FIELD_INDEX_PARAM = "fieldIndex" private val VALUE_PARAM = "value" - private val READER_PARAM = ParameterSpec.builder(ResponseReader::class.java, "reader").build() + private val READER_VAR = "reader" + private val READER_PARAM = ParameterSpec.builder(ResponseReader::class.java, READER_VAR).build() private val SCALAR_TYPES = listOf(ClassNames.STRING, TypeName.INT, TypeName.INT.box(), TypeName.LONG, TypeName.LONG.box(), TypeName.DOUBLE, TypeName.DOUBLE.box(), TypeName.BOOLEAN, TypeName.BOOLEAN.box()) private val API_RESPONSE_FIELD_TYPE = ClassName.get(com.apollographql.android.api.graphql.Field::class.java) @@ -393,8 +470,6 @@ class SchemaTypeResponseMapperBuilder( private val API_RESPONSE_FIELD_MAPPER_TYPE = ClassName.get(ResponseFieldMapper::class.java) private val API_RESPONSE_FIELD_CONDITIONAL_TYPE_READER_TYPE = ClassName.get( com.apollographql.android.api.graphql.Field.ConditionalTypeReader::class.java) - private val FRAGMENTS_FIELD = FieldSpec.builder(ClassName.get("", SchemaTypeSpecBuilder.FRAGMENTS_TYPE_NAME), - SchemaTypeSpecBuilder.FRAGMENTS_TYPE_NAME.decapitalize()).build() private fun normalizeGraphQlType(type: String) = type.removeSuffix("!").removeSurrounding(prefix = "[", suffix = "]").removeSuffix("!") diff --git a/apollo-compiler/src/test/graphql/com/example/pojo_fragment_with_inline_fragment/TestQueryExpected.java b/apollo-compiler/src/test/graphql/com/example/pojo_fragment_with_inline_fragment/TestQueryExpected.java index b57c83c8cb8..90e23a7e89d 100644 --- a/apollo-compiler/src/test/graphql/com/example/pojo_fragment_with_inline_fragment/TestQueryExpected.java +++ b/apollo-compiler/src/test/graphql/com/example/pojo_fragment_with_inline_fragment/TestQueryExpected.java @@ -174,11 +174,11 @@ public Mapper(@Nonnull Factory factory, @Nonnull String conditionalType) { @Override public Fragments map(ResponseReader reader) throws IOException { - HeroDetails herodetails = null; + HeroDetails heroDetails = null; if (conditionalType.equals(HeroDetails.TYPE_CONDITION)) { - herodetails = new HeroDetails.Mapper(factory.heroDetailsFactory()).map(reader); + heroDetails = new HeroDetails.Mapper(factory.heroDetailsFactory()).map(reader); } - return factory.creator().create(herodetails); + return factory.creator().create(heroDetails); } } } From addb9284836401010112a09b3b140d055f7806c8 Mon Sep 17 00:00:00 2001 From: Ivan Savytskyi Date: Sun, 5 Feb 2017 12:05:40 -0500 Subject: [PATCH 4/5] Checkstyle --- .../android/converter/pojo/ApolloConverterFactory.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/ApolloConverterFactory.java b/apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/ApolloConverterFactory.java index cd032e135e4..85a17cc3b64 100644 --- a/apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/ApolloConverterFactory.java +++ b/apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/ApolloConverterFactory.java @@ -35,8 +35,8 @@ public ApolloConverterFactory(Map responseFieldMapper if (Response.class.isAssignableFrom((Class) parameterizedType.getRawType())) { ResponseFieldMapper responseMapper = responseFieldMappers.get(parameterizedType.getActualTypeArguments()[0]); if (responseMapper == null) { - throw new RuntimeException("failed to resolve response field mapper for: " + type + ". Did you forget to " + - "register one"); + throw new RuntimeException("failed to resolve response field mapper for: " + type + ". Did you forget to " + + "register one"); } return new ApolloResponseBodyConverter(responseMapper, customTypeAdapters); } From df79cdf27454762d27a55df1d5d8ec89c25128f2 Mon Sep 17 00:00:00 2001 From: Ivan Savytskyi Date: Sun, 5 Feb 2017 16:27:00 -0500 Subject: [PATCH 5/5] Remove empty file --- .../apollographql/android/compiler/ResponseFieldMapperBuilder.kt | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/ResponseFieldMapperBuilder.kt diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/ResponseFieldMapperBuilder.kt b/apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/ResponseFieldMapperBuilder.kt deleted file mode 100644 index e69de29bb2d..00000000000