Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use TypeChecker for all annotations #98

Merged
merged 9 commits into from
Mar 12, 2019
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ It holds the *DAOs* and, beyond that, takes care of initializing the database an

The figure shows the relationship between *Entity*, *DAO* and *Database*.

![Floor Architecture](img/floor-architecture.png)
![Floor Architecture](https://raw.githubusercontent.com/vitusortner/floor/develop/img/floor-architecture.png)

## Querying
Method signatures turn into query methods by adding the `@Query()` annotation with the query in parenthesis to them.
Expand Down Expand Up @@ -184,7 +184,7 @@ All these methods accept single or multiple entity instances.
- **Insert**

`@insert` marks a method as an insertion method.
WHen using the capitalized `@Insert` you can specify a conflict strategy.
When using the capitalized `@Insert` you can specify a conflict strategy.
Else it just defaults to aborting the insert.
These methods can return a `Future` of either `void`, `int` or `List<int>`.
- `void` return nothing
Expand Down
4 changes: 2 additions & 2 deletions floor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ It holds the *DAOs* and, beyond that, takes care of initializing the database an

The figure shows the relationship between *Entity*, *DAO* and *Database*.

![Floor Architecture](../img/floor-architecture.png)
![Floor Architecture](https://raw.githubusercontent.com/vitusortner/floor/develop/img/floor-architecture.png)

## Querying
Method signatures turn into query methods by adding the `@Query()` annotation with the query in parenthesis to them.
Expand Down Expand Up @@ -184,7 +184,7 @@ All these methods accept single or multiple entity instances.
- **Insert**

`@insert` marks a method as an insertion method.
WHen using the capitalized `@Insert` you can specify a conflict strategy.
When using the capitalized `@Insert` you can specify a conflict strategy.
Else it just defaults to aborting the insert.
These methods can return a `Future` of either `void`, `int` or `List<int>`.
- `void` return nothing
Expand Down
1 change: 1 addition & 0 deletions floor_generator/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ builders:
builder_factories: ["floorBuilder"]
# The 'partId' argument to 'SharedPartBuilder' is 'floor'
build_extensions: {".dart": [".floor.g.part"]}
# Apply the Builder to the package with a direct dependency on the package exposing the builder
auto_apply: dependents
build_to: cache
# To copy the '.g.dart' content into '.g.dart' in the source tree
Expand Down
28 changes: 28 additions & 0 deletions floor_generator/lib/misc/query_method_processor_error.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:source_gen/source_gen.dart';

class QueryMethodProcessorError {
final MethodElement _methodElement;

QueryMethodProcessorError(final MethodElement methodElement)
: assert(methodElement != null),
_methodElement = methodElement;

// ignore: non_constant_identifier_names
InvalidGenerationSourceError get NO_QUERY_DEFINED {
return InvalidGenerationSourceError(
"You didn't define a query.",
todo: 'Define a query by adding SQL to the @Query() annotation.',
element: _methodElement,
);
}

// ignore: non_constant_identifier_names
InvalidGenerationSourceError get DOES_NOT_RETURN_FUTURE_NOR_STREAM {
return InvalidGenerationSourceError(
'All queries have to return a Future or Stream.',
todo: 'Define the return type as Future or Stream.',
element: _methodElement,
);
}
}
60 changes: 9 additions & 51 deletions floor_generator/lib/misc/type_utils.dart
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:floor_generator/misc/annotations.dart';
import 'package:floor_generator/misc/constants.dart';
import 'package:source_gen/source_gen.dart';

@nonNull
TypeChecker typeChecker(final Type type) => TypeChecker.fromRuntime(type);

final _stringTypeChecker = typeChecker(String);

final _boolTypeChecker = typeChecker(bool);

final _intTypeChecker = typeChecker(int);

final _doubleTypeChecker = typeChecker(double);

final _streamTypeChecker = typeChecker(Stream);

bool isString(final DartType type) {
return _stringTypeChecker.isExactlyType(type);
}
Expand All @@ -35,7 +23,7 @@ bool isDouble(final DartType type) {

bool isList(final DartType type) {
// TODO this weirdly fails when using a TypeChecker
return type.name == 'List' && _isDartCore(type);
return type.name == 'List' && type.element.library.isDartCore;
}

bool isSupportedType(final DartType type) {
Expand All @@ -51,38 +39,6 @@ bool isStream(final DartType type) {
return _streamTypeChecker.isExactlyType(type);
}

bool isEntityAnnotation(final ElementAnnotation annotation) {
return _getAnnotationName(annotation) == Annotation.ENTITY;
}

bool isColumnInfoAnnotation(final ElementAnnotation annotation) {
return _getAnnotationName(annotation) == Annotation.COLUMN_INFO;
}

bool isPrimaryKeyAnnotation(final ElementAnnotation annotation) {
return _getAnnotationName(annotation) == Annotation.PRIMARY_KEY;
}

bool isQueryAnnotation(final ElementAnnotation annotation) {
return _getAnnotationName(annotation) == Annotation.QUERY;
}

bool isInsertAnnotation(final ElementAnnotation annotation) {
return _getAnnotationName(annotation) == Annotation.INSERT;
}

bool isUpdateAnnotation(final ElementAnnotation annotation) {
return _getAnnotationName(annotation) == Annotation.UPDATE;
}

bool isDeleteAnnotation(final ElementAnnotation annotation) {
return _getAnnotationName(annotation) == Annotation.DELETE;
}

bool isTransactionAnnotation(final ElementAnnotation annotation) {
return _getAnnotationName(annotation) == Annotation.TRANSACTION;
}

DartType flattenList(final DartType type) {
return (type as ParameterizedType).typeArguments.first;
}
Expand All @@ -91,10 +47,12 @@ DartType flattenStream(final DartType type) {
return (type as ParameterizedType).typeArguments.first;
}

bool _isDartCore(final DartType type) {
return type.element.library.isDartCore;
}
final _stringTypeChecker = typeChecker(String);

String _getAnnotationName(final ElementAnnotation annotation) {
return annotation.computeConstantValue().type.displayName;
}
final _boolTypeChecker = typeChecker(bool);

final _intTypeChecker = typeChecker(int);

final _doubleTypeChecker = typeChecker(double);

final _streamTypeChecker = typeChecker(Stream);
17 changes: 12 additions & 5 deletions floor_generator/lib/processor/dao_processor.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:floor_annotation/floor_annotation.dart' as annotations
show Query, Insert, Update, delete, transaction;
import 'package:floor_generator/misc/type_utils.dart';
import 'package:floor_generator/processor/deletion_method_processor.dart';
import 'package:floor_generator/processor/insertion_method_processor.dart';
Expand Down Expand Up @@ -61,7 +63,8 @@ class DaoProcessor extends Processor<Dao> {

List<QueryMethod> _getQueryMethods(List<MethodElement> methods) {
return methods
.where((method) => method.metadata.any(isQueryAnnotation))
.where((method) =>
typeChecker(annotations.Query).hasAnnotationOfExact(method))
.map((method) => QueryMethodProcessor(method, _entities).process())
.toList();
}
Expand All @@ -70,28 +73,32 @@ class DaoProcessor extends Processor<Dao> {
List<MethodElement> methodElements,
) {
return methodElements
.where((method) => method.metadata.any(isInsertAnnotation))
.where((method) =>
typeChecker(annotations.Insert).hasAnnotationOfExact(method))
.map((method) => InsertionMethodProcessor(method, _entities).process())
.toList();
}

List<UpdateMethod> _getUpdateMethods(List<MethodElement> methods) {
return methods
.where((method) => method.metadata.any(isUpdateAnnotation))
.where((method) =>
typeChecker(annotations.Update).hasAnnotationOfExact(method))
.map((method) => UpdateMethodProcessor(method, _entities).process())
.toList();
}

List<DeletionMethod> _getDeletionMethods(List<MethodElement> methods) {
return methods
.where((method) => method.metadata.any(isDeleteAnnotation))
.where((method) => typeChecker(annotations.delete.runtimeType)
.hasAnnotationOfExact(method))
.map((method) => DeletionMethodProcessor(method, _entities).process())
.toList();
}

List<TransactionMethod> _getTransactionMethods(List<MethodElement> methods) {
return methods
.where((method) => method.metadata.any(isTransactionAnnotation))
.where((method) => typeChecker(annotations.transaction.runtimeType)
.hasAnnotationOfExact(method))
.map((method) =>
TransactionMethodProcessor(method, _daoGetterName, _databaseName)
.process())
Expand Down
3 changes: 2 additions & 1 deletion floor_generator/lib/processor/database_processor.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:floor_annotation/floor_annotation.dart' as annotations;
import 'package:floor_annotation/floor_annotation.dart' as annotations
show Database, dao, Entity;
import 'package:floor_generator/misc/annotations.dart';
import 'package:floor_generator/misc/constants.dart';
import 'package:floor_generator/misc/database_processor_error.dart';
Expand Down
23 changes: 13 additions & 10 deletions floor_generator/lib/processor/field_processor.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:floor_annotation/floor_annotation.dart' as annotations
show ColumnInfo, PrimaryKey;
import 'package:floor_generator/misc/constants.dart';
import 'package:floor_generator/misc/type_utils.dart';
import 'package:floor_generator/processor/processor.dart';
Expand All @@ -8,6 +10,8 @@ import 'package:source_gen/source_gen.dart';
class FieldProcessor extends Processor<Field> {
final FieldElement _fieldElement;

final _columnInfoTypeChecker = typeChecker(annotations.ColumnInfo);

FieldProcessor(final FieldElement fieldElement)
: assert(fieldElement != null),
_fieldElement = fieldElement;
Expand All @@ -16,10 +20,11 @@ class FieldProcessor extends Processor<Field> {
Field process() {
final name = _fieldElement.name;
final hasColumnInfoAnnotation =
_fieldElement.metadata.any(isColumnInfoAnnotation);
_columnInfoTypeChecker.hasAnnotationOfExact(_fieldElement);
final columnName = _getColumnName(hasColumnInfoAnnotation, name);
final isNullable = _getIsNullable(hasColumnInfoAnnotation);
final isPrimaryKey = _fieldElement.metadata.any(isPrimaryKeyAnnotation);
final isPrimaryKey =
typeChecker(annotations.PrimaryKey).hasAnnotationOfExact(_fieldElement);

return Field(
_fieldElement,
Expand All @@ -33,22 +38,20 @@ class FieldProcessor extends Processor<Field> {

String _getColumnName(bool hasColumnInfoAnnotation, String name) {
return hasColumnInfoAnnotation
? _fieldElement.metadata
.firstWhere(isColumnInfoAnnotation)
.computeConstantValue()
? _columnInfoTypeChecker
.firstAnnotationOfExact(_fieldElement)
.getField(AnnotationField.COLUMN_INFO_NAME)
.toStringValue() ??
?.toStringValue() ??
name
: name;
}

bool _getIsNullable(bool hasColumnInfoAnnotation) {
return hasColumnInfoAnnotation
? _fieldElement.metadata
.firstWhere(isColumnInfoAnnotation)
.computeConstantValue()
? _columnInfoTypeChecker
.firstAnnotationOfExact(_fieldElement)
.getField(AnnotationField.COLUMN_INFO_NULLABLE)
.toBoolValue() ??
?.toBoolValue() ??
true
: true; // all Dart fields are nullable by default
}
Expand Down
9 changes: 5 additions & 4 deletions floor_generator/lib/processor/insertion_method_processor.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:floor_annotation/floor_annotation.dart' as annotations
show Insert;
import 'package:floor_generator/misc/annotations.dart';
import 'package:floor_generator/misc/change_method_processor_helper.dart';
import 'package:floor_generator/misc/constants.dart';
import 'package:floor_generator/misc/type_utils.dart';
import 'package:floor_generator/processor/processor.dart';
import 'package:floor_generator/value_object/entity.dart';
import 'package:floor_generator/value_object/insertion_method.dart';
import 'package:floor_generator/misc/type_utils.dart';
import 'package:source_gen/source_gen.dart';

class InsertionMethodProcessor implements Processor<InsertionMethod> {
Expand Down Expand Up @@ -81,9 +83,8 @@ class InsertionMethodProcessor implements Processor<InsertionMethod> {

@nonNull
String _getOnConflictStrategy() {
final strategy = _methodElement.metadata
.firstWhere(isInsertAnnotation)
.computeConstantValue()
final strategy = typeChecker(annotations.Insert)
.firstAnnotationOfExact(_methodElement)
.getField(AnnotationField.ON_CONFLICT)
.toIntValue();

Expand Down
35 changes: 20 additions & 15 deletions floor_generator/lib/processor/query_method_processor.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:floor_annotation/floor_annotation.dart' as annotations
show Query;
import 'package:floor_generator/misc/annotations.dart';
import 'package:floor_generator/misc/constants.dart';
import 'package:floor_generator/misc/query_method_processor_error.dart';
import 'package:floor_generator/misc/type_utils.dart';
import 'package:floor_generator/processor/processor.dart';
import 'package:floor_generator/value_object/entity.dart';
import 'package:floor_generator/value_object/query_method.dart';
import 'package:source_gen/source_gen.dart';

class QueryMethodProcessor extends Processor<QueryMethod> {
final QueryMethodProcessorError _processorError;

final MethodElement _methodElement;
final List<Entity> _entities;

Expand All @@ -17,8 +23,10 @@ class QueryMethodProcessor extends Processor<QueryMethod> {
) : assert(methodElement != null),
assert(entities != null),
_methodElement = methodElement,
_entities = entities;
_entities = entities,
_processorError = QueryMethodProcessorError(methodElement);

@nonNull
@override
QueryMethod process() {
final name = _methodElement.displayName;
Expand Down Expand Up @@ -52,19 +60,17 @@ class QueryMethodProcessor extends Processor<QueryMethod> {
);
}

@nonNull
String _getQuery() {
final query = _methodElement.metadata
.firstWhere(isQueryAnnotation)
.computeConstantValue()
final query = typeChecker(annotations.Query)
.firstAnnotationOfExact(_methodElement)
.getField(AnnotationField.QUERY_VALUE)
.toStringValue();
?.toStringValue();

if (query.isEmpty || query == null) throw _processorError.NO_QUERY_DEFINED;

// TODO parse query

if (query.isEmpty || query == null) {
throw InvalidGenerationSourceError(
"You didn't define a query.",
element: _methodElement,
);
}
return query.replaceAll(RegExp(':'), r'$');
}

Expand All @@ -75,6 +81,7 @@ class QueryMethodProcessor extends Processor<QueryMethod> {
.toList();
}

@nonNull
DartType _getFlattenedReturnType(
final DartType rawReturnType,
final bool returnsStream,
Expand All @@ -90,6 +97,7 @@ class QueryMethodProcessor extends Processor<QueryMethod> {
return type;
}

@nonNull
bool _getReturnsList(final DartType returnType, final bool returnsStream) {
final type = returnsStream
? flattenStream(returnType)
Expand All @@ -103,10 +111,7 @@ class QueryMethodProcessor extends Processor<QueryMethod> {
final bool returnsStream,
) {
if (!rawReturnType.isDartAsyncFuture && !returnsStream) {
throw InvalidGenerationSourceError(
'All queries have to return a Future or Stream.',
element: _methodElement,
);
throw _processorError.DOES_NOT_RETURN_FUTURE_NOR_STREAM;
}
}

Expand Down
Loading