/// <summary>Find or create a schema node if <c>createNodes</c> 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, <c>null</c> otherwise. /// Note: If <c>createNodes</c> is <c>true</c>, 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 var schemaNode = tree.FindChildByName(namespaceUri); if (schemaNode == null && createNodes) { var po = new PropertyOptions { IsSchemaNode = true }; schemaNode = new XmpNode(namespaceUri, po) { IsImplicit = true }; // only previously registered schema namespaces are allowed in the XMP tree. var 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", XmpErrorCode.BadSchema); } } schemaNode.Value = prefix; tree.AddChild(schemaNode); } return schemaNode; }
/// <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); }
/// <exception cref="XmpException"/> public void SetPropertyBase64(string schemaNs, string propName, byte[] propValue, PropertyOptions options) { SetProperty(schemaNs, propName, propValue, options); }
/// <exception cref="XmpException"/> public void SetPropertyCalendar(string schemaNs, string propName, Calendar propValue, PropertyOptions options) { SetProperty(schemaNs, propName, propValue, options); }
/// <exception cref="XmpException"/> public void SetPropertyDate(string schemaNs, string propName, IXmpDateTime propValue, PropertyOptions options) { SetProperty(schemaNs, propName, propValue, options); }
/// <summary>Find or create a child node under a given parent node.</summary> /// <remarks> /// Find or create a child node under a given parent node. If the parent node is no /// Returns the found or created child node. /// </remarks> /// <param name="parent">the parent node</param> /// <param name="childName">the node name to find</param> /// <param name="createNodes">flag, if new nodes shall be created.</param> /// <returns>Returns the found or created node or <c>null</c>.</returns> /// <exception cref="XmpException">Thrown if</exception> internal static XmpNode FindChildNode(XmpNode parent, string childName, bool createNodes) { if (!parent.Options.IsSchemaNode && !parent.Options.IsStruct) { if (!parent.IsImplicit) { throw new XmpException("Named children only allowed for schemas and structs", XmpErrorCode.BadXPath); } if (parent.Options.IsArray) { throw new XmpException("Named children not allowed for arrays", XmpErrorCode.BadXPath); } if (createNodes) { parent.Options.IsStruct = true; } } var childNode = parent.FindChildByName(childName); if (childNode == null && createNodes) { var options = new PropertyOptions(); childNode = new XmpNode(childName, options) { IsImplicit = true }; parent.AddChild(childNode); } Debug.Assert(childNode != null || !createNodes); return childNode; }
/// <summary>Separate a single edit string into an array of strings.</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> /// <exception cref="XmpException"/> public static void SeparateArrayItems(IXmpMeta xmp, string schemaNs, string arrayName, string catedStr, PropertyOptions arrayOptions, bool preserveCommas) { Impl.XmpUtils.SeparateArrayItems(xmp, schemaNs, arrayName, catedStr, arrayOptions, preserveCommas); }
/// <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 SetQualifier(string schemaNs, string propName, string qualNs, string qualName, string qualValue, PropertyOptions options) { ParameterAsserts.AssertSchemaNs(schemaNs); ParameterAsserts.AssertPropName(propName); if (!DoesPropertyExist(schemaNs, propName)) { throw new XmpException("Specified property does not exist!", XmpErrorCode.BadXPath); } var qualPath = propName + XmpPathFactory.ComposeQualifierPath(qualNs, qualName); SetProperty(schemaNs, qualPath, qualValue, options); }
/// <exception cref="XmpException"/> public void SetPropertyBoolean(string schemaNs, string propName, bool propValue, PropertyOptions options) { SetProperty(schemaNs, propName, propValue ? XmpConstants.TrueString : XmpConstants.FalseString, options); }
/// <exception cref="XmpException"/> public 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); var arrayPath = XmpPathParser.ExpandXPath(schemaNs, altTextName); // Find the array node and set the options if it was just created. var arrayNode = XmpNodeUtils.FindNode(_tree, arrayPath, true, new PropertyOptions(PropertyOptions.ArrayFlag | PropertyOptions.ArrayOrderedFlag | PropertyOptions.ArrayAlternateFlag | PropertyOptions.ArrayAltTextFlag)); if (arrayNode == null) { throw new XmpException("Failed to find or create array node", XmpErrorCode.BadXPath); } if (!arrayNode.Options.IsArrayAltText) { if (!arrayNode.HasChildren && arrayNode.Options.IsArrayAlternate) { arrayNode.Options.IsArrayAltText = true; } else { throw new XmpException("Specified property is no alt-text array", XmpErrorCode.BadXPath); } } // Make sure the x-default item, if any, is first. var haveXDefault = false; XmpNode xdItem = null; for (var it = arrayNode.IterateChildren(); it.HasNext(); ) { var currItem = (XmpNode)it.Next(); if (!currItem.HasQualifier || !XmpConstants.XmlLang.Equals(currItem.GetQualifier(1).Name)) { throw new XmpException("Language qualifier must be first", XmpErrorCode.BadXPath); } if (XmpConstants.XDefault.Equals(currItem.GetQualifier(1).Value)) { xdItem = currItem; haveXDefault = true; break; } } // Moves x-default to the beginning of the array if (xdItem != null && arrayNode.GetChildrenLength() > 1) { arrayNode.RemoveChild(xdItem); arrayNode.AddChild(1, xdItem); } // Find the appropriate item. // chooseLocalizedText will make sure the array is a language // alternative. var result = XmpNodeUtils.ChooseLocalizedText(arrayNode, genericLang, specificLang); var match = (int)result[0]; var itemNode = (XmpNode)result[1]; var specificXDefault = XmpConstants.XDefault.Equals(specificLang); switch (match) { case XmpNodeUtils.CltNoValues: { // Create the array items for the specificLang and x-default, with // x-default first. XmpNodeUtils.AppendLangItem(arrayNode, XmpConstants.XDefault, itemValue); haveXDefault = true; if (!specificXDefault) { XmpNodeUtils.AppendLangItem(arrayNode, specificLang, itemValue); } break; } case XmpNodeUtils.CltSpecificMatch: { 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); for (var it1 = arrayNode.IterateChildren(); it1.HasNext(); ) { var currItem = (XmpNode)it1.Next(); 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.CltSingleGeneric: { // 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.CltMultipleGeneric: { // Create the specific language, ignore x-default. XmpNodeUtils.AppendLangItem(arrayNode, specificLang, itemValue); if (specificXDefault) { haveXDefault = true; } break; } case XmpNodeUtils.CltXdefault: { // Create the specific language, update x-default if it was the only // item. if (xdItem != null && arrayNode.GetChildrenLength() == 1) { xdItem.Value = itemValue; } XmpNodeUtils.AppendLangItem(arrayNode, specificLang, itemValue); break; } case XmpNodeUtils.CltFirstItem: { // 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", XmpErrorCode.InternalFailure); } } // Add an x-default at the front if needed. if (!haveXDefault && arrayNode.GetChildrenLength() == 1) { XmpNodeUtils.AppendLangItem(arrayNode, XmpConstants.XDefault, itemValue); } }
/// <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; }
/// <summary> /// See <see cref="XmpCore.XmpUtils.SeparateArrayItems(IXmpMeta, string, string, string, PropertyOptions, bool)"/>. /// </summary> /// <param name="xmp">The XMP object containing the array to be updated.</param> /// <param name="schemaNs"> /// The schema namespace URI for the array. Must not be null or the empty string. /// </param> /// <param name="arrayName"> /// The name of the array. May be a general path expression, must /// not be null or the empty string. Each item in the array must /// be a simple string value. /// </param> /// <param name="catedStr">The string to be separated into the array items.</param> /// <param name="arrayOptions">Option flags to control the separation.</param> /// <param name="preserveCommas">Flag if commas shall be preserved</param> /// <exception cref="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", XmpErrorCode.BadParam); ParameterAsserts.AssertImplementation(xmp); var xmpImpl = (XmpMeta)xmp; // Keep a zero value, has special meaning below. var arrayNode = SeparateFindCreateArray(schemaNs, arrayName, arrayOptions, xmpImpl); // Extract the item values one at a time, until the whole input string is done. var charKind = UnicodeKind.Normal; var ch = (char)0; var itemEnd = 0; var endPos = catedStr.Length; while (itemEnd < endPos) { // Skip any leading spaces and separation characters. Always skip commas here. // They can be kept when within a value, but not when alone between values. int itemStart; for (itemStart = itemEnd; itemStart < endPos; itemStart++) { ch = catedStr[itemStart]; charKind = ClassifyCharacter(ch); if (charKind == UnicodeKind.Normal || charKind == UnicodeKind.Quote) break; } if (itemStart >= endPos) break; string itemValue; if (charKind != UnicodeKind.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 == UnicodeKind.Normal || charKind == UnicodeKind.Quote || (charKind == UnicodeKind.Comma && preserveCommas)) continue; if (charKind != UnicodeKind.Space) break; if ((itemEnd + 1) < endPos) { ch = catedStr[itemEnd + 1]; var nextKind = ClassifyCharacter(ch); if (nextKind == UnicodeKind.Normal || nextKind == UnicodeKind.Quote || (nextKind == UnicodeKind.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. var openQuote = ch; var closeQuote = GetClosingQuote(openQuote); itemStart++; // Skip the opening quote; itemValue = string.Empty; for (itemEnd = itemStart; itemEnd < endPos; itemEnd++) { ch = catedStr[itemEnd]; charKind = ClassifyCharacter(ch); if (charKind != UnicodeKind.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]; else 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 (!IsClosingQuote(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. var foundIndex = -1; for (var oldChild = 1; oldChild <= arrayNode.GetChildrenLength(); oldChild++) { if (itemValue.Equals(arrayNode.GetChild(oldChild).Value)) { foundIndex = oldChild; break; } } if (foundIndex < 0) arrayNode.AddChild(new XmpNode(XmpConstants.ArrayItemName, itemValue, null)); } }
/// <summary>Verifies the PropertyOptions for consistancy and updates them as needed.</summary> /// <remarks> /// Verifies the PropertyOptions for consistancy and updates them as needed. /// If options are <c>null</c> they are created with default values. /// </remarks> /// <param name="options">the <c>PropertyOptions</c></param> /// <param name="itemValue">the node value to set</param> /// <returns>Returns the updated options.</returns> /// <exception cref="XmpException">If the options are not consistant.</exception> internal static PropertyOptions VerifySetOptions(PropertyOptions options, object itemValue) { // create empty and fix existing options if (options == null) { // set default options options = new PropertyOptions(); } if (options.IsArrayAltText) { options.IsArrayAlternate = true; } if (options.IsArrayAlternate) { options.IsArrayOrdered = true; } if (options.IsArrayOrdered) { options.IsArray = true; } if (options.IsCompositeProperty && itemValue != null && itemValue.ToString().Length > 0) { throw new XmpException("Structs and arrays can't have values", XmpErrorCode.BadOptions); } options.AssertConsistency(options.GetOptions()); return options; }
/// <summary>Follow an expanded path expression to find or create a node.</summary> /// <param name="xmpTree">the node to begin the search.</param> /// <param name="xpath">the complete xpath</param> /// <param name="createNodes"> /// flag if nodes shall be created /// (when called by <c>setProperty()</c>) /// </param> /// <param name="leafOptions"> /// the options for the created leaf nodes (only when /// <c>createNodes == true</c>). /// </param> /// <returns>Returns the node if found or created or <c>null</c>.</returns> /// <exception cref="XmpException"> /// An exception is only thrown if an error occurred, /// not if a node was not found. /// </exception> internal static XmpNode FindNode(XmpNode xmpTree, XmpPath xpath, bool createNodes, PropertyOptions leafOptions) { // check if xpath is set. if (xpath == null || xpath.Size() == 0) { throw new XmpException("Empty XMPPath", XmpErrorCode.BadXPath); } // Root of implicitly created subtree to possible delete it later. // Valid only if leaf is new. XmpNode rootImplicitNode = null; XmpNode currNode = null; // resolve schema step currNode = FindSchemaNode(xmpTree, xpath.GetSegment(XmpPath.StepSchema).Name, createNodes); if (currNode == null) { return null; } if (currNode.IsImplicit) { currNode.IsImplicit = false; // Clear the implicit node bit. rootImplicitNode = currNode; } // Save the top most implicit node. // Now follow the remaining steps of the original XMPPath. try { for (var i = 1; i < xpath.Size(); i++) { currNode = FollowXPathStep(currNode, xpath.GetSegment(i), createNodes); if (currNode == null) { if (createNodes) { // delete implicitly created nodes DeleteNode(rootImplicitNode); } return null; } if (currNode.IsImplicit) { // clear the implicit node flag currNode.IsImplicit = false; // if node is an ALIAS (can be only in root step, auto-create array // when the path has been resolved from a not simple alias type if (i == 1 && xpath.GetSegment(i).IsAlias && xpath.GetSegment(i).AliasForm != 0) { currNode.Options.SetOption(xpath.GetSegment(i).AliasForm, true); } else { // "CheckImplicitStruct" in C++ if (i < xpath.Size() - 1 && xpath.GetSegment(i).Kind == XmpPath.StructFieldStep && !currNode.Options.IsCompositeProperty) { currNode.Options.IsStruct = true; } } if (rootImplicitNode == null) { rootImplicitNode = currNode; } } } } catch (XmpException) { // Save the top most implicit node. // if new notes have been created prior to the error, delete them if (rootImplicitNode != null) { DeleteNode(rootImplicitNode); } throw; } if (rootImplicitNode != null) { // set options only if a node has been successful created currNode.Options.MergeWith(leafOptions); currNode.Options = currNode.Options; } return currNode; }
/// <exception cref="XmpException"/> public void InsertArrayItem(string schemaNs, string arrayName, int itemIndex, string itemValue, PropertyOptions options) { ParameterAsserts.AssertSchemaNs(schemaNs); ParameterAsserts.AssertArrayName(arrayName); // Just lookup, don't try to create. var arrayPath = XmpPathParser.ExpandXPath(schemaNs, arrayName); var arrayNode = XmpNodeUtils.FindNode(_tree, arrayPath, false, null); if (arrayNode != null) { DoSetArrayItem(arrayNode, itemIndex, itemValue, options, true); } else { throw new XmpException("Specified array does not exist", XmpErrorCode.BadXPath); } }
/// <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); } }
/// <exception cref="XmpException"/> public void SetPropertyInteger(string schemaNs, string propName, int propValue, PropertyOptions options) { SetProperty(schemaNs, propName, propValue, options); }
/// <exception cref="XmpException"/> public void SetStructField(string schemaNs, string structName, string fieldNs, string fieldName, string fieldValue, PropertyOptions options) { ParameterAsserts.AssertSchemaNs(schemaNs); ParameterAsserts.AssertStructName(structName); var fieldPath = structName + XmpPathFactory.ComposeStructFieldPath(fieldNs, fieldName); SetProperty(schemaNs, fieldPath, fieldValue, options); }
/// <exception cref="XmpException"/> public void SetPropertyLong(string schemaNs, string propName, long propValue, PropertyOptions options) { SetProperty(schemaNs, propName, propValue, options); }
/// <summary> /// The internals for setProperty() and related calls, used after the node is /// found or created. /// </summary> /// <param name="node">the newly created node</param> /// <param name="value">the node value, can be <c>null</c></param> /// <param name="newOptions">options for the new node, must not be <c>null</c>.</param> /// <param name="deleteExisting">flag if the existing value is to be overwritten</param> /// <exception cref="XmpException">thrown if options and value do not correspond</exception> internal void SetNode(XmpNode node, object value, PropertyOptions newOptions, bool deleteExisting) { if (deleteExisting) { node.Clear(); } // its checked by setOptions(), if the merged result is a valid options set node.Options.MergeWith(newOptions); if (!node.Options.IsCompositeProperty) { // This is setting the value of a leaf node. XmpNodeUtils.SetNodeValue(node, value); } else { if (value != null && value.ToString().Length > 0) { throw new XmpException("Composite nodes can't have values", XmpErrorCode.BadXPath); } node.RemoveChildren(); } }
/// <exception cref="XmpException"/> public void SetPropertyDouble(string schemaNs, string propName, double propValue, PropertyOptions options) { SetProperty(schemaNs, propName, propValue, options); }
// Don't let failures (like a bad dc:rights form) stop other // cleanup. /// <summary> /// Initializes the map that contains the known arrays, that are fixed by <see cref="NormalizeDcArrays"/>. /// </summary> private static void InitDcArrays() { var bagForm = new PropertyOptions { IsArray = true }; var seqForm = new PropertyOptions { IsArray = true, IsArrayOrdered = true }; var altTextForm = new PropertyOptions { IsArray = true, IsArrayOrdered = true, IsArrayAlternate = true, IsArrayAltText = true }; _dcArrayForms = new Dictionary<string, PropertyOptions>(); // Properties supposed to be a "Bag". _dcArrayForms["dc:contributor"] = bagForm; _dcArrayForms["dc:language"] = bagForm; _dcArrayForms["dc:publisher"] = bagForm; _dcArrayForms["dc:relation"] = bagForm; _dcArrayForms["dc:subject"] = bagForm; _dcArrayForms["dc:type"] = bagForm; // Properties supposed to be a "Seq". _dcArrayForms["dc:creator"] = seqForm; _dcArrayForms["dc:date"] = seqForm; // Properties supposed to be an "Alt" in alternative-text form. _dcArrayForms["dc:description"] = altTextForm; _dcArrayForms["dc:rights"] = altTextForm; _dcArrayForms["dc:title"] = altTextForm; }
private static XmpNode AddChildNode(XmpMeta xmp, XmpNode xmpParent, XName nodeName, string nodeNamespacePrefix, string value, bool isTopLevel) { var registry = XmpMetaFactory.SchemaRegistry; var ns = nodeName.NamespaceName; string childName; if (ns != string.Empty) { if (XmpConstants.NsDcDeprecated.Equals(ns)) { // Fix a legacy DC namespace ns = XmpConstants.NsDC; } var prefix = registry.GetNamespacePrefix(ns); if (prefix == null) { prefix = nodeNamespacePrefix ?? DefaultPrefix; prefix = registry.RegisterNamespace(ns, prefix); } childName = prefix + nodeName.LocalName; } else { throw new XmpException("XML namespace required for all elements and attributes", XmpErrorCode.BadRdf); } // create schema node if not already there var childOptions = new PropertyOptions(); var isAlias = false; if (isTopLevel) { // Lookup the schema node, adjust the XMP parent pointer. // Incoming parent must be the tree root. var schemaNode = XmpNodeUtils.FindSchemaNode(xmp.GetRoot(), ns, DefaultPrefix, true); schemaNode.IsImplicit = false; // Clear the implicit node bit. // need runtime check for proper 32 bit code. xmpParent = schemaNode; // If this is an alias set the alias flag in the node // and the hasAliases flag in the tree. if (registry.FindAlias(childName) != null) { isAlias = true; xmp.GetRoot().HasAliases = true; schemaNode.HasAliases = true; } } // Make sure that this is not a duplicate of a named node. var isArrayItem = "rdf:li".Equals(childName); var isValueNode = "rdf:value".Equals(childName); // Create XMP node and so some checks var newChild = new XmpNode(childName, value, childOptions) { IsAlias = 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.IsStruct) { throw new XmpException("Misplaced rdf:value element", XmpErrorCode.BadRdf); } xmpParent.HasValueChild = true; } if (isArrayItem) { if (!xmpParent.Options.IsArray) { throw new XmpException("Misplaced rdf:li element", XmpErrorCode.BadRdf); } newChild.Name = XmpConstants.ArrayItemName; } return newChild; }