IterateChildren() public method

public IterateChildren ( ) : IEnumerator
return IEnumerator
Beispiel #1
0
        /// <summary>
        /// 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.
        /// </summary>
        /// <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 (!aliasNode.Value.Equals(baseNode.Value) || aliasNode.ChildrenLength != baseNode.ChildrenLength)
            {
                throw new XmpException("Mismatch between alias and base nodes", XmpError.BADXMP);
            }

            if (!outerCall &&
                (!aliasNode.Name.Equals(baseNode.Name) || !aliasNode.Options.Equals(baseNode.Options) ||
                 aliasNode.QualifierLength != baseNode.QualifierLength))
            {
                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)
            {
                _outerInstance = outerInstance;
                if (parentNode.Options.SchemaNode)
                {
                    outerInstance.BaseNs = parentNode.Name;
                }
                _parentPath = AccumulatePath(parentNode, parentPath, 1);

                _childrenIterator = parentNode.IterateChildren();
            }
Beispiel #3
0
        /// <seealso cref= XMPUtils#appendProperties(XMPMeta, XMPMeta, boolean, boolean) </seealso>
        /// <param name="source"> The source XMP object. </param>
        /// <param name="destination"> The destination XMP object. </param>
        /// <param name="doAllProperties"> Do internal properties in addition to external properties. </param>
        /// <param name="replaceOldValues"> Replace the values of existing properties. </param>
        /// <param name="deleteEmptyValues"> Delete destination values if source property is empty. </param>
        /// <exception cref="XmpException"> Forwards the Exceptions from the metadata processing </exception>
        public static void AppendProperties(IXmpMeta source, IXmpMeta destination, bool doAllProperties,
                                            bool replaceOldValues, bool deleteEmptyValues)
        {
            ParameterAsserts.AssertImplementation(source);
            ParameterAsserts.AssertImplementation(destination);

            XmpMetaImpl src  = (XmpMetaImpl)source;
            XmpMetaImpl dest = (XmpMetaImpl)destination;

            for (IEnumerator it = src.Root.IterateChildren(); it.MoveNext();)
            {
                XmpNode sourceSchema = (XmpNode)it.Current;
                if (sourceSchema == null)
                {
                    continue;
                }
                // Make sure we have a destination schema node
                XmpNode destSchema    = XmpNodeUtils.FindSchemaNode(dest.Root, sourceSchema.Name, false);
                bool    createdSchema = false;
                if (destSchema == null)
                {
                    PropertyOptions propertyOptions = new PropertyOptions();
                    propertyOptions.SchemaNode = true;
                    destSchema = new XmpNode(sourceSchema.Name, sourceSchema.Value,
                                             propertyOptions);
                    dest.Root.AddChild(destSchema);
                    createdSchema = true;
                }

                // Process the source schema's children.
                for (IEnumerator ic = sourceSchema.IterateChildren(); ic.MoveNext();)
                {
                    XmpNode sourceProp = (XmpNode)ic.Current;
                    if (sourceProp == null)
                    {
                        continue;
                    }
                    if (doAllProperties || !Utils.IsInternalProperty(sourceSchema.Name, sourceProp.Name))
                    {
                        AppendSubtree(dest, sourceProp, destSchema, replaceOldValues, deleteEmptyValues);
                    }
                }

                if (!destSchema.HasChildren() && (createdSchema || deleteEmptyValues))
                {
                    // Don't create an empty schema / remove empty schema.
                    dest.Root.RemoveChild(destSchema);
                }
            }
        }
Beispiel #4
0
        /// <summary>
        /// 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". </summary>
        /// <param name="arrayNode"> the property node of the array to repair. </param>
        /// <exception cref="XmpException"> Forwards unexpected exceptions. </exception>
        private static void RepairAltText(XmpNode arrayNode)
        {
            if (arrayNode == null || !arrayNode.Options.Array)
            {
                // Already OK or not even an array.
                return;
            }

            // fix options
            arrayNode.Options.ArrayOrdered   = true;
            arrayNode.Options.ArrayAlternate = true;
            arrayNode.Options.ArrayAltText   = true;
            ArrayList   currChildsToRemove = new ArrayList();
            IEnumerator it = arrayNode.IterateChildren();

            while (it.MoveNext())
            {
                XmpNode currChild = (XmpNode)it.Current;
                if (currChild == null)
                {
                    continue;
                }
                if (currChild.Options.CompositeProperty)
                {
                    // Delete non-simple children.
                    currChildsToRemove.Add(currChild);
                }
                else if (!currChild.Options.HasLanguage)
                {
                    string childValue = currChild.Value;
                    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.Children.Remove(o);
            }
        }
Beispiel #5
0
        /// <summary>
        /// See if an array is an alt-text array. If so, make sure the x-default item
        /// is first.
        /// </summary>
        /// <param name="arrayNode">
        ///            the array node to check if its an alt-text array </param>
        internal static void DetectAltText(XmpNode arrayNode)
        {
            if (arrayNode.Options.ArrayAlternate && arrayNode.HasChildren())
            {
                bool isAltText = false;
                for (IEnumerator it = arrayNode.IterateChildren(); it.MoveNext();)
                {
                    XmpNode child = (XmpNode)it.Current;
                    if (child != null && child.Options != null && child.Options.HasLanguage)
                    {
                        isAltText = true;
                        break;
                    }
                }

                if (isAltText)
                {
                    arrayNode.Options.ArrayAltText = true;
                    NormalizeLangArray(arrayNode);
                }
            }
        }
Beispiel #6
0
        /// <summary>
        /// Remove all schema children according to the flag
        /// <code>doAllProperties</code>. Empty schemas are automatically remove
        /// by <code>XMPNode</code>
        /// </summary>
        /// <param name="schemaNode">
        ///            a schema node </param>
        /// <param name="doAllProperties">
        ///            flag if all properties or only externals shall be removed. </param>
        /// <returns> Returns true if the schema is empty after the operation. </returns>
        private static bool RemoveSchemaChildren(XmpNode schemaNode, bool doAllProperties)
        {
            ArrayList currPropsToRemove = new ArrayList();

            for (IEnumerator it = schemaNode.IterateChildren(); it.MoveNext();)
            {
                XmpNode currProp = (XmpNode)it.Current;
                if (currProp == null)
                {
                    continue;
                }
                if (doAllProperties || !Utils.IsInternalProperty(schemaNode.Name, currProp.Name))
                {
                    currPropsToRemove.Add(currProp);
                }
            }
            foreach (XmpNode xmpNode in currPropsToRemove)
            {
                schemaNode.Children.Remove(xmpNode);
            }
            currPropsToRemove.Clear();
            return(!schemaNode.HasChildren());
        }
Beispiel #7
0
        /// <summary>
        /// Write each of the parent's simple unqualified properties as an attribute. Returns true if all
        /// of the properties are written as attributes.
        /// </summary>
        /// <param name="parentNode"> the parent property node </param>
        /// <param name="indent"> the current indent level </param>
        /// <returns> Returns true if all properties can be rendered as RDF attribute. </returns>
        /// <exception cref="IOException"> </exception>
        private bool SerializeCompactRdfAttrProps(XmpNode parentNode, int indent) {
            bool allAreAttrs = true;

            for (IEnumerator it = parentNode.IterateChildren(); it.MoveNext();) {
                XmpNode prop = (XmpNode) it.Current;

                if (prop != null && canBeRDFAttrProp(prop)) {
                    WriteNewline();
                    WriteIndent(indent);
                    Write(prop.Name);
                    Write("=\"");
                    AppendNodeValue(prop.Value, true);
                    Write('\"');
                }
                else {
                    allAreAttrs = false;
                }
            }
            return allAreAttrs;
        }
Beispiel #8
0
        /// <summary>
        /// Compares two nodes including its children and qualifier. </summary>
        /// <param name="leftNode"> an <code>XMPNode</code> </param>
        /// <param name="rightNode"> an <code>XMPNode</code> </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) {
            PropertyOptions leftForm = leftNode.Options;
            PropertyOptions rightForm = rightNode.Options;

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

            if (leftForm.Options == 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.Struct) {
                // Struct nodes, see if all fields match, ignoring order.

                if (leftNode.ChildrenLength != rightNode.ChildrenLength) {
                    return false;
                }

                for (IEnumerator it = leftNode.IterateChildren(); it.MoveNext();) {
                    XmpNode leftField = (XmpNode) it.Current;
                    if (leftField == null)
                        continue;
                    XmpNode 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.Array);

                for (IEnumerator il = leftNode.IterateChildren(); il.MoveNext();) {
                    XmpNode leftItem = (XmpNode) il.Current;
                    if (leftItem == null)
                        continue;

                    bool match = false;
                    for (IEnumerator ir = rightNode.IterateChildren(); ir.MoveNext();) {
                        XmpNode rightItem = (XmpNode) ir.Current;
                        if (rightItem == null)
                            continue;
                        if (ItemValuesMatch(leftItem, rightItem)) {
                            match = true;
                            break;
                        }
                    }
                    if (!match) {
                        return false;
                    }
                }
            }
            return true; // All of the checks passed.
        }
