/// <summary>Appends a language item to an alt text array.</summary>
 /// <param name="arrayNode">the language array</param>
 /// <param name="itemLang">the language of the item</param>
 /// <param name="itemValue">the content of the item</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Thrown if a duplicate property is added</exception>
 internal static void AppendLangItem(XMPNode arrayNode, string itemLang, string itemValue)
 {
     XMPNode newItem = new XMPNode(XMPConstConstants.ArrayItemName, itemValue, null);
     XMPNode langQual = new XMPNode(XMPConstConstants.XmlLang, itemLang, null);
     newItem.AddQualifier(langQual);
     if (!XMPConstConstants.XDefault.Equals(langQual.GetValue()))
     {
         arrayNode.AddChild(newItem);
     }
     else
     {
         arrayNode.AddChild(1, newItem);
     }
 }
Beispiel #2
0
        /// <summary>Appends a language item to an alt text array.</summary>
        /// <param name="arrayNode">the language array</param>
        /// <param name="itemLang">the language of the item</param>
        /// <param name="itemValue">the content of the item</param>
        /// <exception cref="Com.Adobe.Xmp.XMPException">Thrown if a duplicate property is added</exception>
        internal static void AppendLangItem(XMPNode arrayNode, string itemLang, string itemValue)
        {
            XMPNode newItem  = new XMPNode(XMPConstConstants.ArrayItemName, itemValue, null);
            XMPNode langQual = new XMPNode(XMPConstConstants.XmlLang, itemLang, null);

            newItem.AddQualifier(langQual);
            if (!XMPConstConstants.XDefault.Equals(langQual.GetValue()))
            {
                arrayNode.AddChild(newItem);
            }
            else
            {
                arrayNode.AddChild(1, newItem);
            }
        }
Beispiel #3
0
 /// <summary>Make sure the x-default item is first.</summary>
 /// <remarks>
 /// Make sure the x-default item is first. Touch up &quot;single value&quot;
 /// arrays that have a default plus one real language. This case should have
 /// the same value for both items. Older Adobe apps were hardwired to only
 /// use the &quot;x-default&quot; item, so we copy that value to the other
 /// item.
 /// </remarks>
 /// <param name="arrayNode">an alt text array node</param>
 internal static void NormalizeLangArray(XMPNode arrayNode)
 {
     if (!arrayNode.GetOptions().IsArrayAltText())
     {
         return;
     }
     // check if node with x-default qual is first place
     for (int i = 2; i <= arrayNode.GetChildrenLength(); i++)
     {
         XMPNode child = arrayNode.GetChild(i);
         if (child.HasQualifier() && XMPConstConstants.XDefault.Equals(child.GetQualifier(1).GetValue()))
         {
             // move node to first place
             try
             {
                 arrayNode.RemoveChild(i);
                 arrayNode.AddChild(1, child);
             }
             catch (XMPException)
             {
                 // cannot occur, because same child is removed before
                 System.Diagnostics.Debug.Assert(false);
             }
             if (i == 2)
             {
                 arrayNode.GetChild(2).SetValue(child.GetValue());
             }
             break;
         }
     }
 }
