/// <seealso cref="AppendProperties(Com.Adobe.Xmp.XMPMeta, Com.Adobe.Xmp.XMPMeta, bool, bool, bool)"/> /// <param name="destXMP">The destination XMP object.</param> /// <param name="sourceNode">the source node</param> /// <param name="destParent">the parent of the destination node</param> /// <param name="replaceOldValues">Replace the values of existing properties.</param> /// <param name="deleteEmptyValues"> /// flag if properties with empty values should be deleted /// in the destination object. /// </param> /// <exception cref="Com.Adobe.Xmp.XMPException"/> private static void AppendSubtree(XMPMetaImpl destXMP, XMPNode sourceNode, XMPNode destParent, bool replaceOldValues, bool deleteEmptyValues) { XMPNode destNode = XMPNodeUtils.FindChildNode(destParent, sourceNode.GetName(), false); bool valueIsEmpty = false; if (deleteEmptyValues) { valueIsEmpty = sourceNode.GetOptions().IsSimple() ? sourceNode.GetValue() == null || sourceNode.GetValue().Length == 0 : !sourceNode.HasChildren(); } if (deleteEmptyValues && valueIsEmpty) { if (destNode != null) { destParent.RemoveChild(destNode); } } else { if (destNode == null) { // The one easy case, the destination does not exist. destParent.AddChild((XMPNode)sourceNode.Clone()); } else { if (replaceOldValues) { // The destination exists and should be replaced. destXMP.SetNode(destNode, sourceNode.GetValue(), sourceNode.GetOptions(), true); destParent.RemoveChild(destNode); destNode = (XMPNode)sourceNode.Clone(); destParent.AddChild(destNode); } else { // The destination exists and is not totally replaced. Structs and // arrays are merged. PropertyOptions sourceForm = sourceNode.GetOptions(); PropertyOptions destForm = destNode.GetOptions(); if (sourceForm != destForm) { return; } if (sourceForm.IsStruct()) { // To merge a struct process the fields recursively. E.g. add simple missing fields. // The recursive call to AppendSubtree will handle deletion for fields with empty // values. for (Iterator it = sourceNode.IterateChildren(); it.HasNext();) { XMPNode sourceField = (XMPNode)it.Next(); AppendSubtree(destXMP, sourceField, destNode, replaceOldValues, deleteEmptyValues); if (deleteEmptyValues && !destNode.HasChildren()) { destParent.RemoveChild(destNode); } } } else { if (sourceForm.IsArrayAltText()) { // Merge AltText arrays by the "xml:lang" qualifiers. Make sure x-default is first. // Make a special check for deletion of empty values. Meaningful in AltText arrays // because the "xml:lang" qualifier provides unambiguous source/dest correspondence. for (Iterator it = sourceNode.IterateChildren(); it.HasNext();) { XMPNode sourceItem = (XMPNode)it.Next(); if (!sourceItem.HasQualifier() || !XMPConstConstants.XmlLang.Equals(sourceItem.GetQualifier(1).GetName())) { continue; } int destIndex = XMPNodeUtils.LookupLanguageItem(destNode, sourceItem.GetQualifier(1).GetValue()); if (deleteEmptyValues && (sourceItem.GetValue() == null || sourceItem.GetValue().Length == 0)) { if (destIndex != -1) { destNode.RemoveChild(destIndex); if (!destNode.HasChildren()) { destParent.RemoveChild(destNode); } } } else { if (destIndex == -1) { // Not replacing, keep the existing item. if (!XMPConstConstants.XDefault.Equals(sourceItem.GetQualifier(1).GetValue()) || !destNode.HasChildren()) { sourceItem.CloneSubtree(destNode); } else { XMPNode destItem = new XMPNode(sourceItem.GetName(), sourceItem.GetValue(), sourceItem.GetOptions()); sourceItem.CloneSubtree(destItem); destNode.AddChild(1, destItem); } } } } } else { if (sourceForm.IsArray()) { // Merge other arrays by item values. Don't worry about order or duplicates. Source // items with empty values do not cause deletion, that conflicts horribly with // merging. for (Iterator @is = sourceNode.IterateChildren(); @is.HasNext();) { XMPNode sourceItem = (XMPNode)@is.Next(); bool match = false; for (Iterator id = destNode.IterateChildren(); id.HasNext();) { XMPNode destItem = (XMPNode)id.Next(); if (ItemValuesMatch(sourceItem, destItem)) { match = true; } } if (!match) { destNode = (XMPNode)sourceItem.Clone(); destParent.AddChild(destNode); } } } } } } } } }
/// <seealso cref="AppendProperties(Com.Adobe.Xmp.XMPMeta, Com.Adobe.Xmp.XMPMeta, bool, bool, bool)"/> /// <param name="destXMP">The destination XMP object.</param> /// <param name="sourceNode">the source node</param> /// <param name="destParent">the parent of the destination node</param> /// <param name="replaceOldValues">Replace the values of existing properties.</param> /// <param name="deleteEmptyValues"> /// flag if properties with empty values should be deleted /// in the destination object. /// </param> /// <exception cref="Com.Adobe.Xmp.XMPException"/> private static void AppendSubtree(XMPMetaImpl destXMP, XMPNode sourceNode, XMPNode destParent, bool replaceOldValues, bool deleteEmptyValues) { XMPNode destNode = XMPNodeUtils.FindChildNode(destParent, sourceNode.GetName(), false); bool valueIsEmpty = false; if (deleteEmptyValues) { valueIsEmpty = sourceNode.GetOptions().IsSimple() ? sourceNode.GetValue() == null || sourceNode.GetValue().Length == 0 : !sourceNode.HasChildren(); } if (deleteEmptyValues && valueIsEmpty) { if (destNode != null) { destParent.RemoveChild(destNode); } } else { if (destNode == null) { // The one easy case, the destination does not exist. destParent.AddChild((XMPNode)sourceNode.Clone()); } else { if (replaceOldValues) { // The destination exists and should be replaced. destXMP.SetNode(destNode, sourceNode.GetValue(), sourceNode.GetOptions(), true); destParent.RemoveChild(destNode); destNode = (XMPNode)sourceNode.Clone(); destParent.AddChild(destNode); } else { // The destination exists and is not totally replaced. Structs and // arrays are merged. PropertyOptions sourceForm = sourceNode.GetOptions(); PropertyOptions destForm = destNode.GetOptions(); if (sourceForm != destForm) { return; } if (sourceForm.IsStruct()) { // To merge a struct process the fields recursively. E.g. add simple missing fields. // The recursive call to AppendSubtree will handle deletion for fields with empty // values. for (Iterator it = sourceNode.IterateChildren(); it.HasNext(); ) { XMPNode sourceField = (XMPNode)it.Next(); AppendSubtree(destXMP, sourceField, destNode, replaceOldValues, deleteEmptyValues); if (deleteEmptyValues && !destNode.HasChildren()) { destParent.RemoveChild(destNode); } } } else { if (sourceForm.IsArrayAltText()) { // Merge AltText arrays by the "xml:lang" qualifiers. Make sure x-default is first. // Make a special check for deletion of empty values. Meaningful in AltText arrays // because the "xml:lang" qualifier provides unambiguous source/dest correspondence. for (Iterator it = sourceNode.IterateChildren(); it.HasNext(); ) { XMPNode sourceItem = (XMPNode)it.Next(); if (!sourceItem.HasQualifier() || !XMPConstConstants.XmlLang.Equals(sourceItem.GetQualifier(1).GetName())) { continue; } int destIndex = XMPNodeUtils.LookupLanguageItem(destNode, sourceItem.GetQualifier(1).GetValue()); if (deleteEmptyValues && (sourceItem.GetValue() == null || sourceItem.GetValue().Length == 0)) { if (destIndex != -1) { destNode.RemoveChild(destIndex); if (!destNode.HasChildren()) { destParent.RemoveChild(destNode); } } } else { if (destIndex == -1) { // Not replacing, keep the existing item. if (!XMPConstConstants.XDefault.Equals(sourceItem.GetQualifier(1).GetValue()) || !destNode.HasChildren()) { sourceItem.CloneSubtree(destNode); } else { XMPNode destItem = new XMPNode(sourceItem.GetName(), sourceItem.GetValue(), sourceItem.GetOptions()); sourceItem.CloneSubtree(destItem); destNode.AddChild(1, destItem); } } } } } else { if (sourceForm.IsArray()) { // Merge other arrays by item values. Don't worry about order or duplicates. Source // items with empty values do not cause deletion, that conflicts horribly with // merging. for (Iterator @is = sourceNode.IterateChildren(); @is.HasNext(); ) { XMPNode sourceItem = (XMPNode)@is.Next(); bool match = false; for (Iterator id = destNode.IterateChildren(); id.HasNext(); ) { XMPNode destItem = (XMPNode)id.Next(); if (ItemValuesMatch(sourceItem, destItem)) { match = true; } } if (!match) { destNode = (XMPNode)sourceItem.Clone(); destParent.AddChild(destNode); } } } } } } } } }