A node in the internally XMP tree, which can be a schema node, a property node, an array node, an array item, a struct node or a qualifier node (without '?'). Possible improvements: 1. The kind Node of node might be better represented by a class-hierarchy of different nodes. 2. The array type should be an enum 3. isImplicitNode should be removed completely and replaced by return values of fi. 4. hasLanguage, hasType should be automatically maintained by XMPNode @since 21.02.2006
Inheritance: IComparable, ICloneable
Esempio n. 1
0
        /// <summary>
        /// 7.2.18 parseTypeResourcePropertyElt
        ///		start-element ( URI == propertyElementURIs, 
        ///			attributes == set ( idAttr?, parseResource ) )
        ///		propertyEltList
        ///		end-element()
        /// 
        /// Add a new struct node with a qualifier for the possible rdf:ID attribute. 
        /// Then process the XML child nodes to get the struct fields.
        /// </summary>
        /// <param name="xmp"> the xmp metadata object that is generated </param>
        /// <param name="xmpParent"> the parent xmp node </param>
        /// <param name="xmlNode"> the currently processed XML node </param>
        /// <param name="isTopLevel"> Flag if the node is a top-level node </param>
        /// <exception cref="XmpException"> thown on parsing errors </exception>
        private static void RdfParseTypeResourcePropertyElement(XmpMetaImpl xmp, XmpNode xmpParent, XmlNode xmlNode,
                                                                bool isTopLevel) {
            XmpNode newStruct = AddChildNode(xmp, xmpParent, xmlNode, "", isTopLevel);

            newStruct.Options.Struct = true;

            if (xmlNode.Attributes != null) {
                for (int i = 0; i < xmlNode.Attributes.Count; i++) {
                    XmlNode attribute = xmlNode.Attributes[i];
                    if ("xmlns".Equals(attribute.Prefix) || (attribute.Prefix == null && "xmlns".Equals(attribute.Name))) {
                        continue;
                    }

                    string attrLocal = attribute.LocalName;
                    string attrNs = attribute.NamespaceURI;
                    if (XML_LANG.Equals(attribute.Name)) {
                        AddQualifierNode(newStruct, XML_LANG, attribute.Value);
                    }
                    else if (NS_RDF.Equals(attrNs) && ("ID".Equals(attrLocal) || "parseType".Equals(attrLocal))) {
                        continue; // The caller ensured the value is "Resource".
                        // Ignore all rdf:ID attributes.
                    }
                    throw new XmpException("Invalid attribute for ParseTypeResource property element",
                                           XmpError.BADRDF);
                }
            }

            RdfPropertyElementList(xmp, newStruct, xmlNode, false);

            if (newStruct.HasValueChild) {
                FixupQualifiedNode(newStruct);
            }
        }
Esempio n. 2
0
 /// <summary>
 /// 7.2.13 propertyEltList
 /// ws* ( propertyElt ws* )*
 /// </summary>
 /// <param name="xmp"> the xmp metadata object that is generated </param>
 /// <param name="xmpParent"> the parent xmp node </param>
 /// <param name="xmlParent"> the currently processed XML node </param>
 /// <param name="isTopLevel"> Flag if the node is a top-level node </param>
 /// <exception cref="XmpException"> thown on parsing errors </exception>
 private static void RdfPropertyElementList(XmpMetaImpl xmp, XmpNode xmpParent, XmlNode xmlParent,
                                            bool isTopLevel) {
     for (int i = 0; i < xmlParent.ChildNodes.Count; i++) {
         XmlNode currChild = xmlParent.ChildNodes[i];
         if (IsWhitespaceNode(currChild)) {
             continue;
         }
         if (currChild.NodeType != XmlNodeType.Element) {
             throw new XmpException("Expected property element node not found", XmpError.BADRDF);
         }
         RdfPropertyElement(xmp, xmpParent, currChild, isTopLevel);
     }
 }
Esempio n. 3
0
        /// <summary>
        /// 7.2.15 resourcePropertyElt
        ///		start-element ( URI == propertyElementURIs, attributes == set ( idAttr? ) )
        ///		ws* nodeElement ws*
        ///		end-element()
        /// 
        /// This handles structs using an rdf:Description node, 
        /// arrays using rdf:Bag/Seq/Alt, and typedNodes. It also catches and cleans up qualified 
        /// properties written with rdf:Description and rdf:value.
        /// </summary>
        /// <param name="xmp"> the xmp metadata object that is generated </param>
        /// <param name="xmpParent"> the parent xmp node </param>
        /// <param name="xmlNode"> the currently processed XML node </param>
        /// <param name="isTopLevel"> Flag if the node is a top-level node </param>
        /// <exception cref="XmpException"> thown on parsing errors </exception>
        private static void RdfResourcePropertyElement(XmpMetaImpl xmp, XmpNode xmpParent, XmlNode xmlNode,
                                                       bool isTopLevel) {
            if (isTopLevel && "iX:changes".Equals(xmlNode.Name)) {
                // Strip old "punchcard" chaff which has on the prefix "iX:".
                return;
            }

            XmpNode newCompound = AddChildNode(xmp, xmpParent, xmlNode, "", isTopLevel);

            // walk through the attributes
            if (xmlNode.Attributes != null) {
                for (int i = 0; i < xmlNode.Attributes.Count; i++) {
                    XmlNode attribute = xmlNode.Attributes[i];
                    if ("xmlns".Equals(attribute.Prefix) || (attribute.Prefix == null && "xmlns".Equals(attribute.Name))) {
                        continue;
                    }

                    string attrLocal = attribute.LocalName;
                    string attrNs = attribute.NamespaceURI;
                    if (XML_LANG.Equals(attribute.Name)) {
                        AddQualifierNode(newCompound, XML_LANG, attribute.Value);
                    }
                    else if ("ID".Equals(attrLocal) && NS_RDF.Equals(attrNs)) {
                        continue; // Ignore all rdf:ID attributes.
                    }
                    throw new XmpException("Invalid attribute for resource property element", XmpError.BADRDF);
                }
            }

            // walk through the children

            bool found = false;
            for (int i = 0; i < xmlNode.ChildNodes.Count; i++) {
                XmlNode currChild = xmlNode.ChildNodes[i];
                if (!IsWhitespaceNode(currChild)) {
                    if (currChild.NodeType == XmlNodeType.Element && !found) {
                        bool isRdf = NS_RDF.Equals(currChild.NamespaceURI);
                        string childLocal = currChild.LocalName;

                        if (isRdf && "Bag".Equals(childLocal)) {
                            newCompound.Options.Array = true;
                        }
                        else if (isRdf && "Seq".Equals(childLocal)) {
                            newCompound.Options.Array = true;
                            newCompound.Options.ArrayOrdered = true;
                        }
                        else if (isRdf && "Alt".Equals(childLocal)) {
                            newCompound.Options.Array = true;
                            newCompound.Options.ArrayOrdered = true;
                            newCompound.Options.ArrayAlternate = true;
                        }
                        else {
                            newCompound.Options.Struct = true;
                            if (!isRdf && !"Description".Equals(childLocal)) {
                                string typeName = currChild.NamespaceURI;
                                if (typeName == null) {
                                    throw new XmpException("All XML elements must be in a namespace",
                                                           XmpError.BADXMP);
                                }
                                typeName += ':' + childLocal;
                                AddQualifierNode(newCompound, "rdf:type", typeName);
                            }
                        }

                        RdfNodeElement(xmp, newCompound, currChild, false);

                        if (newCompound.HasValueChild) {
                            FixupQualifiedNode(newCompound);
                        }
                        else if (newCompound.Options.ArrayAlternate) {
                            XmpNodeUtils.DetectAltText(newCompound);
                        }

                        found = true;
                    }
                    else if (found) {
                        // found second child element
                        throw new XmpException("Invalid child of resource property element", XmpError.BADRDF);
                    }
                    else {
                        throw new XmpException("Children of resource property element must be XML elements",
                                               XmpError.BADRDF);
                    }
                }
            }

            if (!found) {
                // didn't found any child elements
                throw new XmpException("Missing child of resource property element", XmpError.BADRDF);
            }
        }
Esempio n. 4
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();
            }
