@@ -51,10 +51,10 @@ class EntityResolver extends Builder {
5151 }
5252
5353 ModelEntity generateForAnnotatedElement (
54- Element element , ConstantReader annotation) {
55- if (element is ! ClassElement ) {
54+ Element classElement , ConstantReader annotation) {
55+ if (classElement is ! ClassElement ) {
5656 throw InvalidGenerationSourceError (
57- "entity ${ element .name }: annotated element isn't a class" );
57+ "Entity '${ classElement .name }' : annotated element must be a class. " );
5858 }
5959
6060 // process basic entity (note that allModels.createEntity is not used, as the entity will be merged)
@@ -63,23 +63,23 @@ class EntityResolver extends Builder {
6363 final entity = ModelEntity .create (
6464 IdUid (0 , entityUid.isNull ? 0 : entityUid.intValue),
6565 entityRealClass.isNull
66- ? element .name
66+ ? classElement .name
6767 : entityRealClass.typeValue.element! .name! ,
6868 null ,
6969 uidRequest: ! entityUid.isNull && entityUid.intValue == 0 );
7070
71- if (_syncChecker.hasAnnotationOfExact (element )) {
71+ if (_syncChecker.hasAnnotationOfExact (classElement )) {
7272 entity.flags | = OBXEntityFlags .SYNC_ENABLED ;
7373 }
7474
7575 log.info (entity);
7676
77- entity.constructorParams = constructorParams (findConstructor (element ));
78- entity.nullSafetyEnabled = nullSafetyEnabled (element );
77+ entity.constructorParams = constructorParams (findConstructor (classElement ));
78+ entity.nullSafetyEnabled = nullSafetyEnabled (classElement );
7979 if (! entity.nullSafetyEnabled) {
8080 log.warning (
81- "Entity ${entity .name } is in a library/application that doesn't use null-safety"
82- ' - consider increasing your SDK version to Flutter 2.0/Dart 2.12' );
81+ "Entity ' ${entity .name }' is in a package that doesn't use null-safety"
82+ ' - consider increasing your SDK version to Flutter 2.0/Dart 2.12. ' );
8383 }
8484
8585 // Make sure all stored fields are writable when reading object from DB.
@@ -88,7 +88,7 @@ class EntityResolver extends Builder {
8888 // * don't have a corresponding argument in the constructor.
8989 // Note: `.correspondingSetter == null` is also true for `final` fields.
9090 final readOnlyFields = < String > {};
91- for (var f in element .accessors) {
91+ for (var f in classElement .accessors) {
9292 if (f.isGetter &&
9393 f.correspondingSetter == null &&
9494 ! entity.constructorParams
@@ -98,19 +98,19 @@ class EntityResolver extends Builder {
9898 }
9999
100100 // read all suitable annotated properties
101- for (var f in element .fields) {
101+ for (var f in classElement .fields) {
102102 if (_transientChecker.hasAnnotationOfExact (f)) {
103- log.info (' skipping property ${f .name } ( annotated with @Transient)' );
103+ log.info (" Skipping property ' ${f .name }': annotated with @Transient." );
104104 continue ;
105105 }
106106
107107 if (readOnlyFields.contains (f.name) && ! isRelationField (f)) {
108- log.info (' skipping read-only/getter ${f .name }' );
108+ log.info (" Skipping property ' ${f .name }': is read-only/getter." );
109109 continue ;
110110 }
111111
112112 if (f.isPrivate) {
113- log.info (' skipping private field ${f .name }' );
113+ log.info (" Skipping property ' ${f .name }': is private." );
114114 continue ;
115115 }
116116
@@ -160,15 +160,15 @@ class EntityResolver extends Builder {
160160 } else if (dartType.element! .name == 'DateTime' ) {
161161 fieldType = OBXPropertyType .Date ;
162162 log.warning (
163- " DateTime property '${f .name }' in entity '${element .name }' is stored and read using millisecond precision. "
163+ " DateTime property '${f .name }' in entity '${classElement .name }' is stored and read using millisecond precision. "
164164 'To silence this warning, add an explicit type using @Property(type: PropertyType.date) or @Property(type: PropertyType.dateNano) annotation.' );
165165 } else if (isToOneRelationField (f)) {
166166 fieldType = OBXPropertyType .Relation ;
167167 } else if (isToManyRelationField (f)) {
168168 isToManyRel = true ;
169169 } else {
170170 log.warning (
171- " skipping property '${f .name }': type '$dartType ' not supported,"
171+ " Skipping property '${f .name }': type '$dartType ' not supported,"
172172 " consider creating a relation for @Entity types (https://docs.objectbox.io/relations),"
173173 " or replace with getter/setter converting to a supported type (https://docs.objectbox.io/advanced/custom-types)." );
174174 continue ;
@@ -178,8 +178,8 @@ class EntityResolver extends Builder {
178178 String ? relTargetName;
179179 if (isRelationField (f)) {
180180 if (f.type is ! ParameterizedType ) {
181- log.severe (
182- " invalid relation property '${ f . name }' in entity '${ element . name }' - must use ToOne/ ToMany<TargetEntity>" );
181+ log.severe (" Skipping property '${ f . name }': invalid relation type, "
182+ "use a type like ToOne<TargetEntity> or ToMany<TargetEntity>. " );
183183 continue ;
184184 }
185185 relTargetName =
@@ -190,7 +190,7 @@ class EntityResolver extends Builder {
190190 if (backlinkAnnotations.isNotEmpty) {
191191 if (! isToManyRel) {
192192 log.severe (
193- ' invalid use of @Backlink() annotation - may only be used on a ToMany<> field' );
193+ " Skipping property '${ f . name }': @Backlink() may only be used with ToMany." );
194194 continue ;
195195 }
196196 final backlinkField =
@@ -226,7 +226,7 @@ class EntityResolver extends Builder {
226226 }
227227
228228 // Index and unique annotation.
229- processAnnotationIndexUnique (f, fieldType, element , prop);
229+ processAnnotationIndexUnique (f, fieldType, classElement , prop);
230230
231231 // for code generation
232232 prop.dartFieldType =
@@ -235,63 +235,65 @@ class EntityResolver extends Builder {
235235 }
236236 }
237237
238- processIdProperty (entity);
238+ processIdProperty (entity, classElement );
239239
240240 // We need to check that the ID field is writable. Otherwise, generated code
241241 // for `setId()` won't compile. The only exception is when user uses
242242 // self-assigned IDs, then a different setter will be generated - one that
243243 // checks the ID being set is already the same, otherwise it must throw.
244- final idField = element .fields
244+ final idField = classElement .fields
245245 .singleWhere ((FieldElement f) => f.name == entity.idProperty.name);
246246 if (idField.setter == null ) {
247247 if (! entity.idProperty.hasFlag (OBXPropertyFlags .ID_SELF_ASSIGNABLE )) {
248248 throw InvalidGenerationSourceError (
249- "Entity ${entity .name } has an ID field '${idField .name }' that is "
250- 'not assignable (that usually means it is declared final). '
251- "This won't work because ObjectBox needs to be able to assign "
252- 'an ID after inserting a new object (if the given ID was zero). '
253- 'If you want to assign IDs manually instead, you can annotate the '
254- "field '${idField .name }' with `@Id(assignable: true)`. Otherwise "
255- 'please provide a setter or remove the `final` keyword.' );
249+ "@Id field '${idField .name }' must be writable:"
250+ " ObjectBox uses it to set the assigned ID after inserting a new object,"
251+ " provide a setter or remove the 'final' keyword."
252+ " If your code needs to assign IDs itself,"
253+ " see https://docs.objectbox.io/advanced/object-ids#self-assigned-object-ids." ,
254+ element: idField);
256255 } else {
257256 // We need to get the information to code generator (code_chunks.dart).
258257 entity.idProperty.fieldIsReadOnly = true ;
259258 }
260259 }
261260
262261 // Verify there is at most 1 unique property with REPLACE strategy.
263- ensureSingleUniqueReplace (entity);
262+ ensureSingleUniqueReplace (entity, classElement );
264263 // If sync enabled, verify all unique properties use REPLACE strategy.
265- ifSyncEnsureAllUniqueAreReplace (entity);
264+ ifSyncEnsureAllUniqueAreReplace (entity, classElement );
266265
267266 entity.properties.forEach ((p) => log.info (' $p ' ));
268267
269268 return entity;
270269 }
271270
272- void processIdProperty (ModelEntity entity) {
271+ void processIdProperty (ModelEntity entity, ClassElement classElement ) {
273272 // check properties explicitly annotated with @Id()
274273 final annotated =
275274 entity.properties.where ((p) => p.hasFlag (OBXPropertyFlags .ID ));
276275 if (annotated.length > 1 ) {
276+ final names = annotated.map ((e) => e.name).join (", " );
277277 throw InvalidGenerationSourceError (
278- 'entity ${entity .name }: multiple fields annotated with Id(), there may only be one' );
278+ "Entity '${entity .name }': multiple fields ($names ) annotated with @Id(), there may only be one." ,
279+ element: classElement);
279280 }
280281
281282 if (annotated.length == 1 ) {
282283 if (annotated.first.type != OBXPropertyType .Long ) {
283284 throw InvalidGenerationSourceError (
284- "entity ${entity .name }: Id() annotated property has invalid type, expected 'int'" );
285+ "Entity '${entity .name }': @Id() property must be 'int'."
286+ " If you need to use other types, see https://docs.objectbox.io/entity-annotations#object-ids-id." ,
287+ element: classElement);
285288 }
286289 } else {
287290 // if there are no annotated props, try to find one by name & type
288291 final candidates = entity.properties.where ((p) =>
289292 p.name.toLowerCase () == 'id' && p.type == OBXPropertyType .Long );
290293 if (candidates.length != 1 ) {
291294 throw InvalidGenerationSourceError (
292- 'entity ${entity .name }: ID property not found - either define'
293- ' an integer field named ID/id/... (case insensitive) or add'
294- ' @Id annotation to any integer field' );
295+ "Entity '${entity .name }': no @Id() property found, add an int field annotated with @Id()." ,
296+ element: classElement);
295297 }
296298 candidates.first.flags | = OBXPropertyFlags .ID ;
297299 }
@@ -310,19 +312,22 @@ class EntityResolver extends Builder {
310312
311313 final indexAnnotation = _indexChecker.firstAnnotationOfExact (f);
312314 final uniqueAnnotation = _uniqueChecker.firstAnnotationOfExact (f);
313- if (indexAnnotation == null && uniqueAnnotation == null ) return null ;
315+ if (indexAnnotation == null && uniqueAnnotation == null ) return ;
314316
315317 // Throw if property type does not support any index.
316318 if (fieldType == OBXPropertyType .Float ||
317319 fieldType == OBXPropertyType .Double ||
318320 fieldType == OBXPropertyType .ByteVector ) {
319321 throw InvalidGenerationSourceError (
320- "entity ${elementBare .name }: @Index/@Unique is not supported for type '${f .type }' of field '${f .name }'" );
322+ "Entity '${elementBare .name }': @Index/@Unique is not supported for type '${f .type }' of field '${f .name }'." ,
323+ element: f);
321324 }
322325
323326 if (prop.hasFlag (OBXPropertyFlags .ID )) {
324327 throw InvalidGenerationSourceError (
325- 'entity ${elementBare .name }: @Index/@Unique is not supported for ID field ${f .name }. IDs are unique by definition and automatically indexed' );
328+ "Entity '${elementBare .name }': @Index/@Unique is not supported for @Id field '${f .name }'."
329+ " IDs are unique by definition and automatically indexed." ,
330+ element: f);
326331 }
327332
328333 // If available use index type from annotation.
@@ -345,7 +350,8 @@ class EntityResolver extends Builder {
345350 if (! supportsHashIndex &&
346351 (indexType == IndexType .hash || indexType == IndexType .hash64)) {
347352 throw InvalidGenerationSourceError (
348- "entity ${elementBare .name }: a hash index is not supported for type '${f .type }' of field '${f .name }'" );
353+ "Entity '${elementBare .name }': a hash index is not supported for type '${f .type }' of field '${f .name }'" ,
354+ element: f);
349355 }
350356
351357 if (uniqueAnnotation != null && ! uniqueAnnotation.isNull) {
@@ -371,28 +377,34 @@ class EntityResolver extends Builder {
371377 break ;
372378 default :
373379 throw InvalidGenerationSourceError (
374- 'entity ${elementBare .name }: invalid index type: $indexType ' );
380+ "Entity '${elementBare .name }': index type $indexType not supported." ,
381+ element: f);
375382 }
376383 }
377384
378- void ensureSingleUniqueReplace (ModelEntity entity) {
385+ void ensureSingleUniqueReplace (
386+ ModelEntity entity, ClassElement classElement) {
379387 final uniqueReplaceProps = entity.properties
380388 .where ((p) => p.hasFlag (OBXPropertyFlags .UNIQUE_ON_CONFLICT_REPLACE ));
381389 if (uniqueReplaceProps.length > 1 ) {
382390 throw InvalidGenerationSourceError (
383- "ConflictStrategy.replace can only be used on a single property, but found multiple in '${entity .name }':\n ${uniqueReplaceProps .join ('\n ' )}" );
391+ "ConflictStrategy.replace can only be used on a single property, but found multiple in '${entity .name }':\n ${uniqueReplaceProps .join ('\n ' )}" ,
392+ element: classElement);
384393 }
385394 }
386395
387- void ifSyncEnsureAllUniqueAreReplace (ModelEntity entity) {
396+ void ifSyncEnsureAllUniqueAreReplace (
397+ ModelEntity entity, ClassElement classElement) {
388398 if (! entity.hasFlag (OBXEntityFlags .SYNC_ENABLED )) return ;
389399 final uniqueButNotReplaceProps = entity.properties.where ((p) {
390400 return p.hasFlag (OBXPropertyFlags .UNIQUE ) &&
391401 ! p.hasFlag (OBXPropertyFlags .UNIQUE_ON_CONFLICT_REPLACE );
392402 });
393403 if (uniqueButNotReplaceProps.isNotEmpty) {
394404 throw InvalidGenerationSourceError (
395- "Synced entities must use @Unique(onConflict: ConflictStrategy.replace) on all unique properties, but found others in '${entity .name }':\n ${uniqueButNotReplaceProps .join ('\n ' )}" );
405+ "Synced entities must use @Unique(onConflict: ConflictStrategy.replace) on all unique properties,"
406+ " but found others in '${entity .name }':\n ${uniqueButNotReplaceProps .join ('\n ' )}" ,
407+ element: classElement);
396408 }
397409 }
398410
0 commit comments