Пример #1
0
        /// <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="XmpException"/>
        private void DoSetArrayItem(XmpNode arrayNode, int itemIndex, string itemValue, PropertyOptions itemOptions, bool insert)
        {
            var itemNode = new XmpNode(XmpConstants.ArrayItemName, 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.
            var maxIndex = insert ? arrayNode.GetChildrenLength() + 1 : arrayNode.GetChildrenLength();

            if (itemIndex == XmpConstants.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", XmpErrorCode.BadIndex);
            }
        }
Пример #2
0
        /// <summary>
        /// After processing by ExpandXPath, a step can be of these forms:
        /// </summary>
        /// <remarks>
        /// After processing by ExpandXPath, a step can be of these forms:
        /// <list type="bullet">
        /// <item>qualName - A top level property or struct field.</item>
        /// <item>[index] - An element of an array.</item>
        /// <item>[last()] - The last element of an array.</item>
        /// <item>[qualName="value"] - An element in an array of structs, chosen by a field value.</item>
        /// <item>[?qualName="value"] - An element in an array, chosen by a qualifier value.</item>
        /// <item>?qualName - A general qualifier.</item>
        /// </list>
        /// Find the appropriate child node, resolving aliases, and optionally creating nodes.
        /// </remarks>
        /// <param name="parentNode">the node to start to start from</param>
        /// <param name="nextStep">the xpath segment</param>
        /// <param name="createNodes"></param>
        /// <returns>returns the found or created XMPPath node</returns>
        /// <exception cref="XmpException"></exception>
        private static XmpNode FollowXPathStep(XmpNode parentNode, XmpPathSegment nextStep, bool createNodes)
        {
            XmpNode nextNode = null;
            var     stepKind = nextStep.Kind;

            switch (stepKind)
            {
            case XmpPathStepType.StructFieldStep:
                nextNode = FindChildNode(parentNode, nextStep.Name, createNodes);
                break;

            case XmpPathStepType.QualifierStep:
                nextNode = FindQualifierNode(parentNode, nextStep.Name.Substring(1), createNodes);
                break;

            default:
                // This is an array indexing step. First get the index, then get the node.
                if (!parentNode.Options.IsArray)
                {
                    throw new XmpException("Indexing applied to non-array", XmpErrorCode.BadXPath);
                }

                int index;
                switch (stepKind)
                {
                case XmpPathStepType.ArrayIndexStep:
                    index = FindIndexedItem(parentNode, nextStep.Name, createNodes);
                    break;

                case XmpPathStepType.ArrayLastStep:
                    index = parentNode.GetChildrenLength();
                    break;

                case XmpPathStepType.FieldSelectorStep:
                    Utils.SplitNameAndValue(nextStep.Name, out string fieldName, out string fieldValue);
                    index = LookupFieldSelector(parentNode, fieldName, fieldValue);
                    break;

                case XmpPathStepType.QualSelectorStep:
                    Utils.SplitNameAndValue(nextStep.Name, out string qualName, out string qualValue);
                    index = LookupQualSelector(parentNode, qualName, qualValue, nextStep.AliasForm);
                    break;

                default:
                    throw new XmpException("Unknown array indexing step in FollowXPathStep", XmpErrorCode.InternalFailure);
                }

                if (1 <= index && index <= parentNode.GetChildrenLength())
                {
                    nextNode = parentNode.GetChild(index);
                }
                break;
            }

            return(nextNode);
        }
Пример #3
0
        /// <summary>The outermost call is special.</summary>
        /// <remarks>
        /// The outermost call is special. The names almost certainly differ. The
        /// qualifiers (and hence options) will differ for an alias to the x-default
        /// item of a langAlt array.
        /// </remarks>
        /// <param name="aliasNode">the alias node</param>
        /// <param name="baseNode">the base node of the alias</param>
        /// <param name="outerCall">marks the outer call of the recursion</param>
        /// <exception cref="XmpException">Forwards XMP errors</exception>
        private static void CompareAliasedSubtrees(XmpNode aliasNode, XmpNode baseNode, bool outerCall)
        {
            if (baseNode.Value != aliasNode.Value || aliasNode.GetChildrenLength() != baseNode.GetChildrenLength())
            {
                throw new XmpException("Mismatch between alias and base nodes", XmpErrorCode.BadXmp);
            }
            if (!outerCall && (baseNode.Name != aliasNode.Name || !aliasNode.Options.Equals(baseNode.Options) || aliasNode.GetQualifierLength() != baseNode.GetQualifierLength()))
            {
                throw new XmpException("Mismatch between alias and base nodes", XmpErrorCode.BadXmp);
            }

            for (IIterator an = aliasNode.IterateChildren(), bn = baseNode.IterateChildren(); an.HasNext() && bn.HasNext();)
            {
                var aliasChild = (XmpNode)an.Next();
                var baseChild  = (XmpNode)bn.Next();
                CompareAliasedSubtrees(aliasChild, baseChild, false);
            }

            for (IIterator an = aliasNode.IterateQualifier(), bn1 = baseNode.IterateQualifier(); an.HasNext() && bn1.HasNext();)
            {
                var aliasQual = (XmpNode)an.Next();
                var baseQual  = (XmpNode)bn1.Next();
                CompareAliasedSubtrees(aliasQual, baseQual, false);
            }
        }
Пример #4
0
        /// <summary>
        /// Searches for a field selector in a node:
        /// [fieldName="value] - an element in an array of structs, chosen by a field value.
        /// </summary>
        /// <remarks>
        /// Searches for a field selector in a node:
        /// [fieldName="value] - an element in an array of structs, chosen by a field value.
        /// No implicit nodes are created by field selectors.
        /// </remarks>
        /// <param name="arrayNode"/>
        /// <param name="fieldName"/>
        /// <param name="fieldValue"/>
        /// <returns>Returns the index of the field if found, otherwise -1.</returns>
        /// <exception cref="XmpException"></exception>
        private static int LookupFieldSelector(XmpNode arrayNode, string fieldName, string fieldValue)
        {
            var result = -1;

            for (var index = 1; index <= arrayNode.GetChildrenLength() && result < 0; index++)
            {
                var currItem = arrayNode.GetChild(index);

                if (!currItem.Options.IsStruct)
                {
                    throw new XmpException("Field selector must be used on array of struct", XmpErrorCode.BadXPath);
                }

                for (var f = 1; f <= currItem.GetChildrenLength(); f++)
                {
                    var currField = currItem.GetChild(f);

                    if (currField.Name != fieldName ||
                        currField.Value != fieldValue)
                    {
                        continue;
                    }

                    result = index;
                    break;
                }
            }

            return(result);
        }
Пример #5
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.Options.IsArrayAltText)
     {
         return;
     }
     // check if node with x-default qual is first place
     for (var i = 2; i <= arrayNode.GetChildrenLength(); i++)
     {
         var child = arrayNode.GetChild(i);
         if (child.HasQualifier && XmpConstants.XDefault.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;
         }
     }
 }
