AddChild() public method

Adds a node as child to this node.
public AddChild ( XmpNode node ) : void
node XmpNode an XMPNode
return void
Example #1
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="XmpException"> Thrown if a duplicate property is added </exception>
        internal static void AppendLangItem(XmpNode arrayNode, string itemLang, string itemValue)
        {
            XmpNode newItem  = new XmpNode(ARRAY_ITEM_NAME, itemValue, null);
            XmpNode langQual = new XmpNode(XML_LANG, itemLang, null);

            newItem.AddQualifier(langQual);

            if (!X_DEFAULT.Equals(langQual.Value))
            {
                arrayNode.AddChild(newItem);
            }
            else
            {
                arrayNode.AddChild(1, newItem);
            }
        }
Example #2
0
        // -------------------------------------------------------------------------------------
        // private


        /// <summary>
        /// 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.
        /// </summary>
        /// <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="XmpException"> </exception>
        private void DoSetArrayItem(XmpNode arrayNode, int itemIndex, string itemValue, PropertyOptions itemOptions,
                                    bool insert)
        {
            XmpNode itemNode = new XmpNode(ARRAY_ITEM_NAME, null);

            itemOptions = XmpNodeUtils.VerifySetOptions(itemOptions, itemValue);

            // in insert mode the index after the last is allowed,
            // even ARRAY_LAST_ITEM points to the index *after* the last.
            int maxIndex = insert ? arrayNode.ChildrenLength + 1 : arrayNode.ChildrenLength;

            if (itemIndex == ARRAY_LAST_ITEM)
            {
                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", XmpError.BADINDEX);
            }
        }
Example #3
0
        /// <summary>
        /// Find or create a child node under a given parent node. If the parent node is no
        /// Returns the found or created child node.
        /// </summary>
        /// <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="XmpException"> Thrown if  </exception>
        internal static XmpNode FindChildNode(XmpNode parent, string childName, bool createNodes)
        {
            if (!parent.Options.SchemaNode && !parent.Options.Struct)
            {
                if (!parent.Implicit)
                {
                    throw new XmpException("Named children only allowed for schemas and structs",
                                           XmpError.BADXPATH);
                }
                if (parent.Options.Array)
                {
                    throw new XmpException("Named children not allowed for arrays", XmpError.BADXPATH);
                }
                if (createNodes)
                {
                    parent.Options.Struct = true;
                }
            }

            XmpNode childNode = parent.FindChildByName(childName);

            if (childNode == null && createNodes)
            {
                PropertyOptions options = new PropertyOptions();
                childNode          = new XmpNode(childName, options);
                childNode.Implicit = true;
                parent.AddChild(childNode);
            }

            Debug.Assert(childNode != null || !createNodes);

            return(childNode);
        }
Example #4
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="XmpException"> Throws Exceptions </exception>
        private static int FindIndexedItem(XmpNode arrayNode, string segment, bool createNodes)
        {
            int index;

            try {
                segment = segment.Substring(1, segment.Length - 1 - 1);
                index   = Convert.ToInt32(segment);
                if (index < 1)
                {
                    throw new XmpException("Array index must be larger than zero", XmpError.BADXPATH);
                }
            }
            catch (FormatException) {
                throw new XmpException("Array index not digits.", XmpError.BADXPATH);
            }

            if (createNodes && index == arrayNode.ChildrenLength + 1)
            {
                // Append a new last + 1 node.
                XmpNode newItem = new XmpNode(ARRAY_ITEM_NAME, null);
                newItem.Implicit = true;
                arrayNode.AddChild(newItem);
            }

            return(index);
        }
Example #5
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="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)
        {
            Debug.Assert(tree.Parent == null); // make sure that its the root
            XmpNode schemaNode = tree.FindChildByName(namespaceUri);

            if (schemaNode == null && createNodes)
            {
                PropertyOptions propertyOptions = new PropertyOptions();
                propertyOptions.SchemaNode = true;
                schemaNode          = new XmpNode(namespaceUri, propertyOptions);
                schemaNode.Implicit = true;

                // only previously registered schema namespaces are allowed in the XMP tree.
                string prefix = XMPMetaFactory.SchemaRegistry.GetNamespacePrefix(namespaceUri);
                if (prefix == null)
                {
                    if (!String.IsNullOrEmpty(suggestedPrefix))
                    {
                        prefix = XMPMetaFactory.SchemaRegistry.RegisterNamespace(namespaceUri, suggestedPrefix);
                    }
                    else
                    {
                        throw new XmpException("Unregistered schema namespace URI", XmpError.BADSCHEMA);
                    }
                }

                schemaNode.Value = prefix;

                tree.AddChild(schemaNode);
            }

            return(schemaNode);
        }
Example #6
0
        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="arrayNode">
        ///            an alt text array node </param>
        internal static void NormalizeLangArray(XmpNode arrayNode)
        {
            if (!arrayNode.Options.ArrayAltText)
            {
                return;
            }

            // check if node with x-default qual is first place
            for (int i = 2; i <= arrayNode.ChildrenLength; i++)
            {
                XmpNode child = arrayNode.GetChild(i);
                if (child.HasQualifier() && X_DEFAULT.Equals(child.GetQualifier(1).Value))
                {
                    // move node to first place
                    try {
                        arrayNode.RemoveChild(i);
                        arrayNode.AddChild(1, child);
                    }
                    catch (XmpException) {
                        // cannot occur, because same child is removed before
                        Debug.Assert(false);
                    }

                    if (i == 2)
                    {
                        arrayNode.GetChild(2).Value = child.Value;
                    }
                    break;
                }
            }
        }
