11/*
2- * Copyright 2017-2024 ObjectBox Ltd. All rights reserved.
2+ * Copyright 2017-2025 ObjectBox Ltd. All rights reserved.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
3434import io .objectbox .model .ModelProperty ;
3535import io .objectbox .model .ModelRelation ;
3636
37- // Remember: IdUid is a struct, not a table, and thus must be inlined
38- @ SuppressWarnings ("WeakerAccess,UnusedReturnValue, unused" )
37+ // To learn how to use the FlatBuffers API see https://flatbuffers.dev/tutorial/
38+ // Note: IdUid is a struct, not a table, and thus must be inlined
39+
40+ /**
41+ * Builds a flatbuffer representation of the database model to be passed when opening a store.
42+ * <p>
43+ * This is an internal API that should only be called by the generated MyObjectBox code.
44+ */
3945@ Internal
4046public class ModelBuilder {
4147 private static final int MODEL_VERSION = 2 ;
4248
43- final FlatBufferBuilder fbb = new FlatBufferBuilder ();
44- final List <Integer > entityOffsets = new ArrayList <>();
49+ private final FlatBufferBuilder fbb = new FlatBufferBuilder ();
50+ private final List <Integer > entityOffsets = new ArrayList <>();
51+
52+ private long version = 1 ;
53+
54+ private Integer lastEntityId ;
55+ private Long lastEntityUid ;
56+
57+ private Integer lastIndexId ;
58+ private Long lastIndexUid ;
59+
60+ private Integer lastRelationId ;
61+ private Long lastRelationUid ;
62+
63+ /**
64+ * Base class for builders.
65+ * <p>
66+ * Methods adding properties to be used by {@link #createFlatBufferTable(FlatBufferBuilder)} should call
67+ * {@link #checkNotFinished()}.
68+ * <p>
69+ * The last call should be {@link #finish()}.
70+ */
71+ abstract static class PartBuilder {
72+
73+ private final FlatBufferBuilder fbb ;
74+ private boolean finished ;
75+
76+ PartBuilder (FlatBufferBuilder fbb ) {
77+ this .fbb = fbb ;
78+ }
4579
46- long version = 1 ;
80+ FlatBufferBuilder getFbb () {
81+ return fbb ;
82+ }
4783
48- Integer lastEntityId ;
49- Long lastEntityUid ;
84+ void checkNotFinished () {
85+ if (finished ) {
86+ throw new IllegalStateException ("Already finished" );
87+ }
88+ }
89+
90+ /**
91+ * Marks this as finished and returns {@link #createFlatBufferTable(FlatBufferBuilder)}.
92+ */
93+ public final int finish () {
94+ checkNotFinished ();
95+ finished = true ;
96+ return createFlatBufferTable (getFbb ());
97+ }
5098
51- Integer lastIndexId ;
52- Long lastIndexUid ;
99+ /**
100+ * Creates a flatbuffer table using the given builder and returns its offset.
101+ */
102+ public abstract int createFlatBufferTable (FlatBufferBuilder fbb );
103+ }
53104
54- Integer lastRelationId ;
55- Long lastRelationUid ;
105+ public static class PropertyBuilder extends PartBuilder {
56106
57- public class PropertyBuilder {
58107 private final int type ;
59108 private final int virtualTargetOffset ;
60109 private final int propertyNameOffset ;
61110 private final int targetEntityOffset ;
62111
63112 private int secondaryNameOffset ;
64- boolean finished ;
65113 private int flags ;
66114 private int id ;
67115 private long uid ;
@@ -71,7 +119,9 @@ public class PropertyBuilder {
71119 private int externalPropertyType ;
72120 private int hnswParamsOffset ;
73121
74- PropertyBuilder (String name , @ Nullable String targetEntityName , @ Nullable String virtualTarget , int type ) {
122+ private PropertyBuilder (FlatBufferBuilder fbb , String name , @ Nullable String targetEntityName ,
123+ @ Nullable String virtualTarget , int type ) {
124+ super (fbb );
75125 this .type = type ;
76126 propertyNameOffset = fbb .createString (name );
77127 targetEntityOffset = targetEntityName != null ? fbb .createString (targetEntityName ) : 0 ;
@@ -129,6 +179,7 @@ public PropertyBuilder hnswParams(long dimensions,
129179 @ Nullable Float reparationBacklinkProbability ,
130180 @ Nullable Long vectorCacheHintSizeKb ) {
131181 checkNotFinished ();
182+ FlatBufferBuilder fbb = getFbb ();
132183 HnswParams .startHnswParams (fbb );
133184 HnswParams .addDimensions (fbb , dimensions );
134185 if (neighborsPerNode != null ) {
@@ -161,19 +212,12 @@ public PropertyBuilder flags(int flags) {
161212
162213 public PropertyBuilder secondaryName (String secondaryName ) {
163214 checkNotFinished ();
164- secondaryNameOffset = fbb .createString (secondaryName );
215+ secondaryNameOffset = getFbb () .createString (secondaryName );
165216 return this ;
166217 }
167218
168- private void checkNotFinished () {
169- if (finished ) {
170- throw new IllegalStateException ("Already finished" );
171- }
172- }
173-
174- public int finish () {
175- checkNotFinished ();
176- finished = true ;
219+ @ Override
220+ public int createFlatBufferTable (FlatBufferBuilder fbb ) {
177221 ModelProperty .startModelProperty (fbb );
178222 ModelProperty .addName (fbb , propertyNameOffset );
179223 if (targetEntityOffset != 0 ) {
@@ -210,7 +254,41 @@ public int finish() {
210254 }
211255 }
212256
213- public class EntityBuilder {
257+ public static class RelationBuilder extends PartBuilder {
258+
259+ private final String name ;
260+ private final int relationId ;
261+ private final long relationUid ;
262+ private final int targetEntityId ;
263+ private final long targetEntityUid ;
264+
265+ private RelationBuilder (FlatBufferBuilder fbb , String name , int relationId , long relationUid ,
266+ int targetEntityId , long targetEntityUid ) {
267+ super (fbb );
268+ this .name = name ;
269+ this .relationId = relationId ;
270+ this .relationUid = relationUid ;
271+ this .targetEntityId = targetEntityId ;
272+ this .targetEntityUid = targetEntityUid ;
273+ }
274+
275+ @ Override
276+ public int createFlatBufferTable (FlatBufferBuilder fbb ) {
277+ int nameOffset = fbb .createString (name );
278+
279+ ModelRelation .startModelRelation (fbb );
280+ ModelRelation .addName (fbb , nameOffset );
281+ int relationIdOffset = IdUid .createIdUid (fbb , relationId , relationUid );
282+ ModelRelation .addId (fbb , relationIdOffset );
283+ int targetEntityIdOffset = IdUid .createIdUid (fbb , targetEntityId , targetEntityUid );
284+ ModelRelation .addTargetEntityId (fbb , targetEntityIdOffset );
285+ return ModelRelation .endModelRelation (fbb );
286+ }
287+ }
288+
289+ public static class EntityBuilder extends PartBuilder {
290+
291+ private final ModelBuilder model ;
214292 final String name ;
215293 final List <Integer > propertyOffsets = new ArrayList <>();
216294 final List <Integer > relationOffsets = new ArrayList <>();
@@ -220,10 +298,13 @@ public class EntityBuilder {
220298 Integer flags ;
221299 Integer lastPropertyId ;
222300 Long lastPropertyUid ;
223- PropertyBuilder propertyBuilder ;
301+ @ Nullable PropertyBuilder propertyBuilder ;
302+ @ Nullable RelationBuilder relationBuilder ;
224303 boolean finished ;
225304
226- EntityBuilder (String name ) {
305+ EntityBuilder (ModelBuilder model , FlatBufferBuilder fbb , String name ) {
306+ super (fbb );
307+ this .model = model ;
227308 this .name = name ;
228309 }
229310
@@ -246,12 +327,6 @@ public EntityBuilder flags(int flags) {
246327 return this ;
247328 }
248329
249- private void checkNotFinished () {
250- if (finished ) {
251- throw new IllegalStateException ("Already finished" );
252- }
253- }
254-
255330 public PropertyBuilder property (String name , int type ) {
256331 return property (name , null , type );
257332 }
@@ -263,43 +338,48 @@ public PropertyBuilder property(String name, @Nullable String targetEntityName,
263338 public PropertyBuilder property (String name , @ Nullable String targetEntityName , @ Nullable String virtualTarget ,
264339 int type ) {
265340 checkNotFinished ();
266- checkFinishProperty ();
267- propertyBuilder = new PropertyBuilder (name , targetEntityName , virtualTarget , type );
341+ finishPropertyOrRelation ();
342+ propertyBuilder = new PropertyBuilder (getFbb (), name , targetEntityName , virtualTarget , type );
268343 return propertyBuilder ;
269344 }
270345
271- void checkFinishProperty () {
346+ public RelationBuilder relation (String name , int relationId , long relationUid , int targetEntityId ,
347+ long targetEntityUid ) {
348+ checkNotFinished ();
349+ finishPropertyOrRelation ();
350+
351+ RelationBuilder relationBuilder = new RelationBuilder (getFbb (), name , relationId , relationUid , targetEntityId , targetEntityUid );
352+ this .relationBuilder = relationBuilder ;
353+ return relationBuilder ;
354+ }
355+
356+ private void finishPropertyOrRelation () {
357+ if (propertyBuilder != null && relationBuilder != null ) {
358+ throw new IllegalStateException ("Must not build property and relation at the same time." );
359+ }
272360 if (propertyBuilder != null ) {
273361 propertyOffsets .add (propertyBuilder .finish ());
274362 propertyBuilder = null ;
275363 }
364+ if (relationBuilder != null ) {
365+ relationOffsets .add (relationBuilder .finish ());
366+ relationBuilder = null ;
367+ }
276368 }
277369
278- public EntityBuilder relation ( String name , int relationId , long relationUid , int targetEntityId ,
279- long targetEntityUid ) {
370+ public ModelBuilder entityDone () {
371+ // Make sure any pending property or relation is finished first
280372 checkNotFinished ();
281- checkFinishProperty ();
282-
283- int propertyNameOffset = fbb .createString (name );
284-
285- ModelRelation .startModelRelation (fbb );
286- ModelRelation .addName (fbb , propertyNameOffset );
287- int relationIdOffset = IdUid .createIdUid (fbb , relationId , relationUid );
288- ModelRelation .addId (fbb , relationIdOffset );
289- int targetEntityIdOffset = IdUid .createIdUid (fbb , targetEntityId , targetEntityUid );
290- ModelRelation .addTargetEntityId (fbb , targetEntityIdOffset );
291- relationOffsets .add (ModelRelation .endModelRelation (fbb ));
292-
293- return this ;
373+ finishPropertyOrRelation ();
374+ model .entityOffsets .add (finish ());
375+ return model ;
294376 }
295377
296- public ModelBuilder entityDone () {
297- checkNotFinished ();
298- checkFinishProperty ();
299- finished = true ;
378+ @ Override
379+ public int createFlatBufferTable (FlatBufferBuilder fbb ) {
300380 int testEntityNameOffset = fbb .createString (name );
301- int propertiesOffset = createVector (propertyOffsets );
302- int relationsOffset = relationOffsets .isEmpty () ? 0 : createVector (relationOffsets );
381+ int propertiesOffset = model . createVector (propertyOffsets );
382+ int relationsOffset = relationOffsets .isEmpty () ? 0 : model . createVector (relationOffsets );
303383
304384 ModelEntity .startModelEntity (fbb );
305385 ModelEntity .addName (fbb , testEntityNameOffset );
@@ -316,12 +396,12 @@ public ModelBuilder entityDone() {
316396 if (flags != null ) {
317397 ModelEntity .addFlags (fbb , flags );
318398 }
319- entityOffsets .add (ModelEntity .endModelEntity (fbb ));
320- return ModelBuilder .this ;
399+ return ModelEntity .endModelEntity (fbb );
321400 }
401+
322402 }
323403
324- int createVector (List <Integer > offsets ) {
404+ private int createVector (List <Integer > offsets ) {
325405 int [] offsetArray = new int [offsets .size ()];
326406 for (int i = 0 ; i < offsets .size (); i ++) {
327407 offsetArray [i ] = offsets .get (i );
@@ -335,7 +415,7 @@ public ModelBuilder version(long version) {
335415 }
336416
337417 public EntityBuilder entity (String name ) {
338- return new EntityBuilder (name );
418+ return new EntityBuilder (this , fbb , name );
339419 }
340420
341421 public ModelBuilder lastEntityId (int lastEntityId , long lastEntityUid ) {
0 commit comments