Class DraftPayloadSupport
DataContext into a JSON payload,
and applies such a payload back onto a (possibly different) DataContext.-
Field Summary
FieldsModifier and TypeFieldDescriptionprotected EntitySerializationprotected EntityStatesstatic final intprotected IdSerializationprotected Metadataprotected MetadataToolsprotected static final Stringprotected static final String -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionprotected voidaddDiffMembers(com.google.gson.JsonObject entry, Object entity, Set<String> attributes) Adds the "attributes" member of a diff entry, by serializing the instance and pruning the result down to the given attribute names, which keeps EntitySerialization's handling of dates, enums, embedded objects and references.protected voidapplyAttribute(DataContext dataContext, Object managed, Object sparse, String path) Copies one decoded attribute value onto the managed instance.protected voidapplyDiffAttributes(com.google.gson.JsonObject entry, DataContext dataContext, Object managed) protected voidapplyDiffCollections(com.google.gson.JsonObject entry, DataContext dataContext, Object managed) Restores a to-many attribute's membership by mutating the managed collection in place.protected voidapplyDiffEntry(com.google.gson.JsonObject entry, DataContext dataContext, List<Object> applied) Replays the drafted attribute changes onto the managed instance.protected voidapplyFullEntry(com.google.gson.JsonObject entry, DataContext dataContext, List<Object> applied) Merges a whole serialized instance into the context.applyPayload(String payload, DataContext dataContext) Applies a draft payload onto the given data context: merges the drafted "entities" instances (keeping the version already loaded into the context, if any), marks them modified, and re-removes the drafted "removed" instances.protected @Nullable com.google.gson.JsonArraycollectionIds(Object entity, String attribute) The ids of a to-many attribute's current elements, in their current order.collectKnownAttributes(MetaClass metaClass, com.google.gson.JsonObject attributes, com.google.gson.JsonObject target) Copies the members that still exist in the metadata intotargetand returns their dotted paths.protected voidcopyAttributeMember(com.google.gson.JsonObject serialized, com.google.gson.JsonObject target, String attribute) Copies one attribute from a serialized instance into the diff, following a dotted path into nested objects so that an embedded attribute keeps only the sub-attribute the user changed.protected com.google.gson.JsonObjectcreateEntry(DataContext dataContext, Object entity) Builds one element of the payload's "entities" array.@Nullable StringcreatePayload(DataContext dataContext) Serializes the modified and removed instances of the given data context into a JSON payload.protected @Nullable ObjectcurrentVersion(DataContext dataContext, Object drafted) protected booleandiffEntryVersionDiffers(com.google.gson.JsonObject entry, DataContext dataContext) protected voidensureEmbeddedExists(Object managed, MetaPropertyPath propertyPath) Creates the intermediate embedded objects a dotted path needs, when the managed instance has none.protected Id<?> entryId(com.google.gson.JsonObject entry) The entry's target id, validated: a missing or non-string "id" member is a format error rather than a raw Gson exception.protected @Nullable ObjectfindManaged(Object drafted, DataContext dataContext) protected booleanfullEntryVersionDiffers(com.google.gson.JsonObject entry, DataContext dataContext) protected booleanhasNoPrimaryKey(Object entity) True when the given entity has no primary key: a JPA@Embeddable, a@JmixEmbeddedvalue object, or any@JmixEntitywith no id at all.protected booleanisNewEntry(com.google.gson.JsonObject entry) protected booleanA dotted path never denotes a to-many attribute: the data context tracks embedded changes only for scalar sub-attributes.protected Stringmode(com.google.gson.JsonObject entry) The entry's mode, validated: a payload with a missing or unrecognized mode is a format error, not something to guess at.protected com.google.gson.JsonObjectParses and structurally validates a draft payload: the root must be a JSON object with a numeric "formatVersion" member matchingFORMAT_VERSION, and array "entities"/"removed" members.protected com.google.gson.JsonArrayrequireArray(com.google.gson.JsonElement element, String memberName) The element as a JSON array, orDraftFormatException- guards agetAsJsonArray()call site against a rawIllegalStateExceptionfor a malformed payload.protected voidrequireArrayMember(com.google.gson.JsonObject root, String memberName) ThrowsDraftFormatExceptionunless the given member of the root object is present and is a JSON array - guardsroot.getAsJsonArray(memberName)call sites inapplyPayload(java.lang.String, io.jmix.flowui.model.DataContext)andversionDiffers(java.lang.String, io.jmix.flowui.model.DataContext)against a rawNullPointerException/IllegalStateExceptionfor a structurally incomplete payload.protected StringrequireString(com.google.gson.JsonElement element, String memberName) The element as a string, orDraftFormatException- guards agetAsString()call site against a rawIllegalStateException/UnsupportedOperationExceptionfor a malformed payload.protected voidsubstituteInCollection(Collection<?> collection, DataContext dataContext, Map<Object, Boolean> visited) protected voidsubstituteManagedReferences(Object root, DataContext dataContext) Replaces every reference reachable fromrootthat the data context already manages with the managed instance itself, so that merging the entry does not deep-merge a drafted copy over it.protected voidsubstituteManagedReferences(Object instance, DataContext dataContext, Map<Object, Boolean> visited) booleanversionDiffers(String payload, DataContext dataContext) Checks whether any drafted instance's version differs from the version of the instance already loaded into the given data context, meaning the persisted data changed since the draft was captured.protected voidwarnIfSkipLosesTheChange(DataContext dataContext, Object entity) Warns when skipping a no-primary-key instance actually drops the user's change.
-
Field Details
-
FORMAT_VERSION
public static final int FORMAT_VERSION- See Also:
-
MODE_DIFF
- See Also:
-
MODE_FULL
- See Also:
-
entitySerialization
-
idSerialization
-
entityStates
-
metadata
-
metadataTools
-
-
Constructor Details
-
DraftPayloadSupport
public DraftPayloadSupport()
-
-
Method Details
-
createPayload
Serializes the modified and removed instances of the given data context into a JSON payload.- Parameters:
dataContext- data context to capture- Returns:
- JSON payload, or
nullif the context has no changes
-
warnIfSkipLosesTheChange
Warns when skipping a no-primary-key instance actually drops the user's change.Normally it does not: an edit to
order.address.cityis also recorded on the order as the dotted attributeaddress.city. But the data context records an embedded change one level deep only, so an edit toorder.address.geo.latis recorded on the address and nothing with a primary key carries it. A dotted path among the skipped instance's own modified attributes tells the two cases apart. -
hasNoPrimaryKey
True when the given entity has no primary key: a JPA@Embeddable, a@JmixEmbeddedvalue object, or any@JmixEntitywith no id at all. Such an instance cannot be the subject of its own payload entry: there is no id to encode it by, or to look it up again on restore. -
createEntry
Builds one element of the payload's "entities" array. A not-yet-persisted instance and an instance with no tracked attribute changes are stored whole: the first because values set before it entered the data context were never tracked, the second because there is nothing to diff. -
addDiffMembers
protected void addDiffMembers(com.google.gson.JsonObject entry, Object entity, Set<String> attributes) Adds the "attributes" member of a diff entry, by serializing the instance and pruning the result down to the given attribute names, which keeps EntitySerialization's handling of dates, enums, embedded objects and references. SERIALIZE_NULLS is mandatory: without it Gson drops null members, so an attribute the user cleared would carry no value and restoring it would leave the old one in place. -
isToMany
A dotted path never denotes a to-many attribute: the data context tracks embedded changes only for scalar sub-attributes. -
collectionIds
The ids of a to-many attribute's current elements, in their current order. Elements are stored as ids rather than nested: an element the user also edited already has its own entry.Returns
nullwhen there is nothing to store ids from - the attribute is not loaded, or its value is not a collection. Both drop a change the tracker did report, so both are logged. -
copyAttributeMember
protected void copyAttributeMember(com.google.gson.JsonObject serialized, com.google.gson.JsonObject target, String attribute) Copies one attribute from a serialized instance into the diff, following a dotted path into nested objects so that an embedded attribute keeps only the sub-attribute the user changed. A JSON null is copied like any other value: the user cleared the attribute, and leaving the member out would restore the old value instead. An attribute the serializer did not emit at all (it is not loaded on the instance) cannot be recorded, and is logged. -
applyPayload
Applies a draft payload onto the given data context: merges the drafted "entities" instances (keeping the version already loaded into the context, if any), marks them modified, and re-removes the drafted "removed" instances.- Parameters:
payload- JSON payload produced bycreatePayload(DataContext)dataContext- data context to apply the payload to- Returns:
- the merged (managed) instances corresponding to the payload's "entities" entries
- Throws:
DraftFormatException- if the payload is unparseable or has an incompatible format version
-
applyFullEntry
protected void applyFullEntry(com.google.gson.JsonObject entry, DataContext dataContext, List<Object> applied) Merges a whole serialized instance into the context. The "new" flag is corrected from the entry before merging, because DataContext.merge() copies the whole system state onto an already managed instance: a genuinely-loaded instance merged from a new-flagged drafted one would be saved with an INSERT that collides with the existing row's primary key. -
substituteManagedReferences
Replaces every reference reachable fromrootthat the data context already manages with the managed instance itself, so that merging the entry does not deep-merge a drafted copy over it.EntitySerializationwrites a not-yet-persisted instance in full depth, so afullentry for a new composition element carries whole nested copies of its parent and of everything else reachable. Merging those reverts what another user changed since capture, brings the parent's capture-timeversionback, and - becausefullentries are applied first - installs the parent's own drafted values before itsdiffentry runs, which then registers no change and leaves the parent unsaved.DataContextImpl.internalMergeskips merging entirely when the source instance is the managed one, so substituting is enough to stop all three.A reference the context does not manage keeps its drafted copy, which is what carries an instance the reopened view never loaded back into the context.
-
substituteManagedReferences
protected void substituteManagedReferences(Object instance, DataContext dataContext, Map<Object, Boolean> visited) -
substituteInCollection
protected void substituteInCollection(Collection<?> collection, DataContext dataContext, Map<Object, Boolean> visited) -
findManaged
- Returns:
- the instance the data context manages for
drafted— which may bedrafteditself — or null when the context manages none, or whendraftedhas no id to look it up by
-
applyDiffEntry
protected void applyDiffEntry(com.google.gson.JsonObject entry, DataContext dataContext, List<Object> applied) Replays the drafted attribute changes onto the managed instance. Nothing is force-marked as modified: setting the values goes through the normal setters, so the data context's own change listener registers exactly the attributes whose value actually differs. -
applyDiffAttributes
protected void applyDiffAttributes(com.google.gson.JsonObject entry, DataContext dataContext, Object managed) -
applyDiffCollections
protected void applyDiffCollections(com.google.gson.JsonObject entry, DataContext dataContext, Object managed) Restores a to-many attribute's membership by mutating the managed collection in place. Replacing the collection instead would restore the membership without recording the attribute as modified.An attribute that is no longer in the metadata is skipped like any other unknown drafted attribute, rather than letting
MetaClass.getPropertythrow and discard the whole draft. -
collectKnownAttributes
protected List<String> collectKnownAttributes(MetaClass metaClass, com.google.gson.JsonObject attributes, com.google.gson.JsonObject target) Copies the members that still exist in the metadata intotargetand returns their dotted paths. Embedded members are walked into, so that {"address":{"city":..}} yields "address.city"; every other member is a leaf. Filtering has to happen before the sparse instance is deserialized, so that an attribute dropped by an application upgrade is skipped rather than failing the whole draft inside EntitySerialization. -
applyAttribute
Copies one decoded attribute value onto the managed instance. A to-one reference arrives as a whole copy of the referenced instance as it was loaded at capture time, not as a bare id: prefer the instance the context already manages, and only when the context manages nothing for that id merge the copy as an already-persisted instance - which installs that copy's loaded attributes into the context. A reference to an instance that was itself new at draft time is found by the lookup, because new instances are stored as full entries and were merged before any diff entry is applied. -
ensureEmbeddedExists
Creates the intermediate embedded objects a dotted path needs, when the managed instance has none. Without this, restoring "address.city" onto an instance whose address is null fails. -
mode
The entry's mode, validated: a payload with a missing or unrecognized mode is a format error, not something to guess at. -
entryId
The entry's target id, validated: a missing or non-string "id" member is a format error rather than a raw Gson exception. -
isNewEntry
protected boolean isNewEntry(com.google.gson.JsonObject entry) -
versionDiffers
Checks whether any drafted instance's version differs from the version of the instance already loaded into the given data context, meaning the persisted data changed since the draft was captured.- Parameters:
payload- JSON payload produced bycreatePayload(DataContext)dataContext- data context to check against- Returns:
- true if a version conflict is detected
-
fullEntryVersionDiffers
protected boolean fullEntryVersionDiffers(com.google.gson.JsonObject entry, DataContext dataContext) -
diffEntryVersionDiffers
protected boolean diffEntryVersionDiffers(com.google.gson.JsonObject entry, DataContext dataContext) -
currentVersion
-
parse
Parses and structurally validates a draft payload: the root must be a JSON object with a numeric "formatVersion" member matchingFORMAT_VERSION, and array "entities"/"removed" members. Any deviation - unparseable JSON, a non-object root, missing/malformed members, or an incompatible format version - is reported asDraftFormatException, never as a raw Gson/JDK exception. -
requireArrayMember
ThrowsDraftFormatExceptionunless the given member of the root object is present and is a JSON array - guardsroot.getAsJsonArray(memberName)call sites inapplyPayload(java.lang.String, io.jmix.flowui.model.DataContext)andversionDiffers(java.lang.String, io.jmix.flowui.model.DataContext)against a rawNullPointerException/IllegalStateExceptionfor a structurally incomplete payload. -
requireArray
protected com.google.gson.JsonArray requireArray(com.google.gson.JsonElement element, String memberName) The element as a JSON array, orDraftFormatException- guards agetAsJsonArray()call site against a rawIllegalStateExceptionfor a malformed payload. -
requireString
The element as a string, orDraftFormatException- guards agetAsString()call site against a rawIllegalStateException/UnsupportedOperationExceptionfor a malformed payload.
-