/// <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);
             }
         }
     }
 }
Beispiel #2
0
        /// <seealso cref="Com.Adobe.Xmp.XMPUtils.AppendProperties(Com.Adobe.Xmp.XMPMeta, Com.Adobe.Xmp.XMPMeta, bool, bool)"/>
        /// <param name="source">The source XMP object.</param>
        /// <param name="destination">The destination XMP object.</param>
        /// <param name="doAllProperties">Do internal properties in addition to external properties.</param>
        /// <param name="replaceOldValues">Replace the values of existing properties.</param>
        /// <param name="deleteEmptyValues">Delete destination values if source property is empty.</param>
        /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards the Exceptions from the metadata processing</exception>
        public static void AppendProperties(XMPMeta source, XMPMeta destination, bool doAllProperties, bool replaceOldValues, bool deleteEmptyValues)
        {
            ParameterAsserts.AssertImplementation(source);
            ParameterAsserts.AssertImplementation(destination);
            XMPMetaImpl src  = (XMPMetaImpl)source;
            XMPMetaImpl dest = (XMPMetaImpl)destination;

            for (Iterator it = src.GetRoot().IterateChildren(); it.HasNext();)
            {
                XMPNode sourceSchema = (XMPNode)it.Next();
                // Make sure we have a destination schema node
                XMPNode destSchema    = XMPNodeUtils.FindSchemaNode(dest.GetRoot(), sourceSchema.GetName(), false);
                bool    createdSchema = false;
                if (destSchema == null)
                {
                    destSchema = new XMPNode(sourceSchema.GetName(), sourceSchema.GetValue(), new PropertyOptions().SetSchemaNode(true));
                    dest.GetRoot().AddChild(destSchema);
                    createdSchema = true;
                }
                // Process the source schema's children.
                for (Iterator ic = sourceSchema.IterateChildren(); ic.HasNext();)
                {
                    XMPNode sourceProp = (XMPNode)ic.Next();
                    if (doAllProperties || !Utils.IsInternalProperty(sourceSchema.GetName(), sourceProp.GetName()))
                    {
                        AppendSubtree(dest, sourceProp, destSchema, replaceOldValues, deleteEmptyValues);
                    }
                }
                if (!destSchema.HasChildren() && (createdSchema || deleteEmptyValues))
                {
                    // Don't create an empty schema / remove empty schema.
                    dest.GetRoot().RemoveChild(destSchema);
                }
            }
        }
		/// <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>Visit all schemas to do general fixes and handle special cases.</summary>
 /// <param name="xmp">the metadata object implementation</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Thrown if the normalisation fails.</exception>
 private static void TouchUpDataModel(XMPMetaImpl xmp)
 {
     // make sure the DC schema is existing, because it might be needed within the normalization
     // if not touched it will be removed by removeEmptySchemas
     XMPNodeUtils.FindSchemaNode(xmp.GetRoot(), XMPConstConstants.NsDc, true);
     // Do the special case fixes within each schema.
     for (Iterator it = xmp.GetRoot().IterateChildren(); it.HasNext();)
     {
         XMPNode currSchema = (XMPNode)it.Next();
         if (XMPConstConstants.NsDc.Equals(currSchema.GetName()))
         {
             NormalizeDCArrays(currSchema);
         }
         else
         {
             if (XMPConstConstants.NsExif.Equals(currSchema.GetName()))
             {
                 // Do a special case fix for exif:GPSTimeStamp.
                 FixGPSTimeStamp(currSchema);
                 XMPNode arrayNode = XMPNodeUtils.FindChildNode(currSchema, "exif:UserComment", false);
                 if (arrayNode != null)
                 {
                     RepairAltText(arrayNode);
                 }
             }
             else
             {
                 if (XMPConstConstants.NsDm.Equals(currSchema.GetName()))
                 {
                     // Do a special case migration of xmpDM:copyright to
                     // dc:rights['x-default'].
                     XMPNode dmCopyright = XMPNodeUtils.FindChildNode(currSchema, "xmpDM:copyright", false);
                     if (dmCopyright != null)
                     {
                         MigrateAudioCopyright(xmp, dmCopyright);
                     }
                 }
                 else
                 {
                     if (XMPConstConstants.NsXmpRights.Equals(currSchema.GetName()))
                     {
                         XMPNode arrayNode = XMPNodeUtils.FindChildNode(currSchema, "xmpRights:UsageTerms", false);
                         if (arrayNode != null)
                         {
                             RepairAltText(arrayNode);
                         }
                     }
                 }
             }
         }
     }
 }
Beispiel #5
0
        /// <summary>
        /// Searches for a field selector in a node:
        /// [fieldName="value] - an element in an array of structs, chosen by a field value.
        /// </summary>
        /// <remarks>
        /// Searches for a field selector in a node:
        /// [fieldName="value] - an element in an array of structs, chosen by a field value.
        /// No implicit nodes are created by field selectors.
        /// </remarks>
        /// <param name="arrayNode"/>
        /// <param name="fieldName"/>
        /// <param name="fieldValue"/>
        /// <returns>Returns the index of the field if found, otherwise -1.</returns>
        /// <exception cref="Com.Adobe.Xmp.XMPException"></exception>
        private static int LookupFieldSelector(XMPNode arrayNode, string fieldName, string fieldValue)
        {
            int result = -1;

            for (int index = 1; index <= arrayNode.GetChildrenLength() && result < 0; index++)
            {
                XMPNode currItem = arrayNode.GetChild(index);
                if (!currItem.GetOptions().IsStruct())
                {
                    throw new XMPException("Field selector must be used on array of struct", XMPErrorConstants.Badxpath);
                }
                for (int f = 1; f <= currItem.GetChildrenLength(); f++)
                {
                    XMPNode currField = currItem.GetChild(f);
                    if (!fieldName.Equals(currField.GetName()))
                    {
                        continue;
                    }
                    if (fieldValue.Equals(currField.GetValue()))
                    {
                        result = index;
                        break;
                    }
                }
            }
            return(result);
        }