Beispiel #9
0
        /// <seealso cref= XMPUtilsImpl#appendProperties(XMPMeta, XMPMeta, boolean, boolean, boolean) </seealso>
        /// <param name="destXmp"> The destination XMP object. </param>
        /// <param name="sourceNode"> the source node </param>
        /// <param name="destParent"> the parent of the destination node </param>
        /// <param name="replaceOldValues"> Replace the values of existing properties. </param>
        /// <param name="deleteEmptyValues"> flag if properties with empty values should be deleted 
        /// 		   in the destination object. </param>
        /// <exception cref="XmpException"> </exception>
        private static void AppendSubtree(XmpMetaImpl destXmp, XmpNode sourceNode, XmpNode destParent,
                                          bool replaceOldValues, bool deleteEmptyValues) {
            XmpNode destNode = XmpNodeUtils.FindChildNode(destParent, sourceNode.Name, false);

            bool valueIsEmpty = false;
            if (deleteEmptyValues) {
                valueIsEmpty = sourceNode.Options.Simple
                                   ? string.IsNullOrEmpty(sourceNode.Value)
                                   : !sourceNode.HasChildren();
            }

            if (deleteEmptyValues && valueIsEmpty) {
                if (destNode != null) {
                    destParent.RemoveChild(destNode);
                }
            }
            else if (destNode == null) {
                // The one easy case, the destination does not exist.
                destParent.AddChild((XmpNode) sourceNode.Clone());
            }
            else if (replaceOldValues) {
                // The destination exists and should be replaced.
                destXmp.SetNode(destNode, sourceNode.Value, sourceNode.Options, true);
                destParent.RemoveChild(destNode);
                destNode = (XmpNode) sourceNode.Clone();
                destParent.AddChild(destNode);
            }
            else {
                // The destination exists and is not totally replaced. Structs and
                // arrays are merged.

                PropertyOptions sourceForm = sourceNode.Options;
                PropertyOptions destForm = destNode.Options;
                if (sourceForm != destForm) {
                    return;
                }
                if (sourceForm.Struct) {
                    // To merge a struct process the fields recursively. E.g. add simple missing fields.
                    // The recursive call to AppendSubtree will handle deletion for fields with empty 
                    // values.
                    for (IEnumerator it = sourceNode.IterateChildren(); it.MoveNext();) {
                        XmpNode sourceField = (XmpNode) it.Current;
                        if (sourceField == null)
                            continue;
                        AppendSubtree(destXmp, sourceField, destNode, replaceOldValues, deleteEmptyValues);
                        if (deleteEmptyValues && !destNode.HasChildren()) {
                            destParent.RemoveChild(destNode);
                        }
                    }
                }
                else if (sourceForm.ArrayAltText) {
                    // Merge AltText arrays by the "xml:lang" qualifiers. Make sure x-default is first. 
                    // Make a special check for deletion of empty values. Meaningful in AltText arrays 
                    // because the "xml:lang" qualifier provides unambiguous source/dest correspondence.
                    for (IEnumerator it = sourceNode.IterateChildren(); it.MoveNext();) {
                        XmpNode sourceItem = (XmpNode) it.Current;
                        if (sourceItem == null)
                            continue;
                        if (!sourceItem.HasQualifier() || !XML_LANG.Equals(sourceItem.GetQualifier(1).Name)) {
                            continue;
                        }

                        int destIndex = XmpNodeUtils.LookupLanguageItem(destNode, sourceItem.GetQualifier(1).Value);
                        if (deleteEmptyValues && (string.IsNullOrEmpty(sourceItem.Value))) {
                            if (destIndex != -1) {
                                destNode.RemoveChild(destIndex);
                                if (!destNode.HasChildren()) {
                                    destParent.RemoveChild(destNode);
                                }
                            }
                        }
                        else if (destIndex == -1) {
                            // Not replacing, keep the existing item.						
                            if (!X_DEFAULT.Equals(sourceItem.GetQualifier(1).Value) || !destNode.HasChildren()) {
                                sourceItem.CloneSubtree(destNode);
                            }
                            else {
                                XmpNode destItem = new XmpNode(sourceItem.Name, sourceItem.Value, sourceItem.Options);
                                sourceItem.CloneSubtree(destItem);
                                destNode.AddChild(1, destItem);
                            }
                        }
                    }
                }
                else if (sourceForm.Array) {
                    // Merge other arrays by item values. Don't worry about order or duplicates. Source 
                    // items with empty values do not cause deletion, that conflicts horribly with 
                    // merging.

                    for (IEnumerator @is = sourceNode.IterateChildren(); @is.MoveNext();) {
                        XmpNode sourceItem = (XmpNode) @is.Current;
                        if (sourceItem == null)
                            continue;
                        bool match = false;
                        for (IEnumerator id = destNode.IterateChildren(); id.MoveNext();) {
                            XmpNode destItem = (XmpNode) id.Current;
                            if (destItem == null)
                                continue;
                            if (ItemValuesMatch(sourceItem, destItem)) {
                                match = true;
                            }
                        }
                        if (!match) {
                            destNode = (XmpNode) sourceItem.Clone();
                            destParent.AddChild(destNode);
                        }
                    }
                }
            }
        }