Example #7
0
        /// <summary>
        /// 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.
        /// </summary>
        /// <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="XmpException">  </exception>
        private static int LookupQualSelector(XmpNode arrayNode, string qualName, string qualValue, uint aliasForm)
        {
            if (XML_LANG.Equals(qualName))
            {
                qualValue = Utils.NormalizeLangValue(qualValue);
                int index = LookupLanguageItem(arrayNode, qualValue);
                if (index < 0 && (aliasForm & AliasOptions.PROP_ARRAY_ALT_TEXT) > 0)
                {
                    XmpNode langNode = new XmpNode(ARRAY_ITEM_NAME, null);
                    XmpNode xdefault = new XmpNode(XML_LANG, X_DEFAULT, null);
                    langNode.AddQualifier(xdefault);
                    arrayNode.AddChild(1, langNode);
                    return(1);
                }
                return(index);
            }
            for (int index = 1; index < arrayNode.ChildrenLength; index++)
            {
                XmpNode currItem = arrayNode.GetChild(index);

                for (IEnumerator it = currItem.IterateQualifier(); it.MoveNext();)
                {
                    XmpNode qualifier = (XmpNode)it.Current;
                    if (qualifier != null && qualName.Equals(qualifier.Name) && qualValue.Equals(qualifier.Value))
                    {
                        return(index);
                    }
                }
            }
            return(-1);
        }
Example #8
0
 /// <summary>
 /// Performs a <b>deep clone</b> of the complete subtree (children and
 /// qualifier )into and add it to the destination node.
 /// </summary>
 /// <param name="destination"> the node to add the cloned subtree </param>
 public virtual void CloneSubtree(XmpNode destination)
 {
     try {
         foreach (XmpNode node in Children)
         {
             destination.AddChild((XmpNode)node.Clone());
         }
         foreach (XmpNode node in Qualifier)
         {
             destination.AddQualifier((XmpNode)node.Clone());
         }
     }
     catch (XmpException) {
         // cannot happen (duplicate childs/quals do not exist in this node)
         Debug.Assert(false);
     }
 }
Example #9
0
        /// <summary>
        /// Moves an alias node of array form to another schema into an array </summary>
        /// <param name="childNode"> the node to be moved </param>
        /// <param name="baseArray"> the base array for the array item </param>
        /// <exception cref="XmpException"> Forwards XMP errors </exception>
        private static void TransplantArrayItemAlias(XmpNode childNode, XmpNode baseArray)
        {
            if (baseArray.Options.ArrayAltText)
            {
                if (childNode.Options.HasLanguage)
                {
                    throw new XmpException("Alias to x-default already has a language qualifier",
                                           XmpError.BADXMP);
                }

                XmpNode langQual = new XmpNode(XmpConst.XML_LANG, XmpConst.X_DEFAULT, null);
                childNode.AddQualifier(langQual);
            }

            childNode.Name = XmpConst.ARRAY_ITEM_NAME;
            baseArray.AddChild(childNode);
        }
Example #10
0
        /// <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. <br>
        /// The <code>xml:lang</code> attribute was dropped from an
        /// <code>alt-text</code> item if the language was <code>x-default</code>.
        /// </summary>
        /// <param name="dcSchema"> the DC schema node </param>
        /// <exception cref="XmpException"> Thrown if normalization fails </exception>
        private static void NormalizeDcArrays(XmpNode dcSchema)
        {
            for (int i = 1; i <= dcSchema.ChildrenLength; i++)
            {
                XmpNode currProp = dcSchema.GetChild(i);

                PropertyOptions arrayForm = (PropertyOptions)_dcArrayForms[currProp.Name];
                if (arrayForm == null)
                {
                    continue;
                }
                if (currProp.Options.Simple)
                {
                    // create a new array and add the current property as child,
                    // if it was formerly simple
                    XmpNode newArray = new XmpNode(currProp.Name, arrayForm);
                    currProp.Name = XmpConst.ARRAY_ITEM_NAME;
                    newArray.AddChild(currProp);
                    dcSchema.ReplaceChild(i, newArray);

                    // fix language alternatives
                    if (arrayForm.ArrayAltText && !currProp.Options.HasLanguage)
                    {
                        XmpNode newLang = new XmpNode(XmpConst.XML_LANG, XmpConst.X_DEFAULT, null);
                        currProp.AddQualifier(newLang);
                    }
                }
                else
                {
                    // clear array options and add corrected array form if it has been an array before
                    currProp.Options.SetOption(
                        PropertyOptions.ARRAY | PropertyOptions.ARRAY_ORDERED | PropertyOptions.ARRAY_ALTERNATE |
                        PropertyOptions.ARRAY_ALT_TEXT, false);
                    currProp.Options.MergeWith(arrayForm);

                    if (arrayForm.ArrayAltText)
                    {
                        // applying for "dc:description", "dc:rights", "dc:title"
                        RepairAltText(currProp);
                    }
                }
            }
        }
Example #11
0
 /// <summary>
 /// Performs a <b>deep clone</b> of the complete subtree (children and
 /// qualifier )into and add it to the destination node.
 /// </summary>
 /// <param name="destination"> the node to add the cloned subtree </param>
 public virtual void CloneSubtree(XmpNode destination) {
     try {
         foreach (XmpNode node in Children) {
             destination.AddChild((XmpNode) node.Clone());
         }
         foreach (XmpNode node in Qualifier) {
             destination.AddQualifier((XmpNode) node.Clone());
         }
     }
     catch (XmpException) {
         // cannot happen (duplicate childs/quals do not exist in this node)
         Debug.Assert(false);
     }
 }
