From 21c22345b6ee0a4234bf4695e98a21ff70e349c5 Mon Sep 17 00:00:00 2001 From: Simon Leier Date: Sun, 6 Oct 2019 23:44:54 +0200 Subject: [PATCH] Support for inheritance and getters --- lib/src/builder.dart | 11 +++++- lib/src/class_builder.dart | 29 ++++++++-------- lib/src/enum_builder.dart | 15 ++++---- lib/src/helper.dart | 16 +++++++++ lib/src/type_adapter_generator.dart | 53 +++++++++++++++++++++++------ 5 files changed, 90 insertions(+), 34 deletions(-) diff --git a/lib/src/builder.dart b/lib/src/builder.dart index cc57425..b172b65 100644 --- a/lib/src/builder.dart +++ b/lib/src/builder.dart @@ -1,8 +1,17 @@ import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/type.dart'; + +class AdapterField { + final int index; + final String name; + final DartType type; + + AdapterField(this.index, this.name, this.type); +} abstract class Builder { final ClassElement cls; - final Map fields; + final List fields; Builder(this.cls, this.fields) : assert(cls != null), diff --git a/lib/src/class_builder.dart b/lib/src/class_builder.dart index f8d44d3..bf179a5 100644 --- a/lib/src/class_builder.dart +++ b/lib/src/class_builder.dart @@ -12,7 +12,7 @@ class ClassBuilder extends Builder { var iterableChecker = const TypeChecker.fromRuntime(Iterable); var uint8ListChecker = const TypeChecker.fromRuntime(Uint8List); - ClassBuilder(ClassElement cls, Map fields) + ClassBuilder(ClassElement cls, List fields) : super(cls, fields); @override @@ -32,14 +32,15 @@ class ClassBuilder extends Builder { ); // The remaining fields to initialize. - var remainingFields = { - for (var entry in fields.entries) entry.value.name: entry.key, - }; + var remainingFields = fields.toList(); for (var param in constructor.parameters .where((param) => param.isInitializingFormal)) { - var index = remainingFields.remove(param.name); - if (index == null) { + var field = remainingFields.firstWhere( + (it) => it.name == param.name, + orElse: () => null, + ); + if (field == null) { // This is a parameter of the form `this.field`, but it's not present // in the binary encoding. continue; @@ -48,18 +49,16 @@ class ClassBuilder extends Builder { if (param.isNamed) { code.write('${param.name}: '); } - code.writeln('${_cast(param.type, 'fields[$index]')},'); + code.writeln('${_cast(param.type, 'fields[${field.index}]')},'); } 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 entry in remainingFields.entries) { - var field = entry.key; - var index = entry.value; - var type = fields[index].type; - code.writeln('..$field = ${_cast(type, 'fields[$index]')}'); + for (var field in remainingFields) { + code.writeln( + '..${field.name} = ${_cast(field.type, 'fields[${field.index}]')}'); } code.writeln(';'); @@ -121,12 +120,12 @@ class ClassBuilder extends Builder { var code = StringBuffer(); code.writeln('writer'); code.writeln('..writeByte(${fields.length})'); - fields.forEach((index, field) { + for (var field in fields) { var value = _convertIterable(field.type, 'obj.${field.name}'); code.writeln(''' - ..writeByte($index) + ..writeByte(${field.index}) ..write($value)'''); - }); + } code.writeln(';'); return code.toString(); diff --git a/lib/src/enum_builder.dart b/lib/src/enum_builder.dart index 22a5852..89f91d4 100644 --- a/lib/src/enum_builder.dart +++ b/lib/src/enum_builder.dart @@ -2,19 +2,18 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:hive_generator/src/builder.dart'; class EnumBuilder extends Builder { - EnumBuilder(ClassElement cls, Map fields) - : super(cls, fields); + EnumBuilder(ClassElement cls, List fields) : super(cls, fields); @override String buildRead() { var code = StringBuffer(); code.writeln('switch(reader.readByte()) {'); - fields.forEach((index, field) { + for (var field in fields) { code.writeln(''' - case $index: + case ${field.index}: return ${cls.name}.${field.name};'''); - }); + } code.writeln(''' default: @@ -29,12 +28,12 @@ class EnumBuilder extends Builder { var code = StringBuffer(); code.writeln('switch(obj) {'); - fields.forEach((index, field) { + for (var field in fields) { code.writeln(''' case ${cls.name}.${field.name}: - writer.writeByte($index); + writer.writeByte(${field.index}); break;'''); - }); + } code.writeln('}'); diff --git a/lib/src/helper.dart b/lib/src/helper.dart index 07dc593..8cac7bd 100644 --- a/lib/src/helper.dart +++ b/lib/src/helper.dart @@ -2,8 +2,17 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:hive/hive.dart'; import 'package:source_gen/source_gen.dart'; +final _hiveTypeChecker = const TypeChecker.fromRuntime(HiveType); final _hiveFieldChecker = const TypeChecker.fromRuntime(HiveField); +HiveType getHiveTypeAnn(Element element) { + var obj = _hiveTypeChecker.firstAnnotationOfExact(element); + if (obj == null) return null; + return HiveType( + adapterName: obj.getField('adapterName').toStringValue(), + ); +} + HiveField getHiveFieldAnn(Element element) { var obj = _hiveFieldChecker.firstAnnotationOfExact(element); if (obj == null) return null; @@ -12,6 +21,13 @@ HiveField getHiveFieldAnn(Element element) { ); } +Iterable getTypeAndAllSupertypes(ClassElement cls) { + var types = {}; + types.add(cls); + types.addAll(cls.allSupertypes.map((it) => it.element)); + return types; +} + void check(bool condition, dynamic error) { if (!condition) { throw error; diff --git a/lib/src/type_adapter_generator.dart b/lib/src/type_adapter_generator.dart index 2f2001b..7d06b48 100644 --- a/lib/src/type_adapter_generator.dart +++ b/lib/src/type_adapter_generator.dart @@ -1,5 +1,6 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:build/build.dart'; +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/helper.dart'; @@ -17,7 +18,6 @@ class TypeAdapterGenerator extends GeneratorForAnnotation { cls.isEnum ? EnumBuilder(cls, fields) : ClassBuilder(cls, fields); return ''' - class $adapterName extends TypeAdapter<${cls.name}> { @override ${cls.name} read(BinaryReader reader) { @@ -44,20 +44,53 @@ class TypeAdapterGenerator extends GeneratorForAnnotation { return cls; } - Map getFields(ClassElement cls) { - var typeFields = {}; + Iterable getAccessors(ClassElement cls) { + var fields = []; for (var field in cls.fields) { var fieldAnn = getHiveFieldAnn(field); if (fieldAnn == null) continue; - var fieldNum = fieldAnn.index; - check(fieldNum >= 0 || fieldNum <= 255, - 'Field numbers can only be in the range 0-255.'); - check(!typeFields.containsKey(fieldNum), - 'Duplicate field number: $fieldNum.'); - typeFields[fieldNum] = field; + fields.add(AdapterField(fieldAnn.index, field.name, field.type)); + } + + for (var field in cls.accessors) { + var fieldAnn = getHiveFieldAnn(field); + if (fieldAnn == null || !field.isGetter) continue; + + fields.add(AdapterField(fieldAnn.index, field.name, field.type)); + } + + return fields; + } + + List getFields(ClassElement cls) { + var types = + getTypeAndAllSupertypes(cls).where((it) => getHiveTypeAnn(it) != null); + for (var type in types) { + print('TYPE: $type'); + } + var adapterFields = []; + for (var type in types) { + var fields = getAccessors(type); + for (var field in fields) { + print(field.name); + 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.', + ); + } + } + + adapterFields.add(field); + } } - return typeFields; + return adapterFields; } String getAdapterName(String typeName, ConstantReader annotation) {