generateFromMapMethod method

void generateFromMapMethod(
  1. ClassBuilder clazz,
  2. BuildContext ctx,
  3. LibraryBuilder file
)

Implementation

void generateFromMapMethod(
    ClassBuilder clazz, BuildContext ctx, LibraryBuilder file) {
  clazz.methods.add(Method((method) {
    method
      ..static = true
      ..name = 'fromMap'
      ..returns = ctx.modelClassType
      ..requiredParameters.add(
        Parameter((b) => b
          ..name = 'map'
          ..type = Reference('Map')),
      );

    // Add all `super` params
    if (ctx.constructorParameters.isNotEmpty) {
      for (var param in ctx.constructorParameters) {
        method.requiredParameters.add(Parameter((b) => b
          ..name = param.name
          ..type = convertTypeReference(param.type)));
      }
    }

    var buf = StringBuffer();

    // Required Fields
    ctx.requiredFields.forEach((key, msg) {
      if (ctx.excluded[key]?.canDeserialize == false) return;
      var name = ctx.resolveFieldName(key);
      if (msg.contains("'")) {
        buf.writeln('''
          if (map['$name'] == null) {
            throw FormatException("$msg");
          }
        ''');
      } else {
        buf.writeln('''
          if (map['$name'] == null) {
            throw FormatException('$msg');
          }
        ''');
      }
    });

    buf.writeln('return ${ctx.modelClassName}(');
    var i = 0;

    // Parameters in the constructor
    for (var param in ctx.constructorParameters) {
      if (i++ > 0) buf.write(', ');
      buf.write(param.name);
    }

    // Fields
    for (var field in ctx.fields) {
      var type = ctx.resolveSerializedFieldType(field.name);

      if (ctx.excluded[field.name]?.canDeserialize == false) continue;

      var alias = ctx.resolveFieldName(field.name);

      if (i++ > 0) buf.write(', ');

      var deserializedRepresentation =
          "map['$alias'] as ${typeToString(type)}";

      if (type.nullabilitySuffix == NullabilitySuffix.question) {
        deserializedRepresentation += '?';
      }

      var defaultValue = 'null';
      var existingDefault = ctx.defaults[field.name];

      if (existingDefault != null) {
        var d = dartObjectToString(existingDefault);
        if (d != null) {
          defaultValue = d;

          if (!deserializedRepresentation.endsWith("?")) {
            deserializedRepresentation += "?";
          }
        }
        deserializedRepresentation =
            '$deserializedRepresentation ?? $defaultValue';
      }

      var fieldNameDeserializer = ctx.fieldInfo[field.name]?.deserializer;
      if (fieldNameDeserializer != null) {
        var name = MirrorSystem.getName(fieldNameDeserializer);
        deserializedRepresentation = "$name(map['$alias'])";
      } else if (dateTimeTypeChecker.isAssignableFromType(type)) {
        if (field.type.nullabilitySuffix != NullabilitySuffix.question) {
          if (defaultValue.toLowerCase() == 'null') {
            defaultValue = 'DateTime.parse("1970-01-01 00:00:00")';
          } else {
            defaultValue = 'DateTime.parse("$defaultValue")';
          }
        }
        deserializedRepresentation = "map['$alias'] != null ? "
            "(map['$alias'] is DateTime ? (map['$alias'] as DateTime) : DateTime.parse(map['$alias'].toString()))"
            ' : $defaultValue';
      }

      // Serialize model classes via `XSerializer.toMap`
      else if (isModelClass(type)) {
        var rc = ReCase(type.getDisplayString(withNullability: true));
        deserializedRepresentation = "map['$alias'] != null"
            " ? ${rc.pascalCase.replaceAll('?', '')}Serializer.fromMap(map['$alias'] as Map)"
            ' : $defaultValue';
      } else if (type is InterfaceType) {
        if (isListOfModelType(type)) {
          if (defaultValue == 'null') {
            defaultValue = '[]';
          }
          var rc = ReCase(
              type.typeArguments[0].getDisplayString(withNullability: true));

          deserializedRepresentation = "map['$alias'] is Iterable"
              " ? List.unmodifiable(((map['$alias'] as Iterable)"
              '.whereType<Map>())'
              '.map(${rc.pascalCase.replaceAll('?', '')}Serializer.fromMap))'
              ' : $defaultValue';
        } else if (isMapToModelType(type)) {
          // TODO: This requires refractoring
          if (defaultValue == 'null') {
            defaultValue = '{}';
          }

          var rc = ReCase(
              type.typeArguments[1].getDisplayString(withNullability: true));
          deserializedRepresentation = '''
              map['$alias'] is Map
                ? Map.unmodifiable((map['$alias'] as Map).keys.fold({}, (out, key) {
                     return out..[key] = ${rc.pascalCase.replaceAll('?', '')}Serializer
                      .fromMap(((map['$alias'] as Map)[key]) as Map);
                  }))
                : $defaultValue
          ''';
        } else if (type.element is Enum) {
          deserializedRepresentation = '''
          map['$alias'] is ${type.getDisplayString(withNullability: true)}
            ? (map['$alias'] as ${type.getDisplayString(withNullability: true)}) ?? $defaultValue
            :
            (
              map['$alias'] is int
              ? ${type.getDisplayString(withNullability: true)}.values[map['$alias'] as int]
              : $defaultValue
            )
          ''';

          //log.warning('Code => $deserializedRepresentation');
        } else if (const TypeChecker.fromRuntime(List)
                .isAssignableFromType(type) &&
            type.typeArguments.length == 1) {
          if (defaultValue == 'null') {
            defaultValue = '[]';
          }
          var arg = convertTypeReference(type.typeArguments[0])
              .accept(DartEmitter(useNullSafetySyntax: true));
          deserializedRepresentation = '''
              map['$alias'] is Iterable
                ? (map['$alias'] as Iterable).cast<$arg>().toList()
                : $defaultValue
              ''';
        } else if (const TypeChecker.fromRuntime(Map)
                .isAssignableFromType(type) &&
            type.typeArguments.length == 2) {
          var key = convertTypeReference(type.typeArguments[0])
              .accept(DartEmitter(useNullSafetySyntax: true));
          var value = convertTypeReference(type.typeArguments[1])
              .accept(DartEmitter(useNullSafetySyntax: true));

          if (defaultValue == 'null') {
            defaultValue = '{}';
          }
          deserializedRepresentation = '''
              map['$alias'] is Map
                ? (map['$alias'] as Map).cast<$key, $value>()
                : $defaultValue
              ''';
        } else if (const TypeChecker.fromRuntime(Uint8List)
            .isAssignableFromType(type)) {
          deserializedRepresentation = '''
          map['$alias'] is Uint8List
            ? (map['$alias'] as Uint8List)
            :
            (
              map['$alias'] is Iterable<int>
                ? Uint8List.fromList((map['$alias'] as Iterable<int>).toList())
                :
                (
                  map['$alias'] is String
                    ? Uint8List.fromList(base64.decode(map['$alias'] as String))
                    : $defaultValue
                )
            )
          ''';
        }
      }

      buf.write('${field.name}: $deserializedRepresentation');
    }

    buf.write(');');
    method.body = Code(buf.toString());
  }));
}