Beispiel #10
0
 /// <summary>
 /// Remove all schema children according to the flag
 /// <code>doAllProperties</code>. Empty schemas are automatically remove
 /// by <code>XMPNode</code>
 /// </summary>
 /// <param name="schemaNode">
 ///            a schema node </param>
 /// <param name="doAllProperties">
 ///            flag if all properties or only externals shall be removed. </param>
 /// <returns> Returns true if the schema is empty after the operation. </returns>
 private static bool RemoveSchemaChildren(XmpNode schemaNode, bool doAllProperties) {
     ArrayList currPropsToRemove = new ArrayList();
     for (IEnumerator it = schemaNode.IterateChildren(); it.MoveNext();) {
         XmpNode currProp = (XmpNode) it.Current;
         if (currProp == null)
             continue;
         if (doAllProperties || !Utils.IsInternalProperty(schemaNode.Name, currProp.Name)) {
             currPropsToRemove.Add(currProp);
         }
     }
     foreach (XmpNode xmpNode in currPropsToRemove) {
         schemaNode.Children.Remove(xmpNode);
     }
     currPropsToRemove.Clear();
     return !schemaNode.HasChildren();
 }
Beispiel #11
0
        /// <summary>
        /// <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>
        /// </summary>
        /// <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="XmpException"> </exception>
        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.Options.ArrayAltText) {
                throw new XmpException("Localized text array is not alt-text", XmpError.BADXPATH);
            }
            if (!arrayNode.HasChildren()) {
                return new object[] {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 == null || currItem.Options == null || currItem.Options.CompositeProperty) {
                    throw new XmpException("Alt-text array item is not simple", XmpError.BADXPATH);
                }
                if (!currItem.HasQualifier() || !XML_LANG.Equals(currItem.GetQualifier(1).Name)) {
                    throw new XmpException("Alt-text array item has no language qualifier", XmpError.BADXPATH);
                }

                string currLang = currItem.GetQualifier(1).Value;

                // Look for an exact match with the specific language.
                if (specificLang.Equals(currLang)) {
                    return new object[] {CLT_SPECIFIC_MATCH, currItem};
                }
                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[] {CLT_SINGLE_GENERIC, resultNode};
            }
            if (foundGenericMatches > 1) {
                return new object[] {CLT_MULTIPLE_GENERIC, resultNode};
            }
            if (xDefault != null) {
                return new object[] {CLT_XDEFAULT, xDefault};
            }
            {
                // Everything failed, choose the first item.
                return new object[] {CLT_FIRST_ITEM, arrayNode.GetChild(1)};
            }
        }
Beispiel #12
0
        /// <summary>
        /// See if an array is an alt-text array. If so, make sure the x-default item
        /// is first.
        /// </summary>
        /// <param name="arrayNode">
        ///            the array node to check if its an alt-text array </param>
        internal static void DetectAltText(XmpNode arrayNode) {
            if (arrayNode.Options.ArrayAlternate && arrayNode.HasChildren()) {
                bool isAltText = false;
                for (IEnumerator it = arrayNode.IterateChildren(); it.MoveNext();) {
                    XmpNode child = (XmpNode) it.Current;
                    if (child != null && child.Options != null && child.Options.HasLanguage) {
                        isAltText = true;
                        break;
                    }
                }

                if (isAltText) {
                    arrayNode.Options.ArrayAltText = true;
                    NormalizeLangArray(arrayNode);
                }
            }
        }
