From dfaba471047255f217d673a708051b9816aebb19 Mon Sep 17 00:00:00 2001 From: Simon Leier Date: Sun, 22 Dec 2019 15:14:34 +0100 Subject: [PATCH] v0.6.0 --- CHANGELOG.md | 5 ++ lib/src/builder.dart | 7 +- lib/src/class_builder.dart | 55 ++++++------- lib/src/enum_builder.dart | 7 +- lib/src/type_adapter_generator.dart | 117 ++++++++++++++++------------ pubspec.yaml | 12 +-- 6 files changed, 115 insertions(+), 88 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f5903c..85061f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.6.0 +- Support for HiveLists +- Support for getters & setters +- Support for inheritance + ## 0.5.2 - Fix bug with Uint8Lists diff --git a/lib/src/builder.dart b/lib/src/builder.dart index b172b65..7284a72 100644 --- a/lib/src/builder.dart +++ b/lib/src/builder.dart @@ -11,11 +11,10 @@ class AdapterField { abstract class Builder { final ClassElement cls; - final List fields; + final List getters; + final List setters; - Builder(this.cls, this.fields) - : assert(cls != null), - assert(fields != null); + Builder(this.cls, this.getters, this.setters) : assert(cls != null); String buildRead(); diff --git a/lib/src/class_builder.dart b/lib/src/class_builder.dart index bf179a5..d2bf1c4 100644 --- a/lib/src/class_builder.dart +++ b/lib/src/class_builder.dart @@ -2,18 +2,23 @@ import 'dart:typed_data'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; +import 'package:hive/hive.dart'; 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'; 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); - ClassBuilder(ClassElement cls, List fields) - : super(cls, fields); + ClassBuilder( + ClassElement cls, List getters, List setters) + : super(cls, getters, setters); @override String buildRead() { @@ -26,37 +31,30 @@ class ClassBuilder extends Builder { return ${cls.name}( '''); - var constructor = cls.constructors.firstWhere( - (constructor) => constructor.name.isEmpty, - orElse: () => throw AssertionError('Provide an unnamed constructor.'), - ); + var constr = cls.constructors.firstOrNullWhere((it) => it.name.isEmpty); + check(constr != null, 'Provide an unnamed constructor.'); // The remaining fields to initialize. - var remainingFields = fields.toList(); - - for (var param in constructor.parameters - .where((param) => param.isInitializingFormal)) { - 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; + var fields = setters.toList(); + + var initializingParams = + constr.parameters.where((param) => param.isInitializingFormal); + for (var param in initializingParams) { + var field = fields.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); } - - if (param.isNamed) { - code.write('${param.name}: '); - } - 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 field in remainingFields) { + for (var field in fields) { code.writeln( '..${field.name} = ${_cast(field.type, 'fields[${field.index}]')}'); } @@ -67,7 +65,10 @@ class ClassBuilder extends Builder { } String _cast(DartType type, String variable) { - if (iterableChecker.isAssignableFromType(type) && !isUint8List(type)) { + if (hiveListChecker.isExactlyType(type)) { + return '($variable as HiveList)?.castHiveList()'; + } else if (iterableChecker.isAssignableFromType(type) && + !isUint8List(type)) { return '($variable as List)${_castIterable(type)}'; } else if (mapChecker.isExactlyType(type)) { return '($variable as Map)${_castMap(type)}'; @@ -119,8 +120,8 @@ class ClassBuilder extends Builder { String buildWrite() { var code = StringBuffer(); code.writeln('writer'); - code.writeln('..writeByte(${fields.length})'); - for (var field in fields) { + code.writeln('..writeByte(${getters.length})'); + for (var field in getters) { var value = _convertIterable(field.type, 'obj.${field.name}'); code.writeln(''' ..writeByte(${field.index}) diff --git a/lib/src/enum_builder.dart b/lib/src/enum_builder.dart index 89f91d4..e8639c7 100644 --- a/lib/src/enum_builder.dart +++ b/lib/src/enum_builder.dart @@ -2,14 +2,15 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:hive_generator/src/builder.dart'; class EnumBuilder extends Builder { - EnumBuilder(ClassElement cls, List fields) : super(cls, fields); + EnumBuilder(ClassElement cls, List getters) + : super(cls, getters, null); @override String buildRead() { var code = StringBuffer(); code.writeln('switch(reader.readByte()) {'); - for (var field in fields) { + for (var field in getters) { code.writeln(''' case ${field.index}: return ${cls.name}.${field.name};'''); @@ -28,7 +29,7 @@ class EnumBuilder extends Builder { var code = StringBuffer(); code.writeln('switch(obj) {'); - for (var field in fields) { + for (var field in getters) { code.writeln(''' case ${cls.name}.${field.name}: writer.writeByte(${field.index}); diff --git a/lib/src/type_adapter_generator.dart b/lib/src/type_adapter_generator.dart index 7d06b48..94c4e7b 100644 --- a/lib/src/type_adapter_generator.dart +++ b/lib/src/type_adapter_generator.dart @@ -9,13 +9,22 @@ import 'package:hive/hive.dart'; class TypeAdapterGenerator extends GeneratorForAnnotation { @override - String generateForAnnotatedElement( - Element element, ConstantReader annotation, BuildStep buildStep) { + Future generateForAnnotatedElement( + Element element, ConstantReader annotation, BuildStep buildStep) async { var cls = getClass(element); - var fields = getFields(cls); + var library = await buildStep.inputLibrary; + var gettersAndSetters = getAccessors(cls, library); + + var getters = gettersAndSetters[0]; + verifyFieldIndices(getters); + + var setters = gettersAndSetters[1]; + verifyFieldIndices(setters); + var adapterName = getAdapterName(cls.name, annotation); - var builder = - cls.isEnum ? EnumBuilder(cls, fields) : ClassBuilder(cls, fields); + var builder = cls.isEnum + ? EnumBuilder(cls, getters) + : ClassBuilder(cls, getters, setters); return ''' class $adapterName extends TypeAdapter<${cls.name}> { @@ -36,61 +45,73 @@ class TypeAdapterGenerator extends GeneratorForAnnotation { check(element.kind == ElementKind.CLASS, 'Only classes or enums are allowed to be annotated with @HiveType.'); - var cls = element as ClassElement; - - check(!cls.isAbstract, - 'Classes annotated with @HiveType must not be abstract.'); - - return cls; + return element as ClassElement; } - Iterable getAccessors(ClassElement cls) { - var fields = []; - for (var field in cls.fields) { - var fieldAnn = getHiveFieldAnn(field); - if (fieldAnn == null) continue; - - fields.add(AdapterField(fieldAnn.index, field.name, field.type)); + 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); + } + } } - for (var field in cls.accessors) { - var fieldAnn = getHiveFieldAnn(field); - if (fieldAnn == null || !field.isGetter) continue; + 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)); + } + } - fields.add(AdapterField(fieldAnn.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 fields; + return [getters, setters]; } - 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.', - ); - } + 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.', + ); } - - adapterFields.add(field); } } - return adapterFields; } String getAdapterName(String typeName, ConstantReader annotation) { diff --git a/pubspec.yaml b/pubspec.yaml index eb0eacd..3d79e9d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,18 +1,18 @@ name: hive_generator description: Extension for Hive. Automatically generates TypeAdapters to store any class. -version: 0.5.2 -author: Simon Leier -homepage: https://github.com/leisim/hive +version: 0.6.0 +homepage: https://github.com/hivedb/hive +documentation: https://docs.hivedb.dev/ environment: - sdk: ">=2.2.2 <3.0.0" + sdk: ">=2.6.0 <3.0.0" dependencies: build: ^1.1.6 source_gen: ^0.9.4+4 - hive: ">=1.0.0 <2.0.0" + hive: ">=1.2.0 <2.0.0" analyzer: ">=0.36.0 <0.40.0" - + dartx: ^0.2.0 dev_dependencies: test: ^1.6.4