/// <exception cref="XmpException"/> public IXmpProperty GetLocalizedText(string schemaNs, string altTextName, string genericLang, string specificLang) { 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); var arrayNode = XmpNodeUtils.FindNode(_tree, arrayPath, false, null); if (arrayNode == null) { return(null); } var result = XmpNodeUtils.ChooseLocalizedText(arrayNode, genericLang, specificLang); var match = (int)result[0]; var itemNode = (XmpNode)result[1]; if (match != XmpNodeUtils.CltNoValues) { return(new XmpProperty407(itemNode)); } return(null); }
/// <exception cref="XmpException"/> public IXmpProperty GetArrayItem(string schemaNs, string arrayName, int itemIndex) { ParameterAsserts.AssertSchemaNs(schemaNs); ParameterAsserts.AssertArrayName(arrayName); var itemPath = XmpPathFactory.ComposeArrayItemPath(arrayName, itemIndex); return(GetProperty(schemaNs, itemPath)); }
/// <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 IXmpProperty GetQualifier(string schemaNs, string propName, string qualNs, string qualName) { // qualNS and qualName are checked inside composeQualfierPath ParameterAsserts.AssertSchemaNs(schemaNs); ParameterAsserts.AssertPropName(propName); var qualPath = propName + XmpPathFactory.ComposeQualifierPath(qualNs, qualName); return(GetProperty(schemaNs, qualPath)); }
/// <exception cref="XmpException"/> public IXmpProperty GetStructField(string schemaNs, string structName, string fieldNs, string fieldName) { // fieldNS and fieldName are checked inside composeStructFieldPath ParameterAsserts.AssertSchemaNs(schemaNs); ParameterAsserts.AssertStructName(structName); var fieldPath = structName + XmpPathFactory.ComposeStructFieldPath(fieldNs, fieldName); return(GetProperty(schemaNs, fieldPath)); }
/// <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 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); }
public void DeleteArrayItem(string schemaNs, string arrayName, int itemIndex) { try { ParameterAsserts.AssertSchemaNs(schemaNs); ParameterAsserts.AssertArrayName(arrayName); var itemPath = XmpPathFactory.ComposeArrayItemPath(arrayName, itemIndex); DeleteProperty(schemaNs, itemPath); } catch (XmpException) { } }
public bool DoesArrayItemExist(string schemaNs, string arrayName, int itemIndex) { try { ParameterAsserts.AssertSchemaNs(schemaNs); ParameterAsserts.AssertArrayName(arrayName); var path = XmpPathFactory.ComposeArrayItemPath(arrayName, itemIndex); return(DoesPropertyExist(schemaNs, path)); } catch (XmpException) { return(false); } }
public void DeleteStructField(string schemaNs, string structName, string fieldNs, string fieldName) { try { // fieldNS and fieldName are checked inside composeStructFieldPath ParameterAsserts.AssertSchemaNs(schemaNs); ParameterAsserts.AssertStructName(structName); var fieldPath = structName + XmpPathFactory.ComposeStructFieldPath(fieldNs, fieldName); DeleteProperty(schemaNs, fieldPath); } catch (XmpException) { } }
public void DeleteQualifier(string schemaNs, string propName, string qualNs, string qualName) { try { // Note: qualNS and qualName are checked inside composeQualfierPath ParameterAsserts.AssertSchemaNs(schemaNs); ParameterAsserts.AssertPropName(propName); var qualPath = propName + XmpPathFactory.ComposeQualifierPath(qualNs, qualName); DeleteProperty(schemaNs, qualPath); } catch (XmpException) { } }
public bool DoesQualifierExist(string schemaNs, string propName, string qualNs, string qualName) { try { // qualNS and qualName are checked inside composeQualifierPath() ParameterAsserts.AssertSchemaNs(schemaNs); ParameterAsserts.AssertPropName(propName); var path = XmpPathFactory.ComposeQualifierPath(qualNs, qualName); return(DoesPropertyExist(schemaNs, propName + path)); } catch (XmpException) { return(false); } }
public bool DoesStructFieldExist(string schemaNs, string structName, string fieldNs, string fieldName) { try { // fieldNS and fieldName are checked inside composeStructFieldPath() ParameterAsserts.AssertSchemaNs(schemaNs); ParameterAsserts.AssertStructName(structName); var path = XmpPathFactory.ComposeStructFieldPath(fieldNs, fieldName); return(DoesPropertyExist(schemaNs, structName + path)); } catch (XmpException) { return(false); } }
public bool DoesPropertyExist(string schemaNs, string propName) { try { ParameterAsserts.AssertSchemaNs(schemaNs); ParameterAsserts.AssertPropName(propName); var expPath = XmpPathParser.ExpandXPath(schemaNs, propName); var propNode = XmpNodeUtils.FindNode(_tree, expPath, false, null); return(propNode != null); } catch (XmpException) { return(false); } }
/// <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); } }
public void DeleteProperty(string schemaNs, string propName) { try { ParameterAsserts.AssertSchemaNs(schemaNs); ParameterAsserts.AssertPropName(propName); var expPath = XmpPathParser.ExpandXPath(schemaNs, propName); var propNode = XmpNodeUtils.FindNode(_tree, expPath, false, null); if (propNode != null) { XmpNodeUtils.DeleteNode(propNode); } } catch (XmpException) { } }
/// <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 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 int CountArrayItems(string schemaNs, string arrayName) { ParameterAsserts.AssertSchemaNs(schemaNs); ParameterAsserts.AssertArrayName(arrayName); var arrayPath = XmpPathParser.ExpandXPath(schemaNs, arrayName); var arrayNode = XmpNodeUtils.FindNode(_tree, arrayPath, false, null); if (arrayNode == null) { return(0); } if (arrayNode.Options.IsArray) { return(arrayNode.GetChildrenLength()); } throw new XmpException("The named property is not an array", XmpErrorCode.BadXPath); }
/// <summary>Returns a property, but the result value can be requested.</summary> /// <param name="schemaNs">a schema namespace</param> /// <param name="propName">a property name or path</param> /// <param name="valueType">the type of the value, see VALUE_...</param> /// <returns> /// Returns the node value as an object according to the /// <c>valueType</c>. /// </returns> /// <exception cref="XmpException">Collects any exception that occurs.</exception> private object GetPropertyObject(string schemaNs, string propName, ValueType valueType) { ParameterAsserts.AssertSchemaNs(schemaNs); ParameterAsserts.AssertPropName(propName); var expPath = XmpPathParser.ExpandXPath(schemaNs, propName); var propNode = XmpNodeUtils.FindNode(_tree, expPath, false, null); if (propNode != null) { if (valueType != ValueType.String && propNode.Options.IsCompositeProperty) { throw new XmpException("Property must be simple when a value type is requested", XmpErrorCode.BadXPath); } return(EvaluateNodeValue(valueType, propNode)); } return(null); }
public string RegisterNamespace(string namespaceUri, string suggestedPrefix) { lock (_lock) { ParameterAsserts.AssertSchemaNs(namespaceUri); ParameterAsserts.AssertPrefix(suggestedPrefix); if (suggestedPrefix[suggestedPrefix.Length - 1] != ':') { suggestedPrefix += ':'; } if (!Utils.IsXmlNameNs(suggestedPrefix.Substring(0, suggestedPrefix.Length - 1 - 0))) { throw new XmpException("The prefix is a bad XML name", XmpErrorCode.BadXml); } string registeredPrefix; if (_namespaceToPrefixMap.TryGetValue(namespaceUri, out registeredPrefix)) { // Return the actual prefix return(registeredPrefix); } if (_prefixToNamespaceMap.ContainsKey(suggestedPrefix)) { // the namespace is new, but the prefix is already engaged, // we generate a new prefix out of the suggested var generatedPrefix = suggestedPrefix; for (var i = 1; _prefixToNamespaceMap.ContainsKey(generatedPrefix); i++) { generatedPrefix = suggestedPrefix.Substring(0, suggestedPrefix.Length - 1 - 0) + "_" + i + "_:"; } suggestedPrefix = generatedPrefix; } _prefixToNamespaceMap[suggestedPrefix] = namespaceUri; _namespaceToPrefixMap[namespaceUri] = suggestedPrefix; // Return the suggested prefix return(suggestedPrefix); } }
/// <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)); } } }
/// <param name="xmp">The XMP object containing the array to be catenated.</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="separator"> /// The string to be used to separate the items in the catenated /// string. Defaults to "; ", ASCII semicolon and space /// (U+003B, U+0020). /// </param> /// <param name="quotes"> /// The characters to be used as quotes around array items that /// contain a separator. Defaults to '"' /// </param> /// <param name="allowCommas">Option flag to control the catenation.</param> /// <returns>Returns the string containing the catenated array items.</returns> /// <exception cref="XmpException">Forwards the Exceptions from the metadata processing</exception> public static string CatenateArrayItems(IXmpMeta xmp, string schemaNs, string arrayName, string separator, string quotes, bool allowCommas) { ParameterAsserts.AssertSchemaNs(schemaNs); ParameterAsserts.AssertArrayName(arrayName); ParameterAsserts.AssertImplementation(xmp); if (string.IsNullOrEmpty(separator)) { separator = "; "; } if (string.IsNullOrEmpty(quotes)) { quotes = "\""; } var xmpImpl = (XmpMeta)xmp; // Return an empty result if the array does not exist, // hurl if it isn't the right form. var arrayPath = XmpPathParser.ExpandXPath(schemaNs, arrayName); var arrayNode = XmpNodeUtils.FindNode(xmpImpl.GetRoot(), arrayPath, false, null); if (arrayNode == null) { return(string.Empty); } if (!arrayNode.Options.IsArray || arrayNode.Options.IsArrayAlternate) { throw new XmpException("Named property must be non-alternate array", XmpErrorCode.BadParam); } // Make sure the separator is OK. CheckSeparator(separator); // Make sure the open and close quotes are a legitimate pair. var openQuote = quotes[0]; var closeQuote = CheckQuotes(quotes, openQuote); // Build the result, quoting the array items, adding separators. // Hurl if any item isn't simple. var catenatedString = new StringBuilder(); for (var it = arrayNode.IterateChildren(); it.HasNext();) { var currItem = (XmpNode)it.Next(); if (currItem.Options.IsCompositeProperty) { throw new XmpException("Array items must be simple", XmpErrorCode.BadParam); } var str = ApplyQuotes(currItem.Value, openQuote, closeQuote, allowCommas); catenatedString.Append(str); if (it.HasNext()) { catenatedString.Append(separator); } } return(catenatedString.ToString()); }
/// <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); } }