From beb9c7f9e16f552a8f4701bcd8601d99c68205ee Mon Sep 17 00:00:00 2001 From: KalilDev Date: Tue, 5 Jan 2021 14:51:56 -0300 Subject: [PATCH 01/22] hive_generator: Support Built codegen * Implemented: Built values, BuiltLists, BuiltMaps and BuiltSets reading and writing. * TODO: EnumClass support --- hive_generator/lib/src/class_builder.dart | 132 ++++++++++++++++++---- 1 file changed, 109 insertions(+), 23 deletions(-) diff --git a/hive_generator/lib/src/class_builder.dart b/hive_generator/lib/src/class_builder.dart index 812702305..a2df220b9 100644 --- a/hive_generator/lib/src/class_builder.dart +++ b/hive_generator/lib/src/class_builder.dart @@ -16,29 +16,30 @@ class ClassBuilder extends Builder { var iterableChecker = const TypeChecker.fromRuntime(Iterable); var uint8ListChecker = const TypeChecker.fromRuntime(Uint8List); + /// The built type checkers. They aren't from runtime because otherwise we + /// would have to depend on both packages. Altough [TypeChecker.fromUrl] is + /// not reccomended because of it's brittleness, this should not be a problem, + /// as these classes are in the same url since the packages got published + /// 6 years ago. + var builtChecker = + const TypeChecker.fromUrl('package:built_value/built_value.dart#Built'); + var builtListChecker = const TypeChecker.fromUrl( + 'package:built_collection/src/list.dart#BuiltList'); + var builtSetChecker = const TypeChecker.fromUrl( + 'package:built_collection/src/set.dart#BuiltSet'); + var builtMapChecker = const TypeChecker.fromUrl( + 'package:built_collection/src/map.dart#BuiltMap'); + ClassBuilder( ClassElement cls, List getters, List setters) : super(cls, getters, setters); - @override - String buildRead() { - var code = StringBuffer(); - code.writeln(''' - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) - reader.readByte(): reader.read(), - }; - return ${cls.name}( - '''); - - var constr = cls.constructors.firstOrNullWhere((it) => it.name.isEmpty); - check(constr != null, 'Provide an unnamed constructor.'); - - // The remaining fields to initialize. - var fields = setters.toList(); - - for (var param in constr.parameters) { + void _buildReadParams( + StringBuffer code, ConstructorElement constr, List fields) { + // The constructor is null only on Built classes, and because of that, this + // loop gets skipped, so every field still needs to be initialized, and they + // are via the builder setters. + for (var param in constr?.parameters ?? []) { var field = fields.firstOrNullWhere((it) => it.name == param.name); // Final fields field ??= getters.firstOrNullWhere((it) => it.name == param.name); @@ -59,14 +60,80 @@ class ClassBuilder extends Builder { code.writeln( '..${field.name} = ${_cast(field.type, 'fields[${field.index}]')}'); } + } + + @override + String buildRead() { + var code = StringBuffer(); + code.writeln(''' + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) + reader.readByte(): reader.read(), + }; + '''); + + final builtType = cls.interfaces.singleWhere(isBuilt, orElse: () => null); + if (builtType == null) { + code.writeln(' return ${cls.name}('); + + var constr = cls.constructors.firstOrNullWhere((it) => it.name.isEmpty); + check(constr != null, 'Provide an unnamed constructor.'); + + _buildReadParams(code, constr, setters.toList()); + } else { + // Find the builder + final builderType = builtType.typeArguments[1]; + + var builderName = builderType?.element?.name ?? builderType?.name; + // In case the builder is being generated, we assume it has the default + // name + if (builderName == null || builderType.isDynamic) { + builderName = '${cls.name}Builder'; + } + + // Instantiate the builder + code.writeln(' return ($builderName('); + + // Initialize the parameters + _buildReadParams( + code, + null, + // We are assuming every getter in the built class has an corresponding + // setter on the Builder class. An reasonable assumption. + getters.toList(), + ); + + // Build the class + code.write(').build()'); + } code.writeln(';'); return code.toString(); } + String _typeParamsString(DartType type) { + var paramType = type as ParameterizedType; + var typeParams = paramType.typeArguments.map(_displayString); + return typeParams.join(', '); + } + + String _builtCast(DartType type, String variable) { + if (builtListChecker.isExactlyType(type)) { + return 'ListBuilder<${_typeParamsString(type)}>($variable as List)'; + } else if (builtSetChecker.isExactlyType(type)) { + return 'SetBuilder<${_typeParamsString(type)}>($variable as List)'; + } else if (builtMapChecker.isExactlyType(type)) { + return 'MapBuilder<${_typeParamsString(type)}>($variable as Map)'; + } + return '($variable as ${_displayString(type)}).toBuilder()'; + } + String _cast(DartType type, String variable) { - if (hiveListChecker.isExactlyType(type)) { + if (isBuiltOrBuiltCollection(type)) { + return _builtCast(type, variable); + } else if (hiveListChecker.isExactlyType(type)) { return '($variable as HiveList)?.castHiveList()'; } else if (iterableChecker.isAssignableFromType(type) && !isUint8List(type)) { @@ -89,6 +156,20 @@ class ClassBuilder extends Builder { return uint8ListChecker.isExactlyType(type); } + bool isBuilt(DartType type) { + return builtChecker.isAssignableFromType(type); + } + + bool isBuiltCollection(DartType type) { + return builtListChecker.isExactlyType(type) || + builtSetChecker.isExactlyType(type) || + builtMapChecker.isExactlyType(type); + } + + bool isBuiltOrBuiltCollection(DartType type) { + return isBuilt(type) || isBuiltCollection(type); + } + String _castIterable(DartType type) { var paramType = type as ParameterizedType; var arg = paramType.typeArguments.first; @@ -124,7 +205,7 @@ class ClassBuilder extends Builder { code.writeln('writer'); code.writeln('..writeByte(${getters.length})'); for (var field in getters) { - var value = _convertIterable(field.type, 'obj.${field.name}'); + var value = _convertWritableValue(field.type, 'obj.${field.name}'); code.writeln(''' ..writeByte(${field.index}) ..write($value)'''); @@ -134,8 +215,13 @@ class ClassBuilder extends Builder { return code.toString(); } - String _convertIterable(DartType type, String accessor) { - if (setChecker.isExactlyType(type) || iterableChecker.isExactlyType(type)) { + String _convertWritableValue(DartType type, String accessor) { + if (isBuiltCollection(type)) { + return builtMapChecker.isExactlyType(type) + ? '$accessor?.toMap()' + : '$accessor?.toList()'; + } else if (setChecker.isExactlyType(type) || + iterableChecker.isExactlyType(type)) { return '$accessor?.toList()'; } else { return accessor; From 731e6d84db8ed14f8efdfb90b11d76bde45f661f Mon Sep 17 00:00:00 2001 From: KalilDev Date: Wed, 6 Jan 2021 12:57:56 -0300 Subject: [PATCH 02/22] hive_generator: Implement EnumClassBuilder --- .../lib/src/enum_class_builder.dart | 25 +++++++++++++++++++ .../lib/src/type_adapter_generator.dart | 21 +++++++++++++--- 2 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 hive_generator/lib/src/enum_class_builder.dart diff --git a/hive_generator/lib/src/enum_class_builder.dart b/hive_generator/lib/src/enum_class_builder.dart new file mode 100644 index 000000000..22cd00e9a --- /dev/null +++ b/hive_generator/lib/src/enum_class_builder.dart @@ -0,0 +1,25 @@ +import 'package:analyzer/dart/element/element.dart'; +import 'package:hive_generator/src/builder.dart'; + +class EnumClassBuilder extends Builder { + EnumClassBuilder( + ClassElement cls, + ) : super(cls, null, null); + + @override + String buildRead() { + // Read the name of the enum class from the single field + var code = StringBuffer() + ..writeln('return ${cls.name}.valueOf(reader.read() as String);'); + return code.toString(); + } + + @override + String buildWrite() { + // Write the name of the enum class as the single field. + var code = StringBuffer() + ..writeln('writer') // + ..writeln('..write(obj.name);'); + return code.toString(); + } +} diff --git a/hive_generator/lib/src/type_adapter_generator.dart b/hive_generator/lib/src/type_adapter_generator.dart index 9df457059..c1a36ef57 100644 --- a/hive_generator/lib/src/type_adapter_generator.dart +++ b/hive_generator/lib/src/type_adapter_generator.dart @@ -1,13 +1,21 @@ import 'package:analyzer/dart/element/element.dart'; -import 'package:build/build.dart'; +import 'package:build/build.dart' hide Builder; import 'package:hive_generator/src/builder.dart'; import 'package:hive_generator/src/class_builder.dart'; import 'package:hive_generator/src/enum_builder.dart'; +import 'package:hive_generator/src/enum_class_builder.dart'; import 'package:hive_generator/src/helper.dart'; import 'package:source_gen/source_gen.dart'; import 'package:hive/hive.dart'; class TypeAdapterGenerator extends GeneratorForAnnotation { + /// The enum class type checker. It isn't from runtime because otherwise we + /// would have to depend on built_value. Altough [TypeChecker.fromUrl] is + /// not reccomended because of it's brittleness, this should not be a problem, + /// as this class is in the same url since it was added, 4 years ago. + var enumClassChecker = const TypeChecker.fromUrl( + 'package:built_value/built_value.dart#EnumClass'); + static String generateName(String typeName) { var adapterName = '${typeName}Adapter'.replaceAll(RegExp(r'[^A-Za-z0-9]+'), ''); @@ -36,9 +44,14 @@ class TypeAdapterGenerator extends GeneratorForAnnotation { var typeId = getTypeId(annotation); var adapterName = getAdapterName(cls.name, annotation); - var builder = cls.isEnum - ? EnumBuilder(cls, getters) - : ClassBuilder(cls, getters, setters); + Builder builder; + if (cls.isEnum) { + builder = EnumBuilder(cls, getters); + } else if (enumClassChecker.isExactlyType(cls.supertype)) { + builder = EnumClassBuilder(cls); + } else { + builder = ClassBuilder(cls, getters, setters); + } return ''' class $adapterName extends TypeAdapter<${cls.name}> { From bec838ea32009819b6317afb0b9f0e539ff32fc7 Mon Sep 17 00:00:00 2001 From: KalilDev Date: Wed, 6 Jan 2021 12:58:16 -0300 Subject: [PATCH 03/22] hive_generator: Null aware toBuilder() --- hive_generator/lib/src/class_builder.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hive_generator/lib/src/class_builder.dart b/hive_generator/lib/src/class_builder.dart index a2df220b9..fc5546772 100644 --- a/hive_generator/lib/src/class_builder.dart +++ b/hive_generator/lib/src/class_builder.dart @@ -127,7 +127,7 @@ class ClassBuilder extends Builder { } else if (builtMapChecker.isExactlyType(type)) { return 'MapBuilder<${_typeParamsString(type)}>($variable as Map)'; } - return '($variable as ${_displayString(type)}).toBuilder()'; + return '($variable as ${_displayString(type)})?.toBuilder()'; } String _cast(DartType type, String variable) { From a933b50ed0014d79ce120e627c54deddea7d02e0 Mon Sep 17 00:00:00 2001 From: KalilDev Date: Wed, 6 Jan 2021 13:20:18 -0300 Subject: [PATCH 04/22] hive_generator: correctly deserialize null ... built_collections --- hive_generator/lib/src/class_builder.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hive_generator/lib/src/class_builder.dart b/hive_generator/lib/src/class_builder.dart index fc5546772..af14b0623 100644 --- a/hive_generator/lib/src/class_builder.dart +++ b/hive_generator/lib/src/class_builder.dart @@ -120,12 +120,13 @@ class ClassBuilder extends Builder { } String _builtCast(DartType type, String variable) { + final pfx = '$variable == null ? null : '; if (builtListChecker.isExactlyType(type)) { - return 'ListBuilder<${_typeParamsString(type)}>($variable as List)'; + return '${pfx}ListBuilder<${_typeParamsString(type)}>($variable as List)'; } else if (builtSetChecker.isExactlyType(type)) { - return 'SetBuilder<${_typeParamsString(type)}>($variable as List)'; + return '${pfx}SetBuilder<${_typeParamsString(type)}>($variable as List)'; } else if (builtMapChecker.isExactlyType(type)) { - return 'MapBuilder<${_typeParamsString(type)}>($variable as Map)'; + return '${pfx}MapBuilder<${_typeParamsString(type)}>($variable as Map)'; } return '($variable as ${_displayString(type)})?.toBuilder()'; } From aa70ffbf876d4e2750014cd193c39898fd6085f4 Mon Sep 17 00:00:00 2001 From: KalilDev Date: Sat, 9 Jan 2021 23:02:31 -0300 Subject: [PATCH 05/22] hive_generator: Refactor class_builder * Separate logic into regular and built methods * Expose cast, buildReadConstructor and convertWritableValue * Will be used to separate into two classes --- hive_generator/lib/src/class_builder.dart | 118 ++++++++++++---------- 1 file changed, 66 insertions(+), 52 deletions(-) diff --git a/hive_generator/lib/src/class_builder.dart b/hive_generator/lib/src/class_builder.dart index af14b0623..86b13a8d0 100644 --- a/hive_generator/lib/src/class_builder.dart +++ b/hive_generator/lib/src/class_builder.dart @@ -34,11 +34,15 @@ class ClassBuilder extends Builder { ClassElement cls, List getters, List setters) : super(cls, getters, setters); - void _buildReadParams( - StringBuffer code, ConstructorElement constr, List fields) { - // The constructor is null only on Built classes, and because of that, this - // loop gets skipped, so every field still needs to be initialized, and they - // are via the builder setters. + void _buildBaseReadConstructor(StringBuffer code) { + code.writeln(' return ${cls.name}('); + + var constr = cls.constructors.firstOrNullWhere((it) => it.name.isEmpty); + check(constr != null, 'Provide an unnamed constructor.'); + + // The remaining fields to be set later + var fields = setters.toList(); + for (var param in constr?.parameters ?? []) { var field = fields.firstOrNullWhere((it) => it.name == param.name); // Final fields @@ -47,7 +51,7 @@ class ClassBuilder extends Builder { if (param.isNamed) { code.write('${param.name}: '); } - code.writeln('${_cast(param.type, 'fields[${field.index}]')},'); + code.writeln('${cast(param.type, 'fields[${field.index}]')},'); fields.remove(field); } } @@ -58,8 +62,46 @@ class ClassBuilder extends Builder { // as initializing formals. We do so using cascades. for (var field in fields) { code.writeln( - '..${field.name} = ${_cast(field.type, 'fields[${field.index}]')}'); + '..${field.name} = ${cast(field.type, 'fields[${field.index}]')}'); + } + } + + void _buildBuiltReadConstructor(StringBuffer code, InterfaceType builtType) { + // Find the builder + final builderType = builtType.typeArguments[1]; + + List fields; + var builderName = builderType?.element?.name ?? builderType?.name; + // In case the builder is being generated, we assume it has the default + // name and fields + if (builderName == null || builderType.isDynamic) { + builderName = '${cls.name}Builder'; + fields = getters; + } else { + throw StateError( + 'We do not support custom builders yet. They would generate invalid code'); + } + + // Instantiate the builder + code.writeln(' return ($builderName()'); + + // Initialize the parameters using setters with cascades on the builder. + for (var field in fields) { + code.writeln( + '..${field.name} = ${cast(field.type, 'fields[${field.index}]')}'); + } + + // Build the class + code.write(').build()'); + } + + void buildReadConstructor(StringBuffer code) { + final builtType = cls.interfaces + .singleWhere(builtChecker.isExactlyType, orElse: () => null); + if (builtType == null) { + return _buildBaseReadConstructor(code); } + return _buildBuiltReadConstructor(code, builtType); } @override @@ -73,40 +115,7 @@ class ClassBuilder extends Builder { }; '''); - final builtType = cls.interfaces.singleWhere(isBuilt, orElse: () => null); - if (builtType == null) { - code.writeln(' return ${cls.name}('); - - var constr = cls.constructors.firstOrNullWhere((it) => it.name.isEmpty); - check(constr != null, 'Provide an unnamed constructor.'); - - _buildReadParams(code, constr, setters.toList()); - } else { - // Find the builder - final builderType = builtType.typeArguments[1]; - - var builderName = builderType?.element?.name ?? builderType?.name; - // In case the builder is being generated, we assume it has the default - // name - if (builderName == null || builderType.isDynamic) { - builderName = '${cls.name}Builder'; - } - - // Instantiate the builder - code.writeln(' return ($builderName('); - - // Initialize the parameters - _buildReadParams( - code, - null, - // We are assuming every getter in the built class has an corresponding - // setter on the Builder class. An reasonable assumption. - getters.toList(), - ); - - // Build the class - code.write(').build()'); - } + buildReadConstructor(code); code.writeln(';'); @@ -119,7 +128,7 @@ class ClassBuilder extends Builder { return typeParams.join(', '); } - String _builtCast(DartType type, String variable) { + String _castBuilt(DartType type, String variable) { final pfx = '$variable == null ? null : '; if (builtListChecker.isExactlyType(type)) { return '${pfx}ListBuilder<${_typeParamsString(type)}>($variable as List)'; @@ -131,10 +140,15 @@ class ClassBuilder extends Builder { return '($variable as ${_displayString(type)})?.toBuilder()'; } - String _cast(DartType type, String variable) { + String cast(DartType type, String variable) { if (isBuiltOrBuiltCollection(type)) { - return _builtCast(type, variable); - } else if (hiveListChecker.isExactlyType(type)) { + return _castBuilt(type, variable); + } + return _castBase(type, variable); + } + + String _castBase(DartType type, String variable) { + if (hiveListChecker.isExactlyType(type)) { return '($variable as HiveList)?.castHiveList()'; } else if (iterableChecker.isAssignableFromType(type) && !isUint8List(type)) { @@ -175,13 +189,13 @@ class ClassBuilder extends Builder { var paramType = type as ParameterizedType; var arg = paramType.typeArguments.first; if (isMapOrIterable(arg) && !isUint8List(arg)) { - var cast = ''; + var castSuffix = ''; if (listChecker.isExactlyType(type)) { - cast = '?.toList()'; + castSuffix = '?.toList()'; } else if (setChecker.isExactlyType(type)) { - cast = '?.toSet()'; + castSuffix = '?.toSet()'; } - return '?.map((dynamic e)=> ${_cast(arg, 'e')})$cast'; + return '?.map((dynamic e)=> ${cast(arg, 'e')})$castSuffix'; } else { return '?.cast<${_displayString(arg)}>()'; } @@ -193,7 +207,7 @@ class ClassBuilder extends Builder { var arg2 = paramType.typeArguments[1]; if (isMapOrIterable(arg1) || isMapOrIterable(arg2)) { return '?.map((dynamic k, dynamic v)=>' - 'MapEntry(${_cast(arg1, 'k')},${_cast(arg2, 'v')}))'; + 'MapEntry(${cast(arg1, 'k')},${cast(arg2, 'v')}))'; } else { return '?.cast<${_displayString(arg1)}, ' '${_displayString(arg2)}>()'; @@ -206,7 +220,7 @@ class ClassBuilder extends Builder { code.writeln('writer'); code.writeln('..writeByte(${getters.length})'); for (var field in getters) { - var value = _convertWritableValue(field.type, 'obj.${field.name}'); + var value = convertWritableValue(field.type, 'obj.${field.name}'); code.writeln(''' ..writeByte(${field.index}) ..write($value)'''); @@ -216,7 +230,7 @@ class ClassBuilder extends Builder { return code.toString(); } - String _convertWritableValue(DartType type, String accessor) { + String convertWritableValue(DartType type, String accessor) { if (isBuiltCollection(type)) { return builtMapChecker.isExactlyType(type) ? '$accessor?.toMap()' From 1b17cf9c16a0b029b7b80775bcdf6a1511a004d5 Mon Sep 17 00:00:00 2001 From: KalilDev Date: Sat, 9 Jan 2021 23:16:35 -0300 Subject: [PATCH 06/22] hive_generator: Refactor ClassBuilder * Split the base behavior into _ClassBuilderBase and the built_value behavior into ClassBuilder. * I think this is less cluttered, as the built_value behavior will get more complex with the support for custom Builder classes and @BuiltValue annotation --- hive_generator/lib/src/class_builder.dart | 193 +++++++++++----------- 1 file changed, 101 insertions(+), 92 deletions(-) diff --git a/hive_generator/lib/src/class_builder.dart b/hive_generator/lib/src/class_builder.dart index 86b13a8d0..246ad8ebe 100644 --- a/hive_generator/lib/src/class_builder.dart +++ b/hive_generator/lib/src/class_builder.dart @@ -8,13 +8,12 @@ import 'package:hive_generator/src/helper.dart'; import 'package:source_gen/source_gen.dart'; import 'package:dartx/dartx.dart'; -class ClassBuilder extends Builder { - var hiveListChecker = const TypeChecker.fromRuntime(HiveList); - var listChecker = const TypeChecker.fromRuntime(List); - var mapChecker = const TypeChecker.fromRuntime(Map); - var setChecker = const TypeChecker.fromRuntime(Set); - var iterableChecker = const TypeChecker.fromRuntime(Iterable); - var uint8ListChecker = const TypeChecker.fromRuntime(Uint8List); +class ClassBuilder extends _ClassBuilderBase { + ClassBuilder( + ClassElement cls, + List getters, + List setters, + ) : super(cls, getters, setters); /// The built type checkers. They aren't from runtime because otherwise we /// would have to depend on both packages. Altough [TypeChecker.fromUrl] is @@ -30,43 +29,13 @@ class ClassBuilder extends Builder { var builtMapChecker = const TypeChecker.fromUrl( 'package:built_collection/src/map.dart#BuiltMap'); - ClassBuilder( - ClassElement cls, List getters, List setters) - : super(cls, getters, setters); - - void _buildBaseReadConstructor(StringBuffer code) { - code.writeln(' return ${cls.name}('); - - var constr = cls.constructors.firstOrNullWhere((it) => it.name.isEmpty); - check(constr != null, 'Provide an unnamed constructor.'); - - // The remaining fields to be set later - var fields = setters.toList(); - - for (var param in constr?.parameters ?? []) { - var field = fields.firstOrNullWhere((it) => it.name == param.name); - // Final fields - field ??= getters.firstOrNullWhere((it) => it.name == param.name); - if (field != null) { - if (param.isNamed) { - code.write('${param.name}: '); - } - code.writeln('${cast(param.type, 'fields[${field.index}]')},'); - fields.remove(field); - } + void buildReadConstructor(StringBuffer code) { + final builtType = cls.interfaces + .singleWhere(builtChecker.isExactlyType, orElse: () => null); + if (builtType == null) { + return super.buildReadConstructor(code); } - code.writeln(')'); - - // There may still be fields to initialize that were not in the constructor - // as initializing formals. We do so using cascades. - for (var field in fields) { - code.writeln( - '..${field.name} = ${cast(field.type, 'fields[${field.index}]')}'); - } - } - - void _buildBuiltReadConstructor(StringBuffer code, InterfaceType builtType) { // Find the builder final builderType = builtType.typeArguments[1]; @@ -95,13 +64,97 @@ class ClassBuilder extends Builder { code.write(').build()'); } + @override + String cast(DartType type, String variable) { + if (!isBuiltOrBuiltCollection(type)) { + return super.cast(type, variable); + } + + final pfx = '$variable == null ? null : '; + if (builtListChecker.isExactlyType(type)) { + return '${pfx}ListBuilder<${_typeParamsString(type)}>($variable as List)'; + } else if (builtSetChecker.isExactlyType(type)) { + return '${pfx}SetBuilder<${_typeParamsString(type)}>($variable as List)'; + } else if (builtMapChecker.isExactlyType(type)) { + return '${pfx}MapBuilder<${_typeParamsString(type)}>($variable as Map)'; + } + return '($variable as ${_displayString(type)})?.toBuilder()'; + } + + bool isBuilt(DartType type) { + return builtChecker.isAssignableFromType(type); + } + + bool isBuiltCollection(DartType type) { + return builtListChecker.isExactlyType(type) || + builtSetChecker.isExactlyType(type) || + builtMapChecker.isExactlyType(type); + } + + bool isBuiltOrBuiltCollection(DartType type) { + return isBuilt(type) || isBuiltCollection(type); + } + + String _typeParamsString(DartType type) { + var paramType = type as ParameterizedType; + var typeParams = paramType.typeArguments.map(_displayString); + return typeParams.join(', '); + } + + @override + String convertWritableValue(DartType type, String accessor) { + if (isBuiltCollection(type)) { + return builtMapChecker.isExactlyType(type) + ? '$accessor?.toMap()' + : '$accessor?.toList()'; + } else { + return super.convertWritableValue(type, accessor); + } + } +} + +class _ClassBuilderBase extends Builder { + var hiveListChecker = const TypeChecker.fromRuntime(HiveList); + var listChecker = const TypeChecker.fromRuntime(List); + var mapChecker = const TypeChecker.fromRuntime(Map); + var setChecker = const TypeChecker.fromRuntime(Set); + var iterableChecker = const TypeChecker.fromRuntime(Iterable); + var uint8ListChecker = const TypeChecker.fromRuntime(Uint8List); + + _ClassBuilderBase( + ClassElement cls, List getters, List setters) + : super(cls, getters, setters); + void buildReadConstructor(StringBuffer code) { - final builtType = cls.interfaces - .singleWhere(builtChecker.isExactlyType, orElse: () => null); - if (builtType == null) { - return _buildBaseReadConstructor(code); + code.writeln(' return ${cls.name}('); + + var constr = cls.constructors.firstOrNullWhere((it) => it.name.isEmpty); + check(constr != null, 'Provide an unnamed constructor.'); + + // The remaining fields to be set later + var fields = setters.toList(); + + for (var param in constr?.parameters ?? []) { + var field = fields.firstOrNullWhere((it) => it.name == param.name); + // Final fields + field ??= getters.firstOrNullWhere((it) => it.name == param.name); + if (field != null) { + if (param.isNamed) { + code.write('${param.name}: '); + } + code.writeln('${cast(param.type, 'fields[${field.index}]')},'); + fields.remove(field); + } + } + + code.writeln(')'); + + // There may still be fields to initialize that were not in the constructor + // as initializing formals. We do so using cascades. + for (var field in fields) { + code.writeln( + '..${field.name} = ${cast(field.type, 'fields[${field.index}]')}'); } - return _buildBuiltReadConstructor(code, builtType); } @override @@ -122,32 +175,7 @@ class ClassBuilder extends Builder { return code.toString(); } - String _typeParamsString(DartType type) { - var paramType = type as ParameterizedType; - var typeParams = paramType.typeArguments.map(_displayString); - return typeParams.join(', '); - } - - String _castBuilt(DartType type, String variable) { - final pfx = '$variable == null ? null : '; - if (builtListChecker.isExactlyType(type)) { - return '${pfx}ListBuilder<${_typeParamsString(type)}>($variable as List)'; - } else if (builtSetChecker.isExactlyType(type)) { - return '${pfx}SetBuilder<${_typeParamsString(type)}>($variable as List)'; - } else if (builtMapChecker.isExactlyType(type)) { - return '${pfx}MapBuilder<${_typeParamsString(type)}>($variable as Map)'; - } - return '($variable as ${_displayString(type)})?.toBuilder()'; - } - String cast(DartType type, String variable) { - if (isBuiltOrBuiltCollection(type)) { - return _castBuilt(type, variable); - } - return _castBase(type, variable); - } - - String _castBase(DartType type, String variable) { if (hiveListChecker.isExactlyType(type)) { return '($variable as HiveList)?.castHiveList()'; } else if (iterableChecker.isAssignableFromType(type) && @@ -171,20 +199,6 @@ class ClassBuilder extends Builder { return uint8ListChecker.isExactlyType(type); } - bool isBuilt(DartType type) { - return builtChecker.isAssignableFromType(type); - } - - bool isBuiltCollection(DartType type) { - return builtListChecker.isExactlyType(type) || - builtSetChecker.isExactlyType(type) || - builtMapChecker.isExactlyType(type); - } - - bool isBuiltOrBuiltCollection(DartType type) { - return isBuilt(type) || isBuiltCollection(type); - } - String _castIterable(DartType type) { var paramType = type as ParameterizedType; var arg = paramType.typeArguments.first; @@ -231,12 +245,7 @@ class ClassBuilder extends Builder { } String convertWritableValue(DartType type, String accessor) { - if (isBuiltCollection(type)) { - return builtMapChecker.isExactlyType(type) - ? '$accessor?.toMap()' - : '$accessor?.toList()'; - } else if (setChecker.isExactlyType(type) || - iterableChecker.isExactlyType(type)) { + if (setChecker.isExactlyType(type) || iterableChecker.isExactlyType(type)) { return '$accessor?.toList()'; } else { return accessor; From f32caa6a5c8d2c2b4c4038f8096fed532b16f4f8 Mon Sep 17 00:00:00 2001 From: KalilDev Date: Sat, 9 Jan 2021 23:19:00 -0300 Subject: [PATCH 07/22] hive_generator: depend on built_value and built_collection * We will need to use more type checkers, and this will get out of hand. * Besides, this is an dev_dependency only, so it isnt that big of a deal --- hive_generator/lib/src/class_builder.dart | 19 ++++++------------- hive_generator/pubspec.yaml | 2 ++ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/hive_generator/lib/src/class_builder.dart b/hive_generator/lib/src/class_builder.dart index 246ad8ebe..f527209d3 100644 --- a/hive_generator/lib/src/class_builder.dart +++ b/hive_generator/lib/src/class_builder.dart @@ -7,6 +7,8 @@ import 'package:hive_generator/src/builder.dart'; import 'package:hive_generator/src/helper.dart'; import 'package:source_gen/source_gen.dart'; import 'package:dartx/dartx.dart'; +import 'package:built_value/built_value.dart' as bv; +import 'package:built_collection/built_collection.dart'; class ClassBuilder extends _ClassBuilderBase { ClassBuilder( @@ -15,19 +17,10 @@ class ClassBuilder extends _ClassBuilderBase { List setters, ) : super(cls, getters, setters); - /// The built type checkers. They aren't from runtime because otherwise we - /// would have to depend on both packages. Altough [TypeChecker.fromUrl] is - /// not reccomended because of it's brittleness, this should not be a problem, - /// as these classes are in the same url since the packages got published - /// 6 years ago. - var builtChecker = - const TypeChecker.fromUrl('package:built_value/built_value.dart#Built'); - var builtListChecker = const TypeChecker.fromUrl( - 'package:built_collection/src/list.dart#BuiltList'); - var builtSetChecker = const TypeChecker.fromUrl( - 'package:built_collection/src/set.dart#BuiltSet'); - var builtMapChecker = const TypeChecker.fromUrl( - 'package:built_collection/src/map.dart#BuiltMap'); + var builtChecker = const TypeChecker.fromRuntime(bv.Built); + var builtListChecker = const TypeChecker.fromRuntime(BuiltList); + var builtSetChecker = const TypeChecker.fromRuntime(BuiltSet); + var builtMapChecker = const TypeChecker.fromRuntime(BuiltMap); void buildReadConstructor(StringBuffer code) { final builtType = cls.interfaces diff --git a/hive_generator/pubspec.yaml b/hive_generator/pubspec.yaml index 6e440e27e..c94c5565d 100644 --- a/hive_generator/pubspec.yaml +++ b/hive_generator/pubspec.yaml @@ -13,6 +13,8 @@ dependencies: hive: ">=1.3.0 <2.0.0" analyzer: ">=0.36.0 <2.0.0" dartx: ">=0.2.0 <1.0.0" + built_value: any + built_collection: any dev_dependencies: test: ^1.6.4 From 7adf101e5974a045947128b7923ad2ebd42fc1c8 Mon Sep 17 00:00:00 2001 From: KalilDev Date: Sat, 9 Jan 2021 23:24:31 -0300 Subject: [PATCH 08/22] hive_generator: lookup the name from custom builders --- hive_generator/lib/src/class_builder.dart | 24 +++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/hive_generator/lib/src/class_builder.dart b/hive_generator/lib/src/class_builder.dart index f527209d3..886b91b33 100644 --- a/hive_generator/lib/src/class_builder.dart +++ b/hive_generator/lib/src/class_builder.dart @@ -22,24 +22,32 @@ class ClassBuilder extends _ClassBuilderBase { var builtSetChecker = const TypeChecker.fromRuntime(BuiltSet); var builtMapChecker = const TypeChecker.fromRuntime(BuiltMap); + bool get isThisBuilt => cls.interfaces.any(builtChecker.isExactlyType); + DartType get builderType => isThisBuilt + ? cls.interfaces + .singleWhere(builtChecker.isExactlyType) + .typeArguments + .last + : throw StateError( + 'Tried to find the builderType on ${cls.name}, which is not Built.'); + void buildReadConstructor(StringBuffer code) { - final builtType = cls.interfaces - .singleWhere(builtChecker.isExactlyType, orElse: () => null); - if (builtType == null) { + if (!isThisBuilt) { return super.buildReadConstructor(code); } - // Find the builder - final builderType = builtType.typeArguments[1]; - + String builderName; List fields; - var builderName = builderType?.element?.name ?? builderType?.name; + // In case the builder is being generated, we assume it has the default // name and fields - if (builderName == null || builderType.isDynamic) { + if (builderType?.isDynamic ?? true) { builderName = '${cls.name}Builder'; fields = getters; } else { + // The builder type was manually created, therefore we look it up. + final builderCls = builderType.element as ClassElement; + builderName = builderCls.name; throw StateError( 'We do not support custom builders yet. They would generate invalid code'); } From f6ffa2de10d1407cecdd760cb4ae460816d73327 Mon Sep 17 00:00:00 2001 From: KalilDev Date: Sat, 9 Jan 2021 23:29:01 -0300 Subject: [PATCH 09/22] hive_generator: expose methods from generator --- .../lib/src/type_adapter_generator.dart | 132 +++++++++--------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/hive_generator/lib/src/type_adapter_generator.dart b/hive_generator/lib/src/type_adapter_generator.dart index c1a36ef57..31aa1c968 100644 --- a/hive_generator/lib/src/type_adapter_generator.dart +++ b/hive_generator/lib/src/type_adapter_generator.dart @@ -88,72 +88,6 @@ class TypeAdapterGenerator extends GeneratorForAnnotation { return element as ClassElement; } - Set getAllAccessorNames(ClassElement cls) { - var accessorNames = {}; - - var supertypes = cls.allSupertypes.map((it) => it.element); - for (var type in [cls, ...supertypes]) { - for (var accessor in type.accessors) { - if (accessor.isSetter) { - var name = accessor.name; - accessorNames.add(name.substring(0, name.length - 1)); - } else { - accessorNames.add(accessor.name); - } - } - } - - return accessorNames; - } - - List> getAccessors( - ClassElement cls, LibraryElement library) { - var accessorNames = getAllAccessorNames(cls); - - var getters = []; - var setters = []; - for (var name in accessorNames) { - var getter = cls.lookUpGetter(name, library); - if (getter != null) { - var getterAnn = - getHiveFieldAnn(getter.variable) ?? getHiveFieldAnn(getter); - if (getterAnn != null) { - var field = getter.variable; - getters.add(AdapterField(getterAnn.index, field.name, field.type)); - } - } - - var setter = cls.lookUpSetter('$name=', library); - if (setter != null) { - var setterAnn = - getHiveFieldAnn(setter.variable) ?? getHiveFieldAnn(setter); - if (setterAnn != null) { - var field = setter.variable; - setters.add(AdapterField(setterAnn.index, field.name, field.type)); - } - } - } - - return [getters, setters]; - } - - void verifyFieldIndices(List fields) { - for (var field in fields) { - check(field.index >= 0 || field.index <= 255, - 'Field numbers can only be in the range 0-255.'); - - for (var otherField in fields) { - if (otherField == field) continue; - if (otherField.index == field.index) { - throw HiveError( - 'Duplicate field number: ${field.index}. Fields "${field.name}" ' - 'and "${otherField.name}" have the same number.', - ); - } - } - } - } - String getAdapterName(String typeName, ConstantReader annotation) { var annAdapterName = annotation.read('adapterName'); if (annAdapterName.isNull) { @@ -171,3 +105,69 @@ class TypeAdapterGenerator extends GeneratorForAnnotation { return annotation.read('typeId').intValue; } } + +Set _getAllAccessorNames(ClassElement cls) { + var accessorNames = {}; + + var supertypes = cls.allSupertypes.map((it) => it.element); + for (var type in [cls, ...supertypes]) { + for (var accessor in type.accessors) { + if (accessor.isSetter) { + var name = accessor.name; + accessorNames.add(name.substring(0, name.length - 1)); + } else { + accessorNames.add(accessor.name); + } + } + } + + return accessorNames; +} + +List> getAccessors( + ClassElement cls, LibraryElement library) { + var accessorNames = _getAllAccessorNames(cls); + + var getters = []; + var setters = []; + for (var name in accessorNames) { + var getter = cls.lookUpGetter(name, library); + if (getter != null) { + var getterAnn = + getHiveFieldAnn(getter.variable) ?? getHiveFieldAnn(getter); + if (getterAnn != null) { + var field = getter.variable; + getters.add(AdapterField(getterAnn.index, field.name, field.type)); + } + } + + var setter = cls.lookUpSetter('$name=', library); + if (setter != null) { + var setterAnn = + getHiveFieldAnn(setter.variable) ?? getHiveFieldAnn(setter); + if (setterAnn != null) { + var field = setter.variable; + setters.add(AdapterField(setterAnn.index, field.name, field.type)); + } + } + } + + return [getters, setters]; +} + +void verifyFieldIndices(List fields) { + for (var field in fields) { + check(field.index >= 0 || field.index <= 255, + 'Field numbers can only be in the range 0-255.'); + + for (var otherField in fields) { + if (otherField == field) continue; + if (otherField.index == field.index) { + throw HiveError( + 'Duplicate field number: ${field.index}. Fields "${field.name}" ' + 'and "${otherField.name}" have the same number.', + ); + } + } + } +} From 8fa51a9d37d4a97af81e62664b3f911c384f1d18 Mon Sep 17 00:00:00 2001 From: KalilDev Date: Sat, 9 Jan 2021 23:29:55 -0300 Subject: [PATCH 10/22] hive_generator: lookup setters for custom builders --- hive_generator/lib/src/class_builder.dart | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/hive_generator/lib/src/class_builder.dart b/hive_generator/lib/src/class_builder.dart index 886b91b33..f7684cc17 100644 --- a/hive_generator/lib/src/class_builder.dart +++ b/hive_generator/lib/src/class_builder.dart @@ -10,6 +10,8 @@ import 'package:dartx/dartx.dart'; import 'package:built_value/built_value.dart' as bv; import 'package:built_collection/built_collection.dart'; +import 'type_adapter_generator.dart'; + class ClassBuilder extends _ClassBuilderBase { ClassBuilder( ClassElement cls, @@ -45,11 +47,18 @@ class ClassBuilder extends _ClassBuilderBase { builderName = '${cls.name}Builder'; fields = getters; } else { - // The builder type was manually created, therefore we look it up. + // The builder type was manually created, therefore we look it up for + // @HiveField annotations final builderCls = builderType.element as ClassElement; builderName = builderCls.name; - throw StateError( - 'We do not support custom builders yet. They would generate invalid code'); + var gettersAndSetters = getAccessors(builderCls, builderCls.library); + + var setters = gettersAndSetters[1]; + verifyFieldIndices(setters); + + // The fields that need to be set on the cascade are the setters in the + // builder class. + fields = setters; } // Instantiate the builder From 9c223e7519ed984991ad31e9064e1d2fe5ecebf4 Mon Sep 17 00:00:00 2001 From: KalilDev Date: Sat, 9 Jan 2021 23:59:52 -0300 Subject: [PATCH 11/22] hive_generator: implement nested and custom builders --- hive_generator/lib/src/class_builder.dart | 133 ++++++++++++++++++++-- 1 file changed, 121 insertions(+), 12 deletions(-) diff --git a/hive_generator/lib/src/class_builder.dart b/hive_generator/lib/src/class_builder.dart index f7684cc17..7c74ff9bd 100644 --- a/hive_generator/lib/src/class_builder.dart +++ b/hive_generator/lib/src/class_builder.dart @@ -19,11 +19,18 @@ class ClassBuilder extends _ClassBuilderBase { List setters, ) : super(cls, getters, setters); + var builtValueChecker = const TypeChecker.fromRuntime(bv.BuiltValue); + var builtChecker = const TypeChecker.fromRuntime(bv.Built); var builtListChecker = const TypeChecker.fromRuntime(BuiltList); var builtSetChecker = const TypeChecker.fromRuntime(BuiltSet); var builtMapChecker = const TypeChecker.fromRuntime(BuiltMap); + var builderChecker = const TypeChecker.fromRuntime(bv.Builder); + var listBuilderChecker = const TypeChecker.fromRuntime(ListBuilder); + var setBuilderChecker = const TypeChecker.fromRuntime(SetBuilder); + var mapBuilderChecker = const TypeChecker.fromRuntime(MapBuilder); + bool get isThisBuilt => cls.interfaces.any(builtChecker.isExactlyType); DartType get builderType => isThisBuilt ? cls.interfaces @@ -33,6 +40,26 @@ class ClassBuilder extends _ClassBuilderBase { : throw StateError( 'Tried to find the builderType on ${cls.name}, which is not Built.'); + bool _nestedBuildersFromAnnotation() { + final annotation = + cls.metadata.map((e) => e.computeConstantValue()).singleWhere( + (e) => builtValueChecker.isExactlyType(e.type), + orElse: () => null, + ); + if (annotation == null) { + print('no annotation on ${cls.displayName}'); + return true; + } + final reader = ConstantReader(annotation); + final nestedBuilders = reader.read('nestedBuilders'); + if (nestedBuilders.isNull) { + return true; + } + print('annotation on ${cls.displayName}: ${nestedBuilders.boolValue}'); + return nestedBuilders.boolValue; + } + + @override void buildReadConstructor(StringBuffer code) { if (!isThisBuilt) { return super.buildReadConstructor(code); @@ -40,12 +67,21 @@ class ClassBuilder extends _ClassBuilderBase { String builderName; List fields; + bool nestedBuilders; // In case the builder is being generated, we assume it has the default // name and fields if (builderType?.isDynamic ?? true) { builderName = '${cls.name}Builder'; + + // The fields that need to be set on the cascade are the getters in the + // built class, because they have an corresponding setter in the builder. fields = getters; + + // We want to set an builder instead of the built class depending on the + // @BuiltValue annotation, but we cant express this easily with DartType, + // so we pass this info to cast() + nestedBuilders = _nestedBuildersFromAnnotation(); } else { // The builder type was manually created, therefore we look it up for // @HiveField annotations @@ -59,6 +95,10 @@ class ClassBuilder extends _ClassBuilderBase { // The fields that need to be set on the cascade are the setters in the // builder class. fields = setters; + + // We do not need to look it up in the annotation, as this information is + // contained in each setter's DartType, allowing for correct casting. + nestedBuilders = false; } // Instantiate the builder @@ -66,29 +106,84 @@ class ClassBuilder extends _ClassBuilderBase { // Initialize the parameters using setters with cascades on the builder. for (var field in fields) { - code.writeln( - '..${field.name} = ${cast(field.type, 'fields[${field.index}]')}'); + code.writeln('..${field.name} = ${cast( + field.type, + 'fields[${field.index}]', + nestedBuilders, + )}'); } // Build the class code.write(').build()'); } + String _castBuiltCollection( + DartType type, + String variable, [ + bool nestedBuilders = false, + ]) { + String builderConstructor; + String typeToBeCasted; + // Wether or not we should call build() on the end. + // + // This when the user annotated with nestedBuilders = false, so the Builder + // for that class expects an Built value, instead of a builder. + // + // This is not the case when either nestedBuilders is true or ommited, or + // when an custom builder was specified with an ListBuilder for example. + var shouldBeBuilt = !nestedBuilders || isBuilderOrCollectionBuilder(type); + + if (builtMapChecker.isExactlyType(type) || + mapBuilderChecker.isExactlyType(type)) { + builderConstructor = 'MapBuilder'; + typeToBeCasted = 'Map'; + } else { + typeToBeCasted = 'Iterable'; + if (builtSetChecker.isExactlyType(type) || + setBuilderChecker.isExactlyType(type)) { + builderConstructor = 'SetBuilder'; + } + if (builtListChecker.isExactlyType(type) || + listBuilderChecker.isExactlyType(type)) { + builderConstructor = 'ListBuilder'; + } + } + check(builderConstructor != null && typeToBeCasted != null, + 'Unrecognized built_collection type ${_displayString(type)}'); + + final buildExpression = '$builderConstructor<${_typeParamsString(type)}>' + '($variable as $typeToBeCasted)' + '${shouldBeBuilt ? '.build()' : ''}'; + + return '$variable == null ? null : $buildExpression'; + } + @override - String cast(DartType type, String variable) { - if (!isBuiltOrBuiltCollection(type)) { + String cast( + DartType type, + String variable, [ + bool nestedBuilders = false, + ]) { + if (!isBuiltOrBuiltCollection(type) && + !isBuilderOrCollectionBuilder(type)) { + // This value needs no special treatment. return super.cast(type, variable); } - final pfx = '$variable == null ? null : '; - if (builtListChecker.isExactlyType(type)) { - return '${pfx}ListBuilder<${_typeParamsString(type)}>($variable as List)'; - } else if (builtSetChecker.isExactlyType(type)) { - return '${pfx}SetBuilder<${_typeParamsString(type)}>($variable as List)'; - } else if (builtMapChecker.isExactlyType(type)) { - return '${pfx}MapBuilder<${_typeParamsString(type)}>($variable as Map)'; + if ((isBuilt(type) && nestedBuilders) || isBuilder(type)) { + // We need to call .toBuilder(), because variable is always an Built + // value, but we need an Builder value. + return '($variable as ${_displayString(type)})?.toBuilder()'; + } + + if (isBuiltCollection(type) || isCollectionBuilder(type)) { + return _castBuiltCollection(type, variable, nestedBuilders ?? false); } - return '($variable as ${_displayString(type)})?.toBuilder()'; + + // We just need to cast the value. This happens when the type is of a Built + // value in a custom Builder which accepts the plain Built value instead of + // a builder, for example. + return '$variable as ${_displayString(type)}'; } bool isBuilt(DartType type) { @@ -105,6 +200,20 @@ class ClassBuilder extends _ClassBuilderBase { return isBuilt(type) || isBuiltCollection(type); } + bool isBuilder(DartType type) { + return builderChecker.isAssignableFromType(type); + } + + bool isCollectionBuilder(DartType type) { + return listBuilderChecker.isExactlyType(type) || + setBuilderChecker.isExactlyType(type) || + mapBuilderChecker.isExactlyType(type); + } + + bool isBuilderOrCollectionBuilder(DartType type) { + return isBuilder(type) || isCollectionBuilder(type); + } + String _typeParamsString(DartType type) { var paramType = type as ParameterizedType; var typeParams = paramType.typeArguments.map(_displayString); From a1ce47f02955f6d8509fb066e770e428e85e4585 Mon Sep 17 00:00:00 2001 From: KalilDev Date: Sun, 10 Jan 2021 00:37:51 -0300 Subject: [PATCH 12/22] hive_generator: fix _castBuiltCollection --- hive_generator/lib/src/class_builder.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hive_generator/lib/src/class_builder.dart b/hive_generator/lib/src/class_builder.dart index 7c74ff9bd..18ba83159 100644 --- a/hive_generator/lib/src/class_builder.dart +++ b/hive_generator/lib/src/class_builder.dart @@ -47,7 +47,6 @@ class ClassBuilder extends _ClassBuilderBase { orElse: () => null, ); if (annotation == null) { - print('no annotation on ${cls.displayName}'); return true; } final reader = ConstantReader(annotation); @@ -55,7 +54,6 @@ class ClassBuilder extends _ClassBuilderBase { if (nestedBuilders.isNull) { return true; } - print('annotation on ${cls.displayName}: ${nestedBuilders.boolValue}'); return nestedBuilders.boolValue; } @@ -131,7 +129,7 @@ class ClassBuilder extends _ClassBuilderBase { // // This is not the case when either nestedBuilders is true or ommited, or // when an custom builder was specified with an ListBuilder for example. - var shouldBeBuilt = !nestedBuilders || isBuilderOrCollectionBuilder(type); + var shouldBeBuilt = isBuiltCollection(type) && !nestedBuilders; if (builtMapChecker.isExactlyType(type) || mapBuilderChecker.isExactlyType(type)) { From dbd9e2c77d5e2c480454ca4d2f828d1db78f9ecd Mon Sep 17 00:00:00 2001 From: KalilDev Date: Sun, 10 Jan 2021 01:18:27 -0300 Subject: [PATCH 13/22] hive_generator: Workaround edge case when... builder is being generated --- hive_generator/lib/src/builder.dart | 8 +++- hive_generator/lib/src/class_builder.dart | 47 ++++++++++++++++++++--- 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/hive_generator/lib/src/builder.dart b/hive_generator/lib/src/builder.dart index 7284a7289..72babe208 100644 --- a/hive_generator/lib/src/builder.dart +++ b/hive_generator/lib/src/builder.dart @@ -5,8 +5,14 @@ class AdapterField { final int index; final String name; final DartType type; + bool builtValueNestedBuilders; - AdapterField(this.index, this.name, this.type); + AdapterField( + this.index, + this.name, + this.type, { + this.builtValueNestedBuilders, + }); } abstract class Builder { diff --git a/hive_generator/lib/src/class_builder.dart b/hive_generator/lib/src/class_builder.dart index 18ba83159..117a85ea0 100644 --- a/hive_generator/lib/src/class_builder.dart +++ b/hive_generator/lib/src/class_builder.dart @@ -65,7 +65,6 @@ class ClassBuilder extends _ClassBuilderBase { String builderName; List fields; - bool nestedBuilders; // In case the builder is being generated, we assume it has the default // name and fields @@ -79,7 +78,10 @@ class ClassBuilder extends _ClassBuilderBase { // We want to set an builder instead of the built class depending on the // @BuiltValue annotation, but we cant express this easily with DartType, // so we pass this info to cast() - nestedBuilders = _nestedBuildersFromAnnotation(); + final nestedBuilders = _nestedBuildersFromAnnotation(); + for (final adapterField in fields) { + adapterField.builtValueNestedBuilders = nestedBuilders; + } } else { // The builder type was manually created, therefore we look it up for // @HiveField annotations @@ -95,8 +97,41 @@ class ClassBuilder extends _ClassBuilderBase { fields = setters; // We do not need to look it up in the annotation, as this information is - // contained in each setter's DartType, allowing for correct casting. - nestedBuilders = false; + // contained in each setter's DartType in most cases, allowing for correct + // casting. + for (final field in fields) { + field.builtValueNestedBuilders = false; + } + // The edge case is when the type is an Builder which is being generated. + // In this case we set nestedBuilders = true and the type to the Built + // type as a workaround. + final clsFieldMap = {for (final field in getters) field.index: field}; + for (var i = 0; i < fields.length; i++) { + final builderField = fields[i]; + final builtField = clsFieldMap[builderField.index]; + if (builtField == null) { + continue; + } + if (!builderField.type.isDynamic || builtField.type.isDynamic) { + continue; + } + // builderField is dynamic, while builtField isnt. this MAY be the edge + // case. To resolve it, we will check if builtField is an Built value. + // If so, this is the edge case + if (!isBuilt(builtField.type)) { + continue; + } + if (builderField.name == 'validatedValue') { + print( + 'Gonna override type on validatedValue because type is ${_displayString(builderField.type)}'); + } + fields[i] = AdapterField( + builderField.index, + builderField.name, + builtField.type, + builtValueNestedBuilders: true, + ); + } } // Instantiate the builder @@ -107,7 +142,7 @@ class ClassBuilder extends _ClassBuilderBase { code.writeln('..${field.name} = ${cast( field.type, 'fields[${field.index}]', - nestedBuilders, + field.builtValueNestedBuilders, )}'); } @@ -118,7 +153,7 @@ class ClassBuilder extends _ClassBuilderBase { String _castBuiltCollection( DartType type, String variable, [ - bool nestedBuilders = false, + bool nestedBuilders = true, ]) { String builderConstructor; String typeToBeCasted; From 84d624a586d007d48861218941ecfdcea2bc24f8 Mon Sep 17 00:00:00 2001 From: KalilDev Date: Sun, 10 Jan 2021 01:21:57 -0300 Subject: [PATCH 14/22] hive_generator: remove debuug print * well, this is embarassing... --- hive_generator/lib/src/class_builder.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hive_generator/lib/src/class_builder.dart b/hive_generator/lib/src/class_builder.dart index 117a85ea0..08f6931b4 100644 --- a/hive_generator/lib/src/class_builder.dart +++ b/hive_generator/lib/src/class_builder.dart @@ -121,10 +121,6 @@ class ClassBuilder extends _ClassBuilderBase { if (!isBuilt(builtField.type)) { continue; } - if (builderField.name == 'validatedValue') { - print( - 'Gonna override type on validatedValue because type is ${_displayString(builderField.type)}'); - } fields[i] = AdapterField( builderField.index, builderField.name, From 55492aca632e06cd102b88f9e0c3b7fb21da9412 Mon Sep 17 00:00:00 2001 From: KalilDev Date: Sun, 10 Jan 2021 19:24:07 -0300 Subject: [PATCH 15/22] hive_generator: add element to AdapterFIeld --- hive_generator/lib/src/builder.dart | 60 +++++++++++++++---- hive_generator/lib/src/class_builder.dart | 37 +++++------- .../lib/src/type_adapter_generator.dart | 10 +++- 3 files changed, 72 insertions(+), 35 deletions(-) diff --git a/hive_generator/lib/src/builder.dart b/hive_generator/lib/src/builder.dart index 72babe208..edfd4f365 100644 --- a/hive_generator/lib/src/builder.dart +++ b/hive_generator/lib/src/builder.dart @@ -1,18 +1,58 @@ import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/nullability_suffix.dart'; import 'package:analyzer/dart/element/type.dart'; class AdapterField { final int index; - final String name; - final DartType type; - bool builtValueNestedBuilders; - - AdapterField( - this.index, - this.name, - this.type, { - this.builtValueNestedBuilders, - }); + final PropertyAccessorElement element; + + String get name => element.variable.name; + DartType get type => element.variable.type; + + bool get isNullable { + switch (element.variable.type?.nullabilitySuffix) { + case NullabilitySuffix.none: + return false; + default: + return true; + } + } + + AdapterField(this.index, this.element); + BuiltAdapterField toBuilt(bool nestedBuilders) => BuiltAdapterField( + index, + element, + nestedBuilders, + ); +} + +class BuiltAdapterField extends AdapterField { + DartType _type; + + @override + DartType get type => _type ?? super.type; + set type(DartType type) => _type = type; + + bool nestedBuilders; + + BuiltAdapterField( + int index, + PropertyAccessorElement element, + this.nestedBuilders, + ) : super(index, element); + + bool get hasNullableAnnotation => element.metadata.any((metadata) => + metadata.computeConstantValue()?.toStringValue() == 'nullable'); + + bool get hasNullableType => + element?.returnType?.nullabilitySuffix == NullabilitySuffix.question; + + @override + bool get isNullable => hasNullableAnnotation || hasNullableType; + + @override + BuiltAdapterField toBuilt(bool nestedBuilders) => + throw StateError('Already is an BuiltAdapterField'); } abstract class Builder { diff --git a/hive_generator/lib/src/class_builder.dart b/hive_generator/lib/src/class_builder.dart index 08f6931b4..86903918b 100644 --- a/hive_generator/lib/src/class_builder.dart +++ b/hive_generator/lib/src/class_builder.dart @@ -64,24 +64,21 @@ class ClassBuilder extends _ClassBuilderBase { } String builderName; - List fields; + List fields; // In case the builder is being generated, we assume it has the default // name and fields if (builderType?.isDynamic ?? true) { builderName = '${cls.name}Builder'; - // The fields that need to be set on the cascade are the getters in the - // built class, because they have an corresponding setter in the builder. - fields = getters; - // We want to set an builder instead of the built class depending on the // @BuiltValue annotation, but we cant express this easily with DartType, // so we pass this info to cast() final nestedBuilders = _nestedBuildersFromAnnotation(); - for (final adapterField in fields) { - adapterField.builtValueNestedBuilders = nestedBuilders; - } + + // The fields that need to be set on the cascade are the getters in the + // built class, because they have an corresponding setter in the builder. + fields = getters.map((field) => field.toBuilt(nestedBuilders)).toList(); } else { // The builder type was manually created, therefore we look it up for // @HiveField annotations @@ -94,20 +91,17 @@ class ClassBuilder extends _ClassBuilderBase { // The fields that need to be set on the cascade are the setters in the // builder class. - fields = setters; + // + // We do not need to look for nested fieldss in the annotation, as this + // information is contained in each setter's DartType in most cases, + // allowing for correct casting. + fields = setters.map((field) => field.toBuilt(false)).toList(); - // We do not need to look it up in the annotation, as this information is - // contained in each setter's DartType in most cases, allowing for correct - // casting. - for (final field in fields) { - field.builtValueNestedBuilders = false; - } // The edge case is when the type is an Builder which is being generated. // In this case we set nestedBuilders = true and the type to the Built // type as a workaround. final clsFieldMap = {for (final field in getters) field.index: field}; - for (var i = 0; i < fields.length; i++) { - final builderField = fields[i]; + for (final builderField in fields) { final builtField = clsFieldMap[builderField.index]; if (builtField == null) { continue; @@ -121,12 +115,9 @@ class ClassBuilder extends _ClassBuilderBase { if (!isBuilt(builtField.type)) { continue; } - fields[i] = AdapterField( - builderField.index, - builderField.name, - builtField.type, - builtValueNestedBuilders: true, - ); + builderField + ..type = builtField.type + ..nestedBuilders = true; } } diff --git a/hive_generator/lib/src/type_adapter_generator.dart b/hive_generator/lib/src/type_adapter_generator.dart index 31aa1c968..84e813319 100644 --- a/hive_generator/lib/src/type_adapter_generator.dart +++ b/hive_generator/lib/src/type_adapter_generator.dart @@ -137,7 +137,10 @@ List> getAccessors( getHiveFieldAnn(getter.variable) ?? getHiveFieldAnn(getter); if (getterAnn != null) { var field = getter.variable; - getters.add(AdapterField(getterAnn.index, field.name, field.type)); + getters.add(AdapterField( + getterAnn.index, + getter, + )); } } @@ -147,7 +150,10 @@ List> getAccessors( getHiveFieldAnn(setter.variable) ?? getHiveFieldAnn(setter); if (setterAnn != null) { var field = setter.variable; - setters.add(AdapterField(setterAnn.index, field.name, field.type)); + setters.add(AdapterField( + setterAnn.index, + setter, + )); } } } From c22b39a9312397cc05f899c1b111807ec71dc4f3 Mon Sep 17 00:00:00 2001 From: KalilDev Date: Sun, 10 Jan 2021 19:30:25 -0300 Subject: [PATCH 16/22] hive_generator: expose castIterable and castMap --- hive_generator/lib/src/class_builder.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hive_generator/lib/src/class_builder.dart b/hive_generator/lib/src/class_builder.dart index 86903918b..1fbc5c666 100644 --- a/hive_generator/lib/src/class_builder.dart +++ b/hive_generator/lib/src/class_builder.dart @@ -319,9 +319,9 @@ class _ClassBuilderBase extends Builder { return '($variable as HiveList)?.castHiveList()'; } else if (iterableChecker.isAssignableFromType(type) && !isUint8List(type)) { - return '($variable as List)${_castIterable(type)}'; + return '($variable as List)${castIterable(type)}'; } else if (mapChecker.isExactlyType(type)) { - return '($variable as Map)${_castMap(type)}'; + return '($variable as Map)${castMap(type)}'; } else { return '$variable as ${_displayString(type)}'; } @@ -338,7 +338,7 @@ class _ClassBuilderBase extends Builder { return uint8ListChecker.isExactlyType(type); } - String _castIterable(DartType type) { + String castIterable(DartType type) { var paramType = type as ParameterizedType; var arg = paramType.typeArguments.first; if (isMapOrIterable(arg) && !isUint8List(arg)) { @@ -354,7 +354,7 @@ class _ClassBuilderBase extends Builder { } } - String _castMap(DartType type) { + String castMap(DartType type) { var paramType = type as ParameterizedType; var arg1 = paramType.typeArguments[0]; var arg2 = paramType.typeArguments[1]; From 2cc0141c964c6ad94da540ff62c6fe1c9092d41f Mon Sep 17 00:00:00 2001 From: KalilDev Date: Sun, 10 Jan 2021 19:33:23 -0300 Subject: [PATCH 17/22] hive_generator: Support nested built_collection read --- hive_generator/lib/src/class_builder.dart | 43 ++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/hive_generator/lib/src/class_builder.dart b/hive_generator/lib/src/class_builder.dart index 1fbc5c666..22625e971 100644 --- a/hive_generator/lib/src/class_builder.dart +++ b/hive_generator/lib/src/class_builder.dart @@ -144,6 +144,7 @@ class ClassBuilder extends _ClassBuilderBase { ]) { String builderConstructor; String typeToBeCasted; + String castExpr = ''; // Wether or not we should call build() on the end. // // This when the user annotated with nestedBuilders = false, so the Builder @@ -157,8 +158,10 @@ class ClassBuilder extends _ClassBuilderBase { mapBuilderChecker.isExactlyType(type)) { builderConstructor = 'MapBuilder'; typeToBeCasted = 'Map'; + castExpr = castMap(type, nullable: false); } else { typeToBeCasted = 'Iterable'; + castExpr = castIterable(type, nullable: false); if (builtSetChecker.isExactlyType(type) || setBuilderChecker.isExactlyType(type)) { builderConstructor = 'SetBuilder'; @@ -171,8 +174,12 @@ class ClassBuilder extends _ClassBuilderBase { check(builderConstructor != null && typeToBeCasted != null, 'Unrecognized built_collection type ${_displayString(type)}'); + final castedVariable = castExpr.isEmpty + ? '$variable as $typeToBeCasted' + : '($variable as $typeToBeCasted)$castExpr'; + final buildExpression = '$builderConstructor<${_typeParamsString(type)}>' - '($variable as $typeToBeCasted)' + '($castedVariable)' '${shouldBeBuilt ? '.build()' : ''}'; return '$variable == null ? null : $buildExpression'; @@ -206,6 +213,40 @@ class ClassBuilder extends _ClassBuilderBase { return '$variable as ${_displayString(type)}'; } + @override + String castIterable(DartType type, {bool nullable = true}) { + var paramType = type as ParameterizedType; + var arg = paramType.typeArguments.first; + if (isBuiltCollection(arg) || isCollectionBuilder(arg)) { + return '${nullable ? '?' : ''}' + '.map((dynamic e)=> ' + '${cast(arg, 'e', nullable: nullable)})'; + } else if (isBuiltCollection(type) || isCollectionBuilder(type)) { + // Built collections use List.from and Map.from, so casting + // manually is not needed. + return ''; + } else { + return super.castIterable(type); + } + } + + @override + String castMap(DartType type, {bool nullable = true}) { + var paramType = type as ParameterizedType; + var arg1 = paramType.typeArguments[0]; + var arg2 = paramType.typeArguments[1]; + if (isBuiltCollection(arg1) || + isCollectionBuilder(arg1) || + isBuiltCollection(arg2) || + isCollectionBuilder(arg2)) { + return '${nullable ? '?' : ''}' + '.map((dynamic k, dynamic v)=>' + 'MapEntry(${cast(arg1, 'k', nullable: nullable)},' + '${cast(arg2, 'v', nullable: nullable)}))'; + } + return super.castMap(type); + } + bool isBuilt(DartType type) { return builtChecker.isAssignableFromType(type); } From c8be0e377c9ba164b5d1ddf22db1dc79999928fc Mon Sep 17 00:00:00 2001 From: KalilDev Date: Sun, 10 Jan 2021 19:34:04 -0300 Subject: [PATCH 18/22] hive_generator: Support nested built_collection write --- hive_generator/lib/src/class_builder.dart | 34 +++++++++++++++++++---- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/hive_generator/lib/src/class_builder.dart b/hive_generator/lib/src/class_builder.dart index 22625e971..efafe1565 100644 --- a/hive_generator/lib/src/class_builder.dart +++ b/hive_generator/lib/src/class_builder.dart @@ -281,15 +281,39 @@ class ClassBuilder extends _ClassBuilderBase { return typeParams.join(', '); } + String _convertWritableBuiltMap(DartType type, String accessor) { + var paramType = type as ParameterizedType; + var arg1 = paramType.typeArguments[0]; + var arg2 = paramType.typeArguments[1]; + if (isBuiltCollection(arg1) || isBuiltCollection(arg2)) { + return '$accessor?.toMap()?.map' + '((k, v) => MapEntry(' + '${convertWritableValue(arg1, 'k')}, ' + '${convertWritableValue(arg2, 'v')}))'; + } + return '$accessor?.toMap()'; + } + + String _convertWritableBuiltIterable(DartType type, String accessor) { + var paramType = type as ParameterizedType; + var arg = paramType.typeArguments.single; + if (isBuiltCollection(arg)) { + return '$accessor?.toList()?.map' + '((e) => ' + '${convertWritableValue(arg, 'e')})'; + } + return '$accessor?.toList()'; + } + @override String convertWritableValue(DartType type, String accessor) { - if (isBuiltCollection(type)) { - return builtMapChecker.isExactlyType(type) - ? '$accessor?.toMap()' - : '$accessor?.toList()'; - } else { + if (!isBuiltCollection(type)) { return super.convertWritableValue(type, accessor); } + if (builtMapChecker.isExactlyType(type)) { + return _convertWritableBuiltMap(type, accessor); + } + return _convertWritableBuiltIterable(type, accessor); } } From cab846ae1f542a5ddcf748c28d311721a3b43c21 Mon Sep 17 00:00:00 2001 From: KalilDev Date: Sun, 10 Jan 2021 19:43:25 -0300 Subject: [PATCH 19/22] hive_generator: fix _castBuiltCollection --- hive_generator/lib/src/class_builder.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hive_generator/lib/src/class_builder.dart b/hive_generator/lib/src/class_builder.dart index efafe1565..4b06d498f 100644 --- a/hive_generator/lib/src/class_builder.dart +++ b/hive_generator/lib/src/class_builder.dart @@ -139,9 +139,9 @@ class ClassBuilder extends _ClassBuilderBase { String _castBuiltCollection( DartType type, - String variable, [ - bool nestedBuilders = true, - ]) { + String variable, { + bool nestedBuilders, + }) { String builderConstructor; String typeToBeCasted; String castExpr = ''; @@ -152,7 +152,7 @@ class ClassBuilder extends _ClassBuilderBase { // // This is not the case when either nestedBuilders is true or ommited, or // when an custom builder was specified with an ListBuilder for example. - var shouldBeBuilt = isBuiltCollection(type) && !nestedBuilders; + var shouldBeBuilt = isBuiltCollection(type) && (nestedBuilders != true); if (builtMapChecker.isExactlyType(type) || mapBuilderChecker.isExactlyType(type)) { From 126ce0aa6d37f17f36e667d22db7877e7a33d054 Mon Sep 17 00:00:00 2001 From: KalilDev Date: Sun, 10 Jan 2021 19:44:24 -0300 Subject: [PATCH 20/22] hive_generator: fix buildReadConstructor --- hive_generator/lib/src/class_builder.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hive_generator/lib/src/class_builder.dart b/hive_generator/lib/src/class_builder.dart index 4b06d498f..f7712219f 100644 --- a/hive_generator/lib/src/class_builder.dart +++ b/hive_generator/lib/src/class_builder.dart @@ -129,7 +129,7 @@ class ClassBuilder extends _ClassBuilderBase { code.writeln('..${field.name} = ${cast( field.type, 'fields[${field.index}]', - field.builtValueNestedBuilders, + nestedBuilders: field.nestedBuilders, )}'); } From ff1181ebfde5df23ffea4addf542cffb9b158923 Mon Sep 17 00:00:00 2001 From: KalilDev Date: Sun, 10 Jan 2021 19:46:19 -0300 Subject: [PATCH 21/22] hive_generator: basic nullability awareness * Only for built_value classes for reducing the amount of generated code to a reasonable level --- hive_generator/lib/src/class_builder.dart | 25 +++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/hive_generator/lib/src/class_builder.dart b/hive_generator/lib/src/class_builder.dart index f7712219f..9c0e2ffbf 100644 --- a/hive_generator/lib/src/class_builder.dart +++ b/hive_generator/lib/src/class_builder.dart @@ -130,6 +130,7 @@ class ClassBuilder extends _ClassBuilderBase { field.type, 'fields[${field.index}]', nestedBuilders: field.nestedBuilders, + nullable: field.isNullable, )}'); } @@ -141,6 +142,7 @@ class ClassBuilder extends _ClassBuilderBase { DartType type, String variable, { bool nestedBuilders, + bool nullable = true, }) { String builderConstructor; String typeToBeCasted; @@ -182,29 +184,40 @@ class ClassBuilder extends _ClassBuilderBase { '($castedVariable)' '${shouldBeBuilt ? '.build()' : ''}'; + if (!nullable) { + return buildExpression; + } + return '$variable == null ? null : $buildExpression'; } @override String cast( DartType type, - String variable, [ - bool nestedBuilders = false, - ]) { + String variable, { + bool nestedBuilders, + bool nullable = true, + }) { if (!isBuiltOrBuiltCollection(type) && !isBuilderOrCollectionBuilder(type)) { // This value needs no special treatment. return super.cast(type, variable); } - if ((isBuilt(type) && nestedBuilders) || isBuilder(type)) { + if ((isBuilt(type) && nestedBuilders == true) || isBuilder(type)) { // We need to call .toBuilder(), because variable is always an Built // value, but we need an Builder value. - return '($variable as ${_displayString(type)})?.toBuilder()'; + final toBuilder = '${nullable ? '?' : ''}.toBuilder()'; + return '($variable as ${_displayString(type)})$toBuilder'; } if (isBuiltCollection(type) || isCollectionBuilder(type)) { - return _castBuiltCollection(type, variable, nestedBuilders ?? false); + return _castBuiltCollection( + type, + variable, + nestedBuilders: nestedBuilders, + nullable: nullable ?? true, + ); } // We just need to cast the value. This happens when the type is of a Built From 5ecf4a69274c37bc81fc7188a51d254b2d633dc5 Mon Sep 17 00:00:00 2001 From: KalilDev Date: Sun, 10 Jan 2021 19:54:36 -0300 Subject: [PATCH 22/22] Revert "hive_generator: basic nullability awareness" * bad idea to do rn. This reverts commit ff1181ebfde5df23ffea4addf542cffb9b158923. --- hive_generator/lib/src/class_builder.dart | 37 ++++++++--------------- 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/hive_generator/lib/src/class_builder.dart b/hive_generator/lib/src/class_builder.dart index 9c0e2ffbf..59f0858d7 100644 --- a/hive_generator/lib/src/class_builder.dart +++ b/hive_generator/lib/src/class_builder.dart @@ -130,7 +130,6 @@ class ClassBuilder extends _ClassBuilderBase { field.type, 'fields[${field.index}]', nestedBuilders: field.nestedBuilders, - nullable: field.isNullable, )}'); } @@ -142,11 +141,10 @@ class ClassBuilder extends _ClassBuilderBase { DartType type, String variable, { bool nestedBuilders, - bool nullable = true, }) { String builderConstructor; String typeToBeCasted; - String castExpr = ''; + var castExpr = ''; // Wether or not we should call build() on the end. // // This when the user annotated with nestedBuilders = false, so the Builder @@ -160,10 +158,10 @@ class ClassBuilder extends _ClassBuilderBase { mapBuilderChecker.isExactlyType(type)) { builderConstructor = 'MapBuilder'; typeToBeCasted = 'Map'; - castExpr = castMap(type, nullable: false); + castExpr = castMap(type); } else { typeToBeCasted = 'Iterable'; - castExpr = castIterable(type, nullable: false); + castExpr = castIterable(type); if (builtSetChecker.isExactlyType(type) || setBuilderChecker.isExactlyType(type)) { builderConstructor = 'SetBuilder'; @@ -184,10 +182,6 @@ class ClassBuilder extends _ClassBuilderBase { '($castedVariable)' '${shouldBeBuilt ? '.build()' : ''}'; - if (!nullable) { - return buildExpression; - } - return '$variable == null ? null : $buildExpression'; } @@ -195,8 +189,7 @@ class ClassBuilder extends _ClassBuilderBase { String cast( DartType type, String variable, { - bool nestedBuilders, - bool nullable = true, + bool nestedBuilders = false, }) { if (!isBuiltOrBuiltCollection(type) && !isBuilderOrCollectionBuilder(type)) { @@ -204,11 +197,10 @@ class ClassBuilder extends _ClassBuilderBase { return super.cast(type, variable); } - if ((isBuilt(type) && nestedBuilders == true) || isBuilder(type)) { + if ((isBuilt(type) && nestedBuilders) || isBuilder(type)) { // We need to call .toBuilder(), because variable is always an Built // value, but we need an Builder value. - final toBuilder = '${nullable ? '?' : ''}.toBuilder()'; - return '($variable as ${_displayString(type)})$toBuilder'; + return '($variable as ${_displayString(type)})?.toBuilder()'; } if (isBuiltCollection(type) || isCollectionBuilder(type)) { @@ -216,7 +208,6 @@ class ClassBuilder extends _ClassBuilderBase { type, variable, nestedBuilders: nestedBuilders, - nullable: nullable ?? true, ); } @@ -227,13 +218,12 @@ class ClassBuilder extends _ClassBuilderBase { } @override - String castIterable(DartType type, {bool nullable = true}) { + String castIterable(DartType type) { var paramType = type as ParameterizedType; var arg = paramType.typeArguments.first; if (isBuiltCollection(arg) || isCollectionBuilder(arg)) { - return '${nullable ? '?' : ''}' - '.map((dynamic e)=> ' - '${cast(arg, 'e', nullable: nullable)})'; + return '?.map((dynamic e)=> ' + '${cast(arg, 'e')})'; } else if (isBuiltCollection(type) || isCollectionBuilder(type)) { // Built collections use List.from and Map.from, so casting // manually is not needed. @@ -244,7 +234,7 @@ class ClassBuilder extends _ClassBuilderBase { } @override - String castMap(DartType type, {bool nullable = true}) { + String castMap(DartType type) { var paramType = type as ParameterizedType; var arg1 = paramType.typeArguments[0]; var arg2 = paramType.typeArguments[1]; @@ -252,10 +242,9 @@ class ClassBuilder extends _ClassBuilderBase { isCollectionBuilder(arg1) || isBuiltCollection(arg2) || isCollectionBuilder(arg2)) { - return '${nullable ? '?' : ''}' - '.map((dynamic k, dynamic v)=>' - 'MapEntry(${cast(arg1, 'k', nullable: nullable)},' - '${cast(arg2, 'v', nullable: nullable)}))'; + return '?.map((dynamic k, dynamic v)=>' + 'MapEntry(${cast(arg1, 'k')},' + '${cast(arg2, 'v')}))'; } return super.castMap(type); }