Beispiel #6
0
 /// <summary>Constructor</summary>
 /// <param name="parentNode">the node which children shall be iterated.</param>
 /// <param name="parentPath">the full path of the former node without the leaf node.</param>
 public NodeIteratorChildren(XMPIteratorImpl _enclosing, XMPNode parentNode, string parentPath)
     : base(_enclosing)
 {
     this._enclosing = _enclosing;
     if (parentNode.GetOptions().IsSchemaNode())
     {
         this._enclosing.SetBaseNS(parentNode.GetName());
     }
     this.parentPath       = this.AccumulatePath(parentNode, parentPath, 1);
     this.childrenIterator = parentNode.IterateChildren();
 }
 /// <summary>
 /// Undo the denormalization performed by the XMP used in Acrobat 5.<br />
 /// If a Dublin Core array had only one item, it was serialized as a simple
 /// property.
 /// </summary>
 /// <remarks>
 /// Undo the denormalization performed by the XMP used in Acrobat 5.<br />
 /// If a Dublin Core array had only one item, it was serialized as a simple
 /// property. <br />
 /// The <code>xml:lang</code> attribute was dropped from an
 /// <code>alt-text</code> item if the language was <code>x-default</code>.
 /// </remarks>
 /// <param name="dcSchema">the DC schema node</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Thrown if normalization fails</exception>
 private static void NormalizeDCArrays(XMPNode dcSchema)
 {
     for (int i = 1; i <= dcSchema.GetChildrenLength(); i++)
     {
         XMPNode         currProp  = dcSchema.GetChild(i);
         PropertyOptions arrayForm = (PropertyOptions)dcArrayForms.Get(currProp.GetName());
         if (arrayForm == null)
         {
             continue;
         }
         else
         {
             if (currProp.GetOptions().IsSimple())
             {
                 // create a new array and add the current property as child,
                 // if it was formerly simple
                 XMPNode newArray = new XMPNode(currProp.GetName(), arrayForm);
                 currProp.SetName(XMPConstConstants.ArrayItemName);
                 newArray.AddChild(currProp);
                 dcSchema.ReplaceChild(i, newArray);
                 // fix language alternatives
                 if (arrayForm.IsArrayAltText() && !currProp.GetOptions().GetHasLanguage())
                 {
                     XMPNode newLang = new XMPNode(XMPConstConstants.XmlLang, XMPConstConstants.XDefault, null);
                     currProp.AddQualifier(newLang);
                 }
             }
             else
             {
                 // clear array options and add corrected array form if it has been an array before
                 currProp.GetOptions().SetOption(PropertyOptions.Array | PropertyOptions.ArrayOrdered | PropertyOptions.ArrayAlternate | PropertyOptions.ArrayAltText, false);
                 currProp.GetOptions().MergeWith(arrayForm);
                 if (arrayForm.IsArrayAltText())
                 {
                     // applying for "dc:description", "dc:rights", "dc:title"
                     RepairAltText(currProp);
                 }
             }
         }
     }
 }
Beispiel #8
0
 /// <summary>
 /// Remove all schema children according to the flag
 /// <code>doAllProperties</code>.
 /// </summary>
 /// <remarks>
 /// Remove all schema children according to the flag
 /// <code>doAllProperties</code>. Empty schemas are automatically remove
 /// by <code>XMPNode</code>
 /// </remarks>
 /// <param name="schemaNode">a schema node</param>
 /// <param name="doAllProperties">flag if all properties or only externals shall be removed.</param>
 /// <returns>Returns true if the schema is empty after the operation.</returns>
 private static bool RemoveSchemaChildren(XMPNode schemaNode, bool doAllProperties)
 {
     for (Iterator it = schemaNode.IterateChildren(); it.HasNext();)
     {
         XMPNode currProp = (XMPNode)it.Next();
         if (doAllProperties || !Utils.IsInternalProperty(schemaNode.GetName(), currProp.GetName()))
         {
             it.Remove();
         }
     }
     return(!schemaNode.HasChildren());
 }
Beispiel #9
0
 /// <summary>Constructor for the node iterator.</summary>
 /// <param name="visitedNode">the currently visited node</param>
 /// <param name="parentPath">the accumulated path of the node</param>
 /// <param name="index">the index within the parent node (only for arrays)</param>
 public NodeIterator(XMPIteratorImpl _enclosing, XMPNode visitedNode, string parentPath, int index)
 {
     this._enclosing = _enclosing;
     // EMPTY
     this.visitedNode = visitedNode;
     this.state       = XMPIteratorImpl.NodeIterator.IterateNode;
     if (visitedNode.GetOptions().IsSchemaNode())
     {
         this._enclosing.SetBaseNS(visitedNode.GetName());
     }
     // for all but the root node and schema nodes
     this.path = this.AccumulatePath(visitedNode, parentPath, index);
 }
Beispiel #10
0
 /// <summary>Prepares the next node to return if not already done.</summary>
 /// <seealso cref="Sharpen.Iterator{E}.HasNext()"/>
 public override bool HasNext()
 {
     if (this.GetReturnProperty() != null)
     {
         // hasNext has been called before
         return(true);
     }
     else
     {
         if (this._enclosing.skipSiblings)
         {
             return(false);
         }
         else
         {
             if (this.childrenIterator.HasNext())
             {
                 XMPNode child = (XMPNode)this.childrenIterator.Next();
                 this.index++;
                 string path = null;
                 if (child.GetOptions().IsSchemaNode())
                 {
                     this._enclosing.SetBaseNS(child.GetName());
                 }
                 else
                 {
                     if (child.GetParent() != null)
                     {
                         // for all but the root node and schema nodes
                         path = this.AccumulatePath(child, this.parentPath, this.index);
                     }
                 }
                 // report next property, skip not-leaf nodes in case options is set
                 if (!this._enclosing.GetOptions().IsJustLeafnodes() || !child.HasChildren())
                 {
                     this.SetReturnProperty(this.CreatePropertyInfo(child, this._enclosing.GetBaseNS(), path));
                     return(true);
                 }
                 else
                 {
                     return(this.HasNext());
                 }
             }
             else
             {
                 return(false);
             }
         }
     }
 }
