/// <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);
            }
        }
        /// <exception cref="XmpException"/>
        public void AppendArrayItem(string schemaNs, string arrayName, PropertyOptions arrayOptions, string itemValue, PropertyOptions itemOptions)
        {
            ParameterAsserts.AssertSchemaNs(schemaNs);
            ParameterAsserts.AssertArrayName(arrayName);
            if (arrayOptions == null)
            {
                arrayOptions = new PropertyOptions();
            }
            if (!arrayOptions.IsOnlyArrayOptions)
            {
                throw new XmpException("Only array form flags allowed for arrayOptions", XmpErrorCode.BadOptions);
            }
            // Check if array options are set correctly.
            arrayOptions = XmpNodeUtils.VerifySetOptions(arrayOptions, null);
            // Locate or create the array. If it already exists, make sure the array
            // form from the options
            // parameter is compatible with the current state.
            var arrayPath = XmpPathParser.ExpandXPath(schemaNs, arrayName);
            // Just lookup, don't try to create.
            var arrayNode = XmpNodeUtils.FindNode(_tree, arrayPath, false, null);

            if (arrayNode != null)
            {
                // The array exists, make sure the form is compatible. Zero
                // arrayForm means take what exists.
                if (!arrayNode.Options.IsArray)
                {
                    throw new XmpException("The named property is not an array", XmpErrorCode.BadXPath);
                }
            }
            else
            {
                // if (arrayOptions != null && !arrayOptions.equalArrayTypes(arrayNode.getOptions()))
                // {
                // throw new XMPException("Mismatch of existing and specified array form", BADOPTIONS);
                // }
                // The array does not exist, try to create it.
                if (arrayOptions.IsArray)
                {
                    arrayNode = XmpNodeUtils.FindNode(_tree, arrayPath, true, arrayOptions);
                    if (arrayNode == null)
                    {
                        throw new XmpException("Failure creating array node", XmpErrorCode.BadXPath);
                    }
                }
                else
                {
                    // array options missing
                    throw new XmpException("Explicit arrayOptions required to create new array", XmpErrorCode.BadOptions);
                }
            }
            DoSetArrayItem(arrayNode, XmpConstants.ArrayLastItem, itemValue, itemOptions, true);
        }
