/// <summary> /// The internals for SetProperty() and related calls, used after the node is /// found or created. /// </summary> /// <param name="node"> /// the newly created node </param> /// <param name="value"> /// the node value, can be <code>null</code> </param> /// <param name="newOptions"> /// options for the new node, must not be <code>null</code>. </param> /// <param name="deleteExisting"> flag if the existing value is to be overwritten </param> /// <exception cref="XmpException"> thrown if options and value do not correspond </exception> internal virtual void SetNode(XmpNode node, object value, PropertyOptions newOptions, bool deleteExisting) { if (deleteExisting) { node.Clear(); } // its checked by setOptions(), if the merged result is a valid options set node.Options.MergeWith(newOptions); if (!node.Options.CompositeProperty) { // This is setting the value of a leaf node. XmpNodeUtils.SetNodeValue(node, value); } else { if (value != null && value.ToString().Length > 0) { throw new XmpException("Composite nodes can't have values", XmpError.BADXPATH); } node.RemoveChildren(); } }
/// <summary> /// Tweak old XMP: Move an instance ID from rdf:about to the /// <em>xmpMM:InstanceID</em> property. An old instance ID usually looks /// like "uuid:bac965c4-9d87-11d9-9a30-000d936b79c4", plus InDesign /// 3.0 wrote them like "bac965c4-9d87-11d9-9a30-000d936b79c4". If /// the name looks like a UUID simply move it to <em>xmpMM:InstanceID</em>, /// don't worry about any existing <em>xmpMM:InstanceID</em>. Both will /// only be present when a newer file with the <em>xmpMM:InstanceID</em> /// property is updated by an old app that uses <em>rdf:about</em>. /// </summary> /// <param name="tree"> the root of the metadata tree </param> /// <exception cref="XmpException"> Thrown if tweaking fails. </exception> private static void TweakOldXmp(XmpNode tree) { if (tree.Name != null && tree.Name.Length >= Utils.UUID_LENGTH) { string nameStr = tree.Name.ToLower(); if (nameStr.StartsWith("uuid:")) { nameStr = nameStr.Substring(5); } if (Utils.CheckUuidFormat(nameStr)) { // move UUID to xmpMM:InstanceID and remove it from the root node XmpPath path = XmpPathParser.ExpandXPath(XmpConst.NS_XMP_MM, "InstanceID"); XmpNode idNode = XmpNodeUtils.FindNode(tree, path, true, null); if (idNode != null) { idNode.Options = null; // Clobber any existing xmpMM:InstanceID. idNode.Value = "uuid:" + nameStr; idNode.RemoveChildren(); idNode.RemoveQualifiers(); tree.Name = null; } else { throw new XmpException("Failure creating xmpMM:InstanceID", XmpError.INTERNALFAILURE); } } } }
/// <summary> /// The internals for SetProperty() and related calls, used after the node is /// found or created. /// </summary> /// <param name="node"> /// the newly created node </param> /// <param name="value"> /// the node value, can be <code>null</code> </param> /// <param name="newOptions"> /// options for the new node, must not be <code>null</code>. </param> /// <param name="deleteExisting"> flag if the existing value is to be overwritten </param> /// <exception cref="XmpException"> thrown if options and value do not correspond </exception> internal virtual void SetNode(XmpNode node, object value, PropertyOptions newOptions, bool deleteExisting) { if (deleteExisting) { node.Clear(); } // its checked by setOptions(), if the merged result is a valid options set node.Options.MergeWith(newOptions); if (!node.Options.CompositeProperty) { // This is setting the value of a leaf node. XmpNodeUtils.SetNodeValue(node, value); } else { if (value != null && value.ToString().Length > 0) { throw new XmpException("Composite nodes can't have values", XmpError.BADXPATH); } node.RemoveChildren(); } }
/// <summary> /// The parent is an RDF pseudo-struct containing an rdf:value field. Fix the /// XMP data model. The rdf:value node must be the first child, the other /// children are qualifiers. The form, value, and children of the rdf:value /// node are the real ones. The rdf:value node's qualifiers must be added to /// the others. /// </summary> /// <param name="xmpParent"> the parent xmp node </param> /// <exception cref="XmpException"> thown on parsing errors </exception> private static void FixupQualifiedNode(XmpNode xmpParent) { Debug.Assert(xmpParent.Options.Struct && xmpParent.HasChildren()); XmpNode valueNode = xmpParent.GetChild(1); Debug.Assert("rdf:value".Equals(valueNode.Name)); // Move the qualifiers on the value node to the parent. // Make sure an xml:lang qualifier stays at the front. // Check for duplicate names between the value node's qualifiers and the parent's children. // The parent's children are about to become qualifiers. Check here, between the groups. // Intra-group duplicates are caught by XMPNode#addChild(...). if (valueNode.Options.HasLanguage) { if (xmpParent.Options.HasLanguage) { throw new XmpException("Redundant xml:lang for rdf:value element", XmpError.BADXMP); } XmpNode langQual = valueNode.GetQualifier(1); valueNode.RemoveQualifier(langQual); xmpParent.AddQualifier(langQual); } // Start the remaining copy after the xml:lang qualifier. for (int i = 1; i <= valueNode.QualifierLength; i++) { XmpNode qualifier = valueNode.GetQualifier(i); xmpParent.AddQualifier(qualifier); } // Change the parent's other children into qualifiers. // This loop starts at 1, child 0 is the rdf:value node. for (int i = 2; i <= xmpParent.ChildrenLength; i++) { XmpNode qualifier = xmpParent.GetChild(i); xmpParent.AddQualifier(qualifier); } // Move the options and value last, other checks need the parent's original options. // Move the value node's children to be the parent's children. Debug.Assert(xmpParent.Options.Struct || xmpParent.HasValueChild); xmpParent.HasValueChild = false; xmpParent.Options.Struct = false; xmpParent.Options.MergeWith(valueNode.Options); xmpParent.Value = valueNode.Value; xmpParent.RemoveChildren(); for (IEnumerator it = valueNode.IterateChildren(); it.MoveNext();) { XmpNode child = (XmpNode) it.Current; xmpParent.AddChild(child); } }