Beispiel #11
0
            /// <param name="currNode">the node that will be added to the path.</param>
            /// <param name="parentPath">the path up to this node.</param>
            /// <param name="currentIndex">the current array index if an arrey is traversed</param>
            /// <returns>Returns the updated path.</returns>
            protected internal virtual string AccumulatePath(XMPNode currNode, string parentPath, int currentIndex)
            {
                string separator;
                string segmentName;

                if (currNode.GetParent() == null || currNode.GetOptions().IsSchemaNode())
                {
                    return(null);
                }
                else
                {
                    if (currNode.GetParent().GetOptions().IsArray())
                    {
                        separator   = string.Empty;
                        segmentName = "[" + currentIndex.ToString() + "]";
                    }
                    else
                    {
                        separator   = "/";
                        segmentName = currNode.GetName();
                    }
                }
                if (parentPath == null || parentPath.Length == 0)
                {
                    return(segmentName);
                }
                else
                {
                    if (this._enclosing.GetOptions().IsJustLeafname())
                    {
                        return(!segmentName.StartsWith("?") ? segmentName : Sharpen.Runtime.Substring(segmentName, 1));
                    }
                    else
                    {
                        // qualifier
                        return(parentPath + separator + segmentName);
                    }
                }
            }
 /// <summary>The outermost call is special.</summary>
 /// <remarks>
 /// The outermost call is special. The names almost certainly differ. The
 /// qualifiers (and hence options) will differ for an alias to the x-default
 /// item of a langAlt array.
 /// </remarks>
 /// <param name="aliasNode">the alias node</param>
 /// <param name="baseNode">the base node of the alias</param>
 /// <param name="outerCall">marks the outer call of the recursion</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards XMP errors</exception>
 private static void CompareAliasedSubtrees(XMPNode aliasNode, XMPNode baseNode, bool outerCall)
 {
     if (!aliasNode.GetValue().Equals(baseNode.GetValue()) || aliasNode.GetChildrenLength() != baseNode.GetChildrenLength())
     {
         throw new XMPException("Mismatch between alias and base nodes", XMPErrorConstants.Badxmp);
     }
     if (!outerCall && (!aliasNode.GetName().Equals(baseNode.GetName()) || !aliasNode.GetOptions().Equals(baseNode.GetOptions()) || aliasNode.GetQualifierLength() != baseNode.GetQualifierLength()))
     {
         throw new XMPException("Mismatch between alias and base nodes", XMPErrorConstants.Badxmp);
     }
     for (Iterator an = aliasNode.IterateChildren(), bn = baseNode.IterateChildren(); an.HasNext() && bn.HasNext();)
     {
         XMPNode aliasChild = (XMPNode)an.Next();
         XMPNode baseChild  = (XMPNode)bn.Next();
         CompareAliasedSubtrees(aliasChild, baseChild, false);
     }
     for (Iterator an_1 = aliasNode.IterateQualifier(), bn_1 = baseNode.IterateQualifier(); an_1.HasNext() && bn_1.HasNext();)
     {
         XMPNode aliasQual = (XMPNode)an_1.Next();
         XMPNode baseQual  = (XMPNode)bn_1.Next();
         CompareAliasedSubtrees(aliasQual, baseQual, false);
     }
 }
 /// <summary>The outermost call is special.</summary>
 /// <remarks>
 /// The outermost call is special. The names almost certainly differ. The
 /// qualifiers (and hence options) will differ for an alias to the x-default
 /// item of a langAlt array.
 /// </remarks>
 /// <param name="aliasNode">the alias node</param>
 /// <param name="baseNode">the base node of the alias</param>
 /// <param name="outerCall">marks the outer call of the recursion</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards XMP errors</exception>
 private static void CompareAliasedSubtrees(XMPNode aliasNode, XMPNode baseNode, bool outerCall)
 {
     if (!aliasNode.GetValue().Equals(baseNode.GetValue()) || aliasNode.GetChildrenLength() != baseNode.GetChildrenLength())
     {
         throw new XMPException("Mismatch between alias and base nodes", XMPErrorConstants.Badxmp);
     }
     if (!outerCall && (!aliasNode.GetName().Equals(baseNode.GetName()) || !aliasNode.GetOptions().Equals(baseNode.GetOptions()) || aliasNode.GetQualifierLength() != baseNode.GetQualifierLength()))
     {
         throw new XMPException("Mismatch between alias and base nodes", XMPErrorConstants.Badxmp);
     }
     for (Iterator an = aliasNode.IterateChildren(), bn = baseNode.IterateChildren(); an.HasNext() && bn.HasNext(); )
     {
         XMPNode aliasChild = (XMPNode)an.Next();
         XMPNode baseChild = (XMPNode)bn.Next();
         CompareAliasedSubtrees(aliasChild, baseChild, false);
     }
     for (Iterator an_1 = aliasNode.IterateQualifier(), bn_1 = baseNode.IterateQualifier(); an_1.HasNext() && bn_1.HasNext(); )
     {
         XMPNode aliasQual = (XMPNode)an_1.Next();
         XMPNode baseQual = (XMPNode)bn_1.Next();
         CompareAliasedSubtrees(aliasQual, baseQual, false);
     }
 }