Esempio n. 5
0
 /// <summary>
 /// 7.2.5 nodeElementURIs
 /// 		anyURI - ( coreSyntaxTerms | rdf:li | oldTerms )
 /// 
 /// 7.2.11 nodeElement
 /// 		start-element ( URI == nodeElementURIs,
 /// 		attributes == set ( ( idAttr | nodeIdAttr | aboutAttr )?, propertyAttr* ) )
 /// 		propertyEltList
 /// 		end-element()
 /// 
 /// A node element URI is rdf:Description or anything else that is not an RDF
 /// term.
 /// </summary>
 /// <param name="xmp"> the xmp metadata object that is generated </param>
 /// <param name="xmpParent"> the parent xmp node </param>
 /// <param name="xmlNode"> the currently processed XML node </param>
 /// <param name="isTopLevel"> Flag if the node is a top-level node </param>
 /// <exception cref="XmpException"> thown on parsing errors </exception>
 private static void RdfNodeElement(XmpMetaImpl xmp, XmpNode xmpParent, XmlNode xmlNode, bool isTopLevel) {
     int nodeTerm = GetRdfTermKind(xmlNode);
     if (nodeTerm != RDFTERM_DESCRIPTION && nodeTerm != RDFTERM_OTHER) {
         throw new XmpException("Node element must be rdf:Description or typed node", XmpError.BADRDF);
     }
     if (isTopLevel && nodeTerm == RDFTERM_OTHER) {
         throw new XmpException("Top level typed node not allowed", XmpError.BADXMP);
     }
     RdfNodeElementAttrs(xmp, xmpParent, xmlNode, isTopLevel);
     RdfPropertyElementList(xmp, xmpParent, xmlNode, isTopLevel);
 }
Esempio n. 6
0
 public XmpPropertyImpl1(XmpNode itemNode) {
     _itemNode = itemNode;
 }
Esempio n. 7
0
        // -------------------------------------------------------------------------------------
        // private


        /// <summary>
        /// Locate or create the item node and set the value. Note the index
        /// parameter is one-based! The index can be in the range [1..size + 1] or
        /// "last()", normalize it and check the insert flags. The order of the
        /// normalization checks is important. If the array is empty we end up with
        /// an index and location to set item size + 1.
        /// </summary>
        /// <param name="arrayNode"> an array node </param>
        /// <param name="itemIndex"> the index where to insert the item </param>
        /// <param name="itemValue"> the item value </param>
        /// <param name="itemOptions"> the options for the new item </param>
        /// <param name="insert"> insert oder overwrite at index position? </param>
        /// <exception cref="XmpException"> </exception>
        private void DoSetArrayItem(XmpNode arrayNode, int itemIndex, string itemValue, PropertyOptions itemOptions,
                                    bool insert) {
            XmpNode itemNode = new XmpNode(ARRAY_ITEM_NAME, null);
            itemOptions = XmpNodeUtils.VerifySetOptions(itemOptions, itemValue);

            // in insert mode the index after the last is allowed,
            // even ARRAY_LAST_ITEM points to the index *after* the last.
            int maxIndex = insert ? arrayNode.ChildrenLength + 1 : arrayNode.ChildrenLength;
            if (itemIndex == ARRAY_LAST_ITEM) {
                itemIndex = maxIndex;
            }

            if (1 <= itemIndex && itemIndex <= maxIndex) {
                if (!insert) {
                    arrayNode.RemoveChild(itemIndex);
                }
                arrayNode.AddChild(itemIndex, itemNode);
                SetNode(itemNode, itemValue, itemOptions, false);
            }
            else {
                throw new XmpException("Array index out of bounds", XmpError.BADINDEX);
            }
        }
Esempio n. 8
0
        /// <summary>
        /// The initial support for WAV files mapped a legacy ID3 audio copyright
        /// into a new xmpDM:copyright property. This is special case code to migrate
        /// that into dc:rights['x-default']. The rules:
        ///
        /// <pre>
        /// 1. If there is no dc:rights array, or an empty array -
        ///    Create one with dc:rights['x-default'] set from double linefeed and xmpDM:copyright.
        ///
        /// 2. If there is a dc:rights array but it has no x-default item -
        ///    Create an x-default item as a copy of the first item then apply rule #3.
        ///
        /// 3. If there is a dc:rights array with an x-default item,
        ///    Look for a double linefeed in the value.
        ///     A. If no double linefeed, compare the x-default value to the xmpDM:copyright value.
        ///         A1. If they match then leave the x-default value alone.
        ///         A2. Otherwise, append a double linefeed and
        ///             the xmpDM:copyright value to the x-default value.
        ///     B. If there is a double linefeed, compare the trailing text to the xmpDM:copyright value.
        ///         B1. If they match then leave the x-default value alone.
        ///         B2. Otherwise, replace the trailing x-default text with the xmpDM:copyright value.
        ///
        /// 4. In all cases, delete the xmpDM:copyright property.
        /// </pre>
        /// </summary>
        /// <param name="xmp"> the metadata object </param>
        /// <param name="dmCopyright"> the "dm:copyright"-property </param>
        private static void MigrateAudioCopyright(XMPMeta xmp, XmpNode dmCopyright)
        {
            try {
                XmpNode dcSchema = XmpNodeUtils.FindSchemaNode(((XmpMetaImpl)xmp).Root, XmpConst.NS_DC, true);

                string       dmValue  = dmCopyright.Value;
                const string doubleLf = "\n\n";

                XmpNode dcRightsArray = XmpNodeUtils.FindChildNode(dcSchema, "dc:rights", false);

                if (dcRightsArray == null || !dcRightsArray.HasChildren())
                {
                    // 1. No dc:rights array, create from double linefeed and xmpDM:copyright.
                    dmValue = doubleLf + dmValue;
                    xmp.SetLocalizedText(XmpConst.NS_DC, "rights", "", XmpConst.X_DEFAULT, dmValue, null);
                }
                else
                {
                    int xdIndex = XmpNodeUtils.LookupLanguageItem(dcRightsArray, XmpConst.X_DEFAULT);

                    if (xdIndex < 0)
                    {
                        // 2. No x-default item, create from the first item.
                        string firstValue = dcRightsArray.GetChild(1).Value;
                        xmp.SetLocalizedText(XmpConst.NS_DC, "rights", "", XmpConst.X_DEFAULT, firstValue, null);
                        xdIndex = XmpNodeUtils.LookupLanguageItem(dcRightsArray, XmpConst.X_DEFAULT);
                    }

                    // 3. Look for a double linefeed in the x-default value.
                    XmpNode defaultNode  = dcRightsArray.GetChild(xdIndex);
                    string  defaultValue = defaultNode.Value;
                    int     lfPos        = defaultValue.IndexOf(doubleLf);

                    if (lfPos < 0)
                    {
                        // 3A. No double LF, compare whole values.
                        if (!dmValue.Equals(defaultValue))
                        {
                            // 3A2. Append the xmpDM:copyright to the x-default
                            // item.
                            defaultNode.Value = defaultValue + doubleLf + dmValue;
                        }
                    }
                    else
                    {
                        // 3B. Has double LF, compare the tail.
                        if (!defaultValue.Substring(lfPos + 2).Equals(dmValue))
                        {
                            // 3B2. Replace the x-default tail.
                            defaultNode.Value = defaultValue.Substring(0, lfPos + 2) + dmValue;
                        }
                    }
                }

                // 4. Get rid of the xmpDM:copyright.
                dmCopyright.Parent.RemoveChild(dmCopyright);
            }
            catch (XmpException) {
                // Don't let failures (like a bad dc:rights form) stop other
                // cleanup.
            }
        }
Esempio n. 9
0
 /// <summary>
 /// Adds a node as child to this node. </summary>
 /// <param name="index"> the index of the node <em>before</em> which the new one is inserted.
 /// <em>Note:</em> The node children are indexed from [1..size]!
 /// An index of size + 1 appends a node. </param>
 /// <param name="node"> an XMPNode </param>
 /// <exception cref="XmpException">  </exception>
 public virtual void AddChild(int index, XmpNode node)
 {
     AssertChildNotExisting(node.Name);
     node.Parent = this;
     Children.Insert(index - 1, node);
 }
Esempio n. 10
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.
        }
Esempio n. 11
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;
            }
        }
