diff --git a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/JavaGenerator.java b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/JavaGenerator.java index 2ad0aa51cd..2f15f43ced 100644 --- a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/JavaGenerator.java +++ b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/JavaGenerator.java @@ -7,7 +7,7 @@ package com.facebook.react.codegen.generator; -import com.facebook.react.codegen.generator.model.RawSchema; +import com.facebook.react.codegen.generator.model.TypeData; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeSpec; @@ -29,9 +29,7 @@ public final class JavaGenerator { } public void build() throws FileNotFoundException, IOException { - final RawSchema rawSchema = SchemaJsonParser.parse(mSchemaFile); - // TODO (T71663018): remove - this is for debugging - System.out.println(rawSchema); + TypeData typeData = SchemaJsonParser.parse(mSchemaFile); final MethodSpec main = MethodSpec.methodBuilder("main") diff --git a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/SchemaJsonParser.java b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/SchemaJsonParser.java index fa9c67352c..b8deb6ed48 100644 --- a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/SchemaJsonParser.java +++ b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/SchemaJsonParser.java @@ -21,12 +21,13 @@ import com.facebook.react.codegen.generator.model.NullableType; import com.facebook.react.codegen.generator.model.NumberType; import com.facebook.react.codegen.generator.model.ObjectType; import com.facebook.react.codegen.generator.model.PromiseType; -import com.facebook.react.codegen.generator.model.RawSchema; import com.facebook.react.codegen.generator.model.ReservedFunctionValueType; import com.facebook.react.codegen.generator.model.StringType; import com.facebook.react.codegen.generator.model.Type; +import com.facebook.react.codegen.generator.model.TypeData; import com.facebook.react.codegen.generator.model.TypeId; import com.facebook.react.codegen.generator.model.VoidType; +import com.google.common.base.CaseFormat; import com.google.common.collect.ImmutableList; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @@ -41,8 +42,16 @@ import java.util.Map; import java.util.stream.Collectors; public final class SchemaJsonParser { + private final TypeData mTypeData = new TypeData(); - public static RawSchema parse(final File schemaFile) throws FileNotFoundException, IOException { + public static TypeData parse(final File schemaFile) throws FileNotFoundException, IOException { + final SchemaJsonParser parser = new SchemaJsonParser(); + parser.buildTypeData(schemaFile); + System.out.println(parser.mTypeData); + return parser.mTypeData; + } + + private void buildTypeData(final File schemaFile) throws FileNotFoundException, IOException { final JsonParser parser = new JsonParser(); final JsonElement rootElement = parser.parse(new FileReader(schemaFile)); @@ -63,62 +72,25 @@ public final class SchemaJsonParser { return; } - collection.put( - jsModuleName, - nativeModules.entrySet().stream() - .collect( - Collectors.toMap( - Map.Entry::getKey, - e -> { - return parseNativeModule( - // TODO (T71955395): NativeModule spec type name does not - // exist in the schema. For now assume it's "Spec". - TypeId.of(jsModuleName, "Spec"), - e.getValue().getAsJsonObject()); - }))); + nativeModules + .entrySet() + .forEach( + e -> { + final Type parsedType = + parseNativeModule( + // TODO (T71955395): NativeModule spec type name does not + // exist in the schema. For now assume it's "Spec". + // The final type name will be the output class name. + TypeId.of(jsModuleName, "Native" + e.getKey() + "Spec"), + e.getValue().getAsJsonObject()); + mTypeData.addType(parsedType); + }); }); } - - return new RawSchema(collection); - } - - private static NativeModuleType parseNativeModule(final TypeId typeId, final JsonObject json) { - final JsonObject aliases = json.getAsJsonObject("aliases"); - final JsonArray properties = json.getAsJsonArray("properties"); - - final ImmutableList collectedAliases = - ImmutableList.copyOf( - aliases.entrySet().stream() - .map( - entry -> { - final String typeName = entry.getKey(); - final JsonObject typeAnnotation = entry.getValue().getAsJsonObject(); - // The alias name is the type name that other types can refer to. - return parseTypeAnnotation( - TypeId.of(typeId.moduleName, typeName), typeAnnotation); - }) - .collect(Collectors.toList())); - - ImmutableList.Builder collectedPropertiesBuilder = - new ImmutableList.Builder<>(); - properties.forEach( - p -> { - final JsonObject node = p.getAsJsonObject(); - final String name = node.has("name") ? node.get("name").getAsString() : null; - final JsonObject typeAnnotation = node.getAsJsonObject("typeAnnotation"); - // TODO (T71845349): "optional" field shouldn't be part of the Function's typeAnnotation. - final boolean optional = typeAnnotation.get("optional").getAsBoolean(); - final TypeId propertyTypeId = TypeId.of(typeId.moduleName); - collectedPropertiesBuilder.add( - new NativeModuleType.Property( - name, parseTypeAnnotation(propertyTypeId, typeAnnotation), optional)); - }); - - return new NativeModuleType(typeId, collectedAliases, collectedPropertiesBuilder.build()); } // Parse type information from a JSON "typeAnnotation" node. - private static Type parseTypeAnnotation(final TypeId typeId, final JsonObject typeAnnotation) + private Type parseTypeAnnotation(final TypeId typeId, final JsonObject typeAnnotation) throws IllegalStateException { final String type = typeAnnotation.get("type").getAsString(); // TODO (T71824250): Support NullableTypeAnnotation in the schema instead of a field here. @@ -176,60 +148,96 @@ public final class SchemaJsonParser { throw new IllegalStateException("Found invalid type annotation: " + type); } - return maybeCreateNullableType(nullable, parsedType); + final Type finalType = maybeCreateNullableType(nullable, parsedType); + mTypeData.addType(finalType); + return finalType; } - private static Type parseAliasTypeAnnotation( - final TypeId typeId, final JsonObject typeAnnotation) { + private NativeModuleType parseNativeModule(final TypeId typeId, final JsonObject json) { + final JsonObject aliases = json.getAsJsonObject("aliases"); + final JsonArray properties = json.getAsJsonArray("properties"); + + final ImmutableList collectedAliases = + ImmutableList.copyOf( + aliases.entrySet().stream() + .map( + entry -> { + final String typeName = entry.getKey(); + final JsonObject typeAnnotation = entry.getValue().getAsJsonObject(); + // The alias name is the type name that other types can refer to. + return parseTypeAnnotation( + TypeId.of(typeId.moduleName, typeName), typeAnnotation); + }) + .collect(Collectors.toList())); + + ImmutableList.Builder collectedPropertiesBuilder = + new ImmutableList.Builder<>(); + properties.forEach( + p -> { + final JsonObject node = p.getAsJsonObject(); + final String name = node.has("name") ? node.get("name").getAsString() : null; + final JsonObject typeAnnotation = node.getAsJsonObject("typeAnnotation"); + // TODO (T71845349): "optional" field shouldn't be part of the Function's typeAnnotation. + final boolean optional = typeAnnotation.get("optional").getAsBoolean(); + final TypeId propertyTypeId = + TypeId.expandOf(typeId, CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, name)); + collectedPropertiesBuilder.add( + new NativeModuleType.Property( + name, parseTypeAnnotation(propertyTypeId, typeAnnotation), optional)); + }); + + return new NativeModuleType(typeId, collectedAliases, collectedPropertiesBuilder.build()); + } + + private Type parseAliasTypeAnnotation(final TypeId typeId, final JsonObject typeAnnotation) { // For now, assume the alias lives inside the same file. return new AliasType( typeId, TypeId.of(typeId.moduleName, typeAnnotation.get("name").getAsString())); } - private static Type parseArrayTypeAnnotation( - final TypeId typeId, final JsonObject typeAnnotation) { + private Type parseArrayTypeAnnotation(final TypeId typeId, final JsonObject typeAnnotation) { final JsonObject elementTypeAnnotation = typeAnnotation.getAsJsonObject("elementType"); + final TypeId elementTypeId = TypeId.expandOf(typeId, "ElementType"); // TODO (T71847026): Some array types are missing elementType annotation. final Type elementType = elementTypeAnnotation != null - ? parseTypeAnnotation(TypeId.of(typeId.moduleName), elementTypeAnnotation) - : new AnyType(TypeId.of(typeId.moduleName)); - + ? parseTypeAnnotation(elementTypeId, elementTypeAnnotation) + : new AnyType(elementTypeId); return new ArrayType(typeId, elementType); } - private static Type parseFunctionTypeAnnotation( - final TypeId typeId, final JsonObject typeAnnotation) { + private Type parseFunctionTypeAnnotation(final TypeId typeId, final JsonObject typeAnnotation) { final JsonArray params = typeAnnotation.getAsJsonArray("params"); ImmutableList.Builder paramsList = new ImmutableList.Builder<>(); // TODO (T71846321): Some functions are missing params specification. if (params != null) { - params.forEach( - p -> { - final JsonObject node = p.getAsJsonObject(); - final String name = node.has("name") ? node.get("name").getAsString() : null; - paramsList.add( - FunctionType.createArgument( - name, - parseTypeAnnotation( - TypeId.of(typeId.moduleName), node.getAsJsonObject("typeAnnotation")))); - }); + for (int i = 0; i < params.size(); i++) { + final JsonElement p = params.get(i); + final JsonObject node = p.getAsJsonObject(); + final String name = node.has("name") ? node.get("name").getAsString() : ("Arg" + i); + paramsList.add( + FunctionType.createArgument( + name, + parseTypeAnnotation( + TypeId.expandOf( + typeId, CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, name)), + node.getAsJsonObject("typeAnnotation")))); + } } // TODO (T71846321): Some functions are missing a return type. final JsonObject returnTypeAnnotation = typeAnnotation.getAsJsonObject("returnTypeAnnotation"); final Type returnType = returnTypeAnnotation != null - ? parseTypeAnnotation(TypeId.of(typeId.moduleName), returnTypeAnnotation) + ? parseTypeAnnotation(TypeId.expandOf(typeId, "ReturnType"), returnTypeAnnotation) : VoidType.VOID; return new FunctionType(typeId, paramsList.build(), returnType); } - private static Type parseObjectTypeAnnotation( - final TypeId typeId, final JsonObject typeAnnotation) { + private Type parseObjectTypeAnnotation(final TypeId typeId, final JsonObject typeAnnotation) { final JsonArray properties = typeAnnotation.getAsJsonArray("properties"); ImmutableList.Builder propertiesList = new ImmutableList.Builder<>(); @@ -239,7 +247,8 @@ public final class SchemaJsonParser { final String name = node.has("name") ? node.get("name").getAsString() : null; final boolean optional = node.get("optional").getAsBoolean(); final JsonObject propertyTypeAnnotation = node.getAsJsonObject("typeAnnotation"); - final TypeId propertyTypeId = TypeId.of(typeId.moduleName); + final TypeId propertyTypeId = + TypeId.expandOf(typeId, CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, name)); // TODO (T67898313): Some object properties are missing typeAnnotation. final Type propertyType = @@ -253,14 +262,14 @@ public final class SchemaJsonParser { return new ObjectType(typeId, propertiesList.build()); } - private static Type parseReservedFunctionValueTypeAnnotation( + private Type parseReservedFunctionValueTypeAnnotation( final TypeId typeId, final JsonObject typeAnnotation) { return new ReservedFunctionValueType( typeId, ReservedFunctionValueType.ReservedName.valueOf(typeAnnotation.get("name").getAsString())); } - private static Type maybeCreateNullableType(final boolean nullable, final Type original) { + private Type maybeCreateNullableType(final boolean nullable, final Type original) { if (!nullable || original instanceof VoidType) { return original; } diff --git a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/RawSchema.java b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/RawSchema.java deleted file mode 100644 index 969164e4a5..0000000000 --- a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/RawSchema.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.codegen.generator.model; - -import java.util.Collections; -import java.util.Map; - -/** Represents the parsed JSON schema without any type resolution. */ -public final class RawSchema { - - public final Map> modules; - - public RawSchema(final Map> modules) { - this.modules = Collections.unmodifiableMap(modules); - } - - @Override - public String toString() { - return modules.toString(); - } -} diff --git a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/TypeData.java b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/TypeData.java new file mode 100644 index 0000000000..1b19bfee0a --- /dev/null +++ b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/TypeData.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.codegen.generator.model; + +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Nullable; + +/** A collection of all types information based on the parsed schema. */ +public final class TypeData { + private final Map mTypes = new HashMap<>(); + + public void addType(final TypeId typeId, final Type type) throws IllegalStateException { + if (getType(typeId) != null) { + throw new IllegalStateException("Found duplicated TypeId: " + typeId + " for: " + type); + } + mTypes.put(typeId, type); + } + + public void addType(final Type type) { + addType(type.getTypeId(), type); + } + + public @Nullable Type getType(final TypeId typeId) { + return mTypes.get(typeId); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + mTypes.forEach( + (k, v) -> { + builder.append(v.toString()); + builder.append("\n"); + }); + return builder.toString(); + } +} diff --git a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/TypeId.java b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/TypeId.java index a8c5208c02..ef9831c525 100644 --- a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/TypeId.java +++ b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/TypeId.java @@ -42,6 +42,10 @@ public final class TypeId { return of(typeId.moduleName, typeId.typeName); } + public static TypeId expandOf(final TypeId typeId, String suffix) { + return of(typeId.moduleName, typeId.typeName + suffix); + } + @Override public String toString() { return String.format(