Class DataContextImpl

java.lang.Object
io.jmix.flowui.model.impl.DataContextImpl
All Implemented Interfaces:
DataContext, DataContextChanges, DataContextInternal

public class DataContextImpl extends Object implements DataContextInternal
Standard implementation of DataContext which saves data to DataManager.
  • Field Details

  • Constructor Details

    • DataContextImpl

      public DataContextImpl()
  • Method Details

    • getParent

      public @Nullable DataContext getParent()
      Description copied from interface: DataContext
      Returns a parent context, if any. If the parent context is set, DataContext.save() method merges the changed instances to it instead of sending them to DataManager or a custom save delegate.
      Specified by:
      getParent in interface DataContext
    • setParent

      public void setParent(DataContext parentContext)
      Description copied from interface: DataContext
      Sets the parent context. If the parent context is set, DataContext.save() and DataContext.save(boolean) methods merge the changed instances to it instead of sending them to DataManager or a custom save delegate.
      Specified by:
      setParent in interface DataContext
    • addChangeListener

      public Subscription addChangeListener(Consumer<DataContext.ChangeEvent> listener)
      Description copied from interface: DataContext
      Adds a listener to DataContext.ChangeEvent.
      Specified by:
      addChangeListener in interface DataContext
    • fireChangeListener

      protected void fireChangeListener(Object entity)
    • flushDeferredChangeEvents

      protected void flushDeferredChangeEvents()
      Fires a single ChangeEvent for each entity queued in deferredChangeEntities during a merge, then clears the queue. Called when the outermost merge unwinds so listeners see one event per entity, after all of the merge's writes are in place.
    • recordMergeApplied

      protected void recordMergeApplied(Object entity, String attribute)
      Records that the merge itself wrote the given (entity, attribute) during the active merge. A change event for an (entity, attribute) that is not recorded is a listener-injected user edit and must be tracked; see isMergeApplied(java.lang.Object, java.lang.String). No-op outside a merge.
    • isMergeApplied

      protected boolean isMergeApplied(Object entity, String attribute)
      Whether the merge itself wrote the given (entity, attribute) during the active merge (see recordMergeApplied(java.lang.Object, java.lang.String)). Used to tell the merge's own writes from listener-injected user edits.
    • find

      public <T> @Nullable T find(Class<T> entityClass, Object entityId)
      Description copied from interface: DataContext
      Returns an entity instance by its class and id.
      Specified by:
      find in interface DataContext
      Returns:
      entity instance or null if there is no such entity in the context
    • find

      public <T> @Nullable T find(T entity)
      Description copied from interface: DataContext
      Returns the instance of entity with the same id if it exists in this context.
      Specified by:
      find in interface DataContext
      Returns:
      entity instance or null if there is no such entity in the context
    • contains

      public boolean contains(Object entity)
      Description copied from interface: DataContext
      Returns true if the context contains the given entity (distinguished by its class and id).
      Specified by:
      contains in interface DataContext
    • merge

      public <T> T merge(T entity, MergeOptions options)
      Description copied from interface: DataContext
      Merge the given entity into the context. The whole object graph with all references will be merged.

      If an entity with the same identifier already exists in the context, the passed entity state is copied into it and the existing instance is returned. Otherwise, a copy of the passed instance is registered in the context and returned.

      If the given instance is new and the context doesn't contain an instance with the same identifier, the context will save the new instance on DataContext.save(). Otherwise, even if some attributes of the merged instance are changed as a result of copying the state of the passed instance, the merged instance will not be saved. Such modifications are considered as a result of loading more fresh state from the database.

      WARNING: use the returned value because it is always a different object instance. The only case when you get the same instance is if the input was previously returned from the same context as a result of DataContext.find(Class, Object) or merge().

      Specified by:
      merge in interface DataContext
      Parameters:
      entity - instance to merge
      options - merge options
      Returns:
      the instance which is tracked by the context
    • merge

      public <T> T merge(T entity)
      Description copied from interface: DataContext
      Same as DataContext.merge(Object, MergeOptions) with default options.
      Specified by:
      merge in interface DataContext
    • merge

      public EntitySet merge(Collection<?> entities, MergeOptions options)
      Description copied from interface: DataContext
      Merge the given entities into the context. The whole object graph for each element of the collection with all references will be merged.

      Same as DataContext.merge(Object) but for a collection of instances.

      Specified by:
      merge in interface DataContext
      Returns:
      set of instances tracked by the context
    • merge

      public EntitySet merge(Collection<?> entities)
      Description copied from interface: DataContext
      Merge the given entities into the context. The whole object graph for each element of the collection with all references will be merged.

      Same as DataContext.merge(Object, MergeOptions) but for a collection of instances.

      Specified by:
      merge in interface DataContext
      Returns:
      set of instances tracked by the context
    • internalMerge

      protected Object internalMerge(Object entity, Map<Object,Object> mergedMap, boolean isRoot, MergeOptions options)
    • makeKey

      protected Object makeKey(Object entity)
    • copyEntity

      protected Object copyEntity(Object srcEntity)
    • mergeState

      protected void mergeState(Object srcEntity, Object dstEntity, Map<Object,Object> mergedMap, boolean isRoot, MergeOptions options, boolean dstExisted)
    • mergeDatatypeProperties

      protected void mergeDatatypeProperties(Object srcEntity, Object dstEntity, MetaClass metaClass, boolean srcNew, boolean dstNew, boolean isRoot, MergeOptions options)
      Copies the loaded datatype and element-collection properties from the source to the destination instance, honouring the dirty-aware merge rule (a destination attribute with an unsaved user edit is never overwritten; a fresh merge rebaselines it instead). Element collections are wrapped in an observable collection so later mutations are tracked.
    • mergeReferenceProperties

      protected void mergeReferenceProperties(Object srcEntity, Object dstEntity, MetaClass metaClass, boolean srcNew, boolean isRoot, MergeOptions options, Map<Object,Object> mergedMap)
      Copies the loaded reference and to-many properties from the source to the destination instance, recursively merging each reached node into the context graph. Delegates the dirty-aware special cases to skipOrRebaselineDirtyReference(java.lang.Object, io.jmix.core.metamodel.model.MetaProperty, java.lang.Object, boolean, io.jmix.flowui.model.MergeOptions, java.util.Map<java.lang.Object, java.lang.Object>) and mergeUnloadedOrNullReference(java.lang.Object, io.jmix.core.metamodel.model.MetaProperty, java.lang.Object, java.util.Map<java.lang.Object, java.lang.Object>, io.jmix.flowui.model.MergeOptions), and collections to mergeList/mergeSet. Embedded references get a change listener installed.
    • skipOrRebaselineDirtyReference

      protected boolean skipOrRebaselineDirtyReference(Object dstEntity, MetaProperty property, @Nullable Object value, boolean isRoot, MergeOptions options, Map<Object,Object> mergedMap)
      Applies the dirty-aware merge rule to a reference or to-many property: when the destination attribute carries an unsaved user edit (including a dirty dotted path under an embedded reference), the incoming value is not installed. Returns true to tell mergeReferenceProperties(java.lang.Object, java.lang.Object, io.jmix.core.metamodel.model.MetaClass, boolean, boolean, io.jmix.flowui.model.MergeOptions, java.util.Map<java.lang.Object, java.lang.Object>) to skip the property. A fresh merge still rebaselines the edit against the incoming value, and any skipped incoming node is still merged into the context graph.
    • mergeUnloadedOrNullReference

      protected void mergeUnloadedOrNullReference(Object dstEntity, MetaProperty property, @Nullable Object value, Map<Object,Object> mergedMap, MergeOptions options)
      Installs a reference/to-many value when the source side is loaded but the destination side is not yet loaded (or the incoming value is null). A null value is set directly; otherwise the reached node(s) are merged so the installed value holds managed instances, a collection is wrapped observable with a tracker baseline, and the attribute is marked loaded. Embedded properties are handled by the loaded-destination path, not here.
    • markLoaded

      protected void markLoaded(Object entity, String propertyName)
      Marks an attribute as loaded on the entity's loaded-state info, so isLoaded reflects a value that is present in memory because it was set (a user edit) or installed by a merge, rather than lazily materialized. No-op for entities without a LoadedPropertiesInfo (e.g. DTOs).
    • reapplySetLoaded

      protected void reapplySetLoaded(Object dstEntity)
      Re-asserts the set-loaded markers of an entity into its current LoadedPropertiesInfo, so a value present in memory because it was set or merge-installed keeps reporting loaded after a fresh merge replaces the loaded-state cache with a narrower source's (see mergeLoadedPropertiesInfo(java.lang.Object, java.lang.Object, boolean, io.jmix.flowui.model.MergeOptions, boolean)). Writes only positive entries, so it can never turn a genuinely-unloaded attribute loaded. No-op for entities without a cache.
    • isOverridingApplied

      protected boolean isOverridingApplied(Object dstEntity, String propertyName, boolean isRoot)
      Whether the given property of the given instance was already written from the child by the root visit of the active mergeFromChild(Object, Set) call, so this later visit must not write it again. The source graph can hold another java instance of the same entity - a collection materialized outside this context, for example - carrying the value from before the child's edit; merging that instance onto the same managed instance would silently revert the edit. The parent context's own dirty-skip rule does not cover these attributes: they are marked dirty here only once the merge has finished.
    • isOverriding

      protected boolean isOverriding(String propertyName)
      Whether the dirty-protection of the given property of the merge root is suspended by the current mergeFromChild(Object, Set) call. A plain property is overridden when it is among the child's dirty attributes; an embedded property is also overridden when any child dirty attribute is a dotted path under it ('address.city' overrides the protection of 'address').
    • getDirtyEmbeddedPaths

      protected Set<String> getDirtyEmbeddedPaths(Object dstEntity, String propertyName)
      Dotted attribute paths ('address.city') of the given embedded property that are dirty on the owning managed instance. User edits to embedded sub-attributes are tracked on the owner under such paths (see DataContextImpl.EmbeddedPropertyChangeListener).
    • rebaselineEmbedded

      protected void rebaselineEmbedded(Object dstEntity, String propertyName, @Nullable Object incomingEmbedded, Set<String> dirtyEmbeddedPaths)
      Fresh-merge rebaselining for an embedded property whose copy was skipped because the owner carries dirty state for it: the embedded reference entry itself (if dirty) and each dirty dotted sub-attribute path, using the sub-attribute values of the incoming (unmanaged) embedded instance.
    • setPropertyValue

      protected void setPropertyValue(Object entity, MetaProperty property, @Nullable Object value)
    • setPropertyValue

      protected void setPropertyValue(Object entity, MetaProperty property, @Nullable Object value, boolean checkEquals)
    • copySystemState

      protected void copySystemState(Object srcEntity, Object dstEntity)
    • mergeSystemState

      protected void mergeSystemState(Object srcEntity, Object dstEntity, boolean isRoot, MergeOptions options)
    • isColdResetTarget

      protected boolean isColdResetTarget(Object dstEntity, boolean isRoot, MergeOptions options, boolean dstExisted)
      Whether this is a root non-fresh or a fresh merge onto a pre-existing managed instance whose loaded-state info is the caching kind. Such an instance carries source-relative negatives cached at earlier merges, and its fetch group was just unioned with the source's in mergeSystemState(java.lang.Object, java.lang.Object, boolean, io.jmix.flowui.model.MergeOptions). In both cases the loaded-state cache is recomputed from that unioned fetch group (blanked before the copy loops in resetLoadedInfoBeforeCopy(java.lang.Object, boolean)) instead of being replaced with the source's, so a fetched attribute the narrower source omits is not reverted to unloaded by a copied source negative.
    • isReferenceSource

      protected boolean isReferenceSource(Object srcEntity)
      Whether the source is a reference returned by DataManager.getReference() or a copy of one. Such a source never makes the destination less loaded.
    • isReferenceTarget

      protected boolean isReferenceTarget(Object dstEntity, boolean dstExisted)
      Whether the destination is a pre-existing reference. It has no unfetched state, so every attribute loaded in the source can be copied into it; the setters mark the copied attributes loaded.
    • isEmbeddedToInstall

      protected boolean isEmbeddedToInstall(Object srcEntity, Object dstEntity, MetaProperty property, boolean isRoot, MergeOptions options)
      Whether a loaded embedded value must be installed on a destination that does not report it loaded: the destination is a reference, or the source is a reference in a root or fresh merge. mergeUnloadedOrNullReference(java.lang.Object, io.jmix.core.metamodel.model.MetaProperty, java.lang.Object, java.util.Map<java.lang.Object, java.lang.Object>, io.jmix.flowui.model.MergeOptions) skips embedded values.
    • resetLoadedInfoBeforeCopy

      protected void resetLoadedInfoBeforeCopy(Object dstEntity, boolean coldReset)
      Blanks the destination's caching loaded-state info before the copy loops when coldReset is set (see isColdResetTarget(java.lang.Object, boolean, io.jmix.flowui.model.MergeOptions, boolean)), so their isLoaded checks recompute from the fetch group just unioned in mergeSystemState(java.lang.Object, java.lang.Object, boolean, io.jmix.flowui.model.MergeOptions) rather than from a stale cached negative. No-op otherwise.
    • mergeLoadedPropertiesInfo

      protected void mergeLoadedPropertiesInfo(Object srcEntity, Object dstEntity, boolean isRoot, MergeOptions options, boolean coldReset)
    • mergeLazyLoadingState

      protected void mergeLazyLoadingState(Object srcEntity, Object dstEntity)
    • mergeList

      protected void mergeList(List<Object> list, Object managedEntity, MetaProperty property, boolean replace, MergeOptions options, Map<Object,Object> mergedMap)
    • mergeSet

      protected void mergeSet(Set<Object> set, Object managedEntity, MetaProperty property, boolean replace, MergeOptions options, Map<Object,Object> mergedMap)
    • beginMergeCollection

      protected boolean beginMergeCollection(Object managedEntity, String propertyName)
      Marks the given owner entity's collection property as being merged within the active merge() call. Returns false if it is already in progress (a re-entry, caused by a source graph holding multiple java instances of the same id joined by a bidirectional reference), in which case the caller must return early and let the outer mergeList/mergeSet finish populating the collection.
    • endMergeCollection

      protected void endMergeCollection(Object managedEntity, String propertyName)
      Clears the in-progress marker set by beginMergeCollection(java.lang.Object, java.lang.String) for the owner's collection property, once its merge finishes.
    • wrapLazyValueIntoObservableCollection

      protected Collection<Object> wrapLazyValueIntoObservableCollection(Collection<Object> collection, Object notifiedEntity, @Nullable String property)
      Wraps a collection in an observable list/set that notifies collectionChanged(java.lang.Object, java.lang.String) on mutation and, when a property is given, snapshots the tracker baseline so a later mutation marks the owner modified. Returns the collection unchanged if it is neither a list nor a set.
    • wrapLazyValueIntoObservableCollection

      protected Collection<Object> wrapLazyValueIntoObservableCollection(Collection<Object> collection, Object notifiedEntity, @Nullable String property, boolean snapshotBaseline)
      Same as wrapLazyValueIntoObservableCollection(Collection, Object, String), with control over the baseline snapshot. Snapshotting reads the collection's contents, so it must be suppressed when the collection is an unmaterialized lazy holder: reading it would trigger a lazy load whose contents bypass this context (see mergeLazyLoadingState(java.lang.Object, java.lang.Object)). Without a baseline a later mutation is tracked conservatively as a change of the attribute, which is the correct answer for a collection whose pre-mutation contents were never known here.
    • createObservableList

      protected List<Object> createObservableList(Object notifiedEntity, @Nullable String property)
    • createObservableList

      protected List<Object> createObservableList(List<Object> list, Object notifiedEntity, @Nullable String property)
    • createObservableSet

      protected Set<Object> createObservableSet(Object notifiedEntity, @Nullable String property)
    • createObservableSet

      protected ObservableSet<Object> createObservableSet(Set<Object> set, Object notifiedEntity, @Nullable String property)
    • collectionChanged

      protected void collectionChanged(Object entity, @Nullable String property)
      Mutation callback for the observable collection wrappers. The merge's own collection writes skip the tracker and defer their ChangeEvent; any other mutation marks the owner modified, updates the tracker's to-many dirty state, and fires (or, inside a merge, defers) a ChangeEvent.
    • remove

      public void remove(Object entity)
      Description copied from interface: DataContext
      Removes the entity from the context and registers it as deleted. The entity will be removed from the data store upon subsequent call to DataContext.save().

      If the given entity is not in the context, nothing happens.

      Specified by:
      remove in interface DataContext
    • removeFromCollections

      protected void removeFromCollections(Object entityToRemove)
    • evict

      public void evict(Object entity)
      Description copied from interface: DataContext
      Removes the entity from the context so the context stops tracking it.

      If the given entity is not in the context, nothing happens.

      Specified by:
      evict in interface DataContext
    • evictModified

      public void evictModified()
      Description copied from interface: DataContext
      Clears the lists of created/modified/deleted entities and evicts these entities.
      Specified by:
      evictModified in interface DataContext
    • clear

      public void clear()
      Description copied from interface: DataContext
      Evicts all tracked entities.
      Specified by:
      clear in interface DataContext
      See Also:
    • create

      public <T> T create(Class<T> entityClass)
      Description copied from interface: DataContext
      Creates an entity instance and merges it into the context.

      Same as:

       Foo foo = dataContext.merge(metadata.create(Foo.class));
       
      Specified by:
      create in interface DataContext
      Parameters:
      entityClass - entity class
      Returns:
      a new instance which is tracked by the context
    • removeListeners

      protected void removeListeners(Object entity)
    • hasChanges

      public boolean hasChanges()
      Description copied from interface: DataContext
      Returns true if the context has detected changes in the tracked entities.
      Specified by:
      hasChanges in interface DataContext
    • clearChanges

      public void clearChanges()
      Description copied from interface: DataContext
      Clears the lists of modified and removed entities. After calling this method, DataContext.hasChanges() returns false, DataContext.getModified() and DataContext.getRemoved() return empty sets.
      Specified by:
      clearChanges in interface DataContext
    • isModified

      public boolean isModified(Object entity)
      Description copied from interface: DataContext
      Returns true if the context has detected changes in the given entity.
      Specified by:
      isModified in interface DataContext
    • setModified

      public void setModified(Object entity, boolean modified)
      Description copied from interface: DataContext
      Registers or unregisters the given entity as modified.
      Specified by:
      setModified in interface DataContext
      Parameters:
      entity - entity instance which is already merged into the context
      modified - true to register or false to unregister
    • getModified

      public Set<Object> getModified()
      Description copied from interface: DataContext
      Returns an immutable set of entities registered as modified.
      Specified by:
      getModified in interface DataContext
    • getModifiedAttributes

      public Set<String> getModifiedAttributes(Object entity)
      Description copied from interface: DataContext
      Returns the names of attributes of the given managed entity that were modified by the user and not yet saved. Empty for unknown or unmodified instances, and for instances registered only via DataContext.setModified(Object, boolean).
      Specified by:
      getModifiedAttributes in interface DataContext
    • entityBecameDirty

      protected void entityBecameDirty(Object entity)
      Change-tracker callback: an entity gained its first dirty attribute, so it joins modifiedInstances.
    • entityBecameClean

      protected void entityBecameClean(Object entity)
      Change-tracker callback: an entity lost its last dirty attribute, so it leaves modifiedInstances unless it was pinned modified via setModified(java.lang.Object, boolean) (manuallyModified).
    • isRemoved

      public boolean isRemoved(Object entity)
      Description copied from interface: DataContext
      Returns true if the context has registered removal of the given entity.
      Specified by:
      isRemoved in interface DataContext
    • getRemoved

      public Set<Object> getRemoved()
      Description copied from interface: DataContext
      Returns an immutable set of entities registered for removal.
      Specified by:
      getRemoved in interface DataContext
    • save

      public EntitySet save()
      Description copied from interface: DataContext
      Saves changed and removed instances using DataManager or a custom save delegate. After successful save, the context contains updated instances returned from the backend code.
      Specified by:
      save in interface DataContext
      Returns:
      set of saved and merged back to the context instances. Does not contain removed instances.
      See Also:
    • save

      public EntitySet save(boolean reloadSaved)
      Description copied from interface: DataContext
      Saves changed and removed instances using DataManager, custom save delegate or merges them to parent data context if it is set. If reloadSaved is set, merge and reload of saved instances is performed so the context will contain updated instances after successful save.
      Specified by:
      save in interface DataContext
      Parameters:
      reloadSaved - flag indicating whether to reload and merge entities after save using DataManager
      Returns:
      set of saved and merged back to the context instances if reloadSaved is true. Empty set otherwise.
      See Also:
    • addPreSaveListener

      public Subscription addPreSaveListener(Consumer<DataContext.PreSaveEvent> listener)
      Description copied from interface: DataContext
      Adds a listener to DataContext.PreSaveEvent.

      You can also add an event listener declaratively using a controller method annotated with Subscribe:

          @Subscribe(target = Target.DATA_CONTEXT)
          protected void onPreSave(DataContext.PreSaveEvent event) {
             // handle event here
          }
       
      Specified by:
      addPreSaveListener in interface DataContext
      Parameters:
      listener - listener
      Returns:
      subscription
    • addPostSaveListener

      public Subscription addPostSaveListener(Consumer<DataContext.PostSaveEvent> listener)
      Description copied from interface: DataContext
      Adds a listener to DataContext.PostSaveEvent.

      You can also add an event listener declaratively using a controller method annotated with Subscribe:

          @Subscribe(target = Target.DATA_CONTEXT)
          protected void onPostSave(DataContext.PostSaveEvent event) {
             // handle event here
          }
       
      Specified by:
      addPostSaveListener in interface DataContext
      Parameters:
      listener - listener
      Returns:
      subscription
    • getSaveDelegate

      public Function<SaveContext,Set<Object>> getSaveDelegate()
      Description copied from interface: DataContext
      Returns a function which will be used to save data instead of standard implementation.
      Specified by:
      getSaveDelegate in interface DataContext
    • setSaveDelegate

      public void setSaveDelegate(Function<SaveContext,Set<Object>> delegate)
      Description copied from interface: DataContext
      Sets a function which will be used to save data instead of standard implementation.
      Specified by:
      setSaveDelegate in interface DataContext
    • performSave

      protected Set<Object> performSave(boolean reloadSaved)
    • saveToDataManager

      protected Set<Object> saveToDataManager(boolean reloadSaved)
    • filterSavedInstances

      protected List<Object> filterSavedInstances(Set<Object> instances)
    • updateFetchPlans

      protected void updateFetchPlans(SaveContext saveContext)
    • isolate

      public Collection<Object> isolate(List<Object> entities)
    • mergeFromChild

      public Object mergeFromChild(Object entity, Set<String> childDirtyAttributes)
      Description copied from interface: DataContextInternal
      Merges an entity saved by a child context into this (parent) context.

      Unlike a plain DataContext.merge(Object), the child's dirty attributes override this context's own unsaved edits of the same attributes (the child's later intent wins), and are then registered as dirty here so the parent's eventual save carries them.

      Specified by:
      mergeFromChild in interface DataContextInternal
      Parameters:
      entity - the entity instance saved by the child context
      childDirtyAttributes - attributes the child tracked as changed for this entity
      Returns:
      the managed instance of this context
    • mergeDirtyFromChild

      protected Object mergeDirtyFromChild(Object entity, Set<String> childDirtyAttributes)
      Body of mergeFromChild(Object, Set) for a non-empty set of child dirty attributes. Runs inside a merge, so the ChangeEvents it causes are deferred until the caller unwinds.
    • remeasureAgainstBaseline

      protected void remeasureAgainstBaseline(Object entity, String attribute)
      Re-measures an attribute that was already dirty in this context before mergeFromChild(java.lang.Object, java.util.Set<java.lang.String>) wrote the child's value onto the managed instance: the attribute keeps this context's own baseline, and the value coming from the child can have returned it to that baseline, which un-dirties it. Never marks an attribute dirty - both tracker calls below only compare against an existing baseline.
    • mergeCompositionOwnerFromChild

      public Object mergeCompositionOwnerFromChild(Object owner)
      Description copied from interface: DataContextInternal
      Merges an entity owning a composition child that a child context saved into this (parent) context.

      Unlike a plain DataContext.merge(Object), the owner is also marked as changed for DataContext.isModified(Object), so a reopened editor of the owner keeps this context's instance instead of reloading a stale copy. The owner does not enter DataContext.getModified() and is never persisted on its own.

      Specified by:
      mergeCompositionOwnerFromChild in interface DataContextInternal
      Parameters:
      owner - the owner instance of the child context
      Returns:
      the managed instance of this context
    • valueForBaseline

      protected @Nullable Object valueForBaseline(Object entity, String attribute)
      Baseline-shaped current value of the given attribute of a managed instance, in the form DataContextChangeTracker compares against: a scalar value as is, a reference as its id, a to-many attribute as the membership bag of its current contents, a dotted path via EntityValues.getValueEx(Object, String). The root segment of the attribute must be loaded on the instance.
    • rootSegment

      protected static String rootSegment(String attribute)
      The first segment of a possibly-dotted attribute path: 'address' for 'address.city', or the whole string when it has no dot. Used to check the loaded state of the top-level property a dotted (embedded) path belongs to.
    • markCompositionOwnersModified

      protected void markCompositionOwnersModified(Object entity)
      Marks the composition owner chain of a just-merged composition child for reopen protection in this (parent) context. A deep-composition edit propagated from a child context dirties only the edited leaf; the intermediate owners the child never marked would otherwise stay clean, so reopening an intermediate editor reloads a stale instance and loses the unsaved deeper edit. Owners go into compositionModifiedOwners (consulted by isModified), NOT into modifiedInstances, so they are never persisted - saving an aggregate must still persist only the entities that actually changed.
    • findCompositionOwner

      protected @Nullable Object findCompositionOwner(Object entity)
      Resolves the composition owner of a managed instance in this context. Inverse first: a reference whose inverse is a composition points back to the owner. Scan fallback (when no loaded inverse owner is found): a managed instance whose composition property references entity. Returns the managed owner instance, or null if none is found.
    • propagateCompositionOwnersToParent

      protected void propagateCompositionOwnersToParent(Object entity)
      Merges the composition owner chain of a modified instance into the parent context, so the parent's owner instances reference the propagated child. A deep-composition edit dirties only the edited leaf, and detail views normally fetch a nested composition collection without the back-reference to its owner, so merging the leaf alone leaves it unreachable from the parent's owner instance: the parent cannot tell that the owner's subtree changed, and reopening an intermediate editor reloads a stale instance and loses the edit. The chain is resolved in this (child) context, the only place where the composition path from the owner down to the edited leaf is known.

      The merged owners are marked for reopen protection only (compositionModifiedOwners) and never added to modifiedInstances, so an untouched owner is still never persisted.

    • saveToParentContext

      protected Set<Object> saveToParentContext()
    • cleanupContextAfterRemoveEntity

      protected void cleanupContextAfterRemoveEntity(DataContextInternal context, Object removedEntity)
    • entityHasReference

      protected boolean entityHasReference(Object entity, Object refEntity)
    • mergeSaved

      protected EntitySet mergeSaved(Set<Object> saved)
    • getAll

      public Collection<?> getAll()
    • printContent

      public String printContent()
    • printEntity

      protected String printEntity(Object entity, int level, Set<Object> visited)
    • printObject

      protected String printObject(Object object)
    • getModifiedInstances

      public Set<Object> getModifiedInstances()
      Specified by:
      getModifiedInstances in interface DataContextChanges
      Returns:
      mutable set of entities registered as modified
    • getRemovedInstances

      public Set<Object> getRemovedInstances()
      Specified by:
      getRemovedInstances in interface DataContextChanges
      Returns:
      mutable set of entities registered as removed
    • getEntityMetaClass

      protected MetaClass getEntityMetaClass(Object entity)
    • getPrimaryKeyPropertyName

      protected @Nullable String getPrimaryKeyPropertyName(Object entity)