/// <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="iText.Kernel.XMP.XMPException">Forwards XMP errors</exception> private static void CompareAliasedSubtrees(XMPNode aliasNode, XMPNode baseNode, bool outerCall) { if (!aliasNode.GetValue().Equals(baseNode.GetValue()) || aliasNode.GetChildrenLength() != baseNode.GetChildrenLength()) { throw new XMPException("Mismatch between alias and base nodes", XMPError.BADXMP); } if (!outerCall && (!aliasNode.GetName().Equals(baseNode.GetName()) || !aliasNode.GetOptions().Equals(baseNode.GetOptions()) || aliasNode.GetQualifierLength() != baseNode.GetQualifierLength())) { throw new XMPException("Mismatch between alias and base nodes", XMPError.BADXMP); } for (IEnumerator an = aliasNode.IterateChildren(), bn = baseNode.IterateChildren(); an.MoveNext() && bn.MoveNext();) { XMPNode aliasChild = (XMPNode)an.Current; XMPNode baseChild = (XMPNode)bn.Current; CompareAliasedSubtrees(aliasChild, baseChild, false); } for (IEnumerator an = aliasNode.IterateQualifier(), bn = baseNode.IterateQualifier(); an.MoveNext() && bn.MoveNext();) { XMPNode aliasQual = (XMPNode)an.Current; XMPNode baseQual = (XMPNode)bn.Current; CompareAliasedSubtrees(aliasQual, baseQual, false); } }
/// <summary> /// Constructor </summary> /// <param name="parentNode"> the node which children shall be iterated. </param> /// <param name="parentPath"> the full path of the former node without the leaf node. </param> public NodeIteratorChildren(XMPIteratorImpl outerInstance, XMPNode parentNode, string parentPath) : base(outerInstance, parentNode, parentPath, 0) { this.outerInstance = outerInstance; if (parentNode.GetOptions().IsSchemaNode()) { outerInstance.SetBaseNS(parentNode.GetName()); } this.parentPath = AccumulatePath(parentNode, parentPath, 1); this.childrenIterator = parentNode.IterateChildren(); }
/// <summary>Make sure that the array is well-formed AltText.</summary> /// <remarks> /// Make sure that the array is well-formed AltText. Each item must be simple /// and have an "xml:lang" qualifier. If repairs are needed, keep simple /// non-empty items by adding the "xml:lang" with value "x-repair". /// </remarks> /// <param name="arrayNode">the property node of the array to repair.</param> /// <exception cref="iText.Kernel.XMP.XMPException">Forwards unexpected exceptions. /// </exception> private static void RepairAltText(XMPNode arrayNode) { if (arrayNode == null || !arrayNode.GetOptions().IsArray()) { // Already OK or not even an array. return; } // fix options arrayNode.GetOptions().SetArrayOrdered(true); arrayNode.GetOptions().SetArrayAlternate(true); arrayNode.GetOptions().SetArrayAltText(true); ArrayList currChildsToRemove = new ArrayList(); IEnumerator it = arrayNode.IterateChildren(); while (it.MoveNext()) { XMPNode currChild = (XMPNode)it.Current; if (currChild == null) { continue; } if (currChild.GetOptions().IsCompositeProperty()) { // Delete non-simple children. currChildsToRemove.Add(currChild); } else if (!currChild.GetOptions().GetHasLanguage()) { string childValue = currChild.GetValue(); if (String.IsNullOrEmpty(childValue)) { // Delete empty valued children that have no xml:lang. currChildsToRemove.Add(currChild); } else { // Add an xml:lang qualifier with the value "x-repair". XMPNode repairLang = new XMPNode(XMPConst.XML_LANG, "x-repair", null); currChild.AddQualifier(repairLang); } } } foreach (object o in currChildsToRemove) { arrayNode.GetChildren().Remove(o); } }
/// <summary>See if an array is an alt-text array.</summary> /// <remarks> /// See if an array is an alt-text array. If so, make sure the x-default item /// is first. /// </remarks> /// <param name="arrayNode">the array node to check if its an alt-text array</param> internal static void DetectAltText(XMPNode arrayNode) { if (arrayNode.GetOptions().IsArrayAlternate() && arrayNode.HasChildren()) { bool isAltText = false; for (IEnumerator it = arrayNode.IterateChildren(); it.MoveNext();) { XMPNode child = (XMPNode)it.Current; if (child.GetOptions().GetHasLanguage()) { isAltText = true; break; } } if (isAltText) { arrayNode.GetOptions().SetArrayAltText(true); NormalizeLangArray(arrayNode); } } }
/// <summary> /// <ol> /// <li>Look for an exact match with the specific language. /// </summary> /// <remarks> /// <ol> /// <li>Look for an exact match with the specific language. /// <li>If a generic language is given, look for partial matches. /// <li>Look for an "x-default"-item. /// <li>Choose the first item. /// </ol> /// </remarks> /// <param name="arrayNode">the alt text array node</param> /// <param name="genericLang">the generic language</param> /// <param name="specificLang">the specific language</param> /// <returns> /// Returns the kind of match as an Integer and the found node in an /// array. /// </returns> /// <exception cref="iText.Kernel.XMP.XMPException"/> internal static Object[] ChooseLocalizedText(XMPNode arrayNode, String genericLang , String specificLang) { // See if the array has the right form. Allow empty alt arrays, // that is what parsing returns. if (!arrayNode.GetOptions().IsArrayAltText()) { throw new XMPException("Localized text array is not alt-text", XMPError.BADXPATH); } else { if (!arrayNode.HasChildren()) { return(new Object[] { XMPNodeUtils.CLT_NO_VALUES, null }); } } int foundGenericMatches = 0; XMPNode resultNode = null; XMPNode xDefault = null; // Look for the first partial match with the generic language. for (IEnumerator it = arrayNode.IterateChildren(); it.MoveNext();) { XMPNode currItem = (XMPNode)it.Current; // perform some checks on the current item if (currItem.GetOptions().IsCompositeProperty()) { throw new XMPException("Alt-text array item is not simple", XMPError.BADXPATH); } else { if (!currItem.HasQualifier() || !XML_LANG.Equals(currItem.GetQualifier(1).GetName ())) { throw new XMPException("Alt-text array item has no language qualifier", XMPError. BADXPATH); } } String currLang = currItem.GetQualifier(1).GetValue(); // Look for an exact match with the specific language. if (specificLang.Equals(currLang)) { return(new Object[] { XMPNodeUtils.CLT_SPECIFIC_MATCH, currItem }); } else { if (genericLang != null && currLang.StartsWith(genericLang)) { if (resultNode == null) { resultNode = currItem; } // ! Don't return/break, need to look for other matches. foundGenericMatches++; } else { if (X_DEFAULT.Equals(currLang)) { xDefault = currItem; } } } } // evaluate loop if (foundGenericMatches == 1) { return(new Object[] { XMPNodeUtils.CLT_SINGLE_GENERIC, resultNode }); } else { if (foundGenericMatches > 1) { return(new Object[] { XMPNodeUtils.CLT_MULTIPLE_GENERIC, resultNode }); } else { if (xDefault != null) { return(new Object[] { XMPNodeUtils.CLT_XDEFAULT, xDefault }); } else { // Everything failed, choose the first item. return(new Object[] { XMPNodeUtils.CLT_FIRST_ITEM, arrayNode.GetChild(1) }); } } } }
/// <summary>Visit all of the top level nodes looking for aliases.</summary> /// <remarks> /// Visit all of the top level nodes looking for aliases. If there is /// no base, transplant the alias subtree. If there is a base and strict /// aliasing is on, make sure the alias and base subtrees match. /// </remarks> /// <param name="tree">the root of the metadata tree</param> /// <param name="options">th parsing options</param> /// <exception cref="iText.Kernel.XMP.XMPException">Forwards XMP errors</exception> private static void MoveExplicitAliases(XMPNode tree, ParseOptions options) { if (!tree.GetHasAliases()) { return; } tree.SetHasAliases(false); bool strictAliasing = options.GetStrictAliasing(); IEnumerator schemaIt = tree.GetUnmodifiableChildren().GetEnumerator(); while (schemaIt.MoveNext()) { XMPNode currSchema = (XMPNode)schemaIt.Current; if (currSchema == null) { continue; } if (!currSchema.GetHasAliases()) { continue; } List <XMPNode> currPropsToRemove = new List <XMPNode>(); IEnumerator propertyIt = currSchema.IterateChildren(); while (propertyIt.MoveNext()) { XMPNode currProp = (XMPNode)propertyIt.Current; if (currProp == null) { continue; } if (!currProp.IsAlias()) { continue; } currProp.SetAlias(false); // Find the base path, look for the base schema and root node. XMPAliasInfo info = XMPMetaFactory.GetSchemaRegistry().FindAlias(currProp.GetName()); if (info != null) { // find or create schema XMPNode baseSchema = XMPNodeUtils.FindSchemaNode(tree, info.GetNamespace(), null, true); baseSchema.SetImplicit(false); XMPNode baseNode = XMPNodeUtils.FindChildNode(baseSchema, info.GetPrefix() + info.GetPropName(), false); if (baseNode == null) { if (info.GetAliasForm().IsSimple()) { // A top-to-top alias, transplant the property. // change the alias property name to the base name string qname = info.GetPrefix() + info.GetPropName(); currProp.SetName(qname); baseSchema.AddChild(currProp); } else { // An alias to an array item, // create the array and transplant the property. baseNode = new XMPNode(info.GetPrefix() + info.GetPropName(), info.GetAliasForm().ToPropertyOptions()); baseSchema.AddChild(baseNode); TransplantArrayItemAlias(currProp, baseNode); } currPropsToRemove.Add(currProp); } else if (info.GetAliasForm().IsSimple()) { // The base node does exist and this is a top-to-top alias. // Check for conflicts if strict aliasing is on. // Remove and delete the alias subtree. if (strictAliasing) { CompareAliasedSubtrees(currProp, baseNode, true); } currPropsToRemove.Add(currProp); } else { // This is an alias to an array item and the array exists. // Look for the aliased item. // Then transplant or check & delete as appropriate. XMPNode itemNode = null; if (info.GetAliasForm().IsArrayAltText()) { int xdIndex = XMPNodeUtils.LookupLanguageItem(baseNode, XMPConst.X_DEFAULT); if (xdIndex != -1) { itemNode = baseNode.GetChild(xdIndex); } } else if (baseNode.HasChildren()) { itemNode = baseNode.GetChild(1); } if (itemNode == null) { TransplantArrayItemAlias(currProp, baseNode); } else { if (strictAliasing) { CompareAliasedSubtrees(currProp, itemNode, true); } } currPropsToRemove.Add(currProp); } } } foreach (XMPNode o in currPropsToRemove) { currSchema.RemoveChild(o); } currPropsToRemove.Clear(); currSchema.SetHasAliases(false); } }
public virtual void SetLocalizedText(String schemaNS, String altTextName, String genericLang, String specificLang, String itemValue, PropertyOptions options) { ParameterAsserts.AssertSchemaNS(schemaNS); ParameterAsserts.AssertArrayName(altTextName); ParameterAsserts.AssertSpecificLang(specificLang); genericLang = genericLang != null ? iText.Kernel.XMP.Impl.Utils.NormalizeLangValue (genericLang) : null; specificLang = iText.Kernel.XMP.Impl.Utils.NormalizeLangValue(specificLang); XMPPath arrayPath = XMPPathParser.ExpandXPath(schemaNS, altTextName); // Find the array node and set the options if it was just created. XMPNode arrayNode = XMPNodeUtils.FindNode(tree, arrayPath, true, new PropertyOptions (PropertyOptions.ARRAY | PropertyOptions.ARRAY_ORDERED | PropertyOptions.ARRAY_ALTERNATE | PropertyOptions.ARRAY_ALT_TEXT)); if (arrayNode == null) { throw new XMPException("Failed to find or create array node", XMPError.BADXPATH); } else { if (!arrayNode.GetOptions().IsArrayAltText()) { if (!arrayNode.HasChildren() && arrayNode.GetOptions().IsArrayAlternate()) { arrayNode.GetOptions().SetArrayAltText(true); } else { throw new XMPException("Specified property is no alt-text array", XMPError.BADXPATH ); } } } // Make sure the x-default item, if any, is first. bool haveXDefault = false; XMPNode xdItem = null; foreach (XMPNode currItem in arrayNode.GetChildren()) { if (!currItem.HasQualifier() || !XMPConst.XML_LANG.Equals(currItem.GetQualifier(1 ).GetName())) { throw new XMPException("Language qualifier must be first", XMPError.BADXPATH); } else { if (XMPConst.X_DEFAULT.Equals(currItem.GetQualifier(1).GetValue())) { 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. Object[] result = XMPNodeUtils.ChooseLocalizedText(arrayNode, genericLang, specificLang ); int match = (int)result[0]; XMPNode itemNode = (XMPNode)result[1]; bool specificXDefault = XMPConst.X_DEFAULT.Equals(specificLang); switch (match) { case XMPNodeUtils.CLT_NO_VALUES: { // Create the array items for the specificLang and x-default, with // x-default first. XMPNodeUtils.AppendLangItem(arrayNode, XMPConst.X_DEFAULT, itemValue); haveXDefault = true; if (!specificXDefault) { XMPNodeUtils.AppendLangItem(arrayNode, specificLang, itemValue); } break; } case XMPNodeUtils.CLT_SPECIFIC_MATCH: { if (!specificXDefault) { // Update the specific item, update x-default if it matches the // old value. if (haveXDefault && xdItem != itemNode && xdItem != null && xdItem.GetValue().Equals (itemNode.GetValue())) { xdItem.SetValue(itemValue); } // ! Do this after the x-default check! itemNode.SetValue(itemValue); } else { // Update all items whose values match the old x-default value. System.Diagnostics.Debug.Assert(haveXDefault && xdItem == itemNode); for (IEnumerator it_1 = arrayNode.IterateChildren(); it_1.MoveNext(); ) { XMPNode currItem = (XMPNode)it_1.Current; if (currItem == xdItem || !currItem.GetValue().Equals(xdItem != null ? xdItem.GetValue () : null)) { continue; } currItem.SetValue(itemValue); } // And finally do the x-default item. if (xdItem != null) { xdItem.SetValue(itemValue); } } break; } case XMPNodeUtils.CLT_SINGLE_GENERIC: { // Update the generic item, update x-default if it matches the old // value. if (haveXDefault && xdItem != itemNode && xdItem != null && xdItem.GetValue().Equals (itemNode.GetValue())) { xdItem.SetValue(itemValue); } itemNode.SetValue(itemValue); // ! Do this after // the x-default // check! break; } case XMPNodeUtils.CLT_MULTIPLE_GENERIC: { // Create the specific language, ignore x-default. XMPNodeUtils.AppendLangItem(arrayNode, specificLang, itemValue); if (specificXDefault) { haveXDefault = true; } break; } case XMPNodeUtils.CLT_XDEFAULT: { // Create the specific language, update x-default if it was the only // item. if (xdItem != null && arrayNode.GetChildrenLength() == 1) { xdItem.SetValue(itemValue); } XMPNodeUtils.AppendLangItem(arrayNode, specificLang, itemValue); break; } case XMPNodeUtils.CLT_FIRST_ITEM: { // Create the specific language, don't add an x-default item. XMPNodeUtils.AppendLangItem(arrayNode, specificLang, itemValue); if (specificXDefault) { haveXDefault = true; } break; } default: { // does not happen under normal circumstances throw new XMPException("Unexpected result from ChooseLocalizedText", XMPError.INTERNALFAILURE ); } } // Add an x-default at the front if needed. if (!haveXDefault && arrayNode.GetChildrenLength() == 1) { XMPNodeUtils.AppendLangItem(arrayNode, XMPConst.X_DEFAULT, itemValue); } }