Beispiel #4
0
        /// <summary>Find or create a schema node if <code>createNodes</code> is true.</summary>
        /// <param name="tree">the root of the xmp tree.</param>
        /// <param name="namespaceURI">a namespace</param>
        /// <param name="suggestedPrefix">If a prefix is suggested, the namespace is allowed to be registered.</param>
        /// <param name="createNodes">
        /// a flag indicating if the node shall be created if not found.
        /// <em>Note:</em> The namespace must be registered prior to this call.
        /// </param>
        /// <returns>
        /// Returns the schema node if found, <code>null</code> otherwise.
        /// Note: If <code>createNodes</code> is <code>true</code>, it is <b>always</b>
        /// returned a valid node.
        /// </returns>
        /// <exception cref="Com.Adobe.Xmp.XMPException">
        /// An exception is only thrown if an error occurred, not if a
        /// node was not found.
        /// </exception>
        internal static XMPNode FindSchemaNode(XMPNode tree, string namespaceURI, string suggestedPrefix, bool createNodes)
        {
            System.Diagnostics.Debug.Assert(tree.GetParent() == null);
            // make sure that its the root
            XMPNode schemaNode = tree.FindChildByName(namespaceURI);

            if (schemaNode == null && createNodes)
            {
                schemaNode = new XMPNode(namespaceURI, new PropertyOptions().SetSchemaNode(true));
                schemaNode.SetImplicit(true);
                // only previously registered schema namespaces are allowed in the XMP tree.
                string prefix = XMPMetaFactory.GetSchemaRegistry().GetNamespacePrefix(namespaceURI);
                if (prefix == null)
                {
                    if (suggestedPrefix != null && suggestedPrefix.Length != 0)
                    {
                        prefix = XMPMetaFactory.GetSchemaRegistry().RegisterNamespace(namespaceURI, suggestedPrefix);
                    }
                    else
                    {
                        throw new XMPException("Unregistered schema namespace URI", XMPErrorConstants.Badschema);
                    }
                }
                schemaNode.SetValue(prefix);
                tree.AddChild(schemaNode);
            }
            return(schemaNode);
        }
Beispiel #5
0
        /// <summary>Find or create a child node under a given parent node.</summary>
        /// <remarks>
        /// Find or create a child node under a given parent node. If the parent node is no
        /// Returns the found or created child node.
        /// </remarks>
        /// <param name="parent">the parent node</param>
        /// <param name="childName">the node name to find</param>
        /// <param name="createNodes">flag, if new nodes shall be created.</param>
        /// <returns>Returns the found or created node or <code>null</code>.</returns>
        /// <exception cref="Com.Adobe.Xmp.XMPException">Thrown if</exception>
        internal static XMPNode FindChildNode(XMPNode parent, string childName, bool createNodes)
        {
            if (!parent.GetOptions().IsSchemaNode() && !parent.GetOptions().IsStruct())
            {
                if (!parent.IsImplicit())
                {
                    throw new XMPException("Named children only allowed for schemas and structs", XMPErrorConstants.Badxpath);
                }
                else
                {
                    if (parent.GetOptions().IsArray())
                    {
                        throw new XMPException("Named children not allowed for arrays", XMPErrorConstants.Badxpath);
                    }
                    else
                    {
                        if (createNodes)
                        {
                            parent.GetOptions().SetStruct(true);
                        }
                    }
                }
            }
            XMPNode childNode = parent.FindChildByName(childName);

            if (childNode == null && createNodes)
            {
                PropertyOptions options = new PropertyOptions();
                childNode = new XMPNode(childName, options);
                childNode.SetImplicit(true);
                parent.AddChild(childNode);
            }
            System.Diagnostics.Debug.Assert(childNode != null || !createNodes);
            return(childNode);
        }
