+ final ToStringHelper helper = MoreObjects.toStringHelper(this).omitNullValues()
+ .add("identifier", identifier).add("operation", operation).add("modificationType", modType);
+ if (!children.isEmpty()) {
+ helper.add("childModification", children);
+ }
+ return helper.toString();
+ }
+
+ void resolveModificationType(final @NonNull ModificationType type) {
+ modType = type;
+ }
+
+ /**
+ * Update this node's value and operation type without disturbing any of its child modifications.
+ *
+ * @param type New operation type
+ * @param newValue New node value
+ */
+ void updateValue(final LogicalOperation type, final NormalizedNode newValue) {
+ this.value = requireNonNull(newValue);
+ updateOperationType(type);
+ }
+
+ /**
+ * Return the physical modification done to data. May return null if the
+ * operation has not been applied to the underlying tree. This is different
+ * from the logical operation in that it can actually be a no-op if the
+ * operation has no side-effects (like an empty merge on a container).
+ *
+ * @return Modification type.
+ */
+ ModificationType getModificationType() {
+ return modType;
+ }
+
+ public static ModifiedNode createUnmodified(final TreeNode metadataTree, final ChildTrackingPolicy childPolicy) {
+ return new ModifiedNode(metadataTree.getIdentifier(), Optional.of(metadataTree), childPolicy);