Esempio n. 12
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);
                        }
                    }
                }
            }
        }
Esempio n. 13
0
        /// <seealso cref= XMPUtils#removeProperties(XMPMeta, String, String, boolean, boolean)
        /// </seealso>
        /// <param name="xmp">
        ///            The XMP object containing the properties to be removed.
        /// </param>
        /// <param name="schemaNs">
        ///            Optional schema namespace URI for the properties to be
        ///            removed.
        /// </param>
        /// <param name="propName">
        ///            Optional path expression for the property to be removed.
        /// </param>
        /// <param name="doAllProperties">
        ///            Option flag to control the deletion: do internal properties in
        ///            addition to external properties. </param>
        /// <param name="includeAliases">
        ///            Option flag to control the deletion: Include aliases in the
        ///            "named schema" case above. </param>
        /// <exception cref="XmpException"> If metadata processing fails </exception>
        public static void RemoveProperties(IXmpMeta xmp, string schemaNs, string propName, bool doAllProperties,
                                            bool includeAliases)
        {
            ParameterAsserts.AssertImplementation(xmp);
            XmpMetaImpl xmpImpl = (XmpMetaImpl)xmp;

            if (!string.IsNullOrEmpty(propName))
            {
                // Remove just the one indicated property. This might be an alias,
                // the named schema might not actually exist. So don't lookup the
                // schema node.

                if (string.IsNullOrEmpty(schemaNs))
                {
                    throw new XmpException("Property name requires schema namespace", XmpError.BADPARAM);
                }

                XmpPath expPath = XmpPathParser.ExpandXPath(schemaNs, propName);

                XmpNode propNode = XmpNodeUtils.FindNode(xmpImpl.Root, expPath, false, null);
                if (propNode != null)
                {
                    if (doAllProperties ||
                        !Utils.IsInternalProperty(expPath.GetSegment((int)XmpPath.STEP_SCHEMA).Name,
                                                  expPath.GetSegment((int)XmpPath.STEP_ROOT_PROP).Name))
                    {
                        XmpNode parent = propNode.Parent;
                        parent.RemoveChild(propNode);
                        if (parent.Options.SchemaNode && !parent.HasChildren())
                        {
                            // remove empty schema node
                            parent.Parent.RemoveChild(parent);
                        }
                    }
                }
            }
            else if (!string.IsNullOrEmpty(schemaNs))
            {
                // Remove all properties from the named schema. Optionally include
                // aliases, in which case
                // there might not be an actual schema node.

                // XMP_NodePtrPos schemaPos;
                XmpNode schemaNode = XmpNodeUtils.FindSchemaNode(xmpImpl.Root, schemaNs, false);
                if (schemaNode != null)
                {
                    if (RemoveSchemaChildren(schemaNode, doAllProperties))
                    {
                        xmpImpl.Root.RemoveChild(schemaNode);
                    }
                }

                if (includeAliases)
                {
                    // We're removing the aliases also. Look them up by their
                    // namespace prefix.
                    // But that takes more code and the extra speed isn't worth it.
                    // Lookup the XMP node
                    // from the alias, to make sure the actual exists.

                    IXmpAliasInfo[] aliases = XmpMetaFactory.SchemaRegistry.FindAliases(schemaNs);
                    for (int i = 0; i < aliases.Length; i++)
                    {
                        IXmpAliasInfo info       = aliases[i];
                        XmpPath       path       = XmpPathParser.ExpandXPath(info.Namespace, info.PropName);
                        XmpNode       actualProp = XmpNodeUtils.FindNode(xmpImpl.Root, path, false, null);
                        if (actualProp != null)
                        {
                            XmpNode parent = actualProp.Parent;
                            parent.RemoveChild(actualProp);
                        }
                    }
                }
            }
            else
            {
                // Remove all appropriate properties from all schema. In this case
                // we don't have to be
                // concerned with aliases, they are handled implicitly from the
                // actual properties.
                ArrayList schemasToRemove = new ArrayList();
                for (IEnumerator it = xmpImpl.Root.IterateChildren(); it.MoveNext();)
                {
                    XmpNode schema = (XmpNode)it.Current;
                    if (schema == null)
                    {
                        continue;
                    }
                    if (RemoveSchemaChildren(schema, doAllProperties))
                    {
                        schemasToRemove.Add(schema);
                    }
                }
                foreach (XmpNode xmpNode in schemasToRemove)
                {
                    xmpImpl.Root.Children.Remove(xmpNode);
                }
                schemasToRemove.Clear();
            }
        }
Esempio n. 14
0
        /// <summary>
        /// see {@link XMPUtils#separateArrayItems(XMPMeta, String, String, String,
        /// PropertyOptions, boolean)}
        /// </summary>
        /// <param name="xmp">
        ///            The XMP object containing the array to be updated. </param>
        /// <param name="schemaNs">
        ///            The schema namespace URI for the array. Must not be null or
        ///            the empty string. </param>
        /// <param name="arrayName">
        ///            The name of the array. May be a general path expression, must
        ///            not be null or the empty string. Each item in the array must
        ///            be a simple string value. </param>
        /// <param name="catedStr">
        ///            The string to be separated into the array items. </param>
        /// <param name="arrayOptions">
        ///            Option flags to control the separation. </param>
        /// <param name="preserveCommas">
        ///            Flag if commas shall be preserved
        /// </param>
        /// <exception cref="XmpException">
        ///             Forwards the Exceptions from the metadata processing </exception>
        public static void SeparateArrayItems(IXmpMeta xmp, string schemaNs, string arrayName, string catedStr,
                                              PropertyOptions arrayOptions, bool preserveCommas)
        {
            ParameterAsserts.AssertSchemaNs(schemaNs);
            ParameterAsserts.AssertArrayName(arrayName);
            if (catedStr == null)
            {
                throw new XmpException("Parameter must not be null", XmpError.BADPARAM);
            }
            ParameterAsserts.AssertImplementation(xmp);
            XmpMetaImpl xmpImpl = (XmpMetaImpl)xmp;

            // Keep a zero value, has special meaning below.
            XmpNode arrayNode = SeparateFindCreateArray(schemaNs, arrayName, arrayOptions, xmpImpl);

            // Extract the item values one at a time, until the whole input string is done.
            int  charKind = UCK_NORMAL;
            char ch       = (char)0;

            int itemEnd = 0;
            int endPos  = catedStr.Length;

            while (itemEnd < endPos)
            {
                string itemValue;
                int    itemStart;
                // Skip any leading spaces and separation characters. Always skip commas here.
                // They can be kept when within a value, but not when alone between values.
                for (itemStart = itemEnd; itemStart < endPos; itemStart++)
                {
                    ch       = catedStr[itemStart];
                    charKind = ClassifyCharacter(ch);
                    if (charKind == UCK_NORMAL || charKind == UCK_QUOTE)
                    {
                        break;
                    }
                }
                if (itemStart >= endPos)
                {
                    break;
                }
                int nextKind;
                if (charKind != UCK_QUOTE)
                {
                    // This is not a quoted value. Scan for the end, create an array
                    // item from the substring.
                    for (itemEnd = itemStart; itemEnd < endPos; itemEnd++)
                    {
                        ch       = catedStr[itemEnd];
                        charKind = ClassifyCharacter(ch);

                        if (charKind == UCK_NORMAL || charKind == UCK_QUOTE || (charKind == UCK_COMMA && preserveCommas))
                        {
                            continue;
                        }
                        if (charKind != UCK_SPACE)
                        {
                            break;
                        }
                        if ((itemEnd + 1) < endPos)
                        {
                            ch       = catedStr[itemEnd + 1];
                            nextKind = ClassifyCharacter(ch);
                            if (nextKind == UCK_NORMAL || nextKind == UCK_QUOTE ||
                                (nextKind == UCK_COMMA && preserveCommas))
                            {
                                continue;
                            }
                        }

                        // Anything left?
                        break; // Have multiple spaces, or a space followed by a
                        // separator.
                    }
                    itemValue = catedStr.Substring(itemStart, itemEnd - itemStart);
                }
                else
                {
                    // Accumulate quoted values into a local string, undoubling
                    // internal quotes that
                    // match the surrounding quotes. Do not undouble "unmatching"
                    // quotes.

                    char openQuote  = ch;
                    char closeQuote = GetClosingQuote(openQuote);

                    itemStart++; // Skip the opening quote;
                    itemValue = "";

                    for (itemEnd = itemStart; itemEnd < endPos; itemEnd++)
                    {
                        ch       = catedStr[itemEnd];
                        charKind = ClassifyCharacter(ch);

                        if (charKind != UCK_QUOTE || !IsSurroundingQuote(ch, openQuote, closeQuote))
                        {
                            // This is not a matching quote, just append it to the
                            // item value.
                            itemValue += ch;
                        }
                        else
                        {
                            // This is a "matching" quote. Is it doubled, or the
                            // final closing quote?
                            // Tolerate various edge cases like undoubled opening
                            // (non-closing) quotes,
                            // or end of input.
                            char nextChar;
                            if ((itemEnd + 1) < endPos)
                            {
                                nextChar = catedStr[itemEnd + 1];
                                nextKind = ClassifyCharacter(nextChar);
                            }
                            else
                            {
                                nextKind = UCK_SEMICOLON;
                                nextChar = (char)0x3B;
                            }

                            if (ch == nextChar)
                            {
                                // This is doubled, copy it and skip the double.
                                itemValue += ch;
                                // Loop will add in charSize.
                                itemEnd++;
                            }
                            else if (!IsClosingingQuote(ch, openQuote, closeQuote))
                            {
                                // This is an undoubled, non-closing quote, copy it.
                                itemValue += ch;
                            }
                            else
                            {
                                // This is an undoubled closing quote, skip it and
                                // exit the loop.
                                itemEnd++;
                                break;
                            }
                        }
                    }
                }

                // Add the separated item to the array.
                // Keep a matching old value in case it had separators.
                int foundIndex = -1;
                for (int oldChild = 1; oldChild <= arrayNode.ChildrenLength; oldChild++)
                {
                    if (itemValue.Equals(arrayNode.GetChild(oldChild).Value))
                    {
                        foundIndex = oldChild;
                        break;
                    }
                }

                if (foundIndex < 0)
                {
                    XmpNode newItem = new XmpNode(ARRAY_ITEM_NAME, itemValue, null);
                    arrayNode.AddChild(newItem);
                }
            }
        }