Beispiel #14
0
 /// <summary>
 /// Searches for a qualifier selector in a node:
 /// [?qualName="value"] - an element in an array, chosen by a qualifier value.
 /// </summary>
 /// <remarks>
 /// Searches for a qualifier selector in a node:
 /// [?qualName="value"] - an element in an array, chosen by a qualifier value.
 /// No implicit nodes are created for qualifier selectors,
 /// except for an alias to an x-default item.
 /// </remarks>
 /// <param name="arrayNode">an array node</param>
 /// <param name="qualName">the qualifier name</param>
 /// <param name="qualValue">the qualifier value</param>
 /// <param name="aliasForm">
 /// in case the qual selector results from an alias,
 /// an x-default node is created if there has not been one.
 /// </param>
 /// <returns>Returns the index of th</returns>
 /// <exception cref="Com.Adobe.Xmp.XMPException"></exception>
 private static int LookupQualSelector(XMPNode arrayNode, string qualName, string qualValue, int aliasForm)
 {
     if (XMPConstConstants.XmlLang.Equals(qualName))
     {
         qualValue = Utils.NormalizeLangValue(qualValue);
         int index = Com.Adobe.Xmp.Impl.XMPNodeUtils.LookupLanguageItem(arrayNode, qualValue);
         if (index < 0 && (aliasForm & AliasOptions.PropArrayAltText) > 0)
         {
             XMPNode langNode = new XMPNode(XMPConstConstants.ArrayItemName, null);
             XMPNode xdefault = new XMPNode(XMPConstConstants.XmlLang, XMPConstConstants.XDefault, null);
             langNode.AddQualifier(xdefault);
             arrayNode.AddChild(1, langNode);
             return(1);
         }
         else
         {
             return(index);
         }
     }
     else
     {
         for (int index = 1; index < arrayNode.GetChildrenLength(); index++)
         {
             XMPNode currItem = arrayNode.GetChild(index);
             for (Iterator it = currItem.IterateQualifier(); it.HasNext();)
             {
                 XMPNode qualifier = (XMPNode)it.Next();
                 if (qualName.Equals(qualifier.GetName()) && qualValue.Equals(qualifier.GetValue()))
                 {
                     return(index);
                 }
             }
         }
         return(-1);
     }
 }
		/// <summary>
		/// Remove all schema children according to the flag
		/// <code>doAllProperties</code>.
		/// </summary>
		/// <remarks>
		/// Remove all schema children according to the flag
		/// <code>doAllProperties</code>. Empty schemas are automatically remove
		/// by <code>XMPNode</code>
		/// </remarks>
		/// <param name="schemaNode">a schema node</param>
		/// <param name="doAllProperties">flag if all properties or only externals shall be removed.</param>
		/// <returns>Returns true if the schema is empty after the operation.</returns>
		private static bool RemoveSchemaChildren(XMPNode schemaNode, bool doAllProperties)
		{
			for (Iterator it = schemaNode.IterateChildren(); it.HasNext(); )
			{
				XMPNode currProp = (XMPNode)it.Next();
				if (doAllProperties || !Utils.IsInternalProperty(schemaNode.GetName(), currProp.GetName()))
				{
					it.Remove();
				}
			}
			return !schemaNode.HasChildren();
		}