Beispiel #13
0
        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="tree"> the root of the metadata tree </param>
        /// <param name="options"> th parsing options </param>
        /// <exception cref="XmpException"> Forwards XMP errors </exception>
        private static void MoveExplicitAliases(XmpNode tree, ParseOptions options)
        {
            if (!tree.HasAliases)
            {
                return;
            }
            tree.HasAliases = false;

            bool        strictAliasing = options.StrictAliasing;
            IEnumerator schemaIt       = tree.UnmodifiableChildren.GetEnumerator();

            while (schemaIt.MoveNext())
            {
                XmpNode currSchema = (XmpNode)schemaIt.Current;
                if (currSchema == null)
                {
                    continue;
                }
                if (!currSchema.HasAliases)
                {
                    continue;
                }

                ArrayList   currPropsToRemove = new ArrayList();
                IEnumerator propertyIt        = currSchema.IterateChildren();
                while (propertyIt.MoveNext())
                {
                    XmpNode currProp = (XmpNode)propertyIt.Current;
                    if (currProp == null)
                    {
                        continue;
                    }

                    if (!currProp.Alias)
                    {
                        continue;
                    }

                    currProp.Alias = false;

                    // Find the base path, look for the base schema and root node.
                    XMPAliasInfo info = XMPMetaFactory.SchemaRegistry.FindAlias(currProp.Name);
                    if (info != null)
                    {
                        // find or create schema
                        XmpNode baseSchema = XmpNodeUtils.FindSchemaNode(tree, info.Namespace, null, true);
                        baseSchema.Implicit = false;

                        XmpNode baseNode = XmpNodeUtils.FindChildNode(baseSchema, info.Prefix + info.PropName, false);
                        if (baseNode == null)
                        {
                            if (info.AliasForm.Simple)
                            {
                                // A top-to-top alias, transplant the property.
                                // change the alias property name to the base name
                                string qname = info.Prefix + info.PropName;
                                currProp.Name = qname;
                                baseSchema.AddChild(currProp);
                            }
                            else
                            {
                                // An alias to an array item,
                                // create the array and transplant the property.
                                baseNode = new XmpNode(info.Prefix + info.PropName, info.AliasForm.ToPropertyOptions());
                                baseSchema.AddChild(baseNode);
                                TransplantArrayItemAlias(currProp, baseNode);
                            }
                            currPropsToRemove.Add(currProp);
                        }
                        else if (info.AliasForm.Simple)
                        {
                            // 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.AliasForm.ArrayAltText)
                            {
                                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 (object o in currPropsToRemove)
                {
                    currSchema.Children.Remove(o);
                }
                currPropsToRemove.Clear();
                currSchema.HasAliases = false;
            }
        }
Beispiel #14
0
        /// <summary>
        /// Recursively handles the "value" for a node. It does not matter if it is a
        /// top level property, a field of a struct, or an item of an array. The
        /// indent is that for the property element. An xml:lang qualifier is written
        /// as an attribute of the property start tag, not by itself forcing the
        /// qualified property form. The patterns below mostly ignore attribute
        /// qualifiers like xml:lang. Except for the one struct case, attribute
        /// qualifiers don't affect the output form.
        /// 
        /// <blockquote>
        /// 
        /// <pre>
        /// 	&lt;ns:UnqualifiedSimpleProperty&gt;value&lt;/ns:UnqualifiedSimpleProperty&gt;
        /// 
        /// 	&lt;ns:UnqualifiedStructProperty&gt; (If no rdf:resource qualifier)
        /// 		&lt;rdf:Description&gt;
        /// 			... Fields, same forms as top level properties
        /// 		&lt;/rdf:Description&gt;
        /// 	&lt;/ns:UnqualifiedStructProperty&gt;
        /// 
        /// 	&lt;ns:ResourceStructProperty rdf:resource=&quot;URI&quot;
        /// 		... Fields as attributes
        /// 	&gt;
        /// 
        /// 	&lt;ns:UnqualifiedArrayProperty&gt;
        /// 		&lt;rdf:Bag&gt; or Seq or Alt
        /// 			... Array items as rdf:li elements, same forms as top level properties
        /// 		&lt;/rdf:Bag&gt;
        /// 	&lt;/ns:UnqualifiedArrayProperty&gt;
        /// 
        /// 	&lt;ns:QualifiedProperty&gt;
        /// 		&lt;rdf:Description&gt;
        /// 			&lt;rdf:value&gt; ... Property &quot;value&quot; following the unqualified 
        /// 				forms ... &lt;/rdf:value&gt;
        /// 			... Qualifiers looking like named struct fields
        /// 		&lt;/rdf:Description&gt;
        /// 	&lt;/ns:QualifiedProperty&gt;
        /// </pre>
        /// 
        /// </blockquote>
        /// </summary>
        /// <param name="node"> the property node </param>
        /// <param name="emitAsRdfValue"> property shall be rendered as attribute rather than tag </param>
        /// <param name="useCanonicalRdf"> use canonical form with inner description tag or 
        /// 		  the compact form with rdf:ParseType=&quot;resource&quot; attribute. </param>
        /// <param name="indent"> the current indent level </param>
        /// <exception cref="IOException"> Forwards all writer exceptions. </exception>
        /// <exception cref="XmpException"> If &quot;rdf:resource&quot; and general qualifiers are mixed. </exception>
        private void SerializeCanonicalRdfProperty(XmpNode node, bool useCanonicalRdf, bool emitAsRdfValue, int indent) {
            bool emitEndTag = true;
            bool indentEndTag = true;

            // Determine the XML element name. Open the start tag with the name and
            // attribute qualifiers.

            string elemName = node.Name;
            if (emitAsRdfValue) {
                elemName = "rdf:value";
            }
            else if (XmpConst.ARRAY_ITEM_NAME.Equals(elemName)) {
                elemName = "rdf:li";
            }

            WriteIndent(indent);
            Write('<');
            Write(elemName);

            bool hasGeneralQualifiers = false;
            bool hasRdfResourceQual = false;

            for (IEnumerator it = node.IterateQualifier(); it.MoveNext();) {
                XmpNode qualifier = (XmpNode) it.Current;
                if (qualifier != null) {
                    if (!RDF_ATTR_QUALIFIER.Contains(qualifier.Name)) {
                        hasGeneralQualifiers = true;
                    }
                    else {
                        hasRdfResourceQual = "rdf:resource".Equals(qualifier.Name);
                        if (!emitAsRdfValue) {
                            Write(' ');
                            Write(qualifier.Name);
                            Write("=\"");
                            AppendNodeValue(qualifier.Value, true);
                            Write('"');
                        }
                    }
                }
            }

            // Process the property according to the standard patterns.

            if (hasGeneralQualifiers && !emitAsRdfValue) {
                // This node has general, non-attribute, qualifiers. Emit using the
                // qualified property form.
                // ! The value is output by a recursive call ON THE SAME NODE with
                // emitAsRDFValue set.

                if (hasRdfResourceQual) {
                    throw new XmpException("Can't mix rdf:resource and general qualifiers", XmpError.BADRDF);
                }

                // Change serialization to canonical format with inner rdf:Description-tag
                // depending on option
                if (useCanonicalRdf) {
                    Write(">");
                    WriteNewline();

                    indent++;
                    WriteIndent(indent);
                    Write(RDF_STRUCT_START);
                    Write(">");
                }
                else {
                    Write(" rdf:parseType=\"Resource\">");
                }
                WriteNewline();

                SerializeCanonicalRdfProperty(node, useCanonicalRdf, true, indent + 1);

                for (IEnumerator it = node.IterateQualifier(); it.MoveNext();) {
                    XmpNode qualifier = (XmpNode) it.Current;
                    if (qualifier != null && !RDF_ATTR_QUALIFIER.Contains(qualifier.Name)) {
                        SerializeCanonicalRdfProperty(qualifier, useCanonicalRdf, false, indent + 1);
                    }
                }

                if (useCanonicalRdf) {
                    WriteIndent(indent);
                    Write(RDF_STRUCT_END);
                    WriteNewline();
                    indent--;
                }
            }
            else {
                // This node has no general qualifiers. Emit using an unqualified form.

                if (!node.Options.CompositeProperty) {
                    // This is a simple property.

                    if (node.Options.Uri) {
                        Write(" rdf:resource=\"");
                        AppendNodeValue(node.Value, true);
                        Write("\"/>");
                        WriteNewline();
                        emitEndTag = false;
                    }
                    else if (node.Value == null || "".Equals(node.Value)) {
                        Write("/>");
                        WriteNewline();
                        emitEndTag = false;
                    }
                    else {
                        Write('>');
                        AppendNodeValue(node.Value, false);
                        indentEndTag = false;
                    }
                }
                else if (node.Options.Array) {
                    // This is an array.
                    Write('>');
                    WriteNewline();
                    EmitRdfArrayTag(node, true, indent + 1);
                    if (node.Options.ArrayAltText) {
                        XmpNodeUtils.NormalizeLangArray(node);
                    }
                    for (IEnumerator it = node.IterateChildren(); it.MoveNext();) {
                        XmpNode child = (XmpNode) it.Current;
                        SerializeCanonicalRdfProperty(child, useCanonicalRdf, false, indent + 2);
                    }
                    EmitRdfArrayTag(node, false, indent + 1);
                }
                else if (!hasRdfResourceQual) {
                    // This is a "normal" struct, use the rdf:parseType="Resource" form.
                    if (!node.HasChildren()) {
                        // Change serialization to canonical format with inner rdf:Description-tag
                        // if option is set
                        if (useCanonicalRdf) {
                            Write(">");
                            WriteNewline();
                            WriteIndent(indent + 1);
                            Write(RDF_EMPTY_STRUCT);
                        }
                        else {
                            Write(" rdf:parseType=\"Resource\"/>");
                            emitEndTag = false;
                        }
                        WriteNewline();
                    }
                    else {
                        // Change serialization to canonical format with inner rdf:Description-tag
                        // if option is set
                        if (useCanonicalRdf) {
                            Write(">");
                            WriteNewline();
                            indent++;
                            WriteIndent(indent);
                            Write(RDF_STRUCT_START);
                            Write(">");
                        }
                        else {
                            Write(" rdf:parseType=\"Resource\">");
                        }
                        WriteNewline();

                        for (IEnumerator it = node.IterateChildren(); it.MoveNext();) {
                            XmpNode child = (XmpNode) it.Current;
                            SerializeCanonicalRdfProperty(child, useCanonicalRdf, false, indent + 1);
                        }

                        if (useCanonicalRdf) {
                            WriteIndent(indent);
                            Write(RDF_STRUCT_END);
                            WriteNewline();
                            indent--;
                        }
                    }
                }
                else {
                    // This is a struct with an rdf:resource attribute, use the
                    // "empty property element" form.
                    for (IEnumerator it = node.IterateChildren(); it.MoveNext();) {
                        XmpNode child = (XmpNode) it.Current;
                        if (child != null) {
                            if (!canBeRDFAttrProp(child)) {
                                throw new XmpException("Can't mix rdf:resource and complex fields",
                                                       XmpError.BADRDF);
                            }
                            WriteNewline();
                            WriteIndent(indent + 1);
                            Write(' ');
                            Write(child.Name);
                            Write("=\"");
                            AppendNodeValue(child.Value, true);
                            Write('"');
                        }
                    }
                    Write("/>");
                    WriteNewline();
                    emitEndTag = false;
                }
            }

            // Emit the property element end tag.
            if (emitEndTag) {
                if (indentEndTag) {
                    WriteIndent(indent);
                }
                Write("</");
                Write(elemName);
                Write('>');
                WriteNewline();
            }
        }
Beispiel #15
0
 /// <summary>
 /// Serializes one schema with all contained properties in pretty-printed
 /// manner.<br> 
 /// Each schema's properties are written to a single
 /// rdf:Description element. All of the necessary namespaces are declared in
 /// the rdf:Description element. The baseIndent is the base level for the
 /// entire serialization, that of the x:xmpmeta element. An xml:lang
 /// qualifier is written as an attribute of the property start tag, not by
 /// itself forcing the qualified property form.
 /// 
 /// <blockquote>
 /// 
 /// <pre>
 ///  	 &lt;rdf:Description rdf:about=&quot;TreeName&quot; xmlns:ns=&quot;URI&quot; ... &gt;
 ///  
 ///  	 	... The actual properties of the schema, see SerializePrettyRDFProperty
 ///  
 ///  	 	&lt;!-- ns1:Alias is aliased to ns2:Actual --&gt;  ... If alias comments are wanted
 ///  
 ///  	 &lt;/rdf:Description&gt;
 /// </pre>
 /// 
 /// </blockquote>
 /// </summary>
 /// <param name="schemaNode"> a schema node </param>
 /// <param name="level"> </param>
 /// <exception cref="IOException"> Forwarded writer exceptions </exception>
 /// <exception cref="XmpException">  </exception>
 private void SerializeCanonicalRdfSchema(XmpNode schemaNode, int level) {
     // Write each of the schema's actual properties.
     for (IEnumerator it = schemaNode.IterateChildren(); it.MoveNext();) {
         XmpNode propNode = (XmpNode) it.Current;
         if (propNode == null)
             continue;
         SerializeCanonicalRdfProperty(propNode, _options.UseCanonicalFormat, false, level + 2);
     }
 }
Beispiel #16
0
        /// <summary>
        /// Recursively handles the "value" for a node that must be written as an RDF
        /// property element. It does not matter if it is a top level property, a
        /// field of a struct, or an item of an array. The indent is that for the
        /// property element. The patterns bwlow ignore attribute qualifiers such as
        /// xml:lang, they don't affect the output form.
        /// 
        /// <blockquote>
        /// 
        /// <pre>
        ///  	&lt;ns:UnqualifiedStructProperty-1
        ///  		... The fields as attributes, if all are simple and unqualified
        ///  	/&gt;
        ///  
        ///  	&lt;ns:UnqualifiedStructProperty-2 rdf:parseType=&quot;Resource&quot;&gt;
        ///  		... The fields as elements, if none are simple and unqualified
        ///  	&lt;/ns:UnqualifiedStructProperty-2&gt;
        ///  
        ///  	&lt;ns:UnqualifiedStructProperty-3&gt;
        ///  		&lt;rdf:Description
        ///  			... The simple and unqualified fields as attributes
        ///  		&gt;
        ///  			... The compound or qualified fields as elements
        ///  		&lt;/rdf:Description&gt;
        ///  	&lt;/ns:UnqualifiedStructProperty-3&gt;
        ///  
        ///  	&lt;ns:UnqualifiedArrayProperty&gt;
        ///  		&lt;rdf:Bag&gt; or Seq or Alt
        ///  			... Array items as rdf:li elements, same forms as top level properties
        ///  		&lt;/rdf:Bag&gt;
        ///  	&lt;/ns:UnqualifiedArrayProperty&gt;
        ///  
        ///  	&lt;ns:QualifiedProperty rdf:parseType=&quot;Resource&quot;&gt;
        ///  		&lt;rdf:value&gt; ... Property &quot;value&quot; 
        ///  			following the unqualified forms ... &lt;/rdf:value&gt;
        ///  		... Qualifiers looking like named struct fields
        ///  	&lt;/ns:QualifiedProperty&gt;
        /// </pre>
        /// 
        /// </blockquote>
        /// 
        /// *** Consider numbered array items, but has compatibility problems. ***
        /// Consider qualified form with rdf:Description and attributes.
        /// </summary>
        /// <param name="parentNode"> the parent node </param>
        /// <param name="indent"> the current indent level </param>
        /// <exception cref="IOException"> Forwards writer exceptions </exception>
        /// <exception cref="XmpException"> If qualifier and element fields are mixed. </exception>
        private void SerializeCompactRdfElementProps(XmpNode parentNode, int indent) {
            for (IEnumerator it = parentNode.IterateChildren(); it.MoveNext();) {
                XmpNode node = (XmpNode) it.Current;
                if (node == null)
                    continue;
                if (canBeRDFAttrProp(node)) {
                    continue;
                }

                bool emitEndTag = true;
                bool indentEndTag = true;

                // Determine the XML element name, write the name part of the start tag. Look over the
                // qualifiers to decide on "normal" versus "rdf:value" form. Emit the attribute
                // qualifiers at the same time.
                string elemName = node.Name;
                if (XmpConst.ARRAY_ITEM_NAME.Equals(elemName)) {
                    elemName = "rdf:li";
                }

                WriteIndent(indent);
                Write('<');
                Write(elemName);

                bool hasGeneralQualifiers = false;
                bool hasRdfResourceQual = false;

                for (IEnumerator iq = node.IterateQualifier(); iq.MoveNext();) {
                    XmpNode qualifier = (XmpNode) iq.Current;
                    if (qualifier == null)
                        continue;
                    if (!RDF_ATTR_QUALIFIER.Contains(qualifier.Name)) {
                        hasGeneralQualifiers = true;
                    }
                    else {
                        hasRdfResourceQual = "rdf:resource".Equals(qualifier.Name);
                        Write(' ');
                        Write(qualifier.Name);
                        Write("=\"");
                        AppendNodeValue(qualifier.Value, true);
                        Write('"');
                    }
                }


                // Process the property according to the standard patterns.
                if (hasGeneralQualifiers) {
                    SerializeCompactRdfGeneralQualifier(indent, node);
                }
                else {
                    // This node has only attribute qualifiers. Emit as a property element.
                    if (!node.Options.CompositeProperty) {
                        object[] result = SerializeCompactRdfSimpleProp(node);
                        emitEndTag = (bool) ((bool?) result[0]);
                        indentEndTag = (bool) ((bool?) result[1]);
                    }
                    else if (node.Options.Array) {
                        SerializeCompactRdfArrayProp(node, indent);
                    }
                    else {
                        emitEndTag = SerializeCompactRdfStructProp(node, indent, hasRdfResourceQual);
                    }
                }

                // Emit the property element end tag.
                if (emitEndTag) {
                    if (indentEndTag) {
                        WriteIndent(indent);
                    }
                    Write("</");
                    Write(elemName);
                    Write('>');
                    WriteNewline();
                }
            }
        }
Beispiel #17
0
        /// <seealso cref= XMPUtils#catenateArrayItems(XMPMeta, String, String, String, String,
        ///      boolean)
        /// </seealso>
        /// <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 &quot;; &quot;, 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 &apos;&quot;&apos; </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 = "\"";
            }

            XmpMetaImpl xmpImpl = (XmpMetaImpl)xmp;

            // Return an empty result if the array does not exist,
            // hurl if it isn't the right form.
            XmpPath arrayPath = XmpPathParser.ExpandXPath(schemaNs, arrayName);
            XmpNode arrayNode = XmpNodeUtils.FindNode(xmpImpl.Root, arrayPath, false, null);

            if (arrayNode == null)
            {
                return("");
            }
            if (!arrayNode.Options.Array || arrayNode.Options.ArrayAlternate)
            {
                throw new XmpException("Named property must be non-alternate array", XmpError.BADPARAM);
            }

            // Make sure the separator is OK.
            CheckSeparator(separator);
            // Make sure the open and close quotes are a legitimate pair.
            char openQuote  = quotes[0];
            char closeQuote = CheckQuotes(quotes, openQuote);

            // Build the result, quoting the array items, adding separators.
            // Hurl if any item isn't simple.

            StringBuilder catinatedString = new StringBuilder();

            for (IEnumerator it = arrayNode.IterateChildren(); it.MoveNext();)
            {
                XmpNode currItem = (XmpNode)it.Current;
                if (currItem == null)
                {
                    continue;
                }
                if (currItem.Options.CompositeProperty)
                {
                    throw new XmpException("Array items must be simple", XmpError.BADPARAM);
                }
                string str = ApplyQuotes(currItem.Value, openQuote, closeQuote, allowCommas);

                catinatedString.Append(str);
                if (it.MoveNext())
                {
                    catinatedString.Append(separator);
                }
            }

            return(catinatedString.ToString());
        }
Beispiel #18
0
        /// <summary>
        /// Compares two nodes including its children and qualifier. </summary>
        /// <param name="leftNode"> an <code>XMPNode</code> </param>
        /// <param name="rightNode"> an <code>XMPNode</code> </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)
        {
            PropertyOptions leftForm  = leftNode.Options;
            PropertyOptions rightForm = rightNode.Options;

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

            if (leftForm.Options == 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.Struct)
            {
                // Struct nodes, see if all fields match, ignoring order.

                if (leftNode.ChildrenLength != rightNode.ChildrenLength)
                {
                    return(false);
                }

                for (IEnumerator it = leftNode.IterateChildren(); it.MoveNext();)
                {
                    XmpNode leftField = (XmpNode)it.Current;
                    if (leftField == null)
                    {
                        continue;
                    }
                    XmpNode 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.Array);

                for (IEnumerator il = leftNode.IterateChildren(); il.MoveNext();)
                {
                    XmpNode leftItem = (XmpNode)il.Current;
                    if (leftItem == null)
                    {
                        continue;
                    }

                    bool match = false;
                    for (IEnumerator ir = rightNode.IterateChildren(); ir.MoveNext();)
                    {
                        XmpNode rightItem = (XmpNode)ir.Current;
                        if (rightItem == null)
                        {
                            continue;
                        }
                        if (ItemValuesMatch(leftItem, rightItem))
                        {
                            match = true;
                            break;
                        }
                    }
                    if (!match)
                    {
                        return(false);
                    }
                }
            }
            return(true); // All of the checks passed.
        }
Beispiel #19
0
        /// <seealso cref= XMPUtilsImpl#appendProperties(XMPMeta, XMPMeta, boolean, boolean, boolean) </seealso>
        /// <param name="destXmp"> The destination XMP object. </param>
        /// <param name="sourceNode"> the source node </param>
        /// <param name="destParent"> the parent of the destination node </param>
        /// <param name="replaceOldValues"> Replace the values of existing properties. </param>
        /// <param name="deleteEmptyValues"> flag if properties with empty values should be deleted
        ///            in the destination object. </param>
        /// <exception cref="XmpException"> </exception>
        private static void AppendSubtree(XmpMetaImpl destXmp, XmpNode sourceNode, XmpNode destParent,
                                          bool replaceOldValues, bool deleteEmptyValues)
        {
            XmpNode destNode = XmpNodeUtils.FindChildNode(destParent, sourceNode.Name, false);

            bool valueIsEmpty = false;

            if (deleteEmptyValues)
            {
                valueIsEmpty = sourceNode.Options.Simple
                                   ? string.IsNullOrEmpty(sourceNode.Value)
                                   : !sourceNode.HasChildren();
            }

            if (deleteEmptyValues && valueIsEmpty)
            {
                if (destNode != null)
                {
                    destParent.RemoveChild(destNode);
                }
            }
            else if (destNode == null)
            {
                // The one easy case, the destination does not exist.
                destParent.AddChild((XmpNode)sourceNode.Clone());
            }
            else if (replaceOldValues)
            {
                // The destination exists and should be replaced.
                destXmp.SetNode(destNode, sourceNode.Value, sourceNode.Options, true);
                destParent.RemoveChild(destNode);
                destNode = (XmpNode)sourceNode.Clone();
                destParent.AddChild(destNode);
            }
            else
            {
                // The destination exists and is not totally replaced. Structs and
                // arrays are merged.

                PropertyOptions sourceForm = sourceNode.Options;
                PropertyOptions destForm   = destNode.Options;
                if (sourceForm != destForm)
                {
                    return;
                }
                if (sourceForm.Struct)
                {
                    // To merge a struct process the fields recursively. E.g. add simple missing fields.
                    // The recursive call to AppendSubtree will handle deletion for fields with empty
                    // values.
                    for (IEnumerator it = sourceNode.IterateChildren(); it.MoveNext();)
                    {
                        XmpNode sourceField = (XmpNode)it.Current;
                        if (sourceField == null)
                        {
                            continue;
                        }
                        AppendSubtree(destXmp, sourceField, destNode, replaceOldValues, deleteEmptyValues);
                        if (deleteEmptyValues && !destNode.HasChildren())
                        {
                            destParent.RemoveChild(destNode);
                        }
                    }
                }
                else if (sourceForm.ArrayAltText)
                {
                    // Merge AltText arrays by the "xml:lang" qualifiers. Make sure x-default is first.
                    // Make a special check for deletion of empty values. Meaningful in AltText arrays
                    // because the "xml:lang" qualifier provides unambiguous source/dest correspondence.
                    for (IEnumerator it = sourceNode.IterateChildren(); it.MoveNext();)
                    {
                        XmpNode sourceItem = (XmpNode)it.Current;
                        if (sourceItem == null)
                        {
                            continue;
                        }
                        if (!sourceItem.HasQualifier() || !XML_LANG.Equals(sourceItem.GetQualifier(1).Name))
                        {
                            continue;
                        }

                        int destIndex = XmpNodeUtils.LookupLanguageItem(destNode, sourceItem.GetQualifier(1).Value);
                        if (deleteEmptyValues && (string.IsNullOrEmpty(sourceItem.Value)))
                        {
                            if (destIndex != -1)
                            {
                                destNode.RemoveChild(destIndex);
                                if (!destNode.HasChildren())
                                {
                                    destParent.RemoveChild(destNode);
                                }
                            }
                        }
                        else if (destIndex == -1)
                        {
                            // Not replacing, keep the existing item.
                            if (!X_DEFAULT.Equals(sourceItem.GetQualifier(1).Value) || !destNode.HasChildren())
                            {
                                sourceItem.CloneSubtree(destNode);
                            }
                            else
                            {
                                XmpNode destItem = new XmpNode(sourceItem.Name, sourceItem.Value, sourceItem.Options);
                                sourceItem.CloneSubtree(destItem);
                                destNode.AddChild(1, destItem);
                            }
                        }
                    }
                }
                else if (sourceForm.Array)
                {
                    // Merge other arrays by item values. Don't worry about order or duplicates. Source
                    // items with empty values do not cause deletion, that conflicts horribly with
                    // merging.

                    for (IEnumerator @is = sourceNode.IterateChildren(); @is.MoveNext();)
                    {
                        XmpNode sourceItem = (XmpNode)@is.Current;
                        if (sourceItem == null)
                        {
                            continue;
                        }
                        bool match = false;
                        for (IEnumerator id = destNode.IterateChildren(); id.MoveNext();)
                        {
                            XmpNode destItem = (XmpNode)id.Current;
                            if (destItem == null)
                            {
                                continue;
                            }
                            if (ItemValuesMatch(sourceItem, destItem))
                            {
                                match = true;
                            }
                        }
                        if (!match)
                        {
                            destNode = (XmpNode)sourceItem.Clone();
                            destParent.AddChild(destNode);
                        }
                    }
                }
            }
        }
Beispiel #20
0
        /// <summary>
        /// Serializes a struct property.
        /// </summary>
        /// <param name="node"> an XMPNode </param>
        /// <param name="indent"> the current indent level </param>
        /// <param name="hasRdfResourceQual"> Flag if the element has resource qualifier </param>
        /// <returns> Returns true if an end flag shall be emitted. </returns>
        /// <exception cref="IOException"> Forwards the writer exceptions. </exception>
        /// <exception cref="XmpException"> If qualifier and element fields are mixed. </exception>
        private bool SerializeCompactRdfStructProp(XmpNode node, int indent, bool hasRdfResourceQual) {
            // This must be a struct.
            bool hasAttrFields = false;
            bool hasElemFields = false;
            bool emitEndTag = true;

            for (IEnumerator ic = node.IterateChildren(); ic.MoveNext();) {
                XmpNode field = (XmpNode) ic.Current;
                if (field == null)
                    continue;
                if (canBeRDFAttrProp(field)) {
                    hasAttrFields = true;
                }
                else {
                    hasElemFields = true;
                }

                if (hasAttrFields && hasElemFields) {
                    break; // No sense looking further.
                }
            }

            if (hasRdfResourceQual && hasElemFields) {
                throw new XmpException("Can't mix rdf:resource qualifier and element fields", XmpError.BADRDF);
            }

            if (!node.HasChildren()) {
                // Catch an empty struct as a special case. The case
                // below would emit an empty
                // XML element, which gets reparsed as a simple property
                // with an empty value.
                Write(" rdf:parseType=\"Resource\"/>");
                WriteNewline();
                emitEndTag = false;
            }
            else if (!hasElemFields) {
                // All fields can be attributes, use the
                // emptyPropertyElt form.
                SerializeCompactRdfAttrProps(node, indent + 1);
                Write("/>");
                WriteNewline();
                emitEndTag = false;
            }
            else if (!hasAttrFields) {
                // All fields must be elements, use the
                // parseTypeResourcePropertyElt form.
                Write(" rdf:parseType=\"Resource\">");
                WriteNewline();
                SerializeCompactRdfElementProps(node, indent + 1);
            }
            else {
                // Have a mix of attributes and elements, use an inner rdf:Description.
                Write('>');
                WriteNewline();
                WriteIndent(indent + 1);
                Write(RDF_STRUCT_START);
                SerializeCompactRdfAttrProps(node, indent + 2);
                Write(">");
                WriteNewline();
                SerializeCompactRdfElementProps(node, indent + 1);
                WriteIndent(indent + 1);
                Write(RDF_STRUCT_END);
                WriteNewline();
            }
            return emitEndTag;
        }
Beispiel #21
0
        /// <summary>
        /// <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>
        /// </summary>
        /// <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="XmpException"> </exception>
        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.Options.ArrayAltText)
            {
                throw new XmpException("Localized text array is not alt-text", XmpError.BADXPATH);
            }
            if (!arrayNode.HasChildren())
            {
                return(new object[] { 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 == null || currItem.Options == null || currItem.Options.CompositeProperty)
                {
                    throw new XmpException("Alt-text array item is not simple", XmpError.BADXPATH);
                }
                if (!currItem.HasQualifier() || !XML_LANG.Equals(currItem.GetQualifier(1).Name))
                {
                    throw new XmpException("Alt-text array item has no language qualifier", XmpError.BADXPATH);
                }

                string currLang = currItem.GetQualifier(1).Value;

                // Look for an exact match with the specific language.
                if (specificLang.Equals(currLang))
                {
                    return(new object[] { CLT_SPECIFIC_MATCH, currItem });
                }
                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[] { CLT_SINGLE_GENERIC, resultNode });
            }
            if (foundGenericMatches > 1)
            {
                return(new object[] { CLT_MULTIPLE_GENERIC, resultNode });
            }
            if (xDefault != null)
            {
                return(new object[] { CLT_XDEFAULT, xDefault });
            }
            {
                // Everything failed, choose the first item.
                return(new object[] { CLT_FIRST_ITEM, arrayNode.GetChild(1) });
            }
        }