Esempio n. 15
0
        /// <summary>
        /// Adds a child node.
        /// </summary>
        /// <param name="xmp"> the xmp metadata object that is generated </param>
        /// <param name="xmpParent"> the parent xmp node </param>
        /// <param name="xmlNode"> the currently processed XML node </param>
        /// <param name="value"> Node value </param>
        /// <param name="isTopLevel"> Flag if the node is a top-level node </param>
        /// <returns> Returns the newly created child node. </returns>
        /// <exception cref="XmpException"> thown on parsing errors </exception>
        private static XmpNode AddChildNode(XmpMetaImpl xmp, XmpNode xmpParent, XmlNode xmlNode, string value,
                                            bool isTopLevel) {
            IXmpSchemaRegistry registry = XmpMetaFactory.SchemaRegistry;
            string @namespace = xmlNode.NamespaceURI;
            string childName;
            if (@namespace != null) {
                if (NS_DC_DEPRECATED.Equals(@namespace)) {
                    // Fix a legacy DC namespace
                    @namespace = NS_DC;
                }

                string prefix = registry.GetNamespacePrefix(@namespace);
                if (prefix == null) {
                    prefix = xmlNode.Prefix ?? DEFAULT_PREFIX;
                    prefix = registry.RegisterNamespace(@namespace, prefix);
                }
                childName = prefix + xmlNode.LocalName;
            }
            else {
                throw new XmpException("XML namespace required for all elements and attributes",
                                       XmpError.BADRDF);
            }


            // create schema node if not already there
            PropertyOptions childOptions = new PropertyOptions();
            bool isAlias = false;
            if (isTopLevel) {
                // Lookup the schema node, adjust the XMP parent pointer.
                // Incoming parent must be the tree root.
                XmpNode schemaNode = XmpNodeUtils.FindSchemaNode(xmp.Root, @namespace, DEFAULT_PREFIX, true);
                schemaNode.Implicit = false; // Clear the implicit node bit.
                // need runtime check for proper 32 bit code.
                xmpParent = schemaNode;

                // If this is an alias set the alias flag in the node 
                // and the hasAliases flag in the tree.
                if (registry.FindAlias(childName) != null) {
                    isAlias = true;
                    xmp.Root.HasAliases = true;
                    schemaNode.HasAliases = true;
                }
            }


            // Make sure that this is not a duplicate of a named node.
            bool isArrayItem = "rdf:li".Equals(childName);
            bool isValueNode = "rdf:value".Equals(childName);

            // Create XMP node and so some checks
            XmpNode newChild = new XmpNode(childName, value, childOptions);
            newChild.Alias = isAlias;

            // Add the new child to the XMP parent node, a value node first.
            if (!isValueNode) {
                xmpParent.AddChild(newChild);
            }
            else {
                xmpParent.AddChild(1, newChild);
            }


            if (isValueNode) {
                if (isTopLevel || !xmpParent.Options.Struct) {
                    throw new XmpException("Misplaced rdf:value element", XmpError.BADRDF);
                }
                xmpParent.HasValueChild = true;
            }

            if (isArrayItem) {
                if (!xmpParent.Options.Array) {
                    throw new XmpException("Misplaced rdf:li element", XmpError.BADRDF);
                }
                newChild.Name = ARRAY_ITEM_NAME;
            }

            return newChild;
        }
Esempio n. 16
0
 /// <summary>
 /// Replaces a node with another one. </summary>
 /// <param name="index"> the index of the node that will be replaced.
 /// <em>Note:</em> The node children are indexed from [1..size]! </param>
 /// <param name="node"> the replacement XMPNode </param>
 public virtual void ReplaceChild(int index, XmpNode node)
 {
     node.Parent         = this;
     Children[index - 1] = node;
 }
Esempio n. 17
0
        /// <summary>
        /// The parent is an RDF pseudo-struct containing an rdf:value field. Fix the
        /// XMP data model. The rdf:value node must be the first child, the other
        /// children are qualifiers. The form, value, and children of the rdf:value
        /// node are the real ones. The rdf:value node's qualifiers must be added to
        /// the others.
        /// </summary>
        /// <param name="xmpParent"> the parent xmp node </param>
        /// <exception cref="XmpException"> thown on parsing errors </exception>
        private static void FixupQualifiedNode(XmpNode xmpParent) {
            Debug.Assert(xmpParent.Options.Struct && xmpParent.HasChildren());

            XmpNode valueNode = xmpParent.GetChild(1);
            Debug.Assert("rdf:value".Equals(valueNode.Name));

            // Move the qualifiers on the value node to the parent. 
            // Make sure an xml:lang qualifier stays at the front.
            // Check for duplicate names between the value node's qualifiers and the parent's children. 
            // The parent's children are about to become qualifiers. Check here, between the groups. 
            // Intra-group duplicates are caught by XMPNode#addChild(...).
            if (valueNode.Options.HasLanguage) {
                if (xmpParent.Options.HasLanguage) {
                    throw new XmpException("Redundant xml:lang for rdf:value element", XmpError.BADXMP);
                }
                XmpNode langQual = valueNode.GetQualifier(1);
                valueNode.RemoveQualifier(langQual);
                xmpParent.AddQualifier(langQual);
            }

            // Start the remaining copy after the xml:lang qualifier.		
            for (int i = 1; i <= valueNode.QualifierLength; i++) {
                XmpNode qualifier = valueNode.GetQualifier(i);
                xmpParent.AddQualifier(qualifier);
            }


            // Change the parent's other children into qualifiers. 
            // This loop starts at 1, child 0 is the rdf:value node.
            for (int i = 2; i <= xmpParent.ChildrenLength; i++) {
                XmpNode qualifier = xmpParent.GetChild(i);
                xmpParent.AddQualifier(qualifier);
            }

            // Move the options and value last, other checks need the parent's original options. 
            // Move the value node's children to be the parent's children.
            Debug.Assert(xmpParent.Options.Struct || xmpParent.HasValueChild);

            xmpParent.HasValueChild = false;
            xmpParent.Options.Struct = false;
            xmpParent.Options.MergeWith(valueNode.Options);
            xmpParent.Value = valueNode.Value;

            xmpParent.RemoveChildren();
            for (IEnumerator it = valueNode.IterateChildren(); it.MoveNext();) {
                XmpNode child = (XmpNode) it.Current;
                xmpParent.AddChild(child);
            }
        }