Beispiel #6
0
        /// <param name="arrayNode">an array node</param>
        /// <param name="segment">the segment containing the array index</param>
        /// <param name="createNodes">flag if new nodes are allowed to be created.</param>
        /// <returns>Returns the index or index = -1 if not found</returns>
        /// <exception cref="Com.Adobe.Xmp.XMPException">Throws Exceptions</exception>
        private static int FindIndexedItem(XMPNode arrayNode, string segment, bool createNodes)
        {
            int index = 0;

            try
            {
                segment = Sharpen.Runtime.Substring(segment, 1, segment.Length - 1);
                index   = System.Convert.ToInt32(segment);
                if (index < 1)
                {
                    throw new XMPException("Array index must be larger than zero", XMPErrorConstants.Badxpath);
                }
            }
            catch (FormatException)
            {
                throw new XMPException("Array index not digits.", XMPErrorConstants.Badxpath);
            }
            if (createNodes && index == arrayNode.GetChildrenLength() + 1)
            {
                // Append a new last + 1 node.
                XMPNode newItem = new XMPNode(XMPConstConstants.ArrayItemName, null);
                newItem.SetImplicit(true);
                arrayNode.AddChild(newItem);
            }
            return(index);
        }
 /// <summary>Moves an alias node of array form to another schema into an array</summary>
 /// <param name="propertyIt">the property iterator of the old schema (used to delete the property)</param>
 /// <param name="childNode">the node to be moved</param>
 /// <param name="baseArray">the base array for the array item</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards XMP errors</exception>
 private static void TransplantArrayItemAlias(Iterator propertyIt, XMPNode childNode, XMPNode baseArray)
 {
     if (baseArray.GetOptions().IsArrayAltText())
     {
         if (childNode.GetOptions().GetHasLanguage())
         {
             throw new XMPException("Alias to x-default already has a language qualifier", XMPErrorConstants.Badxmp);
         }
         XMPNode langQual = new XMPNode(XMPConstConstants.XmlLang, XMPConstConstants.XDefault, null);
         childNode.AddQualifier(langQual);
     }
     propertyIt.Remove();
     childNode.SetName(XMPConstConstants.ArrayItemName);
     baseArray.AddChild(childNode);
 }
 /// <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 #9
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);
     }
 }
		// -------------------------------------------------------------------------------------
		// private
		/// <summary>Locate or create the item node and set the value.</summary>
		/// <remarks>
		/// Locate or create the item node and set the value. Note the index
		/// parameter is one-based! The index can be in the range [1..size + 1] or
		/// "last()", normalize it and check the insert flags. The order of the
		/// normalization checks is important. If the array is empty we end up with
		/// an index and location to set item size + 1.
		/// </remarks>
		/// <param name="arrayNode">an array node</param>
		/// <param name="itemIndex">the index where to insert the item</param>
		/// <param name="itemValue">the item value</param>
		/// <param name="itemOptions">the options for the new item</param>
		/// <param name="insert">insert oder overwrite at index position?</param>
		/// <exception cref="Com.Adobe.Xmp.XMPException"/>
		private void DoSetArrayItem(XMPNode arrayNode, int itemIndex, string itemValue, PropertyOptions itemOptions, bool insert)
		{
			XMPNode itemNode = new XMPNode(XMPConstConstants.ArrayItemName, null);
			itemOptions = XMPNodeUtils.VerifySetOptions(itemOptions, itemValue);
			// in insert mode the index after the last is allowed,
			// even ArrayLastItem points to the index *after* the last.
			int maxIndex = insert ? arrayNode.GetChildrenLength() + 1 : arrayNode.GetChildrenLength();
			if (itemIndex == XMPConstConstants.ArrayLastItem)
			{
				itemIndex = maxIndex;
			}
			if (1 <= itemIndex && itemIndex <= maxIndex)
			{
				if (!insert)
				{
					arrayNode.RemoveChild(itemIndex);
				}
				arrayNode.AddChild(itemIndex, itemNode);
				SetNode(itemNode, itemValue, itemOptions, false);
			}
			else
			{
				throw new XMPException("Array index out of bounds", XMPErrorConstants.Badindex);
			}
		}
        /// <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);
            }
        }
 /// <summary>Find or create a schema node if <code>createNodes</code> is true.</summary>
 /// <param name="tree">the root of the xmp tree.</param>
 /// <param name="namespaceURI">a namespace</param>
 /// <param name="suggestedPrefix">If a prefix is suggested, the namespace is allowed to be registered.</param>
 /// <param name="createNodes">
 /// a flag indicating if the node shall be created if not found.
 /// <em>Note:</em> The namespace must be registered prior to this call.
 /// </param>
 /// <returns>
 /// Returns the schema node if found, <code>null</code> otherwise.
 /// Note: If <code>createNodes</code> is <code>true</code>, it is <b>always</b>
 /// returned a valid node.
 /// </returns>
 /// <exception cref="Com.Adobe.Xmp.XMPException">
 /// An exception is only thrown if an error occurred, not if a
 /// node was not found.
 /// </exception>
 internal static XMPNode FindSchemaNode(XMPNode tree, string namespaceURI, string suggestedPrefix, bool createNodes)
 {
     System.Diagnostics.Debug.Assert(tree.GetParent() == null);
     // make sure that its the root
     XMPNode schemaNode = tree.FindChildByName(namespaceURI);
     if (schemaNode == null && createNodes)
     {
         schemaNode = new XMPNode(namespaceURI, new PropertyOptions().SetSchemaNode(true));
         schemaNode.SetImplicit(true);
         // only previously registered schema namespaces are allowed in the XMP tree.
         string prefix = XMPMetaFactory.GetSchemaRegistry().GetNamespacePrefix(namespaceURI);
         if (prefix == null)
         {
             if (suggestedPrefix != null && suggestedPrefix.Length != 0)
             {
                 prefix = XMPMetaFactory.GetSchemaRegistry().RegisterNamespace(namespaceURI, suggestedPrefix);
             }
             else
             {
                 throw new XMPException("Unregistered schema namespace URI", XMPErrorConstants.Badschema);
             }
         }
         schemaNode.SetValue(prefix);
         tree.AddChild(schemaNode);
     }
     return schemaNode;
 }
 /// <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);
     }
 }
 /// <summary>Find or create a child node under a given parent node.</summary>
 /// <remarks>
 /// Find or create a child node under a given parent node. If the parent node is no
 /// Returns the found or created child node.
 /// </remarks>
 /// <param name="parent">the parent node</param>
 /// <param name="childName">the node name to find</param>
 /// <param name="createNodes">flag, if new nodes shall be created.</param>
 /// <returns>Returns the found or created node or <code>null</code>.</returns>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Thrown if</exception>
 internal static XMPNode FindChildNode(XMPNode parent, string childName, bool createNodes)
 {
     if (!parent.GetOptions().IsSchemaNode() && !parent.GetOptions().IsStruct())
     {
         if (!parent.IsImplicit())
         {
             throw new XMPException("Named children only allowed for schemas and structs", XMPErrorConstants.Badxpath);
         }
         else
         {
             if (parent.GetOptions().IsArray())
             {
                 throw new XMPException("Named children not allowed for arrays", XMPErrorConstants.Badxpath);
             }
             else
             {
                 if (createNodes)
                 {
                     parent.GetOptions().SetStruct(true);
                 }
             }
         }
     }
     XMPNode childNode = parent.FindChildByName(childName);
     if (childNode == null && createNodes)
     {
         PropertyOptions options = new PropertyOptions();
         childNode = new XMPNode(childName, options);
         childNode.SetImplicit(true);
         parent.AddChild(childNode);
     }
     System.Diagnostics.Debug.Assert(childNode != null || !createNodes);
     return childNode;
 }
 /// <summary>Moves an alias node of array form to another schema into an array</summary>
 /// <param name="propertyIt">the property iterator of the old schema (used to delete the property)</param>
 /// <param name="childNode">the node to be moved</param>
 /// <param name="baseArray">the base array for the array item</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards XMP errors</exception>
 private static void TransplantArrayItemAlias(Iterator propertyIt, XMPNode childNode, XMPNode baseArray)
 {
     if (baseArray.GetOptions().IsArrayAltText())
     {
         if (childNode.GetOptions().GetHasLanguage())
         {
             throw new XMPException("Alias to x-default already has a language qualifier", XMPErrorConstants.Badxmp);
         }
         XMPNode langQual = new XMPNode(XMPConstConstants.XmlLang, XMPConstConstants.XDefault, null);
         childNode.AddQualifier(langQual);
     }
     propertyIt.Remove();
     childNode.SetName(XMPConstConstants.ArrayItemName);
     baseArray.AddChild(childNode);
 }
 /// <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);
                 }
             }
         }
     }
 }
 /// <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;
     }
 }
 /// <param name="arrayNode">an array node</param>
 /// <param name="segment">the segment containing the array index</param>
 /// <param name="createNodes">flag if new nodes are allowed to be created.</param>
 /// <returns>Returns the index or index = -1 if not found</returns>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Throws Exceptions</exception>
 private static int FindIndexedItem(XMPNode arrayNode, string segment, bool createNodes)
 {
     int index = 0;
     try
     {
         segment = Sharpen.Runtime.Substring(segment, 1, segment.Length - 1);
         index = System.Convert.ToInt32(segment);
         if (index < 1)
         {
             throw new XMPException("Array index must be larger than zero", XMPErrorConstants.Badxpath);
         }
     }
     catch (FormatException)
     {
         throw new XMPException("Array index not digits.", XMPErrorConstants.Badxpath);
     }
     if (createNodes && index == arrayNode.GetChildrenLength() + 1)
     {
         // Append a new last + 1 node.
         XMPNode newItem = new XMPNode(XMPConstConstants.ArrayItemName, null);
         newItem.SetImplicit(true);
         arrayNode.AddChild(newItem);
     }
     return index;
 }