Example #12
0
        /// <summary>
        /// The parent is an RDF pseudo-struct containing an rdf:value field. Fix the
        /// XMP data model. The rdf:value node must be the first child, the other
        /// children are qualifiers. The form, value, and children of the rdf:value
        /// node are the real ones. The rdf:value node's qualifiers must be added to
        /// the others.
        /// </summary>
        /// <param name="xmpParent"> the parent xmp node </param>
        /// <exception cref="XmpException"> thown on parsing errors </exception>
        private static void FixupQualifiedNode(XmpNode xmpParent) {
            Debug.Assert(xmpParent.Options.Struct && xmpParent.HasChildren());

            XmpNode valueNode = xmpParent.GetChild(1);
            Debug.Assert("rdf:value".Equals(valueNode.Name));

            // Move the qualifiers on the value node to the parent. 
            // Make sure an xml:lang qualifier stays at the front.
            // Check for duplicate names between the value node's qualifiers and the parent's children. 
            // The parent's children are about to become qualifiers. Check here, between the groups. 
            // Intra-group duplicates are caught by XMPNode#addChild(...).
            if (valueNode.Options.HasLanguage) {
                if (xmpParent.Options.HasLanguage) {
                    throw new XmpException("Redundant xml:lang for rdf:value element", XmpError.BADXMP);
                }
                XmpNode langQual = valueNode.GetQualifier(1);
                valueNode.RemoveQualifier(langQual);
                xmpParent.AddQualifier(langQual);
            }

            // Start the remaining copy after the xml:lang qualifier.		
            for (int i = 1; i <= valueNode.QualifierLength; i++) {
                XmpNode qualifier = valueNode.GetQualifier(i);
                xmpParent.AddQualifier(qualifier);
            }


            // Change the parent's other children into qualifiers. 
            // This loop starts at 1, child 0 is the rdf:value node.
            for (int i = 2; i <= xmpParent.ChildrenLength; i++) {
                XmpNode qualifier = xmpParent.GetChild(i);
                xmpParent.AddQualifier(qualifier);
            }

            // Move the options and value last, other checks need the parent's original options. 
            // Move the value node's children to be the parent's children.
            Debug.Assert(xmpParent.Options.Struct || xmpParent.HasValueChild);

            xmpParent.HasValueChild = false;
            xmpParent.Options.Struct = false;
            xmpParent.Options.MergeWith(valueNode.Options);
            xmpParent.Value = valueNode.Value;

            xmpParent.RemoveChildren();
            for (IEnumerator it = valueNode.IterateChildren(); it.MoveNext();) {
                XmpNode child = (XmpNode) it.Current;
                xmpParent.AddChild(child);
            }
        }
Example #13
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="XmpException"> Thrown if a duplicate property is added </exception>
        internal static void AppendLangItem(XmpNode arrayNode, string itemLang, string itemValue) {
            XmpNode newItem = new XmpNode(ARRAY_ITEM_NAME, itemValue, null);
            XmpNode langQual = new XmpNode(XML_LANG, itemLang, null);
            newItem.AddQualifier(langQual);

            if (!X_DEFAULT.Equals(langQual.Value)) {
                arrayNode.AddChild(newItem);
            }
            else {
                arrayNode.AddChild(1, newItem);
            }
        }
Example #14
0
        /// <seealso cref= XMPUtilsImpl#appendProperties(XMPMeta, XMPMeta, boolean, boolean, boolean) </seealso>
        /// <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="XmpException"> </exception>
        private static void AppendSubtree(XmpMetaImpl destXmp, XmpNode sourceNode, XmpNode destParent,
                                          bool replaceOldValues, bool deleteEmptyValues) {
            XmpNode destNode = XmpNodeUtils.FindChildNode(destParent, sourceNode.Name, false);

            bool valueIsEmpty = false;
            if (deleteEmptyValues) {
                valueIsEmpty = sourceNode.Options.Simple
                                   ? string.IsNullOrEmpty(sourceNode.Value)
                                   : !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.Value, sourceNode.Options, 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.Options;
                PropertyOptions destForm = destNode.Options;
                if (sourceForm != destForm) {
                    return;
                }
                if (sourceForm.Struct) {
                    // 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 (IEnumerator it = sourceNode.IterateChildren(); it.MoveNext();) {
                        XmpNode sourceField = (XmpNode) it.Current;
                        if (sourceField == null)
                            continue;
                        AppendSubtree(destXmp, sourceField, destNode, replaceOldValues, deleteEmptyValues);
                        if (deleteEmptyValues && !destNode.HasChildren()) {
                            destParent.RemoveChild(destNode);
                        }
                    }
                }
                else if (sourceForm.ArrayAltText) {
                    // 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 (IEnumerator it = sourceNode.IterateChildren(); it.MoveNext();) {
                        XmpNode sourceItem = (XmpNode) it.Current;
                        if (sourceItem == null)
                            continue;
                        if (!sourceItem.HasQualifier() || !XML_LANG.Equals(sourceItem.GetQualifier(1).Name)) {
                            continue;
                        }

                        int destIndex = XmpNodeUtils.LookupLanguageItem(destNode, sourceItem.GetQualifier(1).Value);
                        if (deleteEmptyValues && (string.IsNullOrEmpty(sourceItem.Value))) {
                            if (destIndex != -1) {
                                destNode.RemoveChild(destIndex);
                                if (!destNode.HasChildren()) {
                                    destParent.RemoveChild(destNode);
                                }
                            }
                        }
                        else if (destIndex == -1) {
                            // Not replacing, keep the existing item.						
                            if (!X_DEFAULT.Equals(sourceItem.GetQualifier(1).Value) || !destNode.HasChildren()) {
                                sourceItem.CloneSubtree(destNode);
                            }
                            else {
                                XmpNode destItem = new XmpNode(sourceItem.Name, sourceItem.Value, sourceItem.Options);
                                sourceItem.CloneSubtree(destItem);
                                destNode.AddChild(1, destItem);
                            }
                        }
                    }
                }
                else if (sourceForm.Array) {
                    // 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 (IEnumerator @is = sourceNode.IterateChildren(); @is.MoveNext();) {
                        XmpNode sourceItem = (XmpNode) @is.Current;
                        if (sourceItem == null)
                            continue;
                        bool match = false;
                        for (IEnumerator id = destNode.IterateChildren(); id.MoveNext();) {
                            XmpNode destItem = (XmpNode) id.Current;
                            if (destItem == null)
                                continue;
                            if (ItemValuesMatch(sourceItem, destItem)) {
                                match = true;
                            }
                        }
                        if (!match) {
                            destNode = (XmpNode) sourceItem.Clone();
                            destParent.AddChild(destNode);
                        }
                    }
                }
            }
        }