Пример #6
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="XmpException"></exception>
 private static int LookupQualSelector(XmpNode arrayNode, string qualName, string qualValue, int aliasForm)
 {
     if (XmpConstants.XmlLang.Equals(qualName))
     {
         qualValue = Utils.NormalizeLangValue(qualValue);
         var index = LookupLanguageItem(arrayNode, qualValue);
         if (index < 0 && (aliasForm & AliasOptions.PropArrayAltText) > 0)
         {
             var langNode = new XmpNode(XmpConstants.ArrayItemName, null);
             var xdefault = new XmpNode(XmpConstants.XmlLang, XmpConstants.XDefault, null);
             langNode.AddQualifier(xdefault);
             arrayNode.AddChild(1, langNode);
             return(1);
         }
         return(index);
     }
     for (var index = 1; index < arrayNode.GetChildrenLength(); index++)
     {
         var currItem = arrayNode.GetChild(index);
         for (var it = currItem.IterateQualifier(); it.HasNext();)
         {
             var qualifier = (XmpNode)it.Next();
             if (qualName.Equals(qualifier.Name) && qualValue.Equals(qualifier.Value))
             {
                 return(index);
             }
         }
     }
     return(-1);
 }
Пример #7
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)
        {
            var index = 0;

            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", XmpErrorCode.BadXPath);
                }
            }
            catch (FormatException)
            {
                throw new XmpException("Array index not digits.", XmpErrorCode.BadXPath);
            }
            if (createNodes && index == arrayNode.GetChildrenLength() + 1)
            {
                // Append a new last + 1 node.
                var newItem = new XmpNode(XmpConstants.ArrayItemName, null)
                {
                    IsImplicit = true
                };
                arrayNode.AddChild(newItem);
            }
            return(index);
        }