Esempio n. 18
0
 /// <summary>
 /// Removes a child node.
 /// If its a schema node and doesn't have any children anymore, its deleted.
 /// </summary>
 /// <param name="node"> the child node to delete. </param>
 public virtual void RemoveChild(XmpNode node)
 {
     Children.Remove(node);
     CleanupChildren();
 }
Esempio n. 19
0
 /// <summary>
 /// Constructor for an empty metadata object.
 /// </summary>
 public XmpMetaImpl() {
     // create root node
     _tree = new XmpNode(null, null, null);
 }
Esempio n. 20
0
        //------------------------------------------------------------------------------ private methods


        /// <summary>
        /// Dumps this node and its qualifier and children recursively.
        /// <em>Note:</em> It creats empty options on every node.
        /// </summary>
        /// <param name="result"> the buffer to append the dump. </param>
        /// <param name="recursive"> Flag is qualifier and child nodes shall be rendered too </param>
        /// <param name="indent"> the current indent level. </param>
        /// <param name="index"> the index within the parent node (important for arrays)  </param>
        private void DumpNode(StringBuilder result, bool recursive, int indent, int index)
        {
            // write indent
            for (int i = 0; i < indent; i++)
            {
                result.Append('\t');
            }

            // render Node
            if (_parent != null)
            {
                if (Options.Qualifier)
                {
                    result.Append('?');
                    result.Append(_name);
                }
                else if (Parent.Options.Array)
                {
                    result.Append('[');
                    result.Append(index);
                    result.Append(']');
                }
                else
                {
                    result.Append(_name);
                }
            }
            else
            {
                // applies only to the root node
                result.Append("ROOT NODE");
                if (!String.IsNullOrEmpty(_name))
                {
                    // the "about" attribute
                    result.Append(" (");
                    result.Append(_name);
                    result.Append(')');
                }
            }

            if (!String.IsNullOrEmpty(_value))
            {
                result.Append(" = \"");
                result.Append(_value);
                result.Append('"');
            }

            // render options if at least one is set
            if (Options.ContainsOneOf(0xffffffff))
            {
                result.Append("\t(");
                result.Append(Options.ToString());
                result.Append(" : ");
                result.Append(Options.OptionsString);
                result.Append(')');
            }

            result.Append('\n');

            // render qualifier
            if (recursive && HasQualifier())
            {
                XmpNode[] quals = new XmpNode[Qualifier.Count];
                Qualifier.CopyTo(quals, 0);
                int i = 0;
                while (quals.Length > i && (XmpConst.XML_LANG.Equals(quals[i].Name) || "rdf:type".Equals(quals[i].Name)))
                {
                    i++;
                }
                Array.Sort(quals, i, quals.Length - i);
                for (i = 0; i < quals.Length; i++)
                {
                    XmpNode qualifier = quals[i];
                    qualifier.DumpNode(result, recursive, indent + 2, i + 1);
                }
            }

            // render children
            if (recursive && HasChildren())
            {
                XmpNode[] children = new XmpNode[Children.Count];
                Children.CopyTo(children, 0);
                if (!Options.Array)
                {
                    Array.Sort(children);
                }
                for (int i = 0; i < children.Length; i++)
                {
                    XmpNode child = children[i];
                    child.DumpNode(result, recursive, indent + 1, i + 1);
                }
            }
        }
Esempio n. 21
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());
        }
Esempio n. 22
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;
        }
Esempio n. 23
0
 /// <summary>
 /// 7.2.10 nodeElementList<br>
 /// ws* ( nodeElement ws* )*
 /// 
 /// Note: this method is only called from the rdf:RDF-node (top level) </summary>
 /// <param name="xmp"> the xmp metadata object that is generated </param>
 /// <param name="xmpParent"> the parent xmp node </param>
 /// <param name="rdfRdfNode"> the top-level xml node </param>
 /// <exception cref="XmpException"> thown on parsing errors </exception>
 private static void RdfNodeElementList(XmpMetaImpl xmp, XmpNode xmpParent, XmlNode rdfRdfNode) {
     for (int i = 0; i < rdfRdfNode.ChildNodes.Count; i++) {
         XmlNode child = rdfRdfNode.ChildNodes[i];
         // filter whitespaces (and all text nodes)
         if (!IsWhitespaceNode(child)) {
             RdfNodeElement(xmp, xmpParent, child, true);
         }
     }
 }
Esempio n. 24
0
        /// <summary>
        /// Serializes the general qualifier. </summary>
        /// <param name="node"> the root node of the subtree </param>
        /// <param name="indent"> the current indent level </param>
        /// <exception cref="IOException"> Forwards all writer exceptions. </exception>
        /// <exception cref="XmpException"> If qualifier and element fields are mixed. </exception>
        private void SerializeCompactRdfGeneralQualifier(int indent, XmpNode node) {
            // The node has general qualifiers, ones that can't be
            // attributes on a property element.
            // Emit using the qualified property pseudo-struct form. The
            // value is output by a call
            // to SerializePrettyRDFProperty with emitAsRDFValue set.
            Write(" rdf:parseType=\"Resource\">");
            WriteNewline();

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

            for (IEnumerator iq = node.IterateQualifier(); iq.MoveNext();) {
                XmpNode qualifier = (XmpNode) iq.Current;
                if (qualifier == null)
                    continue;
                SerializeCanonicalRdfProperty(qualifier, false, false, indent + 1);
            }
        }
Esempio n. 25
0
        /// 
        /// <summary>
        /// 7.2.7 propertyAttributeURIs
        /// 		anyURI - ( coreSyntaxTerms | rdf:Description | rdf:li | oldTerms )
        /// 
        /// 7.2.11 nodeElement
        /// start-element ( URI == nodeElementURIs,
        /// 					attributes == set ( ( idAttr | nodeIdAttr | aboutAttr )?, propertyAttr* ) )
        /// 					propertyEltList
        /// 					end-element()
        /// 
        /// Process the attribute list for an RDF node element. A property attribute URI is 
        /// anything other than an RDF term. The rdf:ID and rdf:nodeID attributes are simply ignored, 
        /// as are rdf:about attributes on inner nodes.
        /// </summary>
        /// <param name="xmp"> the xmp metadata object that is generated </param>
        /// <param name="xmpParent"> the parent xmp node </param>
        /// <param name="xmlNode"> the currently processed XML node </param>
        /// <param name="isTopLevel"> Flag if the node is a top-level node </param>
        /// <exception cref="XmpException"> thown on parsing errors </exception>
        private static void RdfNodeElementAttrs(XmpMetaImpl xmp, XmpNode xmpParent, XmlNode xmlNode, bool isTopLevel) {
            // Used to detect attributes that are mutually exclusive.
            int exclusiveAttrs = 0;
            if (xmlNode == null || xmlNode.Attributes == null)
                return;

            for (int i = 0; i < xmlNode.Attributes.Count; i++) {
                XmlNode attribute = xmlNode.Attributes[i];

                // quick hack, ns declarations do not appear in C++
                // ignore "ID" without namespace
                if ("xmlns".Equals(attribute.Prefix) || (attribute.Prefix == null && "xmlns".Equals(attribute.Name))) {
                    continue;
                }

                int attrTerm = GetRdfTermKind(attribute);

                switch (attrTerm) {
                    case RDFTERM_ID:
                    case RDFTERM_NODE_ID:
                    case RDFTERM_ABOUT:
                        if (exclusiveAttrs > 0) {
                            throw new XmpException("Mutally exclusive about, ID, nodeID attributes",
                                                   XmpError.BADRDF);
                        }

                        exclusiveAttrs++;

                        if (isTopLevel && (attrTerm == RDFTERM_ABOUT)) {
                            // This is the rdf:about attribute on a top level node. Set
                            // the XMP tree name if
                            // it doesn't have a name yet. Make sure this name matches
                            // the XMP tree name.
                            if (!string.IsNullOrEmpty(xmpParent.Name)) {
                                if (!xmpParent.Name.Equals(attribute.Value)) {
                                    throw new XmpException("Mismatched top level rdf:about values",
                                                           XmpError.BADXMP);
                                }
                            }
                            else {
                                xmpParent.Name = attribute.Value;
                            }
                        }
                        break;

                    case RDFTERM_OTHER:
                        AddChildNode(xmp, xmpParent, attribute, attribute.Value, isTopLevel);
                        break;

                    default:
                        throw new XmpException("Invalid nodeElement attribute", XmpError.BADRDF);
                }
            }
        }