Example #15
0
        /// <summary>
        /// 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.
        /// </summary>
        /// <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="XmpException">  </exception>
        private static int LookupQualSelector(XmpNode arrayNode, string qualName, string qualValue, uint aliasForm) {
            if (XML_LANG.Equals(qualName)) {
                qualValue = Utils.NormalizeLangValue(qualValue);
                int index = LookupLanguageItem(arrayNode, qualValue);
                if (index < 0 && (aliasForm & AliasOptions.PROP_ARRAY_ALT_TEXT) > 0) {
                    XmpNode langNode = new XmpNode(ARRAY_ITEM_NAME, null);
                    XmpNode xdefault = new XmpNode(XML_LANG, X_DEFAULT, null);
                    langNode.AddQualifier(xdefault);
                    arrayNode.AddChild(1, langNode);
                    return 1;
                }
                return index;
            }
            for (int index = 1; index < arrayNode.ChildrenLength; index++) {
                XmpNode currItem = arrayNode.GetChild(index);

                for (IEnumerator it = currItem.IterateQualifier(); it.MoveNext();) {
                    XmpNode qualifier = (XmpNode) it.Current;
                    if (qualifier != null && qualName.Equals(qualifier.Name) && qualValue.Equals(qualifier.Value)) {
                        return index;
                    }
                }
            }
            return -1;
        }
Example #16
0
        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="arrayNode">
        ///            an alt text array node </param>
        internal static void NormalizeLangArray(XmpNode arrayNode) {
            if (!arrayNode.Options.ArrayAltText) {
                return;
            }

            // check if node with x-default qual is first place
            for (int i = 2; i <= arrayNode.ChildrenLength; i++) {
                XmpNode child = arrayNode.GetChild(i);
                if (child.HasQualifier() && X_DEFAULT.Equals(child.GetQualifier(1).Value)) {
                    // move node to first place
                    try {
                        arrayNode.RemoveChild(i);
                        arrayNode.AddChild(1, child);
                    }
                    catch (XmpException) {
                        // cannot occur, because same child is removed before
                        Debug.Assert(false);
                    }

                    if (i == 2) {
                        arrayNode.GetChild(2).Value = child.Value;
                    }
                    break;
                }
            }
        }
Example #17
0
        /// <summary>
        /// Find or create a child node under a given parent node. If the parent node is no 
        /// Returns the found or created child node.
        /// </summary>
        /// <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="XmpException"> Thrown if  </exception>
        internal static XmpNode FindChildNode(XmpNode parent, string childName, bool createNodes) {
            if (!parent.Options.SchemaNode && !parent.Options.Struct) {
                if (!parent.Implicit) {
                    throw new XmpException("Named children only allowed for schemas and structs",
                                           XmpError.BADXPATH);
                }
                if (parent.Options.Array) {
                    throw new XmpException("Named children not allowed for arrays", XmpError.BADXPATH);
                }
                if (createNodes) {
                    parent.Options.Struct = true;
                }
            }

            XmpNode childNode = parent.FindChildByName(childName);

            if (childNode == null && createNodes) {
                PropertyOptions options = new PropertyOptions();
                childNode = new XmpNode(childName, options);
                childNode.Implicit = true;
                parent.AddChild(childNode);
            }

            Debug.Assert(childNode != null || !createNodes);

            return childNode;
        }
Example #18
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="XmpException"> Throws Exceptions </exception>
        private static int FindIndexedItem(XmpNode arrayNode, string segment, bool createNodes) {
            int index;

            try {
                segment = segment.Substring(1, segment.Length - 1 - 1);
                index = Convert.ToInt32(segment);
                if (index < 1) {
                    throw new XmpException("Array index must be larger than zero", XmpError.BADXPATH);
                }
            }
            catch (FormatException) {
                throw new XmpException("Array index not digits.", XmpError.BADXPATH);
            }

            if (createNodes && index == arrayNode.ChildrenLength + 1) {
                // Append a new last + 1 node.
                XmpNode newItem = new XmpNode(ARRAY_ITEM_NAME, null);
                newItem.Implicit = true;
                arrayNode.AddChild(newItem);
            }

            return index;
        }
