/// <summary>
 /// Tweak old XMP: Move an instance ID from rdf:about to the
 /// <em>xmpMM:InstanceID</em> property.
 /// </summary>
 /// <remarks>
 /// Tweak old XMP: Move an instance ID from rdf:about to the
 /// <em>xmpMM:InstanceID</em> property. An old instance ID usually looks
 /// like &quot;uuid:bac965c4-9d87-11d9-9a30-000d936b79c4&quot;, plus InDesign
 /// 3.0 wrote them like &quot;bac965c4-9d87-11d9-9a30-000d936b79c4&quot;. 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>.
 /// </remarks>
 /// <param name="tree">the root of the metadata tree</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Thrown if tweaking fails.</exception>
 private static void TweakOldXMP(XMPNode tree)
 {
     if (tree.GetName() != null && tree.GetName().Length >= Utils.UuidLength)
     {
         string nameStr = tree.GetName().ToLower();
         if (nameStr.StartsWith("uuid:"))
         {
             nameStr = Sharpen.Runtime.Substring(nameStr, 5);
         }
         if (Utils.CheckUUIDFormat(nameStr))
         {
             // move UUID to xmpMM:InstanceID and remove it from the root node
             XMPPath path   = XMPPathParser.ExpandXPath(XMPConstConstants.NsXmpMm, "InstanceID");
             XMPNode idNode = XMPNodeUtils.FindNode(tree, path, true, null);
             if (idNode != null)
             {
                 idNode.SetOptions(null);
                 // Clobber any existing xmpMM:InstanceID.
                 idNode.SetValue("uuid:" + nameStr);
                 idNode.RemoveChildren();
                 idNode.RemoveQualifiers();
                 tree.SetName(null);
             }
             else
             {
                 throw new XMPException("Failure creating xmpMM:InstanceID", XMPErrorConstants.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="Com.Adobe.Xmp.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.GetOptions().MergeWith(newOptions);
			if (!node.GetOptions().IsCompositeProperty())
			{
				// 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", XMPErrorConstants.Badxpath);
				}
				node.RemoveChildren();
			}
		}
 /// <summary>The parent is an RDF pseudo-struct containing an rdf:value field.</summary>
 /// <remarks>
 /// 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.
 /// </remarks>
 /// <param name="xmpParent">the parent xmp node</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">thown on parsing errors</exception>
 private static void FixupQualifiedNode(XMPNode xmpParent)
 {
     System.Diagnostics.Debug.Assert(xmpParent.GetOptions().IsStruct() && xmpParent.HasChildren());
     XMPNode valueNode = xmpParent.GetChild(1);
     System.Diagnostics.Debug.Assert("rdf:value".Equals(valueNode.GetName()));
     // 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.GetOptions().GetHasLanguage())
     {
         if (xmpParent.GetOptions().GetHasLanguage())
         {
             throw new XMPException("Redundant xml:lang for rdf:value element", XMPErrorConstants.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.GetQualifierLength(); 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_1 = 2; i_1 <= xmpParent.GetChildrenLength(); i_1++)
     {
         XMPNode qualifier = xmpParent.GetChild(i_1);
         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.
     System.Diagnostics.Debug.Assert(xmpParent.GetOptions().IsStruct() || xmpParent.GetHasValueChild());
     xmpParent.SetHasValueChild(false);
     xmpParent.GetOptions().SetStruct(false);
     xmpParent.GetOptions().MergeWith(valueNode.GetOptions());
     xmpParent.SetValue(valueNode.GetValue());
     xmpParent.RemoveChildren();
     for (Iterator it = valueNode.IterateChildren(); it.HasNext(); )
     {
         XMPNode child = (XMPNode)it.Next();
         xmpParent.AddChild(child);
     }
 }