Esempio n. 26
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);
     }
 }
Esempio n. 27
0
        /// <summary>
        /// 7.2.14 propertyElt
        /// 
        ///		resourcePropertyElt | literalPropertyElt | parseTypeLiteralPropertyElt |
        ///		parseTypeResourcePropertyElt | parseTypeCollectionPropertyElt | 
        ///		parseTypeOtherPropertyElt | emptyPropertyElt
        /// 
        /// 7.2.15 resourcePropertyElt
        ///		start-element ( URI == propertyElementURIs, attributes == set ( idAttr? ) )
        ///		ws* nodeElement ws*
        ///		end-element()
        /// 
        /// 7.2.16 literalPropertyElt
        ///		start-element (
        ///			URI == propertyElementURIs, attributes == set ( idAttr?, datatypeAttr?) )
        ///		text()
        ///		end-element()
        /// 
        /// 7.2.17 parseTypeLiteralPropertyElt
        ///		start-element (
        ///			URI == propertyElementURIs, attributes == set ( idAttr?, parseLiteral ) )
        ///		literal
        ///		end-element()
        /// 
        /// 7.2.18 parseTypeResourcePropertyElt
        ///		start-element (
        ///			 URI == propertyElementURIs, attributes == set ( idAttr?, parseResource ) )
        ///		propertyEltList
        ///		end-element()
        /// 
        /// 7.2.19 parseTypeCollectionPropertyElt
        ///		start-element (
        ///			URI == propertyElementURIs, attributes == set ( idAttr?, parseCollection ) )
        ///		nodeElementList
        ///		end-element()
        /// 
        /// 7.2.20 parseTypeOtherPropertyElt
        ///		start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseOther ) )
        ///		propertyEltList
        ///		end-element()
        /// 
        /// 7.2.21 emptyPropertyElt
        ///		start-element ( URI == propertyElementURIs,
        ///			attributes == set ( idAttr?, ( resourceAttr | nodeIdAttr )?, propertyAttr* ) )
        ///		end-element()
        /// 
        /// The various property element forms are not distinguished by the XML element name, 
        /// but by their attributes for the most part. The exceptions are resourcePropertyElt and 
        /// literalPropertyElt. They are distinguished by their XML element content.
        /// 
        /// NOTE: The RDF syntax does not explicitly include the xml:lang attribute although it can 
        /// appear in many of these. We have to allow for it in the attibute counts below.	 
        /// </summary>
        /// <param name="xmp"> the xmp metadata object that is generated </param>
        /// <param name="xmpParent"> the parent xmp node </param>
        /// <param name="xmlNode"> the currently processed XML node </param>
        /// <param name="isTopLevel"> Flag if the node is a top-level node </param>
        /// <exception cref="XmpException"> thown on parsing errors </exception>
        private static void RdfPropertyElement(XmpMetaImpl xmp, XmpNode xmpParent, XmlNode xmlNode, bool isTopLevel) {
            int nodeTerm = GetRdfTermKind(xmlNode);
            if (!IsPropertyElementName(nodeTerm)) {
                throw new XmpException("Invalid property element name", XmpError.BADRDF);
            }

            // remove the namespace-definitions from the list
            XmlAttributeCollection attributes = xmlNode.Attributes;
            if (attributes == null)
                return;
            IList nsAttrs = null;
            for (int i = 0; i < attributes.Count; i++) {
                XmlNode attribute = attributes[i];
                if ("xmlns".Equals(attribute.Prefix) || (attribute.Prefix == null && "xmlns".Equals(attribute.Name))) {
                    if (nsAttrs == null) {
                        nsAttrs = new ArrayList();
                    }
                    nsAttrs.Add(attribute.Name);
                }
            }
            if (nsAttrs != null) {
                for (IEnumerator it = nsAttrs.GetEnumerator(); it.MoveNext();) {
                    string ns = (string) it.Current;
                    attributes.RemoveNamedItem(ns);
                }
            }


            if (attributes.Count > 3) {
                // Only an emptyPropertyElt can have more than 3 attributes.
                RdfEmptyPropertyElement(xmp, xmpParent, xmlNode, isTopLevel);
            }
            else {
                // Look through the attributes for one that isn't rdf:ID or xml:lang, 
                // it will usually tell what we should be dealing with. 
                // The called routines must verify their specific syntax!

                for (int i = 0; i < attributes.Count; i++) {
                    XmlNode attribute = attributes[i];
                    string attrLocal = attribute.LocalName;
                    string attrNs = attribute.NamespaceURI;
                    string attrValue = attribute.Value;
                    if (!(XML_LANG.Equals(attribute.Name) && !("ID".Equals(attrLocal) && NS_RDF.Equals(attrNs)))) {
                        if ("datatype".Equals(attrLocal) && NS_RDF.Equals(attrNs)) {
                            RdfLiteralPropertyElement(xmp, xmpParent, xmlNode, isTopLevel);
                        }
                        else if (!("parseType".Equals(attrLocal) && NS_RDF.Equals(attrNs))) {
                            RdfEmptyPropertyElement(xmp, xmpParent, xmlNode, isTopLevel);
                        }
                        else if ("Literal".Equals(attrValue)) {
                            RdfParseTypeLiteralPropertyElement();
                        }
                        else if ("Resource".Equals(attrValue)) {
                            RdfParseTypeResourcePropertyElement(xmp, xmpParent, xmlNode, isTopLevel);
                        }
                        else if ("Collection".Equals(attrValue)) {
                            RdfParseTypeCollectionPropertyElement();
                        }
                        else {
                            RdfParseTypeOtherPropertyElement();
                        }

                        return;
                    }
                }

                // Only rdf:ID and xml:lang, could be a resourcePropertyElt, a literalPropertyElt, 
                // or an emptyPropertyElt. Look at the child XML nodes to decide which.

                if (xmlNode.HasChildNodes) {
                    for (int i = 0; i < xmlNode.ChildNodes.Count; i++) {
                        XmlNode currChild = xmlNode.ChildNodes[i];
                        if (currChild.NodeType != XmlNodeType.Text) {
                            RdfResourcePropertyElement(xmp, xmpParent, xmlNode, isTopLevel);
                            return;
                        }
                    }

                    RdfLiteralPropertyElement(xmp, xmpParent, xmlNode, isTopLevel);
                }
                else {
                    RdfEmptyPropertyElement(xmp, xmpParent, xmlNode, isTopLevel);
                }
            }
        }
Esempio n. 28
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);
            }
        }
Esempio n. 29
0
        /// <summary>
        /// 7.2.16 literalPropertyElt
        ///		start-element ( URI == propertyElementURIs, 
        ///				attributes == set ( idAttr?, datatypeAttr?) )
        ///		text()
        ///		end-element()
        /// 
        /// Add a leaf node with the text value and qualifiers for the attributes. </summary>
        /// <param name="xmp"> the xmp metadata object that is generated </param>
        /// <param name="xmpParent"> the parent xmp node </param>
        /// <param name="xmlNode"> the currently processed XML node </param>
        /// <param name="isTopLevel"> Flag if the node is a top-level node </param>
        /// <exception cref="XmpException"> thown on parsing errors </exception>
        private static void RdfLiteralPropertyElement(XmpMetaImpl xmp, XmpNode xmpParent, XmlNode xmlNode,
                                                      bool isTopLevel) {
            XmpNode newChild = AddChildNode(xmp, xmpParent, xmlNode, null, isTopLevel);
            if (xmlNode.Attributes != null) {
                for (int i = 0; i < xmlNode.Attributes.Count; i++) {
                    XmlNode attribute = xmlNode.Attributes[i];
                    if ("xmlns".Equals(attribute.Prefix) || (attribute.Prefix == null && "xmlns".Equals(attribute.Name))) {
                        continue;
                    }

                    string attrNs = attribute.NamespaceURI;
                    string attrLocal = attribute.LocalName;
                    if (XML_LANG.Equals(attribute.Name)) {
                        AddQualifierNode(newChild, XML_LANG, attribute.Value);
                    } else if (NS_RDF.Equals(attrNs) && ("ID".Equals(attrLocal) || "datatype".Equals(attrLocal))) {
                        continue; // Ignore all rdf:ID and rdf:datatype attributes.
                    } else
                        throw new XmpException("Invalid attribute for literal property element", XmpError.BADRDF);
                }
            }
            string textValue = "";
            for (int i = 0; i < xmlNode.ChildNodes.Count; i++) {
                XmlNode child = xmlNode.ChildNodes[i];
                if (child.NodeType == XmlNodeType.Text) {
                    textValue += child.Value;
                }
                else {
                    throw new XmpException("Invalid child of literal property element", XmpError.BADRDF);
                }
            }
            newChild.Value = textValue;
        }