Beispiel #16
0
        /// <summary>This is setting the value of a leaf node.</summary>
        /// <param name="node">an XMPNode</param>
        /// <param name="value">a value</param>
        internal static void SetNodeValue(XMPNode node, object value)
        {
            string strValue = SerializeNodeValue(value);

            if (!(node.GetOptions().IsQualifier() && XMPConstConstants.XmlLang.Equals(node.GetName())))
            {
                node.SetValue(strValue);
            }
            else
            {
                node.SetValue(Utils.NormalizeLangValue(strValue));
            }
        }
		/// <summary>Writes all used namespaces of the subtree in node to the output.</summary>
		/// <remarks>
		/// Writes all used namespaces of the subtree in node to the output.
		/// The subtree is recursivly traversed.
		/// </remarks>
		/// <param name="node">the root node of the subtree</param>
		/// <param name="usedPrefixes">a set containing currently used prefixes</param>
		/// <param name="indent">the current indent level</param>
		/// <exception cref="System.IO.IOException">Forwards all writer exceptions.</exception>
        private void DeclareUsedNamespaces(XMPNode node, HashSet<string> usedPrefixes, int indent)
		{
			if (node.GetOptions().IsSchemaNode())
			{
				// The schema node name is the URI, the value is the prefix.
				string prefix = Sharpen.Runtime.Substring(node.GetValue(), 0, node.GetValue().Length - 1);
				DeclareNamespace(prefix, node.GetName(), usedPrefixes, indent);
			}
			else
			{
				if (node.GetOptions().IsStruct())
				{
					for (Iterator it = node.IterateChildren(); it.HasNext(); )
					{
						XMPNode field = (XMPNode)it.Next();
						DeclareNamespace(field.GetName(), null, usedPrefixes, indent);
					}
				}
			}
			for (Iterator it_1 = node.IterateChildren(); it_1.HasNext(); )
			{
				XMPNode child = (XMPNode)it_1.Next();
				DeclareUsedNamespaces(child, usedPrefixes, indent);
			}
			for (Iterator it_2 = node.IterateQualifier(); it_2.HasNext(); )
			{
				XMPNode qualifier = (XMPNode)it_2.Next();
				DeclareNamespace(qualifier.GetName(), null, usedPrefixes, indent);
				DeclareUsedNamespaces(qualifier, usedPrefixes, indent);
			}
		}
        /// <summary>
        /// 7.2.7 propertyAttributeURIs
        /// anyURI - ( coreSyntaxTerms | rdf:Description | rdf:li | oldTerms )
        /// 7.2.11 nodeElement
        /// start-element ( URI == nodeElementURIs,
        /// attributes == set ( ( idAttr | nodeIdAttr | aboutAttr )?, propertyAttr* ) )
        /// propertyEltList
        /// end-element()
        /// Process the attribute list for an RDF node element.
        /// </summary>
        /// <remarks>
        /// 7.2.7 propertyAttributeURIs
        /// anyURI - ( coreSyntaxTerms | rdf:Description | rdf:li | oldTerms )
        /// 7.2.11 nodeElement
        /// start-element ( URI == nodeElementURIs,
        /// attributes == set ( ( idAttr | nodeIdAttr | aboutAttr )?, propertyAttr* ) )
        /// propertyEltList
        /// end-element()
        /// Process the attribute list for an RDF node element. A property attribute URI is
        /// anything other than an RDF term. The rdf:ID and rdf:nodeID attributes are simply ignored,
        /// as are rdf:about attributes on inner nodes.
        /// </remarks>
        /// <param name="xmp">the xmp metadata object that is generated</param>
        /// <param name="xmpParent">the parent xmp node</param>
        /// <param name="xmlNode">the currently processed XML node</param>
        /// <param name="isTopLevel">Flag if the node is a top-level node</param>
        /// <exception cref="Com.Adobe.Xmp.XMPException">thown on parsing errors</exception>
        private static void Rdf_NodeElementAttrs(XMPMetaImpl xmp, XMPNode xmpParent, XmlNode xmlNode, bool isTopLevel)
        {
            // Used to detect attributes that are mutually exclusive.
            int exclusiveAttrs = 0;
            for (int i = 0; i < xmlNode.Attributes.Count; i++)
            {
                XmlNode attribute = xmlNode.Attributes.Item(i);
                // quick hack, ns declarations do not appear in C++
                // ignore "ID" without namespace
                if ("xmlns".Equals(attribute.Prefix) || (attribute.Prefix == null && "xmlns".Equals(attribute.Name)))
                {
                    continue;
                }
                int attrTerm = GetRDFTermKind(attribute);
                switch (attrTerm)
                {
                    case RdftermId:
                    case RdftermNodeId:
                    case RdftermAbout:
                    {
                        if (exclusiveAttrs > 0)
                        {
                            throw new XMPException("Mutally exclusive about, ID, nodeID attributes", XMPErrorConstants.Badrdf);
                        }
                        exclusiveAttrs++;
                        if (isTopLevel && (attrTerm == RdftermAbout))
                        {
                            // This is the rdf:about attribute on a top level node. Set
                            // the XMP tree name if
                            // it doesn't have a name yet. Make sure this name matches
                            // the XMP tree name.
                            if (xmpParent.GetName() != null && xmpParent.GetName().Length > 0)
                            {
                                if (!xmpParent.GetName().Equals(attribute.Value))
                                {
                                    throw new XMPException("Mismatched top level rdf:about values", XMPErrorConstants.Badxmp);
                                }
                            }
                            else
                            {
                                xmpParent.SetName(attribute.Value);
                            }
                        }
                        break;
                    }

                    case RdftermOther:
                    {
                        AddChildNode(xmp, xmpParent, attribute, attribute.Value, isTopLevel);
                        break;
                    }

                    default:
                    {
                        throw new XMPException("Invalid nodeElement attribute", XMPErrorConstants.Badrdf);
                    }
                }
            }
        }
		/// <summary>Recursively handles the "value" for a node.</summary>
		/// <remarks>
		/// Recursively handles the "value" for a node. It does not matter if it is a
		/// top level property, a field of a struct, or an item of an array. The
		/// indent is that for the property element. An xml:lang qualifier is written
		/// as an attribute of the property start tag, not by itself forcing the
		/// qualified property form. The patterns below mostly ignore attribute
		/// qualifiers like xml:lang. Except for the one struct case, attribute
		/// qualifiers don't affect the output form.
		/// <blockquote>
		/// <pre>
		/// &lt;ns:UnqualifiedSimpleProperty&gt;value&lt;/ns:UnqualifiedSimpleProperty&gt;
		/// &lt;ns:UnqualifiedStructProperty&gt; (If no rdf:resource qualifier)
		/// &lt;rdf:Description&gt;
		/// ... Fields, same forms as top level properties
		/// &lt;/rdf:Description&gt;
		/// &lt;/ns:UnqualifiedStructProperty&gt;
		/// &lt;ns:ResourceStructProperty rdf:resource=&quot;URI&quot;
		/// ... Fields as attributes
		/// &gt;
		/// &lt;ns:UnqualifiedArrayProperty&gt;
		/// &lt;rdf:Bag&gt; or Seq or Alt
		/// ... Array items as rdf:li elements, same forms as top level properties
		/// &lt;/rdf:Bag&gt;
		/// &lt;/ns:UnqualifiedArrayProperty&gt;
		/// &lt;ns:QualifiedProperty&gt;
		/// &lt;rdf:Description&gt;
		/// &lt;rdf:value&gt; ... Property &quot;value&quot; following the unqualified
		/// forms ... &lt;/rdf:value&gt;
		/// ... Qualifiers looking like named struct fields
		/// &lt;/rdf:Description&gt;
		/// &lt;/ns:QualifiedProperty&gt;
		/// </pre>
		/// </blockquote>
		/// </remarks>
		/// <param name="node">the property node</param>
		/// <param name="emitAsRDFValue">property shall be rendered as attribute rather than tag</param>
		/// <param name="useCanonicalRDF">
		/// use canonical form with inner description tag or
		/// the compact form with rdf:ParseType=&quot;resource&quot; attribute.
		/// </param>
		/// <param name="indent">the current indent level</param>
		/// <exception cref="System.IO.IOException">Forwards all writer exceptions.</exception>
		/// <exception cref="Com.Adobe.Xmp.XMPException">If &quot;rdf:resource&quot; and general qualifiers are mixed.</exception>
		private void SerializeCanonicalRDFProperty(XMPNode node, bool useCanonicalRDF, bool emitAsRDFValue, int indent)
		{
			bool emitEndTag = true;
			bool indentEndTag = true;
			// Determine the XML element name. Open the start tag with the name and
			// attribute qualifiers.
			string elemName = node.GetName();
			if (emitAsRDFValue)
			{
				elemName = "rdf:value";
			}
			else
			{
				if (XMPConstConstants.ArrayItemName.Equals(elemName))
				{
					elemName = "rdf:li";
				}
			}
			WriteIndent(indent);
			Write('<');
			Write(elemName);
			bool hasGeneralQualifiers = false;
			bool hasRDFResourceQual = false;
			for (Iterator it = node.IterateQualifier(); it.HasNext(); )
			{
				XMPNode qualifier = (XMPNode)it.Next();
				if (!RdfAttrQualifier.Contains(qualifier.GetName()))
				{
					hasGeneralQualifiers = true;
				}
				else
				{
					hasRDFResourceQual = "rdf:resource".Equals(qualifier.GetName());
					if (!emitAsRDFValue)
					{
						Write(' ');
						Write(qualifier.GetName());
						Write("=\"");
						AppendNodeValue(qualifier.GetValue(), true);
						Write('"');
					}
				}
			}
			// Process the property according to the standard patterns.
			if (hasGeneralQualifiers && !emitAsRDFValue)
			{
				// This node has general, non-attribute, qualifiers. Emit using the
				// qualified property form.
				// ! The value is output by a recursive call ON THE SAME NODE with
				// emitAsRDFValue set.
				if (hasRDFResourceQual)
				{
					throw new XMPException("Can't mix rdf:resource and general qualifiers", XMPErrorConstants.Badrdf);
				}
				// Change serialization to canonical format with inner rdf:Description-tag
				// depending on option
				if (useCanonicalRDF)
				{
					Write(">");
					WriteNewline();
					indent++;
					WriteIndent(indent);
					Write(RdfStructStart);
					Write(">");
				}
				else
				{
					Write(" rdf:parseType=\"Resource\">");
				}
				WriteNewline();
				SerializeCanonicalRDFProperty(node, useCanonicalRDF, true, indent + 1);
				for (Iterator it_1 = node.IterateQualifier(); it_1.HasNext(); )
				{
					XMPNode qualifier = (XMPNode)it_1.Next();
					if (!RdfAttrQualifier.Contains(qualifier.GetName()))
					{
						SerializeCanonicalRDFProperty(qualifier, useCanonicalRDF, false, indent + 1);
					}
				}
				if (useCanonicalRDF)
				{
					WriteIndent(indent);
					Write(RdfStructEnd);
					WriteNewline();
					indent--;
				}
			}
			else
			{
				// This node has no general qualifiers. Emit using an unqualified form.
				if (!node.GetOptions().IsCompositeProperty())
				{
					// This is a simple property.
					if (node.GetOptions().IsURI())
					{
						Write(" rdf:resource=\"");
						AppendNodeValue(node.GetValue(), true);
						Write("\"/>");
						WriteNewline();
						emitEndTag = false;
					}
					else
					{
						if (node.GetValue() == null || string.Empty.Equals(node.GetValue()))
						{
							Write("/>");
							WriteNewline();
							emitEndTag = false;
						}
						else
						{
							Write('>');
							AppendNodeValue(node.GetValue(), false);
							indentEndTag = false;
						}
					}
				}
				else
				{
					if (node.GetOptions().IsArray())
					{
						// This is an array.
						Write('>');
						WriteNewline();
						EmitRDFArrayTag(node, true, indent + 1);
						if (node.GetOptions().IsArrayAltText())
						{
							XMPNodeUtils.NormalizeLangArray(node);
						}
						for (Iterator it_1 = node.IterateChildren(); it_1.HasNext(); )
						{
							XMPNode child = (XMPNode)it_1.Next();
							SerializeCanonicalRDFProperty(child, useCanonicalRDF, false, indent + 2);
						}
						EmitRDFArrayTag(node, false, indent + 1);
					}
					else
					{
						if (!hasRDFResourceQual)
						{
							// This is a "normal" struct, use the rdf:parseType="Resource" form.
							if (!node.HasChildren())
							{
								// Change serialization to canonical format with inner rdf:Description-tag
								// if option is set
								if (useCanonicalRDF)
								{
									Write(">");
									WriteNewline();
									WriteIndent(indent + 1);
									Write(RdfEmptyStruct);
								}
								else
								{
									Write(" rdf:parseType=\"Resource\"/>");
									emitEndTag = false;
								}
								WriteNewline();
							}
							else
							{
								// Change serialization to canonical format with inner rdf:Description-tag
								// if option is set
								if (useCanonicalRDF)
								{
									Write(">");
									WriteNewline();
									indent++;
									WriteIndent(indent);
									Write(RdfStructStart);
									Write(">");
								}
								else
								{
									Write(" rdf:parseType=\"Resource\">");
								}
								WriteNewline();
								for (Iterator it_1 = node.IterateChildren(); it_1.HasNext(); )
								{
									XMPNode child = (XMPNode)it_1.Next();
									SerializeCanonicalRDFProperty(child, useCanonicalRDF, false, indent + 1);
								}
								if (useCanonicalRDF)
								{
									WriteIndent(indent);
									Write(RdfStructEnd);
									WriteNewline();
									indent--;
								}
							}
						}
						else
						{
							// This is a struct with an rdf:resource attribute, use the
							// "empty property element" form.
							for (Iterator it_1 = node.IterateChildren(); it_1.HasNext(); )
							{
								XMPNode child = (XMPNode)it_1.Next();
								if (!CanBeRDFAttrProp(child))
								{
									throw new XMPException("Can't mix rdf:resource and complex fields", XMPErrorConstants.Badrdf);
								}
								WriteNewline();
								WriteIndent(indent + 1);
								Write(' ');
								Write(child.GetName());
								Write("=\"");
								AppendNodeValue(child.GetValue(), true);
								Write('"');
							}
							Write("/>");
							WriteNewline();
							emitEndTag = false;
						}
					}
				}
			}
			// Emit the property element end tag.
			if (emitEndTag)
			{
				if (indentEndTag)
				{
					WriteIndent(indent);
				}
				Write("</");
				Write(elemName);
				Write('>');
				WriteNewline();
			}
		}
 /// <summary>Constructor for the node iterator.</summary>
 /// <param name="visitedNode">the currently visited node</param>
 /// <param name="parentPath">the accumulated path of the node</param>
 /// <param name="index">the index within the parent node (only for arrays)</param>
 public NodeIterator(XMPIteratorImpl _enclosing, XMPNode visitedNode, string parentPath, int index)
 {
     this._enclosing = _enclosing;
     // EMPTY
     this.visitedNode = visitedNode;
     this.state = XMPIteratorImpl.NodeIterator.IterateNode;
     if (visitedNode.GetOptions().IsSchemaNode())
     {
         this._enclosing.SetBaseNS(visitedNode.GetName());
     }
     // for all but the root node and schema nodes
     this.path = this.AccumulatePath(visitedNode, parentPath, index);
 }
        /// <summary>Visit all of the top level nodes looking for aliases.</summary>
        /// <remarks>
        /// Visit all of the top level nodes looking for aliases. If there is
        /// no base, transplant the alias subtree. If there is a base and strict
        /// aliasing is on, make sure the alias and base subtrees match.
        /// </remarks>
        /// <param name="tree">the root of the metadata tree</param>
        /// <param name="options">th parsing options</param>
        /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards XMP errors</exception>
        private static void MoveExplicitAliases(XMPNode tree, ParseOptions options)
        {
            if (!tree.GetHasAliases())
            {
                return;
            }
            tree.SetHasAliases(false);
            bool strictAliasing = options.GetStrictAliasing();

            for (Iterator schemaIt = tree.GetUnmodifiableChildren().Iterator(); schemaIt.HasNext();)
            {
                XMPNode currSchema = (XMPNode)schemaIt.Next();
                if (!currSchema.GetHasAliases())
                {
                    continue;
                }
                for (Iterator propertyIt = currSchema.IterateChildren(); propertyIt.HasNext();)
                {
                    XMPNode currProp = (XMPNode)propertyIt.Next();
                    if (!currProp.IsAlias())
                    {
                        continue;
                    }
                    currProp.SetAlias(false);
                    // Find the base path, look for the base schema and root node.
                    XMPAliasInfo info = XMPMetaFactory.GetSchemaRegistry().FindAlias(currProp.GetName());
                    if (info != null)
                    {
                        // find or create schema
                        XMPNode baseSchema = XMPNodeUtils.FindSchemaNode(tree, info.GetNamespace(), null, true);
                        baseSchema.SetImplicit(false);
                        XMPNode baseNode = XMPNodeUtils.FindChildNode(baseSchema, info.GetPrefix() + info.GetPropName(), false);
                        if (baseNode == null)
                        {
                            if (info.GetAliasForm().IsSimple())
                            {
                                // A top-to-top alias, transplant the property.
                                // change the alias property name to the base name
                                string qname = info.GetPrefix() + info.GetPropName();
                                currProp.SetName(qname);
                                baseSchema.AddChild(currProp);
                                // remove the alias property
                                propertyIt.Remove();
                            }
                            else
                            {
                                // An alias to an array item,
                                // create the array and transplant the property.
                                baseNode = new XMPNode(info.GetPrefix() + info.GetPropName(), info.GetAliasForm().ToPropertyOptions());
                                baseSchema.AddChild(baseNode);
                                TransplantArrayItemAlias(propertyIt, currProp, baseNode);
                            }
                        }
                        else
                        {
                            if (info.GetAliasForm().IsSimple())
                            {
                                // The base node does exist and this is a top-to-top alias.
                                // Check for conflicts if strict aliasing is on.
                                // Remove and delete the alias subtree.
                                if (strictAliasing)
                                {
                                    CompareAliasedSubtrees(currProp, baseNode, true);
                                }
                                propertyIt.Remove();
                            }
                            else
                            {
                                // This is an alias to an array item and the array exists.
                                // Look for the aliased item.
                                // Then transplant or check & delete as appropriate.
                                XMPNode itemNode = null;
                                if (info.GetAliasForm().IsArrayAltText())
                                {
                                    int xdIndex = XMPNodeUtils.LookupLanguageItem(baseNode, XMPConstConstants.XDefault);
                                    if (xdIndex != -1)
                                    {
                                        itemNode = baseNode.GetChild(xdIndex);
                                    }
                                }
                                else
                                {
                                    if (baseNode.HasChildren())
                                    {
                                        itemNode = baseNode.GetChild(1);
                                    }
                                }
                                if (itemNode == null)
                                {
                                    TransplantArrayItemAlias(propertyIt, currProp, baseNode);
                                }
                                else
                                {
                                    if (strictAliasing)
                                    {
                                        CompareAliasedSubtrees(currProp, itemNode, true);
                                    }
                                    propertyIt.Remove();
                                }
                            }
                        }
                    }
                }
                currSchema.SetHasAliases(false);
            }
        }
