Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions generator/integration-tests/common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,18 @@ commonModelTests(ModelDefinition defs, ModelInfo jsonModel) {
jsonModel.retiredEntityUids);
});

// TODO when indexes are available
// TODO when indexes are available
// TODO see one-liner in the next lastRelationId test
// test('lastIndexId', () {
// testLastId(defs.model.lastIndexId, defs.model.entities.map((el) => ...), jsonModel.retiredIndexUids);
// });

// TODO when relations are available
// test('lastRelationId', () {
// testLastId(defs.model.lastRelationId, defs.model.entities.map((el) => ...), jsonModel.retiredRelationUids);
// });
test('lastRelationId', () {
testLastId(
defs.model.lastRelationId,
defs.model.entities
.map((e) => e.properties.where((p) => p.isRelation))
.fold([], (a, b) => a + b).map((e) => e.id),
jsonModel.retiredRelationUids);
});
}
75 changes: 75 additions & 0 deletions generator/lib/src/code_builder.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'dart:convert';
import 'package:build/build.dart';
import 'package:glob/glob.dart';
Expand Down Expand Up @@ -82,6 +83,7 @@ class CodeBuilder extends Builder {

// merge existing model and annotated model that was just read, then write new final model to file
merge(model, entities);
assignLastPropertyIds(model);
model.validate();

// write model info
Expand All @@ -93,6 +95,20 @@ class CodeBuilder extends Builder {
return model;
}

void assignLastPropertyIds(ModelInfo model) {
model.entities.forEach((e) {
e.properties.forEach((p) {
// if (p.isIndexer) {
// model.lastIndexId = p.id; }else
if (p.isToOne) {
model.lastIndexId = p.relIndexId;
} else if (p.isToMany) {
model.lastRelationId = p.relationId;
}
});
});
}

void updateCode(
ModelInfo model, List<String> infoFiles, BuildStep buildStep) async {
// transform '/lib/path/entity.objectbox.info' to 'path/entity.dart'
Expand Down Expand Up @@ -131,6 +147,17 @@ class CodeBuilder extends Builder {
entities.forEach((entity) => mergeEntity(model, entity));
}

int generateUid(int seed, Set<int> prohibited) {
final rng = Random(seed + DateTime.now().millisecondsSinceEpoch);
for (int i = 0; i < 1000; i++) {
var uid = rng.nextInt(1 << 32);
uid |= rng.nextInt(1 << 32) << 32;
uid &= ~(1 << 63);
if (!prohibited.contains(uid)) return uid;
}
return 0;
}

void mergeProperty(ModelEntity entity, ModelProperty prop) {
final propInModel = entity.findSameProperty(prop);
if (propInModel == null) {
Expand All @@ -140,6 +167,10 @@ class CodeBuilder extends Builder {
propInModel.name = prop.name;
propInModel.type = prop.type;
propInModel.flags = prop.flags;
if (prop.type.isRelation) {
propInModel.targetEntityName = prop.targetEntityName;
propInModel.relIndexId = prop.relIndexId;
}
}
}

Expand All @@ -160,6 +191,50 @@ class CodeBuilder extends Builder {
// here, the entity was found already and entityInModel and readEntity might differ, i.e. conflicts need to be resolved, so merge all properties first
entity.properties.forEach((p) => mergeProperty(entityInModel, p));

// one to one relation: get the highest relIndexId.id
// TODO Ask: Do we want to repair contiguous ids, if one prop is removed in the middle?
var relIndexIdCounter = entityInModel.properties
.where((p) => p.isToOne && p.relIndexId != null)
.map((p) => p.relIndexId.id)
.fold(1, (a, b) => a < b ? b : a);

// Prohibit reuse of indexUids
final avoidSameUidSet = Set<int>()..addAll(modelInfo.retiredIndexUids);

// one to one relation
entityInModel.properties.where((p) => p.isToOne).forEach((p) {
if (p.relIndexId == null) {
p.relIndexId = IdUid.empty();
}
if (p.relIndexId.isEmpty) {
p.relIndexId.id = relIndexIdCounter++;
p.relIndexId.uid = generateUid(entityInModel.id.uid, avoidSameUidSet);
avoidSameUidSet.add(p.relIndexId.uid);
}
});

// many to many relation
var relationIdCounter = entityInModel.properties
.where((p) => p.isToMany && p.relationId != null)
.map((p) => p.relationId.id)
.fold(1, (a, b) => a < b ? b : a);

final targetEntityMap = <String, IdUid>{};
final mapEntityNameToId =
modelInfo.entities.forEach((e) => targetEntityMap[e.name] = e.id);
entityInModel.properties.where((p) => p.isToMany).forEach((p) {
if (p.relationId == null) {
p.relationId = IdUid.empty();
}
if (p.relationId.isEmpty) {
p.relationId.id = relationIdCounter++;
p.relationId.uid = generateUid(entityInModel.id.uid, avoidSameUidSet);
avoidSameUidSet.add(p.relationId.uid);
}

p.targetEntityId = targetEntityMap[p.targetEntityName];
});

// then remove all properties not present anymore in readEntity
entityInModel.properties
.where((p) => entity.findSameProperty(p) == null)
Expand Down
8 changes: 7 additions & 1 deletion generator/lib/src/code_chunks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ class CodeChunks {
case OBXPropertyType.String:
fieldType = "String";
break;
case OBXPropertyType.Relation:
fieldType = 'Relation';
break;
float:
case OBXPropertyType.Double:
fieldType = "Double";
Expand All @@ -80,8 +83,11 @@ class CodeChunks {
"Unsupported property type (${prop.type}): ${entity.name}.${name}");
}

final relationTypeGenericParam =
prop.type == OBXPropertyType.Relation ? '<${prop.targetEntityName}>' : '';

ret.add("""
static final ${name} = Query${fieldType}Property(entityId:${entity.id.id}, propertyId:${prop.id.id}, obxType:${prop.type});
static final ${name} = Query${fieldType}Property$relationTypeGenericParam(entityId:${entity.id.id}, propertyId:${prop.id.id}, obxType:${prop.type});
""");
}
return ret.join();
Expand Down
43 changes: 35 additions & 8 deletions generator/lib/src/entity_resolver.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:convert';

import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:build/build.dart';
import 'package:objectbox/objectbox.dart' as obx;
import 'package:objectbox/src/bindings/constants.dart';
Expand All @@ -17,7 +18,7 @@ class EntityResolver extends Builder {
'.dart': [suffix]
};

final _annotationChecker = const TypeChecker.fromRuntime(obx.Entity);
final _entityChecker = const TypeChecker.fromRuntime(obx.Entity);
final _propertyChecker = const TypeChecker.fromRuntime(obx.Property);
final _idChecker = const TypeChecker.fromRuntime(obx.Id);
final _transientChecker = const TypeChecker.fromRuntime(obx.Transient);
Expand All @@ -30,9 +31,13 @@ class EntityResolver extends Builder {

// generate for all entities
final entities = <Map<String, dynamic>>[];
for (var annotatedEl in libReader.annotatedWith(_annotationChecker)) {
entities.add(generateForAnnotatedElement(
annotatedEl.element, annotatedEl.annotation)
final annotatedWithEntity = libReader.annotatedWith(_entityChecker);
final entityNames = annotatedWithEntity.map((a) => a.element.name).toSet();
final entityNamesAsList =
annotatedWithEntity.map((a) => 'List<${a.element.name}>').toSet();
for (var annotatedEl in annotatedWithEntity) {
entities.add(generateForAnnotatedElement(annotatedEl.element,
annotatedEl.annotation, entityNames, entityNamesAsList)
.toMap());
}

Expand All @@ -44,7 +49,10 @@ class EntityResolver extends Builder {
}

ModelEntity generateForAnnotatedElement(
Element elementBare, ConstantReader annotation) {
Element elementBare,
ConstantReader annotation,
Set<String> relatableEntityNames,
Set<String> relatableEntityNamesAsList) {
if (elementBare is! ClassElement) {
throw InvalidGenerationSourceError(
"in target ${elementBare.name}: annotated element isn't a class");
Expand All @@ -70,6 +78,7 @@ class EntityResolver extends Builder {

int fieldType, flags = 0;
int propUid;
final dartTypeString = f.type.toString().replaceAll('*', '');

if (_idChecker.hasAnnotationOfExact(f)) {
if (hasIdProperty) {
Expand All @@ -96,7 +105,7 @@ class EntityResolver extends Builder {
}

if (fieldType == null) {
var fieldTypeDart = f.type;
final fieldTypeDart = f.type;

if (fieldTypeDart.isDartCoreInt) {
// dart: 8 bytes
Expand All @@ -112,6 +121,9 @@ class EntityResolver extends Builder {
// dart: 8 bytes
// ob: 8 bytes
fieldType = OBXPropertyType.Double;
} else if (areRelated(
dartTypeString, relatableEntityNames, relatableEntityNamesAsList)) {
fieldType = OBXPropertyType.Relation;
} else {
log.warning(
" skipping property '${f.name}' in entity '${element.name}', as it has the unsupported type '${fieldTypeDart.toString()}'");
Expand All @@ -120,8 +132,20 @@ class EntityResolver extends Builder {
}

// create property (do not use readEntity.createProperty in order to avoid generating new ids)
final prop =
ModelProperty(IdUid.empty(), f.name, fieldType, flags, readEntity);
final isRelation = fieldType == OBXPropertyType.Relation;

// setup relations
final isToOne =
isRelation && areRelated(dartTypeString, relatableEntityNames);
final isToMany =
isRelation && areRelated(dartTypeString, relatableEntityNamesAsList);
final prop = ModelProperty(
IdUid.empty(), f.name, fieldType, flags, readEntity,
targetEntityName: isRelation ? dartTypeString : null,
relIndexId: isToOne ? IdUid.empty() : null,
relationId: isToMany ? IdUid.empty() : null,
targetEntityId: isToMany ? IdUid.empty() : null);

if (propUid != null) prop.id.uid = propUid;
readEntity.properties.add(prop);

Expand All @@ -137,4 +161,7 @@ class EntityResolver extends Builder {

return readEntity;
}

bool areRelated(String typeString, Set<String> s1, [Set<String> s2]) =>
s1.contains(typeString) || (s2 != null && s2.contains(typeString));
}
2 changes: 1 addition & 1 deletion generator/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ homepage: https://objectbox.io
description: ObjectBox binding code generator - finds annotated entities and adds them to the ObjectBox DB model.

environment:
sdk: ">=2.5.0 <3.0.0"
sdk: ">=2.6.0 <3.0.0"

dependencies:
objectbox: 0.8.0
Expand Down
1 change: 1 addition & 0 deletions lib/objectbox.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export 'src/model.dart';
export 'src/modelinfo/index.dart';
export 'src/query/query.dart';
export 'src/store.dart';
export 'src/util.dart';
Loading