@@ -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,24 +160,26 @@ 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 }' in entity '${element .name }', as it has an unsupported type: '$dartType '" );
171+ " Skipping property '${f .name }': type '$dartType ' not supported,"
172+ " consider creating a relation for @Entity types (https://docs.objectbox.io/relations),"
173+ " or replace with getter/setter converting to a supported type (https://docs.objectbox.io/advanced/custom-types)." );
172174 continue ;
173175 }
174176 }
175177
176178 String ? relTargetName;
177179 if (isRelationField (f)) {
178180 if (f.type is ! ParameterizedType ) {
179- log.severe (
180- " 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>. " );
181183 continue ;
182184 }
183185 relTargetName =
@@ -188,7 +190,7 @@ class EntityResolver extends Builder {
188190 if (backlinkAnnotations.isNotEmpty) {
189191 if (! isToManyRel) {
190192 log.severe (
191- ' 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." );
192194 continue ;
193195 }
194196 final backlinkField =
@@ -224,7 +226,7 @@ class EntityResolver extends Builder {
224226 }
225227
226228 // Index and unique annotation.
227- processAnnotationIndexUnique (f, fieldType, element , prop);
229+ processAnnotationIndexUnique (f, fieldType, classElement , prop);
228230
229231 // for code generation
230232 prop.dartFieldType =
@@ -233,63 +235,65 @@ class EntityResolver extends Builder {
233235 }
234236 }
235237
236- processIdProperty (entity);
238+ processIdProperty (entity, classElement );
237239
238240 // We need to check that the ID field is writable. Otherwise, generated code
239241 // for `setId()` won't compile. The only exception is when user uses
240242 // self-assigned IDs, then a different setter will be generated - one that
241243 // checks the ID being set is already the same, otherwise it must throw.
242- final idField = element .fields
244+ final idField = classElement .fields
243245 .singleWhere ((FieldElement f) => f.name == entity.idProperty.name);
244246 if (idField.setter == null ) {
245247 if (! entity.idProperty.hasFlag (OBXPropertyFlags .ID_SELF_ASSIGNABLE )) {
246248 throw InvalidGenerationSourceError (
247- "Entity ${entity .name } has an ID field '${idField .name }' that is "
248- 'not assignable (that usually means it is declared final). '
249- "This won't work because ObjectBox needs to be able to assign "
250- 'an ID after inserting a new object (if the given ID was zero). '
251- 'If you want to assign IDs manually instead, you can annotate the '
252- "field '${idField .name }' with `@Id(assignable: true)`. Otherwise "
253- '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);
254255 } else {
255256 // We need to get the information to code generator (code_chunks.dart).
256257 entity.idProperty.fieldIsReadOnly = true ;
257258 }
258259 }
259260
260261 // Verify there is at most 1 unique property with REPLACE strategy.
261- ensureSingleUniqueReplace (entity);
262+ ensureSingleUniqueReplace (entity, classElement );
262263 // If sync enabled, verify all unique properties use REPLACE strategy.
263- ifSyncEnsureAllUniqueAreReplace (entity);
264+ ifSyncEnsureAllUniqueAreReplace (entity, classElement );
264265
265266 entity.properties.forEach ((p) => log.info (' $p ' ));
266267
267268 return entity;
268269 }
269270
270- void processIdProperty (ModelEntity entity) {
271+ void processIdProperty (ModelEntity entity, ClassElement classElement ) {
271272 // check properties explicitly annotated with @Id()
272273 final annotated =
273274 entity.properties.where ((p) => p.hasFlag (OBXPropertyFlags .ID ));
274275 if (annotated.length > 1 ) {
276+ final names = annotated.map ((e) => e.name).join (", " );
275277 throw InvalidGenerationSourceError (
276- '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);
277280 }
278281
279282 if (annotated.length == 1 ) {
280283 if (annotated.first.type != OBXPropertyType .Long ) {
281284 throw InvalidGenerationSourceError (
282- "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);
283288 }
284289 } else {
285290 // if there are no annotated props, try to find one by name & type
286291 final candidates = entity.properties.where ((p) =>
287292 p.name.toLowerCase () == 'id' && p.type == OBXPropertyType .Long );
288293 if (candidates.length != 1 ) {
289294 throw InvalidGenerationSourceError (
290- 'entity ${entity .name }: ID property not found - either define'
291- ' an integer field named ID/id/... (case insensitive) or add'
292- ' @Id annotation to any integer field' );
295+ "Entity '${entity .name }': no @Id() property found, add an int field annotated with @Id()." ,
296+ element: classElement);
293297 }
294298 candidates.first.flags | = OBXPropertyFlags .ID ;
295299 }
@@ -308,19 +312,22 @@ class EntityResolver extends Builder {
308312
309313 final indexAnnotation = _indexChecker.firstAnnotationOfExact (f);
310314 final uniqueAnnotation = _uniqueChecker.firstAnnotationOfExact (f);
311- if (indexAnnotation == null && uniqueAnnotation == null ) return null ;
315+ if (indexAnnotation == null && uniqueAnnotation == null ) return ;
312316
313317 // Throw if property type does not support any index.
314318 if (fieldType == OBXPropertyType .Float ||
315319 fieldType == OBXPropertyType .Double ||
316320 fieldType == OBXPropertyType .ByteVector ) {
317321 throw InvalidGenerationSourceError (
318- "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);
319324 }
320325
321326 if (prop.hasFlag (OBXPropertyFlags .ID )) {
322327 throw InvalidGenerationSourceError (
323- '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);
324331 }
325332
326333 // If available use index type from annotation.
@@ -343,7 +350,8 @@ class EntityResolver extends Builder {
343350 if (! supportsHashIndex &&
344351 (indexType == IndexType .hash || indexType == IndexType .hash64)) {
345352 throw InvalidGenerationSourceError (
346- "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);
347355 }
348356
349357 if (uniqueAnnotation != null && ! uniqueAnnotation.isNull) {
@@ -369,28 +377,34 @@ class EntityResolver extends Builder {
369377 break ;
370378 default :
371379 throw InvalidGenerationSourceError (
372- 'entity ${elementBare .name }: invalid index type: $indexType ' );
380+ "Entity '${elementBare .name }': index type $indexType not supported." ,
381+ element: f);
373382 }
374383 }
375384
376- void ensureSingleUniqueReplace (ModelEntity entity) {
385+ void ensureSingleUniqueReplace (
386+ ModelEntity entity, ClassElement classElement) {
377387 final uniqueReplaceProps = entity.properties
378388 .where ((p) => p.hasFlag (OBXPropertyFlags .UNIQUE_ON_CONFLICT_REPLACE ));
379389 if (uniqueReplaceProps.length > 1 ) {
380390 throw InvalidGenerationSourceError (
381- "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);
382393 }
383394 }
384395
385- void ifSyncEnsureAllUniqueAreReplace (ModelEntity entity) {
396+ void ifSyncEnsureAllUniqueAreReplace (
397+ ModelEntity entity, ClassElement classElement) {
386398 if (! entity.hasFlag (OBXEntityFlags .SYNC_ENABLED )) return ;
387399 final uniqueButNotReplaceProps = entity.properties.where ((p) {
388400 return p.hasFlag (OBXPropertyFlags .UNIQUE ) &&
389401 ! p.hasFlag (OBXPropertyFlags .UNIQUE_ON_CONFLICT_REPLACE );
390402 });
391403 if (uniqueButNotReplaceProps.isNotEmpty) {
392404 throw InvalidGenerationSourceError (
393- "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);
394408 }
395409 }
396410
0 commit comments