Esempio n. 30
0
        /// <summary>
        /// Start the outer rdf:Description element, including all needed xmlns attributes.
        /// Leave the element open so that the compact form can add property attributes.
        /// </summary>
        /// <exception cref="IOException"> If the writing to   </exception>
        private void StartOuterRdfDescription(XmpNode schemaNode, int level) {
            WriteIndent(level + 1);
            Write(RDF_SCHEMA_START);
            WriteTreeName();

            ISet usedPrefixes = new HashSet();
            usedPrefixes.Add("xml");
            usedPrefixes.Add("rdf");

            DeclareUsedNamespaces(schemaNode, usedPrefixes, level + 3);

            Write('>');
            WriteNewline();
        }
Esempio n. 31
0
        /// <summary>
        /// 7.2.21 emptyPropertyElt
        ///		start-element ( URI == propertyElementURIs,
        ///						attributes == set (
        ///							idAttr?, ( resourceAttr | nodeIdAttr )?, propertyAttr* ) )
        ///		end-element()
        /// 
        /// <ns:Prop1/>  <!-- a simple property with an empty value --> 
        /// <ns:Prop2 rdf:resource="http: *www.adobe.com/"/> <!-- a URI value --> 
        /// <ns:Prop3 rdf:value="..." ns:Qual="..."/> <!-- a simple qualified property --> 
        /// <ns:Prop4 ns:Field1="..." ns:Field2="..."/> <!-- a struct with simple fields -->
        /// 
        /// An emptyPropertyElt is an element with no contained content, just a possibly empty set of
        /// attributes. An emptyPropertyElt can represent three special cases of simple XMP properties: a
        /// simple property with an empty value (ns:Prop1), a simple property whose value is a URI
        /// (ns:Prop2), or a simple property with simple qualifiers (ns:Prop3). 
        /// An emptyPropertyElt can also represent an XMP struct whose fields are all simple and 
        /// unqualified (ns:Prop4).
        /// 
        /// It is an error to use both rdf:value and rdf:resource - that can lead to invalid  RDF in the
        /// verbose form written using a literalPropertyElt.
        /// 
        /// The XMP mapping for an emptyPropertyElt is a bit different from generic RDF, partly for 
        /// design reasons and partly for historical reasons. The XMP mapping rules are:
        /// <ol> 
        ///		<li> If there is an rdf:value attribute then this is a simple property
        ///				 with a text value.
        ///		All other attributes are qualifiers.
        ///		<li> If there is an rdf:resource attribute then this is a simple property 
        ///			with a URI value. 
        ///		All other attributes are qualifiers.
        ///		<li> If there are no attributes other than xml:lang, rdf:ID, or rdf:nodeID
        ///				then this is a simple 
        ///		property with an empty value. 
        ///		<li> Otherwise this is a struct, the attributes other than xml:lang, rdf:ID, 
        ///				or rdf:nodeID are fields. 
        /// </ol>
        /// </summary>
        /// <param name="xmp"> the xmp metadata object that is generated </param>
        /// <param name="xmpParent"> the parent xmp node </param>
        /// <param name="xmlNode"> the currently processed XML node </param>
        /// <param name="isTopLevel"> Flag if the node is a top-level node </param>
        /// <exception cref="XmpException"> thown on parsing errors </exception>
        private static void RdfEmptyPropertyElement(XmpMetaImpl xmp, XmpNode xmpParent, XmlNode xmlNode,
                                                    bool isTopLevel) {
            bool hasPropertyAttrs = false;
            bool hasResourceAttr = false;
            bool hasNodeIdAttr = false;
            bool hasValueAttr = false;

            XmlNode valueNode = null; // ! Can come from rdf:value or rdf:resource.

            if (xmlNode.HasChildNodes) {
                throw new XmpException("Nested content not allowed with rdf:resource or property attributes",
                                       XmpError.BADRDF);
            }

            // First figure out what XMP this maps to and remember the XML node for a simple value.
            if (xmlNode.Attributes != null) {
                for (int i = 0; i < xmlNode.Attributes.Count; i++) {
                    XmlNode attribute = xmlNode.Attributes[i];
                    if ("xmlns".Equals(attribute.Prefix) || (attribute.Prefix == null && "xmlns".Equals(attribute.Name))) {
                        continue;
                    }

                    int attrTerm = GetRdfTermKind(attribute);

                    switch (attrTerm) {
                        case RDFTERM_ID:
                            // Nothing to do.
                            break;

                        case RDFTERM_RESOURCE:
                            if (hasNodeIdAttr) {
                                throw new XmpException(
                                    "Empty property element can't have both rdf:resource and rdf:nodeID",
                                    XmpError.BADRDF);
                            }
                            if (hasValueAttr) {
                                throw new XmpException(
                                    "Empty property element can't have both rdf:value and rdf:resource",
                                    XmpError.BADXMP);
                            }

                            hasResourceAttr = true;
                            if (!hasValueAttr) {
                                valueNode = attribute;
                            }
                            break;

                        case RDFTERM_NODE_ID:
                            if (hasResourceAttr) {
                                throw new XmpException(
                                    "Empty property element can't have both rdf:resource and rdf:nodeID",
                                    XmpError.BADRDF);
                            }
                            hasNodeIdAttr = true;
                            break;

                        case RDFTERM_OTHER:
                            if ("value".Equals(attribute.LocalName) && NS_RDF.Equals(attribute.NamespaceURI)) {
                                if (hasResourceAttr) {
                                    throw new XmpException(
                                        "Empty property element can't have both rdf:value and rdf:resource",
                                        XmpError.BADXMP);
                                }
                                hasValueAttr = true;
                                valueNode = attribute;
                            }
                            else if (!XML_LANG.Equals(attribute.Name)) {
                                hasPropertyAttrs = true;
                            }
                            break;

                        default:
                            throw new XmpException("Unrecognized attribute of empty property element",
                                                   XmpError.BADRDF);
                    }
                }
            }

            // Create the right kind of child node and visit the attributes again 
            // to add the fields or qualifiers.
            // ! Because of implementation vagaries, 
            //   the xmpParent is the tree root for top level properties.
            // ! The schema is found, created if necessary, by addChildNode.

            XmpNode childNode = AddChildNode(xmp, xmpParent, xmlNode, "", isTopLevel);
            bool childIsStruct = false;

            if (hasValueAttr || hasResourceAttr) {
                childNode.Value = valueNode != null ? valueNode.Value : "";
                if (!hasValueAttr) {
                    // ! Might have both rdf:value and rdf:resource.
                    childNode.Options.Uri = true;
                }
            }
            else if (hasPropertyAttrs) {
                childNode.Options.Struct = true;
                childIsStruct = true;
            }

            if (xmlNode.Attributes != null) {
                for (int i = 0; i < xmlNode.Attributes.Count; i++) {
                    XmlNode attribute = xmlNode.Attributes[i];
                    if (attribute == valueNode || "xmlns".Equals(attribute.Prefix) ||
                        (attribute.Prefix == null && "xmlns".Equals(attribute.Name))) {
                        continue; // Skip the rdf:value or rdf:resource attribute holding the value.
                    }

                    int attrTerm = GetRdfTermKind(attribute);

                    switch (attrTerm) {
                        case RDFTERM_ID:
                        case RDFTERM_NODE_ID:
                            break; // Ignore all rdf:ID and rdf:nodeID attributes.
                        case RDFTERM_RESOURCE:
                            AddQualifierNode(childNode, "rdf:resource", attribute.Value);
                            break;

                        case RDFTERM_OTHER:
                            if (!childIsStruct) {
                                AddQualifierNode(childNode, attribute.Name, attribute.Value);
                            }
                            else if (XML_LANG.Equals(attribute.Name)) {
                                AddQualifierNode(childNode, XML_LANG, attribute.Value);
                            }
                            else {
                                AddChildNode(xmp, childNode, attribute, attribute.Value, false);
                            }
                            break;

                        default:
                            throw new XmpException("Unrecognized attribute of empty property element",
                                                   XmpError.BADRDF);
                    }
                }
            }
        }