Example #19
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="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) {
            Debug.Assert(tree.Parent == null); // make sure that its the root
            XmpNode schemaNode = tree.FindChildByName(namespaceUri);

            if (schemaNode == null && createNodes) {
                PropertyOptions propertyOptions = new PropertyOptions();
                propertyOptions.SchemaNode = true;
                schemaNode = new XmpNode(namespaceUri, propertyOptions);
                schemaNode.Implicit = true;

                // only previously registered schema namespaces are allowed in the XMP tree.
                string prefix = XMPMetaFactory.SchemaRegistry.GetNamespacePrefix(namespaceUri);
                if (prefix == null) {
                    if (!String.IsNullOrEmpty(suggestedPrefix)) {
                        prefix = XMPMetaFactory.SchemaRegistry.RegisterNamespace(namespaceUri, suggestedPrefix);
                    }
                    else {
                        throw new XmpException("Unregistered schema namespace URI", XmpError.BADSCHEMA);
                    }
                }

                schemaNode.Value = prefix;

                tree.AddChild(schemaNode);
            }

            return schemaNode;
        }
Example #20
0
        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="tree"> the root of the metadata tree </param>
        /// <param name="options"> th parsing options </param>
        /// <exception cref="XmpException"> Forwards XMP errors </exception>
        private static void MoveExplicitAliases(XmpNode tree, ParseOptions options)
        {
            if (!tree.HasAliases)
            {
                return;
            }
            tree.HasAliases = false;

            bool        strictAliasing = options.StrictAliasing;
            IEnumerator schemaIt       = tree.UnmodifiableChildren.GetEnumerator();

            while (schemaIt.MoveNext())
            {
                XmpNode currSchema = (XmpNode)schemaIt.Current;
                if (currSchema == null)
                {
                    continue;
                }
                if (!currSchema.HasAliases)
                {
                    continue;
                }

                ArrayList   currPropsToRemove = new ArrayList();
                IEnumerator propertyIt        = currSchema.IterateChildren();
                while (propertyIt.MoveNext())
                {
                    XmpNode currProp = (XmpNode)propertyIt.Current;
                    if (currProp == null)
                    {
                        continue;
                    }

                    if (!currProp.Alias)
                    {
                        continue;
                    }

                    currProp.Alias = false;

                    // Find the base path, look for the base schema and root node.
                    XMPAliasInfo info = XMPMetaFactory.SchemaRegistry.FindAlias(currProp.Name);
                    if (info != null)
                    {
                        // find or create schema
                        XmpNode baseSchema = XmpNodeUtils.FindSchemaNode(tree, info.Namespace, null, true);
                        baseSchema.Implicit = false;

                        XmpNode baseNode = XmpNodeUtils.FindChildNode(baseSchema, info.Prefix + info.PropName, false);
                        if (baseNode == null)
                        {
                            if (info.AliasForm.Simple)
                            {
                                // A top-to-top alias, transplant the property.
                                // change the alias property name to the base name
                                string qname = info.Prefix + info.PropName;
                                currProp.Name = qname;
                                baseSchema.AddChild(currProp);
                            }
                            else
                            {
                                // An alias to an array item,
                                // create the array and transplant the property.
                                baseNode = new XmpNode(info.Prefix + info.PropName, info.AliasForm.ToPropertyOptions());
                                baseSchema.AddChild(baseNode);
                                TransplantArrayItemAlias(currProp, baseNode);
                            }
                            currPropsToRemove.Add(currProp);
                        }
                        else if (info.AliasForm.Simple)
                        {
                            // 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);
                            }
                            currPropsToRemove.Add(currProp);
                        }
                        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.AliasForm.ArrayAltText)
                            {
                                int xdIndex = XmpNodeUtils.LookupLanguageItem(baseNode, XmpConst.X_DEFAULT);
                                if (xdIndex != -1)
                                {
                                    itemNode = baseNode.GetChild(xdIndex);
                                }
                            }
                            else if (baseNode.HasChildren())
                            {
                                itemNode = baseNode.GetChild(1);
                            }

                            if (itemNode == null)
                            {
                                TransplantArrayItemAlias(currProp, baseNode);
                            }
                            else
                            {
                                if (strictAliasing)
                                {
                                    CompareAliasedSubtrees(currProp, itemNode, true);
                                }
                            }
                            currPropsToRemove.Add(currProp);
                        }
                    }
                }
                foreach (object o in currPropsToRemove)
                {
                    currSchema.Children.Remove(o);
                }
                currPropsToRemove.Clear();
                currSchema.HasAliases = false;
            }
        }
