Skip to content
This repository has been archived by the owner on Feb 13, 2020. It is now read-only.

Commit

Permalink
v0.6.0
Browse files Browse the repository at this point in the history
  • Loading branch information
simc committed Dec 22, 2019
1 parent 21c2234 commit dfaba47
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 88 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.6.0
- Support for HiveLists
- Support for getters & setters
- Support for inheritance

## 0.5.2
- Fix bug with Uint8Lists

Expand Down
7 changes: 3 additions & 4 deletions lib/src/builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ class AdapterField {

abstract class Builder {
final ClassElement cls;
final List<AdapterField> fields;
final List<AdapterField> getters;
final List<AdapterField> setters;

Builder(this.cls, this.fields)
: assert(cls != null),
assert(fields != null);
Builder(this.cls, this.getters, this.setters) : assert(cls != null);

String buildRead();

Expand Down
55 changes: 28 additions & 27 deletions lib/src/class_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<AdapterField> fields)
: super(cls, fields);
ClassBuilder(
ClassElement cls, List<AdapterField> getters, List<AdapterField> setters)
: super(cls, getters, setters);

@override
String buildRead() {
Expand All @@ -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}]')}');
}
Expand All @@ -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)}';
Expand Down Expand Up @@ -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})
Expand Down
7 changes: 4 additions & 3 deletions lib/src/enum_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<AdapterField> fields) : super(cls, fields);
EnumBuilder(ClassElement cls, List<AdapterField> 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};''');
Expand All @@ -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});
Expand Down
117 changes: 69 additions & 48 deletions lib/src/type_adapter_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,22 @@ import 'package:hive/hive.dart';

class TypeAdapterGenerator extends GeneratorForAnnotation<HiveType> {
@override
String generateForAnnotatedElement(
Element element, ConstantReader annotation, BuildStep buildStep) {
Future<String> 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}> {
Expand All @@ -36,61 +45,73 @@ class TypeAdapterGenerator extends GeneratorForAnnotation<HiveType> {
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<AdapterField> getAccessors(ClassElement cls) {
var fields = <AdapterField>[];
for (var field in cls.fields) {
var fieldAnn = getHiveFieldAnn(field);
if (fieldAnn == null) continue;

fields.add(AdapterField(fieldAnn.index, field.name, field.type));
Set<String> getAllAccessorNames(ClassElement cls) {
var accessorNames = <String>{};

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<List<AdapterField>> getAccessors(
ClassElement cls, LibraryElement library) {
var accessorNames = getAllAccessorNames(cls);

var getters = <AdapterField>[];
var setters = <AdapterField>[];
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<AdapterField> getFields(ClassElement cls) {
var types =
getTypeAndAllSupertypes(cls).where((it) => getHiveTypeAnn(it) != null);
for (var type in types) {
print('TYPE: $type');
}
var adapterFields = <AdapterField>[];
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<AdapterField> 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) {
Expand Down
12 changes: 6 additions & 6 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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 <simonleier@gmail.com>
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
Expand Down

0 comments on commit dfaba47

Please sign in to comment.