Beispiel #22
0
        /// <summary>Compares two nodes including its children and qualifier.</summary>
        /// <param name="leftNode">an <code>XMPNode</code></param>
        /// <param name="rightNode">an <code>XMPNode</code></param>
        /// <returns>Returns true if the nodes are equal, false otherwise.</returns>
        /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards exceptions to the calling method.</exception>
        private static bool ItemValuesMatch(XMPNode leftNode, XMPNode rightNode)
        {
            PropertyOptions leftForm  = leftNode.GetOptions();
            PropertyOptions rightForm = rightNode.GetOptions();

            if (leftForm.Equals(rightForm))
            {
                return(false);
            }
            if (leftForm.GetOptions() == 0)
            {
                // Simple nodes, check the values and xml:lang qualifiers.
                if (!leftNode.GetValue().Equals(rightNode.GetValue()))
                {
                    return(false);
                }
                if (leftNode.GetOptions().GetHasLanguage() != rightNode.GetOptions().GetHasLanguage())
                {
                    return(false);
                }
                if (leftNode.GetOptions().GetHasLanguage() && !leftNode.GetQualifier(1).GetValue().Equals(rightNode.GetQualifier(1).GetValue()))
                {
                    return(false);
                }
            }
            else
            {
                if (leftForm.IsStruct())
                {
                    // Struct nodes, see if all fields match, ignoring order.
                    if (leftNode.GetChildrenLength() != rightNode.GetChildrenLength())
                    {
                        return(false);
                    }
                    for (Iterator it = leftNode.IterateChildren(); it.HasNext();)
                    {
                        XMPNode leftField  = (XMPNode)it.Next();
                        XMPNode rightField = XMPNodeUtils.FindChildNode(rightNode, leftField.GetName(), false);
                        if (rightField == null || !ItemValuesMatch(leftField, rightField))
                        {
                            return(false);
                        }
                    }
                }
                else
                {
                    // Array nodes, see if the "leftNode" values are present in the
                    // "rightNode", ignoring order, duplicates,
                    // and extra values in the rightNode-> The rightNode is the
                    // destination for AppendProperties.
                    System.Diagnostics.Debug.Assert(leftForm.IsArray());
                    for (Iterator il = leftNode.IterateChildren(); il.HasNext();)
                    {
                        XMPNode leftItem = (XMPNode)il.Next();
                        bool    match    = false;
                        for (Iterator ir = rightNode.IterateChildren(); ir.HasNext();)
                        {
                            XMPNode rightItem = (XMPNode)ir.Next();
                            if (ItemValuesMatch(leftItem, rightItem))
                            {
                                match = true;
                                break;
                            }
                        }
                        if (!match)
                        {
                            return(false);
                        }
                    }
                }
            }
            return(true);
        }
 /// <param name="currNode">the node that will be added to the path.</param>
 /// <param name="parentPath">the path up to this node.</param>
 /// <param name="currentIndex">the current array index if an arrey is traversed</param>
 /// <returns>Returns the updated path.</returns>
 protected internal virtual string AccumulatePath(XMPNode currNode, string parentPath, int currentIndex)
 {
     string separator;
     string segmentName;
     if (currNode.GetParent() == null || currNode.GetOptions().IsSchemaNode())
     {
         return null;
     }
     else
     {
         if (currNode.GetParent().GetOptions().IsArray())
         {
             separator = string.Empty;
             segmentName = "[" + currentIndex.ToString() + "]";
         }
         else
         {
             separator = "/";
             segmentName = currNode.GetName();
         }
     }
     if (parentPath == null || parentPath.Length == 0)
     {
         return segmentName;
     }
     else
     {
         if (this._enclosing.GetOptions().IsJustLeafname())
         {
             return !segmentName.StartsWith("?") ? segmentName : Sharpen.Runtime.Substring(segmentName, 1);
         }
         else
         {
             // qualifier
             return parentPath + separator + segmentName;
         }
     }
 }