Esempio n. 32
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();
            }
        }
Esempio n. 33
0
        /// <summary>
        /// Adds a qualifier node.
        /// </summary>
        /// <param name="xmpParent"> the parent xmp node </param>
        /// <param name="name"> the name of the qualifier which has to be 
        /// 		QName including the <b>default prefix</b> </param>
        /// <param name="value"> the value of the qualifier </param>
        /// <returns> Returns the newly created child node. </returns>
        /// <exception cref="XmpException"> thown on parsing errors </exception>
        private static XmpNode AddQualifierNode(XmpNode xmpParent, string name, string value) {
            bool isLang = XML_LANG.Equals(name);
            // normalize value of language qualifiers
            XmpNode newQual = new XmpNode(name, isLang ? Utils.NormalizeLangValue(value) : value, null);
            xmpParent.AddQualifier(newQual);

            return newQual;
        }
Esempio n. 34
0
            /// <summary>
            /// Constructor for the node iterator. </summary>
            /// <param name="visitedNode"> the currently visited node </param>
            /// <param name="parentPath"> the accumulated path of the node </param>
            /// <param name="index"> the index within the parent node (only for arrays) </param>
            public NodeIterator(XmpIteratorImpl outerInstance, XmpNode visitedNode, string parentPath, int index) {
                _outerInstance = outerInstance;
                _visitedNode = visitedNode;
                _state = ITERATE_NODE;
                if (visitedNode.Options.SchemaNode) {
                    outerInstance.BaseNs = visitedNode.Name;
                }

                // for all but the root node and schema nodes
                _path = AccumulatePath(visitedNode, parentPath, index);
            }
Esempio n. 35
0
 /// <summary>
 /// Evaluates a raw node value to the given value type, apply special
 /// conversions for defined types in XMP.
 /// </summary>
 /// <param name="valueType">
 ///            an int indicating the value type </param>
 /// <param name="propNode">
 ///            the node containing the value </param>
 /// <returns> Returns a literal value for the node. </returns>
 /// <exception cref="XmpException"> </exception>
 private object evaluateNodeValue(int valueType, XmpNode propNode) {
     object value;
     string rawValue = propNode.Value;
     switch (valueType) {
         case VALUE_BOOLEAN:
             value = XmpUtils.ConvertToBoolean(rawValue);
             break;
         case VALUE_INTEGER:
             value = XmpUtils.ConvertToInteger(rawValue);
             break;
         case VALUE_LONG:
             value = XmpUtils.ConvertToLong(rawValue);
             break;
         case VALUE_DOUBLE:
             value = XmpUtils.ConvertToDouble(rawValue);
             break;
         case VALUE_DATE:
             value = XmpUtils.ConvertToDate(rawValue);
             break;
         case VALUE_CALENDAR:
             IXmpDateTime dt = XmpUtils.ConvertToDate(rawValue);
             value = dt.Calendar;
             break;
         case VALUE_BASE64:
             value = XmpUtils.DecodeBase64(rawValue);
             break;
         default:
             // leaf values return empty string instead of null
             // for the other cases the converter methods provides a "null"
             // value.
             // a default value can only occur if this method is made public.
             value = rawValue != null || propNode.Options.CompositeProperty ? rawValue : "";
             break;
     }
     return value;
 }
Esempio n. 36
0
            /// <param name="currNode"> the node that will be added to the path. </param>
            /// <param name="parentPath"> the path up to this node. </param>
            /// <param name="currentIndex"> the current array index if an arrey is traversed </param>
            /// <returns> Returns the updated path. </returns>
            protected internal virtual string AccumulatePath(XmpNode currNode, string parentPath, int currentIndex) {
                string separator;
                string segmentName;
                if (currNode.Parent == null || currNode.Options.SchemaNode) {
                    return null;
                }
                if (currNode.Parent.Options.Array) {
                    separator = "";
                    segmentName = "[" + Convert.ToString(currentIndex) + "]";
                }
                else {
                    separator = "/";
                    segmentName = currNode.Name;
                }


                if (String.IsNullOrEmpty(parentPath)) {
                    return segmentName;
                }
                if (_outerInstance.Options.JustLeafname) {
                    return !segmentName.StartsWith("?") ? segmentName : segmentName.Substring(1); // qualifier
                }
                return parentPath + separator + segmentName;
            }
Esempio n. 37
0
 public XmpPropertyImpl2(XmpNode propNode, object value) {
     _value = value;
     _propNode = propNode;
 }
Esempio n. 38
0
            /// <summary>
            /// Creates a property info object from an <code>XMPNode</code>. </summary>
            /// <param name="node"> an <code>XMPNode</code> </param>
            /// <param name="baseNs"> the base namespace to report </param>
            /// <param name="path"> the full property path </param>
            /// <returns> Returns a <code>XMPProperty</code>-object that serves representation of the node. </returns>
            protected internal virtual IXmpPropertyInfo CreatePropertyInfo(XmpNode node, string baseNs, string path) {
                string value = node.Options.SchemaNode ? null : node.Value;

                return new XmpPropertyInfoImpl(node, baseNs, path, value);
            }
Esempio n. 39
0
 /// <summary>
 /// Constructor for a cloned metadata tree.
 /// </summary>
 /// <param name="tree">
 ///            an prefilled metadata tree which fulfills all
 ///            <code>XMPNode</code> contracts. </param>
 public XmpMetaImpl(XmpNode tree) {
     _tree = tree;
 }
Esempio n. 40
0
 public XmpPropertyInfoImpl(XmpNode node, string baseNs, string path, string value) {
     _node = node;
     _baseNs = baseNs;
     _path = path;
     _value = value;
 }
Esempio n. 41
0
        /// <summary>
        /// The internals for SetProperty() and related calls, used after the node is
        /// found or created.
        /// </summary>
        /// <param name="node">
        ///            the newly created node </param>
        /// <param name="value">
        ///            the node value, can be <code>null</code> </param>
        /// <param name="newOptions">
        ///            options for the new node, must not be <code>null</code>. </param>
        /// <param name="deleteExisting"> flag if the existing value is to be overwritten </param>
        /// <exception cref="XmpException"> thrown if options and value do not correspond </exception>
        internal virtual void SetNode(XmpNode node, object value, PropertyOptions newOptions, bool deleteExisting) {
            if (deleteExisting) {
                node.Clear();
            }

            // its checked by setOptions(), if the merged result is a valid options set
            node.Options.MergeWith(newOptions);

            if (!node.Options.CompositeProperty) {
                // This is setting the value of a leaf node.
                XmpNodeUtils.SetNodeValue(node, value);
            }
            else {
                if (value != null && value.ToString().Length > 0) {
                    throw new XmpException("Composite nodes can't have values", XmpError.BADXPATH);
                }

                node.RemoveChildren();
            }
        }
Esempio n. 42
0
            /// <summary>
            /// Creates a property info object from an <code>XMPNode</code>. </summary>
            /// <param name="node"> an <code>XMPNode</code> </param>
            /// <param name="baseNs"> the base namespace to report </param>
            /// <param name="path"> the full property path </param>
            /// <returns> Returns a <code>XMPProperty</code>-object that serves representation of the node. </returns>
            protected internal virtual IXmpPropertyInfo CreatePropertyInfo(XmpNode node, string baseNs, string path)
            {
                string value = node.Options.SchemaNode ? null : node.Value;

                return(new XmpPropertyInfoImpl(node, baseNs, path, value));
            }