Beispiel #19
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);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
		/// <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>Make sure the x-default item is first.</summary>
 /// <remarks>
 /// Make sure the x-default item is first. Touch up &quot;single value&quot;
 /// arrays that have a default plus one real language. This case should have
 /// the same value for both items. Older Adobe apps were hardwired to only
 /// use the &quot;x-default&quot; item, so we copy that value to the other
 /// item.
 /// </remarks>
 /// <param name="arrayNode">an alt text array node</param>
 internal static void NormalizeLangArray(XMPNode arrayNode)
 {
     if (!arrayNode.GetOptions().IsArrayAltText())
     {
         return;
     }
     // check if node with x-default qual is first place
     for (int i = 2; i <= arrayNode.GetChildrenLength(); i++)
     {
         XMPNode child = arrayNode.GetChild(i);
         if (child.HasQualifier() && XMPConstConstants.XDefault.Equals(child.GetQualifier(1).GetValue()))
         {
             // move node to first place
             try
             {
                 arrayNode.RemoveChild(i);
                 arrayNode.AddChild(1, child);
             }
             catch (XMPException)
             {
                 // cannot occur, because same child is removed before
                 System.Diagnostics.Debug.Assert(false);
             }
             if (i == 2)
             {
                 arrayNode.GetChild(2).SetValue(child.GetValue());
             }
             break;
         }
     }
 }
