From 4d74404813f10f36e64c11c9c8ff4954735a441f Mon Sep 17 00:00:00 2001 From: Vitus Date: Sat, 2 Feb 2019 01:09:13 +0100 Subject: [PATCH] Add delete methods code generation and fix update methods (#22) --- example/lib/database.dart | 3 ++ floor/lib/src/annotations.dart | 8 +++ floor_generator/lib/misc/constants.dart | 1 + floor_generator/lib/misc/type_utils.dart | 4 ++ floor_generator/lib/model/database.dart | 8 +++ floor_generator/lib/model/delete_method.dart | 51 +++++++++++++++++++ .../lib/writer/database_writer.dart | 29 +++++++---- .../lib/writer/delete_method_writer.dart | 48 +++++++++++++++++ .../lib/writer/update_method_writer.dart | 37 ++++++++++++-- 9 files changed, 174 insertions(+), 15 deletions(-) create mode 100644 floor_generator/lib/model/delete_method.dart create mode 100644 floor_generator/lib/writer/delete_method_writer.dart diff --git a/example/lib/database.dart b/example/lib/database.dart index cf82f795..a778592a 100644 --- a/example/lib/database.dart +++ b/example/lib/database.dart @@ -88,6 +88,9 @@ abstract class MyDatabase extends FloorDatabase { @update Future updatePerson(Person person); + + @delete + Future deletePerson(Person person); } Future main() async { diff --git a/floor/lib/src/annotations.dart b/floor/lib/src/annotations.dart index d1b4d28c..3ab06dcc 100644 --- a/floor/lib/src/annotations.dart +++ b/floor/lib/src/annotations.dart @@ -62,3 +62,11 @@ class Update { /// Marks a method as an update method. const update = Update(); + +/// Marks a method as a delete method. +class Delete { + const Delete(); +} + +/// Marks a method as a delete method. +const delete = Delete(); diff --git a/floor_generator/lib/misc/constants.dart b/floor_generator/lib/misc/constants.dart index f14c9167..0bcf60ae 100644 --- a/floor_generator/lib/misc/constants.dart +++ b/floor_generator/lib/misc/constants.dart @@ -14,6 +14,7 @@ abstract class Annotation { static const QUERY = 'Query'; static const INSERT = 'Insert'; static const UPDATE = 'Update'; + static const DELETE = 'Delete'; } abstract class AnnotationField { diff --git a/floor_generator/lib/misc/type_utils.dart b/floor_generator/lib/misc/type_utils.dart index ef3e1c06..9eb1c42e 100644 --- a/floor_generator/lib/misc/type_utils.dart +++ b/floor_generator/lib/misc/type_utils.dart @@ -60,6 +60,10 @@ bool isUpdateAnnotation(ElementAnnotation annotation) { return _getAnnotationName(annotation) == Annotation.UPDATE; } +bool isDeleteAnnotation(ElementAnnotation annotation) { + return _getAnnotationName(annotation) == Annotation.DELETE; +} + DartType flattenList(DartType type) { return (type as ParameterizedType).typeArguments.first; } diff --git a/floor_generator/lib/model/database.dart b/floor_generator/lib/model/database.dart index 108216a4..22b04580 100644 --- a/floor_generator/lib/model/database.dart +++ b/floor_generator/lib/model/database.dart @@ -1,5 +1,6 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:floor_generator/misc/type_utils.dart'; +import 'package:floor_generator/model/delete_method.dart'; import 'package:floor_generator/model/entity.dart'; import 'package:floor_generator/model/insert_method.dart'; import 'package:floor_generator/model/query_method.dart'; @@ -36,6 +37,13 @@ class Database { .toList(); } + List get deleteMethods { + return methods + .where((method) => method.metadata.any(isDeleteAnnotation)) + .map((method) => DeleteMethod(method)) + .toList(); + } + List getEntities(LibraryReader library) { return library.classes .where((clazz) => diff --git a/floor_generator/lib/model/delete_method.dart b/floor_generator/lib/model/delete_method.dart new file mode 100644 index 00000000..f2629848 --- /dev/null +++ b/floor_generator/lib/model/delete_method.dart @@ -0,0 +1,51 @@ +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/type.dart'; +import 'package:floor_generator/misc/type_utils.dart'; +import 'package:floor_generator/model/entity.dart'; +import 'package:source_gen/source_gen.dart'; + +class DeleteMethod { + final MethodElement method; + + DeleteMethod(this.method); + + DartType get returnType => method.returnType; + + String get name => method.displayName; + + ParameterElement get parameter { + final parameters = method.parameters; + if (parameters.isEmpty) { + throw InvalidGenerationSourceError( + 'There is no parameter supplied for an update method. Please add one.', + element: method, + ); + } else if (parameters.length > 1) { + throw InvalidGenerationSourceError( + 'Only one parameter is allowed on update methods.', + element: method, + ); + } + return parameters.first; + } + + Entity getEntity(LibraryReader library) { + final entityClass = _getEntities(library).firstWhere( + (entity) => entity.displayName == parameter.type.displayName); + + return Entity(entityClass); + } + + bool insertsEntity(LibraryReader library) { + return _getEntities(library) + .map((clazz) => clazz.displayName) + .any((entity) => entity == parameter.type.displayName); + } + + List _getEntities(LibraryReader library) { + return library.classes + .where((clazz) => + !clazz.isAbstract && clazz.metadata.any(isEntityAnnotation)) + .toList(); + } +} diff --git a/floor_generator/lib/writer/database_writer.dart b/floor_generator/lib/writer/database_writer.dart index d085c1c2..876fc9c1 100644 --- a/floor_generator/lib/writer/database_writer.dart +++ b/floor_generator/lib/writer/database_writer.dart @@ -1,10 +1,12 @@ import 'package:floor_generator/misc/annotation_expression.dart'; import 'package:floor_generator/misc/type_utils.dart'; import 'package:floor_generator/model/database.dart'; +import 'package:floor_generator/model/delete_method.dart'; import 'package:floor_generator/model/entity.dart'; import 'package:floor_generator/model/insert_method.dart'; import 'package:floor_generator/model/query_method.dart'; import 'package:floor_generator/model/update_method.dart'; +import 'package:floor_generator/writer/delete_method_writer.dart'; import 'package:floor_generator/writer/insert_method_writer.dart'; import 'package:floor_generator/writer/query_method_writer.dart'; import 'package:floor_generator/writer/update_method_writer.dart'; @@ -82,14 +84,8 @@ class DatabaseWriter implements Writer { ..methods.add(_generateOpenMethod(databaseName, createTableStatements)) ..methods.addAll(_generateQueryMethods(database.queryMethods)) ..methods.addAll(_generateInsertMethods(database.insertMethods)) - ..methods.addAll(_generateUpdateMethods(database.updateMethods))); - } - - List _generateUpdateMethods(List updateMethods) { - return updateMethods - .map( - (updateMethod) => UpdateMethodWriter(library, updateMethod).write()) - .toList(); + ..methods.addAll(_generateUpdateMethods(database.updateMethods)) + ..methods.addAll(_generateDeleteMethods(database.deleteMethods))); } Method _generateOpenMethod( @@ -116,14 +112,25 @@ class DatabaseWriter implements Writer { List _generateInsertMethods(List insertMethods) { return insertMethods - .map( - (insertMethod) => InsertMethodWriter(library, insertMethod).write()) + .map((method) => InsertMethodWriter(library, method).write()) .toList(); } List _generateQueryMethods(List queryMethods) { return queryMethods - .map((queryMethod) => QueryMethodWriter(library, queryMethod).write()) + .map((method) => QueryMethodWriter(library, method).write()) + .toList(); + } + + List _generateUpdateMethods(List updateMethods) { + return updateMethods + .map((method) => UpdateMethodWriter(library, method).write()) + .toList(); + } + + List _generateDeleteMethods(List deleteMethods) { + return deleteMethods + .map((method) => DeleteMethodWriter(library, method).write()) .toList(); } diff --git a/floor_generator/lib/writer/delete_method_writer.dart b/floor_generator/lib/writer/delete_method_writer.dart new file mode 100644 index 00000000..04ffaa88 --- /dev/null +++ b/floor_generator/lib/writer/delete_method_writer.dart @@ -0,0 +1,48 @@ +import 'package:code_builder/code_builder.dart'; +import 'package:floor_generator/misc/annotation_expression.dart'; +import 'package:floor_generator/model/delete_method.dart'; +import 'package:floor_generator/writer/writer.dart'; +import 'package:source_gen/source_gen.dart'; + +class DeleteMethodWriter implements Writer { + final LibraryReader library; + final DeleteMethod deleteMethod; + + DeleteMethodWriter(this.library, this.deleteMethod); + + @override + Method write() { + return _generateDeleteMethod(); + } + + Method _generateDeleteMethod() { + // TODO assert deletes entity + + return Method((builder) => builder + ..annotations.add(overrideAnnotationExpression) + ..returns = refer(deleteMethod.returnType.displayName) + ..name = deleteMethod.name + ..requiredParameters.add(_generateParameter()) + ..modifier = MethodModifier.async + ..body = Code(_generateMethodBody())); + } + + Parameter _generateParameter() { + final parameter = deleteMethod.parameter; + + return Parameter((builder) => builder + ..name = parameter.name + ..type = refer(parameter.type.displayName)); + } + + String _generateMethodBody() { + final entity = deleteMethod.getEntity(library); + final primaryKeyColumn = + entity.columns.firstWhere((column) => column.isPrimaryKey); + final methodHeadParameterName = deleteMethod.parameter.name; + + return ''' + await this.database.rawDelete('DELETE FROM ${entity.name} WHERE ${primaryKeyColumn.name} = \${$methodHeadParameterName.${primaryKeyColumn.field.displayName}}'); + '''; + } +} diff --git a/floor_generator/lib/writer/update_method_writer.dart b/floor_generator/lib/writer/update_method_writer.dart index 60dc2414..0399ea74 100644 --- a/floor_generator/lib/writer/update_method_writer.dart +++ b/floor_generator/lib/writer/update_method_writer.dart @@ -1,5 +1,7 @@ +import 'package:analyzer/dart/element/element.dart'; import 'package:code_builder/code_builder.dart'; import 'package:floor_generator/misc/annotation_expression.dart'; +import 'package:floor_generator/misc/type_utils.dart'; import 'package:floor_generator/model/update_method.dart'; import 'package:floor_generator/writer/writer.dart'; import 'package:source_gen/source_gen.dart'; @@ -36,13 +38,40 @@ class UpdateMethodWriter implements Writer { } String _generateMethodBody() { + final parameter = updateMethod.parameter; + final methodHeadParameterName = parameter.displayName; + + final keyValueList = (parameter.type.element as ClassElement) + .constructors + .first + .parameters + .map((parameter) { + final valueMapping = _getValueMapping(parameter, methodHeadParameterName); + + return "'${parameter.displayName}': $valueMapping"; + }).join(', '); + final entity = updateMethod.getEntity(library); - final primaryKeyColumn = - entity.columns.firstWhere((column) => column.isPrimaryKey); - final methodHeadParameterName = updateMethod.parameter.name; + // TODO exclude id? return ''' - await this.database.rawDelete('DELETE FROM ${entity.name} WHERE ${primaryKeyColumn.name} = \${$methodHeadParameterName.${primaryKeyColumn.field.displayName}}'); + final values = { + $keyValueList + }; + await this.database.update('${entity.name}', values); '''; } + + String _getValueMapping( + ParameterElement parameter, + String methodParameterName, + ) { + final parameterName = parameter.displayName; + + if (isBool(parameter.type)) { + return '$methodParameterName.$parameterName ? 1 : 0'; + } else { + return '$methodParameterName.$parameterName'; + } + } }