Пример #8
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)
        {
            if (!int.TryParse(segment.Substring(1, segment.Length - 1 - 1), out int index))
            {
                throw new XmpException("Array index not digits.", XmpErrorCode.BadXPath);
            }

            if (createNodes && index == arrayNode.GetChildrenLength() + 1)
            {
                // Append a new last + 1 node.
                var newItem = new XmpNode(XmpConstants.ArrayItemName, null)
                {
                    IsImplicit = true
                };
                arrayNode.AddChild(newItem);
            }

            return(index);
        }
Пример #9
0
 /// <summary>Looks for the appropriate language item in a text alternative array.item</summary>
 /// <param name="arrayNode">an array node</param>
 /// <param name="language">the requested language</param>
 /// <returns>Returns the index if the language has been found, -1 otherwise.</returns>
 /// <exception cref="XmpException"/>
 internal static int LookupLanguageItem(XmpNode arrayNode, string language)
 {
     if (!arrayNode.Options.IsArray)
     {
         throw new XmpException("Language item must be used on array", XmpErrorCode.BadXPath);
     }
     for (var index = 1; index <= arrayNode.GetChildrenLength(); index++)
     {
         var child = arrayNode.GetChild(index);
         if (!child.HasQualifier || !XmpConstants.XmlLang.Equals(child.GetQualifier(1).Name))
         {
             continue;
         }
         if (language.Equals(child.GetQualifier(1).Value))
         {
             return(index);
         }
     }
     return(-1);
 }
Пример #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.
 /// </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 <c>xml:lang</c> attribute was dropped from an
 /// <c>alt-text</c> item if the language was <c>x-default</c>.
 /// </remarks>
 /// <param name="dcSchema">the DC schema node</param>
 /// <exception cref="XmpException">Thrown if normalization fails</exception>
 private static void NormalizeDcArrays(XmpNode dcSchema)
 {
     for (var i = 1; i <= dcSchema.GetChildrenLength(); i++)
     {
         var currProp  = dcSchema.GetChild(i);
         var arrayForm = (PropertyOptions)_dcArrayForms[currProp.Name];
         if (arrayForm == null)
         {
             continue;
         }
         if (currProp.Options.IsSimple)
         {
             // create a new array and add the current property as child,
             // if it was formerly simple
             var newArray = new XmpNode(currProp.Name, arrayForm);
             currProp.Name = XmpConstants.ArrayItemName;
             newArray.AddChild(currProp);
             dcSchema.ReplaceChild(i, newArray);
             // fix language alternatives
             if (arrayForm.IsArrayAltText && !currProp.Options.HasLanguage)
             {
                 var newLang = new XmpNode(XmpConstants.XmlLang, XmpConstants.XDefault, null);
                 currProp.AddQualifier(newLang);
             }
         }
         else
         {
             // clear array options and add corrected array form if it has been an array before
             currProp.Options.SetOption(PropertyOptions.ArrayFlag | PropertyOptions.ArrayOrderedFlag | PropertyOptions.ArrayAlternateFlag | PropertyOptions.ArrayAltTextFlag, false);
             currProp.Options.MergeWith(arrayForm);
             if (arrayForm.IsArrayAltText)
             {
                 // applying for "dc:description", "dc:rights", "dc:title"
                 RepairAltText(currProp);
             }
         }
     }
 }
Пример #11
0
        /// <summary>Compares two nodes including its children and qualifier.</summary>
        /// <param name="leftNode">an <c>XMPNode</c></param>
        /// <param name="rightNode">an <c>XMPNode</c></param>
        /// <returns>Returns true if the nodes are equal, false otherwise.</returns>
        /// <exception cref="XmpException">Forwards exceptions to the calling method.</exception>
        private static bool ItemValuesMatch(XmpNode leftNode, XmpNode rightNode)
        {
            var leftForm  = leftNode.Options;
            var rightForm = rightNode.Options;

            if (leftForm.Equals(rightForm))
            {
                return(false);
            }

            if (leftForm.GetOptions() == 0)
            {
                // Simple nodes, check the values and xml:lang qualifiers.
                if (!leftNode.Value.Equals(rightNode.Value))
                {
                    return(false);
                }
                if (leftNode.Options.HasLanguage != rightNode.Options.HasLanguage)
                {
                    return(false);
                }
                if (leftNode.Options.HasLanguage && !leftNode.GetQualifier(1).Value.Equals(rightNode.GetQualifier(1).Value))
                {
                    return(false);
                }
            }
            else
            {
                if (leftForm.IsStruct)
                {
                    // Struct nodes, see if all fields match, ignoring order.
                    if (leftNode.GetChildrenLength() != rightNode.GetChildrenLength())
                    {
                        return(false);
                    }

                    for (var it = leftNode.IterateChildren(); it.HasNext();)
                    {
                        var leftField  = (XmpNode)it.Next();
                        var rightField = XmpNodeUtils.FindChildNode(rightNode, leftField.Name, false);
                        if (rightField == null || !ItemValuesMatch(leftField, rightField))
                        {
                            return(false);
                        }
                    }
                }
                else
                {
                    // Array nodes, see if the "leftNode" values are present in the
                    // "rightNode", ignoring order, duplicates,
                    // and extra values in the rightNode-> The rightNode is the
                    // destination for AppendProperties.
                    Debug.Assert(leftForm.IsArray);
                    for (var il = leftNode.IterateChildren(); il.HasNext();)
                    {
                        var leftItem = (XmpNode)il.Next();
                        var match    = false;

                        for (var ir = rightNode.IterateChildren(); ir.HasNext();)
                        {
                            var rightItem = (XmpNode)ir.Next();
                            if (ItemValuesMatch(leftItem, rightItem))
                            {
                                match = true;
                                break;
                            }
                        }

                        if (!match)
                        {
                            return(false);
                        }
                    }
                }
            }
            return(true);
        }