Beispiel #22
0
        /// <summary>
        /// see
        /// <see cref="Com.Adobe.Xmp.XMPUtils.SeparateArrayItems(Com.Adobe.Xmp.XMPMeta, string, string, string, Com.Adobe.Xmp.Options.PropertyOptions, bool)"/>
        /// </summary>
        /// <param name="xmp">The XMP object containing the array to be updated.</param>
        /// <param name="schemaNS">
        /// The schema namespace URI for the array. Must not be null or
        /// the empty string.
        /// </param>
        /// <param name="arrayName">
        /// The name of the array. May be a general path expression, must
        /// not be null or the empty string. Each item in the array must
        /// be a simple string value.
        /// </param>
        /// <param name="catedStr">The string to be separated into the array items.</param>
        /// <param name="arrayOptions">Option flags to control the separation.</param>
        /// <param name="preserveCommas">Flag if commas shall be preserved</param>
        /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards the Exceptions from the metadata processing</exception>
        public static void SeparateArrayItems(XMPMeta xmp, string schemaNS, string arrayName, string catedStr, PropertyOptions arrayOptions, bool preserveCommas)
        {
            ParameterAsserts.AssertSchemaNS(schemaNS);
            ParameterAsserts.AssertArrayName(arrayName);
            if (catedStr == null)
            {
                throw new XMPException("Parameter must not be null", XMPErrorConstants.Badparam);
            }
            ParameterAsserts.AssertImplementation(xmp);
            XMPMetaImpl xmpImpl = (XMPMetaImpl)xmp;
            // Keep a zero value, has special meaning below.
            XMPNode arrayNode = SeparateFindCreateArray(schemaNS, arrayName, arrayOptions, xmpImpl);
            // Extract the item values one at a time, until the whole input string is done.
            string itemValue;
            int    itemStart;
            int    itemEnd;
            int    nextKind = UckNormal;
            int    charKind = UckNormal;
            char   ch       = (char)0;
            char   nextChar = (char)0;

            itemEnd = 0;
            int endPos = catedStr.Length;

            while (itemEnd < endPos)
            {
                // Skip any leading spaces and separation characters. Always skip commas here.
                // They can be kept when within a value, but not when alone between values.
                for (itemStart = itemEnd; itemStart < endPos; itemStart++)
                {
                    ch       = catedStr[itemStart];
                    charKind = ClassifyCharacter(ch);
                    if (charKind == UckNormal || charKind == UckQuote)
                    {
                        break;
                    }
                }
                if (itemStart >= endPos)
                {
                    break;
                }
                if (charKind != UckQuote)
                {
                    // This is not a quoted value. Scan for the end, create an array
                    // item from the substring.
                    for (itemEnd = itemStart; itemEnd < endPos; itemEnd++)
                    {
                        ch       = catedStr[itemEnd];
                        charKind = ClassifyCharacter(ch);
                        if (charKind == UckNormal || charKind == UckQuote || (charKind == UckComma && preserveCommas))
                        {
                            continue;
                        }
                        else
                        {
                            if (charKind != UckSpace)
                            {
                                break;
                            }
                            else
                            {
                                if ((itemEnd + 1) < endPos)
                                {
                                    ch       = catedStr[itemEnd + 1];
                                    nextKind = ClassifyCharacter(ch);
                                    if (nextKind == UckNormal || nextKind == UckQuote || (nextKind == UckComma && preserveCommas))
                                    {
                                        continue;
                                    }
                                }
                            }
                        }
                        // Anything left?
                        break;
                    }
                    // Have multiple spaces, or a space followed by a
                    // separator.
                    itemValue = Sharpen.Runtime.Substring(catedStr, itemStart, itemEnd);
                }
                else
                {
                    // Accumulate quoted values into a local string, undoubling
                    // internal quotes that
                    // match the surrounding quotes. Do not undouble "unmatching"
                    // quotes.
                    char openQuote  = ch;
                    char closeQuote = GetClosingQuote(openQuote);
                    itemStart++;
                    // Skip the opening quote;
                    itemValue = string.Empty;
                    for (itemEnd = itemStart; itemEnd < endPos; itemEnd++)
                    {
                        ch       = catedStr[itemEnd];
                        charKind = ClassifyCharacter(ch);
                        if (charKind != UckQuote || !IsSurroundingQuote(ch, openQuote, closeQuote))
                        {
                            // This is not a matching quote, just append it to the
                            // item value.
                            itemValue += ch;
                        }
                        else
                        {
                            // This is a "matching" quote. Is it doubled, or the
                            // final closing quote?
                            // Tolerate various edge cases like undoubled opening
                            // (non-closing) quotes,
                            // or end of input.
                            if ((itemEnd + 1) < endPos)
                            {
                                nextChar = catedStr[itemEnd + 1];
                                nextKind = ClassifyCharacter(nextChar);
                            }
                            else
                            {
                                nextKind = UckSemicolon;
                                nextChar = (char)unchecked ((int)(0x3B));
                            }
                            if (ch == nextChar)
                            {
                                // This is doubled, copy it and skip the double.
                                itemValue += ch;
                                // Loop will add in charSize.
                                itemEnd++;
                            }
                            else
                            {
                                if (!IsClosingingQuote(ch, openQuote, closeQuote))
                                {
                                    // This is an undoubled, non-closing quote, copy it.
                                    itemValue += ch;
                                }
                                else
                                {
                                    // This is an undoubled closing quote, skip it and
                                    // exit the loop.
                                    itemEnd++;
                                    break;
                                }
                            }
                        }
                    }
                }
                // Add the separated item to the array.
                // Keep a matching old value in case it had separators.
                int foundIndex = -1;
                for (int oldChild = 1; oldChild <= arrayNode.GetChildrenLength(); oldChild++)
                {
                    if (itemValue.Equals(arrayNode.GetChild(oldChild).GetValue()))
                    {
                        foundIndex = oldChild;
                        break;
                    }
                }
                XMPNode newItem = null;
                if (foundIndex < 0)
                {
                    newItem = new XMPNode(XMPConstConstants.ArrayItemName, itemValue, null);
                    arrayNode.AddChild(newItem);
                }
            }
        }
 /// <summary>Adds a child node.</summary>
 /// <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="value">Node value</param>
 /// <param name="isTopLevel">Flag if the node is a top-level node</param>
 /// <returns>Returns the newly created child node.</returns>
 /// <exception cref="Com.Adobe.Xmp.XMPException">thown on parsing errors</exception>
 private static XMPNode AddChildNode(XMPMetaImpl xmp, XMPNode xmpParent, XmlNode xmlNode, string value, bool isTopLevel)
 {
     XMPSchemaRegistry registry = XMPMetaFactory.GetSchemaRegistry();
     string @namespace = xmlNode.NamespaceURI;
     string childName;
     if (@namespace != null)
     {
         if (XMPConstConstants.NsDcDeprecated.Equals(@namespace))
         {
             // Fix a legacy DC namespace
             @namespace = XMPConstConstants.NsDc;
         }
         string prefix = registry.GetNamespacePrefix(@namespace);
         if (prefix == null)
         {
             prefix = xmlNode.Prefix != null ? xmlNode.Prefix : DefaultPrefix;
             prefix = registry.RegisterNamespace(@namespace, prefix);
         }
         childName = prefix + xmlNode.LocalName;
     }
     else
     {
         throw new XMPException("XML namespace required for all elements and attributes", XMPErrorConstants.Badrdf);
     }
     // create schema node if not already there
     PropertyOptions childOptions = new PropertyOptions();
     bool isAlias = false;
     if (isTopLevel)
     {
         // Lookup the schema node, adjust the XMP parent pointer.
         // Incoming parent must be the tree root.
         XMPNode schemaNode = XMPNodeUtils.FindSchemaNode(xmp.GetRoot(), @namespace, DefaultPrefix, true);
         schemaNode.SetImplicit(false);
         // Clear the implicit node bit.
         // need runtime check for proper 32 bit code.
         xmpParent = schemaNode;
         // If this is an alias set the alias flag in the node
         // and the hasAliases flag in the tree.
         if (registry.FindAlias(childName) != null)
         {
             isAlias = true;
             xmp.GetRoot().SetHasAliases(true);
             schemaNode.SetHasAliases(true);
         }
     }
     // Make sure that this is not a duplicate of a named node.
     bool isArrayItem = "rdf:li".Equals(childName);
     bool isValueNode = "rdf:value".Equals(childName);
     // Create XMP node and so some checks
     XMPNode newChild = new XMPNode(childName, value, childOptions);
     newChild.SetAlias(isAlias);
     // Add the new child to the XMP parent node, a value node first.
     if (!isValueNode)
     {
         xmpParent.AddChild(newChild);
     }
     else
     {
         xmpParent.AddChild(1, newChild);
     }
     if (isValueNode)
     {
         if (isTopLevel || !xmpParent.GetOptions().IsStruct())
         {
             throw new XMPException("Misplaced rdf:value element", XMPErrorConstants.Badrdf);
         }
         xmpParent.SetHasValueChild(true);
     }
     if (isArrayItem)
     {
         if (!xmpParent.GetOptions().IsArray())
         {
             throw new XMPException("Misplaced rdf:li element", XMPErrorConstants.Badrdf);
         }
         newChild.SetName(XMPConstConstants.ArrayItemName);
     }
     return newChild;
 }