Example #21
0
        /// <summary>
        /// see {@link XMPUtils#separateArrayItems(XMPMeta, String, String, String,
        /// PropertyOptions, boolean)}
        /// </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="XmpException">
        ///             Forwards the Exceptions from the metadata processing </exception>
        public static void SeparateArrayItems(IXmpMeta 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", XmpError.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.
            int  charKind = UCK_NORMAL;
            char ch       = (char)0;

            int itemEnd = 0;
            int endPos  = catedStr.Length;

            while (itemEnd < endPos)
            {
                string itemValue;
                int    itemStart;
                // 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 == UCK_NORMAL || charKind == UCK_QUOTE)
                    {
                        break;
                    }
                }
                if (itemStart >= endPos)
                {
                    break;
                }
                int nextKind;
                if (charKind != UCK_QUOTE)
                {
                    // 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 == UCK_NORMAL || charKind == UCK_QUOTE || (charKind == UCK_COMMA && preserveCommas))
                        {
                            continue;
                        }
                        if (charKind != UCK_SPACE)
                        {
                            break;
                        }
                        if ((itemEnd + 1) < endPos)
                        {
                            ch       = catedStr[itemEnd + 1];
                            nextKind = ClassifyCharacter(ch);
                            if (nextKind == UCK_NORMAL || nextKind == UCK_QUOTE ||
                                (nextKind == UCK_COMMA && preserveCommas))
                            {
                                continue;
                            }
                        }

                        // Anything left?
                        break; // Have multiple spaces, or a space followed by a
                        // separator.
                    }
                    itemValue = catedStr.Substring(itemStart, itemEnd - itemStart);
                }
                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 = "";

                    for (itemEnd = itemStart; itemEnd < endPos; itemEnd++)
                    {
                        ch       = catedStr[itemEnd];
                        charKind = ClassifyCharacter(ch);

                        if (charKind != UCK_QUOTE || !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.
                            char nextChar;
                            if ((itemEnd + 1) < endPos)
                            {
                                nextChar = catedStr[itemEnd + 1];
                                nextKind = ClassifyCharacter(nextChar);
                            }
                            else
                            {
                                nextKind = UCK_SEMICOLON;
                                nextChar = (char)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.ChildrenLength; oldChild++)
                {
                    if (itemValue.Equals(arrayNode.GetChild(oldChild).Value))
                    {
                        foundIndex = oldChild;
                        break;
                    }
                }

                if (foundIndex < 0)
                {
                    XmpNode newItem = new XmpNode(ARRAY_ITEM_NAME, itemValue, null);
                    arrayNode.AddChild(newItem);
                }
            }
        }
Example #22
0
        /// <seealso cref= XMPMeta#setLocalizedText(String, String, String, String, String,
        ///      PropertyOptions) </seealso>
        public virtual void SetLocalizedText(string schemaNs, string altTextName, string genericLang,
                                             string specificLang, string itemValue, PropertyOptions options)
        {
            ParameterAsserts.AssertSchemaNs(schemaNs);
            ParameterAsserts.AssertArrayName(altTextName);
            ParameterAsserts.AssertSpecificLang(specificLang);

            genericLang = genericLang != null?Utils.NormalizeLangValue(genericLang) : null;

            specificLang = Utils.NormalizeLangValue(specificLang);

            XmpPath arrayPath = XmpPathParser.ExpandXPath(schemaNs, altTextName);

            // Find the array node and set the options if it was just created.
            XmpNode arrayNode = XmpNodeUtils.FindNode(_tree, arrayPath, true,
                                                      new PropertyOptions(PropertyOptions.ARRAY |
                                                                          PropertyOptions.ARRAY_ORDERED |
                                                                          PropertyOptions.ARRAY_ALTERNATE |
                                                                          PropertyOptions.ARRAY_ALT_TEXT));

            if (arrayNode == null)
            {
                throw new XmpException("Failed to find or create array node", XmpError.BADXPATH);
            }
            if (!arrayNode.Options.ArrayAltText)
            {
                if (!arrayNode.HasChildren() && arrayNode.Options.ArrayAlternate)
                {
                    arrayNode.Options.ArrayAltText = true;
                }
                else
                {
                    throw new XmpException("Specified property is no alt-text array", XmpError.BADXPATH);
                }
            }

            // Make sure the x-default item, if any, is first.
            bool    haveXDefault = false;
            XmpNode xdItem       = null;

            foreach (XmpNode currItem in arrayNode.Children)
            {
                if (!currItem.HasQualifier() || !XML_LANG.Equals(currItem.GetQualifier(1).Name))
                {
                    throw new XmpException("Language qualifier must be first", XmpError.BADXPATH);
                }
                if (X_DEFAULT.Equals(currItem.GetQualifier(1).Value))
                {
                    xdItem       = currItem;
                    haveXDefault = true;
                    break;
                }
            }

            // Moves x-default to the beginning of the array
            if (xdItem != null && arrayNode.ChildrenLength > 1)
            {
                arrayNode.RemoveChild(xdItem);
                arrayNode.AddChild(1, xdItem);
            }

            // Find the appropriate item.
            // chooseLocalizedText will make sure the array is a language
            // alternative.
            object[] result   = XmpNodeUtils.ChooseLocalizedText(arrayNode, genericLang, specificLang);
            int      match    = (int)((int?)result[0]);
            XmpNode  itemNode = (XmpNode)result[1];

            bool specificXDefault = X_DEFAULT.Equals(specificLang);

            switch (match)
            {
            case XmpNodeUtils.CLT_NO_VALUES:

                // Create the array items for the specificLang and x-default, with
                // x-default first.
                XmpNodeUtils.AppendLangItem(arrayNode, X_DEFAULT, itemValue);
                haveXDefault = true;
                if (!specificXDefault)
                {
                    XmpNodeUtils.AppendLangItem(arrayNode, specificLang, itemValue);
                }
                break;

            case XmpNodeUtils.CLT_SPECIFIC_MATCH:

                if (!specificXDefault)
                {
                    // Update the specific item, update x-default if it matches the
                    // old value.
                    if (haveXDefault && xdItem != itemNode && xdItem != null && xdItem.Value.Equals(itemNode.Value))
                    {
                        xdItem.Value = itemValue;
                    }
                    // ! Do this after the x-default check!
                    itemNode.Value = itemValue;
                }
                else
                {
                    // Update all items whose values match the old x-default value.
                    Debug.Assert(haveXDefault && xdItem == itemNode);
                    foreach (XmpNode currItem in arrayNode.Children)
                    {
                        if (currItem == xdItem || !currItem.Value.Equals(xdItem != null ? xdItem.Value : null))
                        {
                            continue;
                        }
                        currItem.Value = itemValue;
                    }
                    // And finally do the x-default item.
                    if (xdItem != null)
                    {
                        xdItem.Value = itemValue;
                    }
                }
                break;

            case XmpNodeUtils.CLT_SINGLE_GENERIC:

                // Update the generic item, update x-default if it matches the old
                // value.
                if (haveXDefault && xdItem != itemNode && xdItem != null && xdItem.Value.Equals(itemNode.Value))
                {
                    xdItem.Value = itemValue;
                }
                itemNode.Value = itemValue;     // ! Do this after
                // the x-default
                // check!
                break;

            case XmpNodeUtils.CLT_MULTIPLE_GENERIC:

                // Create the specific language, ignore x-default.
                XmpNodeUtils.AppendLangItem(arrayNode, specificLang, itemValue);
                if (specificXDefault)
                {
                    haveXDefault = true;
                }
                break;

            case XmpNodeUtils.CLT_XDEFAULT:

                // Create the specific language, update x-default if it was the only
                // item.
                if (xdItem != null && arrayNode.ChildrenLength == 1)
                {
                    xdItem.Value = itemValue;
                }
                XmpNodeUtils.AppendLangItem(arrayNode, specificLang, itemValue);
                break;

            case XmpNodeUtils.CLT_FIRST_ITEM:

                // Create the specific language, don't add an x-default item.
                XmpNodeUtils.AppendLangItem(arrayNode, specificLang, itemValue);
                if (specificXDefault)
                {
                    haveXDefault = true;
                }
                break;

            default:
                // does not happen under normal circumstances
                throw new XmpException("Unexpected result from ChooseLocalizedText",
                                       XmpError.INTERNALFAILURE);
            }

            // Add an x-default at the front if needed.
            if (!haveXDefault && arrayNode.ChildrenLength == 1)
            {
                XmpNodeUtils.AppendLangItem(arrayNode, X_DEFAULT, itemValue);
            }
        }