Пример #12
0
        /// <summary>
        /// After processing by ExpandXPath, a step can be of these forms:
        /// </summary>
        /// <remarks>
        /// After processing by ExpandXPath, a step can be of these forms:
        /// <list type="bullet">
        /// <item>qualName - A top level property or struct field.</item>
        /// <item>[index] - An element of an array.</item>
        /// <item>[last()] - The last element of an array.</item>
        /// <item>[qualName="value"] - An element in an array of structs, chosen by a field value.</item>
        /// <item>[?qualName="value"] - An element in an array, chosen by a qualifier value.</item>
        /// <item>?qualName - A general qualifier.</item>
        /// </list>
        /// Find the appropriate child node, resolving aliases, and optionally creating nodes.
        /// </remarks>
        /// <param name="parentNode">the node to start to start from</param>
        /// <param name="nextStep">the xpath segment</param>
        /// <param name="createNodes"></param>
        /// <returns>returns the found or created XMPPath node</returns>
        /// <exception cref="XmpException"></exception>
        private static XmpNode FollowXPathStep(XmpNode parentNode, XmpPathSegment nextStep, bool createNodes)
        {
            XmpNode nextNode = null;
            var     stepKind = nextStep.Kind;

            if (stepKind == XmpPath.StructFieldStep)
            {
                nextNode = FindChildNode(parentNode, nextStep.Name, createNodes);
            }
            else
            {
                if (stepKind == XmpPath.QualifierStep)
                {
                    nextNode = FindQualifierNode(parentNode, nextStep.Name.Substring(1), createNodes);
                }
                else
                {
                    // This is an array indexing step. First get the index, then get the node.
                    if (!parentNode.Options.IsArray)
                    {
                        throw new XmpException("Indexing applied to non-array", XmpErrorCode.BadXPath);
                    }
                    var index = 0;
                    if (stepKind == XmpPath.ArrayIndexStep)
                    {
                        index = FindIndexedItem(parentNode, nextStep.Name, createNodes);
                    }
                    else
                    {
                        if (stepKind == XmpPath.ArrayLastStep)
                        {
                            index = parentNode.GetChildrenLength();
                        }
                        else
                        {
                            if (stepKind == XmpPath.FieldSelectorStep)
                            {
                                var result     = Utils.SplitNameAndValue(nextStep.Name);
                                var fieldName  = result[0];
                                var fieldValue = result[1];
                                index = LookupFieldSelector(parentNode, fieldName, fieldValue);
                            }
                            else
                            {
                                if (stepKind == XmpPath.QualSelectorStep)
                                {
                                    var result    = Utils.SplitNameAndValue(nextStep.Name);
                                    var qualName  = result[0];
                                    var qualValue = result[1];
                                    index = LookupQualSelector(parentNode, qualName, qualValue, nextStep.AliasForm);
                                }
                                else
                                {
                                    throw new XmpException("Unknown array indexing step in FollowXPathStep", XmpErrorCode.InternalFailure);
                                }
                            }
                        }
                    }
                    if (1 <= index && index <= parentNode.GetChildrenLength())
                    {
                        nextNode = parentNode.GetChild(index);
                    }
                }
            }
            return(nextNode);
        }