Exemple #3
0
        /// <summary>Associates an alias name with an actual name.</summary>
        /// <remarks>
        /// Associates an alias name with an actual name.
        /// <para />
        /// Define a alias mapping from one namespace/property to another. Both
        /// property names must be simple names. An alias can be a direct mapping,
        /// where the alias and actual have the same data type. It is also possible
        /// to map a simple alias to an item in an array. This can either be to the
        /// first item in the array, or to the 'x-default' item in an alt-text array.
        /// Multiple alias names may map to the same actual, as long as the forms
        /// match. It is a no-op to reregister an alias in an identical fashion.
        /// Note: This method is not locking because only called by registerStandardAliases
        /// which is only called by the constructor.
        /// Note2: The method is only package-private so that it can be tested with unittests
        /// </remarks>
        /// <param name="aliasNs">The namespace URI for the alias. Must not be null or the empty string.</param>
        /// <param name="aliasProp">The name of the alias. Must be a simple name, not null or the empty string and not a general path expression.</param>
        /// <param name="actualNs">The namespace URI for the actual. Must not be null or the empty string.</param>
        /// <param name="actualProp">The name of the actual. Must be a simple name, not null or the empty string and not a general path expression.</param>
        /// <param name="aliasForm">Provides options for aliases for simple aliases to array items. This is needed to know what kind of array to create if
        /// set for the first time via the simple alias. Pass <c>XMP_NoOptions</c>, the default value, for all direct aliases regardless of whether the actual
        /// data type is an array or not (see <see cref="AliasOptions"/>).</param>
        /// <exception cref="XmpException">for inconsistant aliases.</exception>
        private void RegisterAlias(string aliasNs, string aliasProp, string actualNs, string actualProp, AliasOptions aliasForm)
        {
            lock (_lock)
            {
                ParameterAsserts.AssertSchemaNs(aliasNs);
                ParameterAsserts.AssertPropName(aliasProp);
                ParameterAsserts.AssertSchemaNs(actualNs);
                ParameterAsserts.AssertPropName(actualProp);

                // FfF: if we need the decoration with [1] or
                // FfF: [?xml:lang="x-default"] for array forms

                // Fix the alias options
                var aliasOpts = aliasForm != null ? new AliasOptions(XmpNodeUtils.VerifySetOptions(aliasForm.ToPropertyOptions(), null).GetOptions()) : new AliasOptions();
                if (_p.IsMatch(aliasProp) || _p.IsMatch(actualProp))
                {
                    throw new XmpException("Alias and actual property names must be simple", XmpErrorCode.BadXPath);
                }

                // check if both namespaces are registered
                var aliasPrefix  = GetNamespacePrefix(aliasNs);
                var actualPrefix = GetNamespacePrefix(actualNs);

                if (aliasPrefix == null)
                {
                    throw new XmpException("Alias namespace is not registered", XmpErrorCode.BadSchema);
                }
                if (actualPrefix == null)
                {
                    throw new XmpException("Actual namespace is not registered", XmpErrorCode.BadSchema);
                }

                var key = aliasPrefix + aliasProp;

                // check if alias is already existing
                if (_aliasMap.ContainsKey(key))
                {
                    throw new XmpException("Alias is already existing", XmpErrorCode.BadParam);
                }
                if (_aliasMap.ContainsKey(actualPrefix + actualProp))
                {
                    throw new XmpException("Actual property is already an alias, use the base property", XmpErrorCode.BadParam);
                }

                _aliasMap[key] = new XmpAliasInfo(actualNs, actualPrefix, actualProp, aliasOpts);
            }
        }
        /// <exception cref="XmpException"/>
        public void SetProperty(string schemaNs, string propName, object propValue, PropertyOptions options)
        {
            ParameterAsserts.AssertSchemaNs(schemaNs);
            ParameterAsserts.AssertPropName(propName);
            options = XmpNodeUtils.VerifySetOptions(options, propValue);
            var expPath  = XmpPathParser.ExpandXPath(schemaNs, propName);
            var propNode = XmpNodeUtils.FindNode(_tree, expPath, true, options);

            if (propNode != null)
            {
                SetNode(propNode, propValue, options, false);
            }
            else
            {
                throw new XmpException("Specified property does not exist", XmpErrorCode.BadXPath);
            }
        }
        /// <summary>Utility to find or create the array used by <c>separateArrayItems()</c>.</summary>
        /// <param name="schemaNs">a the namespace fo the array</param>
        /// <param name="arrayName">the name of the array</param>
        /// <param name="arrayOptions">the options for the array if newly created</param>
        /// <param name="xmp">the xmp object</param>
        /// <returns>Returns the array node.</returns>
        /// <exception cref="XmpException">Forwards exceptions</exception>
        private static XmpNode SeparateFindCreateArray(string schemaNs, string arrayName, PropertyOptions arrayOptions, XmpMeta xmp)
        {
            arrayOptions = XmpNodeUtils.VerifySetOptions(arrayOptions, null);

            if (!arrayOptions.IsOnlyArrayOptions)
            {
                throw new XmpException("Options can only provide array form", XmpErrorCode.BadOptions);
            }

            // Find the array node, make sure it is OK. Move the current children
            // aside, to be readded later if kept.
            var arrayPath = XmpPathParser.ExpandXPath(schemaNs, arrayName);
            var arrayNode = XmpNodeUtils.FindNode(xmp.GetRoot(), arrayPath, false, null);

            if (arrayNode != null)
            {
                // The array exists, make sure the form is compatible. Zero
                // arrayForm means take what exists.
                var arrayForm = arrayNode.Options;

                if (!arrayForm.IsArray || arrayForm.IsArrayAlternate)
                {
                    throw new XmpException("Named property must be non-alternate array", XmpErrorCode.BadXPath);
                }
                if (arrayOptions.EqualArrayTypes(arrayForm))
                {
                    throw new XmpException("Mismatch of specified and existing array form", XmpErrorCode.BadXPath);
                }
            }
            else
            {
                // *** Right error?
                // The array does not exist, try to create it.
                // don't modify the options handed into the method
                arrayOptions.IsArray = true;
                arrayNode            = XmpNodeUtils.FindNode(xmp.GetRoot(), arrayPath, true, arrayOptions);
                if (arrayNode == null)
                {
                    throw new XmpException("Failed to create named array", XmpErrorCode.BadXPath);
                }
            }
            return(arrayNode);
        }