Example #23
0
        /// <seealso cref= XMPUtilsImpl#appendProperties(XMPMeta, XMPMeta, boolean, boolean, boolean) </seealso>
        /// <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="XmpException"> </exception>
        private static void AppendSubtree(XmpMetaImpl destXmp, XmpNode sourceNode, XmpNode destParent,
                                          bool replaceOldValues, bool deleteEmptyValues)
        {
            XmpNode destNode = XmpNodeUtils.FindChildNode(destParent, sourceNode.Name, false);

            bool valueIsEmpty = false;

            if (deleteEmptyValues)
            {
                valueIsEmpty = sourceNode.Options.Simple
                                   ? string.IsNullOrEmpty(sourceNode.Value)
                                   : !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.Value, sourceNode.Options, 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.Options;
                PropertyOptions destForm   = destNode.Options;
                if (sourceForm != destForm)
                {
                    return;
                }
                if (sourceForm.Struct)
                {
                    // 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 (IEnumerator it = sourceNode.IterateChildren(); it.MoveNext();)
                    {
                        XmpNode sourceField = (XmpNode)it.Current;
                        if (sourceField == null)
                        {
                            continue;
                        }
                        AppendSubtree(destXmp, sourceField, destNode, replaceOldValues, deleteEmptyValues);
                        if (deleteEmptyValues && !destNode.HasChildren())
                        {
                            destParent.RemoveChild(destNode);
                        }
                    }
                }
                else if (sourceForm.ArrayAltText)
                {
                    // 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 (IEnumerator it = sourceNode.IterateChildren(); it.MoveNext();)
                    {
                        XmpNode sourceItem = (XmpNode)it.Current;
                        if (sourceItem == null)
                        {
                            continue;
                        }
                        if (!sourceItem.HasQualifier() || !XML_LANG.Equals(sourceItem.GetQualifier(1).Name))
                        {
                            continue;
                        }

                        int destIndex = XmpNodeUtils.LookupLanguageItem(destNode, sourceItem.GetQualifier(1).Value);
                        if (deleteEmptyValues && (string.IsNullOrEmpty(sourceItem.Value)))
                        {
                            if (destIndex != -1)
                            {
                                destNode.RemoveChild(destIndex);
                                if (!destNode.HasChildren())
                                {
                                    destParent.RemoveChild(destNode);
                                }
                            }
                        }
                        else if (destIndex == -1)
                        {
                            // Not replacing, keep the existing item.
                            if (!X_DEFAULT.Equals(sourceItem.GetQualifier(1).Value) || !destNode.HasChildren())
                            {
                                sourceItem.CloneSubtree(destNode);
                            }
                            else
                            {
                                XmpNode destItem = new XmpNode(sourceItem.Name, sourceItem.Value, sourceItem.Options);
                                sourceItem.CloneSubtree(destItem);
                                destNode.AddChild(1, destItem);
                            }
                        }
                    }
                }
                else if (sourceForm.Array)
                {
                    // 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 (IEnumerator @is = sourceNode.IterateChildren(); @is.MoveNext();)
                    {
                        XmpNode sourceItem = (XmpNode)@is.Current;
                        if (sourceItem == null)
                        {
                            continue;
                        }
                        bool match = false;
                        for (IEnumerator id = destNode.IterateChildren(); id.MoveNext();)
                        {
                            XmpNode destItem = (XmpNode)id.Current;
                            if (destItem == null)
                            {
                                continue;
                            }
                            if (ItemValuesMatch(sourceItem, destItem))
                            {
                                match = true;
                            }
                        }
                        if (!match)
                        {
                            destNode = (XmpNode)sourceItem.Clone();
                            destParent.AddChild(destNode);
                        }
                    }
                }
            }
        }