Beispiel #22
0
        /// <summary>
        /// Writes all used namespaces of the subtree in node to the output. 
        /// The subtree is recursivly traversed. </summary>
        /// <param name="node"> the root node of the subtree </param>
        /// <param name="usedPrefixes"> a set containing currently used prefixes </param>
        /// <param name="indent"> the current indent level </param>
        /// <exception cref="IOException"> Forwards all writer exceptions. </exception>
        private void DeclareUsedNamespaces(XmpNode node, ISet usedPrefixes, int indent) {
            if (node.Options.SchemaNode) {
                // The schema node name is the URI, the value is the prefix.
                string prefix = node.Value.Substring(0, node.Value.Length - 1);
                DeclareNamespace(prefix, node.Name, usedPrefixes, indent);
            }
            else if (node.Options.Struct) {
                for (IEnumerator it = node.IterateChildren(); it.MoveNext();) {
                    XmpNode field = (XmpNode) it.Current;
                    if (field == null)
                        continue;
                    DeclareNamespace(field.Name, null, usedPrefixes, indent);
                }
            }

            for (IEnumerator it = node.IterateChildren(); it.MoveNext();) {
                XmpNode child = (XmpNode) it.Current;
                if (child == null)
                    continue;
                DeclareUsedNamespaces(child, usedPrefixes, indent);
            }

            for (IEnumerator it = node.IterateQualifier(); it.MoveNext();) {
                XmpNode qualifier = (XmpNode) it.Current;
                if (qualifier == null)
                    continue;
                DeclareNamespace(qualifier.Name, null, usedPrefixes, indent);
                DeclareUsedNamespaces(qualifier, usedPrefixes, indent);
            }
        }