Beispiel #24
0
        /// <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);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
 /// <summary>Constructor</summary>
 /// <param name="parentNode">the node which children shall be iterated.</param>
 /// <param name="parentPath">the full path of the former node without the leaf node.</param>
 public NodeIteratorChildren(XMPIteratorImpl _enclosing, XMPNode parentNode, string parentPath)
     : base(_enclosing)
 {
     this._enclosing = _enclosing;
     if (parentNode.GetOptions().IsSchemaNode())
     {
         this._enclosing.SetBaseNS(parentNode.GetName());
     }
     this.parentPath = this.AccumulatePath(parentNode, parentPath, 1);
     this.childrenIterator = parentNode.IterateChildren();
 }
 /// <summary>This is setting the value of a leaf node.</summary>
 /// <param name="node">an XMPNode</param>
 /// <param name="value">a value</param>
 internal static void SetNodeValue(XMPNode node, object value)
 {
     string strValue = SerializeNodeValue(value);
     if (!(node.GetOptions().IsQualifier() && XMPConstConstants.XmlLang.Equals(node.GetName())))
     {
         node.SetValue(strValue);
     }
     else
     {
         node.SetValue(Utils.NormalizeLangValue(strValue));
     }
 }
		/// <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);
										}
									}
								}
							}
						}
					}
				}
			}
		}
		/// <summary>
		/// A node can be serialized as RDF-Attribute, if it meets the following conditions:
		/// <ul>
		/// <li>is not array item
		/// <li>don't has qualifier
		/// <li>is no URI
		/// <li>is no composite property
		/// </ul>
		/// </summary>
		/// <param name="node">an XMPNode</param>
		/// <returns>Returns true if the node serialized as RDF-Attribute</returns>
		private bool CanBeRDFAttrProp(XMPNode node)
		{
			return !node.HasQualifier() && !node.GetOptions().IsURI() && !node.GetOptions().IsCompositeProperty() && !XMPConstConstants.ArrayItemName.Equals(node.GetName());
		}