Example #24
0
        /// <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="XmpException"> thown on parsing errors </exception>
        private static XmpNode AddChildNode(XmpMetaImpl xmp, XmpNode xmpParent, XmlNode xmlNode, string value,
                                            bool isTopLevel) {
            IXmpSchemaRegistry registry = XmpMetaFactory.SchemaRegistry;
            string @namespace = xmlNode.NamespaceURI;
            string childName;
            if (@namespace != null) {
                if (NS_DC_DEPRECATED.Equals(@namespace)) {
                    // Fix a legacy DC namespace
                    @namespace = NS_DC;
                }

                string prefix = registry.GetNamespacePrefix(@namespace);
                if (prefix == null) {
                    prefix = xmlNode.Prefix ?? DEFAULT_PREFIX;
                    prefix = registry.RegisterNamespace(@namespace, prefix);
                }
                childName = prefix + xmlNode.LocalName;
            }
            else {
                throw new XmpException("XML namespace required for all elements and attributes",
                                       XmpError.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.Root, @namespace, DEFAULT_PREFIX, true);
                schemaNode.Implicit = 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.Root.HasAliases = true;
                    schemaNode.HasAliases = 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.Alias = 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.Options.Struct) {
                    throw new XmpException("Misplaced rdf:value element", XmpError.BADRDF);
                }
                xmpParent.HasValueChild = true;
            }

            if (isArrayItem) {
                if (!xmpParent.Options.Array) {
                    throw new XmpException("Misplaced rdf:li element", XmpError.BADRDF);
                }
                newChild.Name = ARRAY_ITEM_NAME;
            }

            return newChild;
        }
Example #25
0
        /// <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. <br>
        /// The <code>xml:lang</code> attribute was dropped from an
        /// <code>alt-text</code> item if the language was <code>x-default</code>.
        /// </summary>
        /// <param name="dcSchema"> the DC schema node </param>
        /// <exception cref="XmpException"> Thrown if normalization fails </exception>
        private static void NormalizeDcArrays(XmpNode dcSchema) {
            for (int i = 1; i <= dcSchema.ChildrenLength; i++) {
                XmpNode currProp = dcSchema.GetChild(i);

                PropertyOptions arrayForm = (PropertyOptions) _dcArrayForms[currProp.Name];
                if (arrayForm == null) {
                    continue;
                }
                if (currProp.Options.Simple) {
                    // create a new array and add the current property as child, 
                    // if it was formerly simple 
                    XmpNode newArray = new XmpNode(currProp.Name, arrayForm);
                    currProp.Name = XmpConst.ARRAY_ITEM_NAME;
                    newArray.AddChild(currProp);
                    dcSchema.ReplaceChild(i, newArray);

                    // fix language alternatives
                    if (arrayForm.ArrayAltText && !currProp.Options.HasLanguage) {
                        XmpNode newLang = new XmpNode(XmpConst.XML_LANG, XmpConst.X_DEFAULT, null);
                        currProp.AddQualifier(newLang);
                    }
                }
                else {
                    // clear array options and add corrected array form if it has been an array before
                    currProp.Options.SetOption(
                        PropertyOptions.ARRAY | PropertyOptions.ARRAY_ORDERED | PropertyOptions.ARRAY_ALTERNATE |
                        PropertyOptions.ARRAY_ALT_TEXT, false);
                    currProp.Options.MergeWith(arrayForm);

                    if (arrayForm.ArrayAltText) {
                        // applying for "dc:description", "dc:rights", "dc:title"
                        RepairAltText(currProp);
                    }
                }
            }
        }
Example #26
0
        // -------------------------------------------------------------------------------------
        // private


        /// <summary>
        /// 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.
        /// </summary>
        /// <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="XmpException"> </exception>
        private void DoSetArrayItem(XmpNode arrayNode, int itemIndex, string itemValue, PropertyOptions itemOptions,
                                    bool insert) {
            XmpNode itemNode = new XmpNode(ARRAY_ITEM_NAME, null);
            itemOptions = XmpNodeUtils.VerifySetOptions(itemOptions, itemValue);

            // in insert mode the index after the last is allowed,
            // even ARRAY_LAST_ITEM points to the index *after* the last.
            int maxIndex = insert ? arrayNode.ChildrenLength + 1 : arrayNode.ChildrenLength;
            if (itemIndex == ARRAY_LAST_ITEM) {
                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", XmpError.BADINDEX);
            }
        }
Example #27
0
        /// <summary>
        /// Moves an alias node of array form to another schema into an array </summary>
        /// <param name="childNode"> the node to be moved </param>
        /// <param name="baseArray"> the base array for the array item </param>
        /// <exception cref="XmpException"> Forwards XMP errors </exception>
        private static void TransplantArrayItemAlias(XmpNode childNode, XmpNode baseArray) {
            if (baseArray.Options.ArrayAltText) {
                if (childNode.Options.HasLanguage) {
                    throw new XmpException("Alias to x-default already has a language qualifier",
                                           XmpError.BADXMP);
                }

                XmpNode langQual = new XmpNode(XmpConst.XML_LANG, XmpConst.X_DEFAULT, null);
                childNode.AddQualifier(langQual);
            }

            childNode.Name = XmpConst.ARRAY_ITEM_NAME;
            baseArray.AddChild(childNode);
        }