Skip to content

Commit

Permalink
Support accessing data from Data Access Objects (#82)
Browse files Browse the repository at this point in the history
  • Loading branch information
vitusortner authored Mar 6, 2019
1 parent e86944a commit 07767af
Show file tree
Hide file tree
Showing 17 changed files with 513 additions and 303 deletions.
2 changes: 0 additions & 2 deletions floor/lib/src/database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ abstract class FloorDatabase {
final changeListener = StreamController<String>.broadcast();

/// Use this whenever you want need direct access to the sqflite database.
@protected
sqflite.DatabaseExecutor database;

// TODO remove this
/// Opens the database to be able to query it.
Future<sqflite.Database> open(List<Migration> migrations);

Expand Down
1 change: 1 addition & 0 deletions floor_annotation/lib/floor_annotation.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
library floor_annotation;

export 'src/column_info.dart';
export 'src/dao.dart';
export 'src/database.dart';
export 'src/delete.dart';
export 'src/entity.dart';
Expand Down
15 changes: 15 additions & 0 deletions floor_annotation/lib/src/dao.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class _Dao {
const _Dao();
}

/// Marks the class as a Data Access Object.
///
/// Data Access Objects are the main classes where you define your database
/// interactions. They can include a variety of query methods.
/// The class marked with @dao should either be an abstract class. At compile
/// time, Floor will generate an implementation of this class when it is
/// referenced by a Database.
///
/// It is recommended to have multiple Dao classes in your codebase depending
/// on the tables they touch.
const dao = _Dao();
6 changes: 1 addition & 5 deletions floor_generator/lib/generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@ class FloorGenerator implements Generator {
final BuildStep buildStep,
) {
final database = DatabaseWriter(library).write();

// TODO generator runs for every file of the project, so this fails without
if (database == null) {
return null;
}
if (database == null) return null;

return database.accept(DartEmitter()).toString();
}
Expand Down
1 change: 1 addition & 0 deletions floor_generator/lib/misc/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ abstract class Annotation {
static const COLUMN_INFO = 'ColumnInfo';
static const PRIMARY_KEY = 'PrimaryKey';
static const TRANSACTION = '_Transaction';
static const DAO = '_Dao';

static const QUERY = 'Query';
static const INSERT = 'Insert';
Expand Down
4 changes: 4 additions & 0 deletions floor_generator/lib/misc/type_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ bool isTransactionAnnotation(final ElementAnnotation annotation) {
return _getAnnotationName(annotation) == Annotation.TRANSACTION;
}

bool isDaoAnnotation(final ElementAnnotation annotation) {
return _getAnnotationName(annotation) == Annotation.DAO;
}

DartType flattenList(final DartType type) {
return (type as ParameterizedType).typeArguments.first;
}
Expand Down
79 changes: 79 additions & 0 deletions floor_generator/lib/model/dao.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
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';
import 'package:floor_generator/model/transaction_method.dart';
import 'package:floor_generator/model/update_method.dart';
import 'package:source_gen/source_gen.dart';

class Dao {
final ClassElement clazz;
final String daoFieldName;
final String databaseName;

Dao(final this.clazz, final this.daoFieldName, final this.databaseName);

String get name => _nameCache ??= clazz.displayName;

String _nameCache;

List<MethodElement> get methods => _methodsCache ??= clazz.methods;

List<MethodElement> _methodsCache;

List<QueryMethod> get queryMethods {
return _queryMethodsCache ??= methods
.where((method) => method.metadata.any(isQueryAnnotation))
.map((method) => QueryMethod(method))
.toList();
}

List<QueryMethod> _queryMethodsCache;

List<InsertMethod> get insertMethods {
return _insertMethodCache ??= methods
.where((method) => method.metadata.any(isInsertAnnotation))
.map((method) => InsertMethod(method))
.toList();
}

List<InsertMethod> _insertMethodCache;

List<UpdateMethod> get updateMethods {
return _updateMethodCache ??= methods
.where((method) => method.metadata.any(isUpdateAnnotation))
.map((method) => UpdateMethod(method))
.toList();
}

List<UpdateMethod> _updateMethodCache;

List<DeleteMethod> get deleteMethods {
return _deleteMethodCache ??= methods
.where((method) => method.metadata.any(isDeleteAnnotation))
.map((method) => DeleteMethod(method))
.toList();
}

List<DeleteMethod> _deleteMethodCache;

List<TransactionMethod> get transactionMethods {
return _transactionMethodCache ??= methods
.where((method) => method.metadata.any(isTransactionAnnotation))
.map((method) => TransactionMethod(method, daoFieldName, databaseName))
.toList();
}

List<TransactionMethod> _transactionMethodCache;

List<Entity> getStreamEntities(final LibraryReader library) {
return _streamEntitiesCache ??= queryMethods
.where((method) => method.returnsStream)
.map((method) => method.getEntity(library))
.toList();
}

List<Entity> _streamEntitiesCache;
}
87 changes: 48 additions & 39 deletions floor_generator/lib/model/database.dart
Original file line number Diff line number Diff line change
@@ -1,89 +1,98 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:floor_generator/misc/constants.dart';
import 'package:floor_generator/misc/type_utils.dart';
import 'package:floor_generator/model/delete_method.dart';
import 'package:floor_generator/model/dao.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/transaction_method.dart';
import 'package:floor_generator/model/update_method.dart';
import 'package:source_gen/source_gen.dart';

class Database {
final ClassElement clazz;

Database(final this.clazz);

String get name => clazz.displayName;
String _nameCache;

String get name => _nameCache ??= clazz.displayName;

int _versionCache;

int get version {
if (_versionCache != null) return _versionCache;

final databaseVersion = clazz.metadata
.firstWhere(isDatabaseAnnotation)
.computeConstantValue()
.getField(AnnotationField.DATABASE_VERSION)
?.toIntValue();

return databaseVersion != null
return _versionCache ??= databaseVersion != null
? databaseVersion
: throw InvalidGenerationSourceError(
'No version for this database specified even though it is required.',
element: clazz,
);
}

List<MethodElement> get methods => clazz.methods;
List<MethodElement> _methodsCache;

List<MethodElement> get methods => _methodsCache ??= clazz.methods;

List<Entity> getEntities(final LibraryReader library) {
return library.classes
.where((clazz) =>
!clazz.isAbstract && clazz.metadata.any(isEntityAnnotation))
.map((entity) => Entity(entity))
.toList();
}

List<QueryMethod> _queryMethodsCache;

List<QueryMethod> get queryMethods {
List<QueryMethod> get _queryMethods {
return _queryMethodsCache ??= methods
.where((method) => method.metadata.any(isQueryAnnotation))
.map((method) => QueryMethod(method))
.toList();
}

List<InsertMethod> get insertMethods {
return methods
.where((method) => method.metadata.any(isInsertAnnotation))
.map((method) => InsertMethod(method))
.toList();
}
List<Entity> _streamEntities;

List<UpdateMethod> get updateMethods {
return methods
.where((method) => method.metadata.any(isUpdateAnnotation))
.map((method) => UpdateMethod(method))
List<Entity> getStreamEntities(final LibraryReader library) {
return _streamEntities ??= _queryMethods
.where((method) => method.returnsStream)
.map((method) => method.getEntity(library))
.toList();
}

List<DeleteMethod> get deleteMethods {
return methods
.where((method) => method.metadata.any(isDeleteAnnotation))
.map((method) => DeleteMethod(method))
List<Dao> _daosCache;

List<Dao> getDaos(final LibraryReader library) {
return _daosCache ??= library.classes
.where(_isDaoClass)
.where(_isDefinedInDatabase)
.map((daoClass) => Dao(daoClass, _getDaoFieldName(daoClass), name))
.toList();
}

List<TransactionMethod> get transactionMethods {
return methods
.where((method) => method.metadata.any(isTransactionAnnotation))
.map((method) => TransactionMethod(method, name))
.toList();
String _getDaoFieldName(final ClassElement daoClass) {
return clazz.fields
.firstWhere((field) => field.type.displayName == daoClass.displayName)
.displayName;
}

List<Entity> getEntities(final LibraryReader library) {
return library.classes
.where((clazz) =>
!clazz.isAbstract && clazz.metadata.any(isEntityAnnotation))
.map((entity) => Entity(entity))
.toList();
bool _isDaoClass(final ClassElement clazz) {
return clazz.metadata.any(isDaoAnnotation) && clazz.isAbstract;
}

List<Entity> _streamEntities;
List<String> _fieldTypeNamesCache;

List<Entity> getStreamEntities(final LibraryReader library) {
return _streamEntities ??= queryMethods
.where((method) => method.returnsStream)
.map((method) => method.getEntity(library))
.toList();
List<String> get _fieldTypeNames {
return _fieldTypeNamesCache ??=
clazz.fields.map((field) => field.type.displayName).toList();
}

bool _isDefinedInDatabase(final ClassElement daoClass) {
return _fieldTypeNames
.any((fieldType) => daoClass.displayName == fieldType);
}
}
7 changes: 6 additions & 1 deletion floor_generator/lib/model/transaction_method.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@ import 'package:analyzer/dart/element/type.dart';

class TransactionMethod {
final MethodElement method;
final String daoFieldName;
final String databaseName;

TransactionMethod(final this.method, final this.databaseName);
TransactionMethod(
final this.method,
final this.daoFieldName,
final this.databaseName,
);

DartType get returnType => method.returnType;

Expand Down
Loading

0 comments on commit 07767af

Please sign in to comment.