Beispiel #23
0
        /// <summary>
        /// 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". </summary>
        /// <param name="arrayNode"> the property node of the array to repair. </param>
        /// <exception cref="XmpException"> Forwards unexpected exceptions. </exception>
        private static void RepairAltText(XmpNode arrayNode) {
            if (arrayNode == null || !arrayNode.Options.Array) {
                // Already OK or not even an array.
                return;
            }

            // fix options
            arrayNode.Options.ArrayOrdered = true;
            arrayNode.Options.ArrayAlternate = true;
            arrayNode.Options.ArrayAltText = true;
            ArrayList currChildsToRemove = new ArrayList();
            IEnumerator it = arrayNode.IterateChildren();
            while (it.MoveNext()) {
                XmpNode currChild = (XmpNode) it.Current;
                if (currChild == null)
                    continue;
                if (currChild.Options.CompositeProperty) {
                    // Delete non-simple children.
                    currChildsToRemove.Add(currChild);
                }
                else if (!currChild.Options.HasLanguage) {
                    string childValue = currChild.Value;
                    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.Children.Remove(o);
            }
        }
Beispiel #24
0
            /// <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) {
                _outerInstance = outerInstance;
                if (parentNode.Options.SchemaNode) {
                    outerInstance.BaseNs = parentNode.Name;
                }
                _parentPath = AccumulatePath(parentNode, parentPath, 1);

                _childrenIterator = parentNode.IterateChildren();
            }
Beispiel #25
0
        /// <summary>
        /// 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.
        /// </summary>
        /// <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 (!aliasNode.Value.Equals(baseNode.Value) || aliasNode.ChildrenLength != baseNode.ChildrenLength) {
                throw new XmpException("Mismatch between alias and base nodes", XmpError.BADXMP);
            }

            if (!outerCall &&
                (!aliasNode.Name.Equals(baseNode.Name) || !aliasNode.Options.Equals(baseNode.Options) ||
                 aliasNode.QualifierLength != baseNode.QualifierLength)) {
                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);
            }
        }