示例#1
0
        /// <seealso cref="Com.Adobe.Xmp.XMPUtils.AppendProperties(Com.Adobe.Xmp.XMPMeta, Com.Adobe.Xmp.XMPMeta, bool, bool)"/>
        /// <param name="source">The source XMP object.</param>
        /// <param name="destination">The destination XMP object.</param>
        /// <param name="doAllProperties">Do internal properties in addition to external properties.</param>
        /// <param name="replaceOldValues">Replace the values of existing properties.</param>
        /// <param name="deleteEmptyValues">Delete destination values if source property is empty.</param>
        /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards the Exceptions from the metadata processing</exception>
        public static void AppendProperties(XMPMeta source, XMPMeta destination, bool doAllProperties, bool replaceOldValues, bool deleteEmptyValues)
        {
            ParameterAsserts.AssertImplementation(source);
            ParameterAsserts.AssertImplementation(destination);
            XMPMetaImpl src  = (XMPMetaImpl)source;
            XMPMetaImpl dest = (XMPMetaImpl)destination;

            for (Iterator it = src.GetRoot().IterateChildren(); it.HasNext();)
            {
                XMPNode sourceSchema = (XMPNode)it.Next();
                // Make sure we have a destination schema node
                XMPNode destSchema    = XMPNodeUtils.FindSchemaNode(dest.GetRoot(), sourceSchema.GetName(), false);
                bool    createdSchema = false;
                if (destSchema == null)
                {
                    destSchema = new XMPNode(sourceSchema.GetName(), sourceSchema.GetValue(), new PropertyOptions().SetSchemaNode(true));
                    dest.GetRoot().AddChild(destSchema);
                    createdSchema = true;
                }
                // Process the source schema's children.
                for (Iterator ic = sourceSchema.IterateChildren(); ic.HasNext();)
                {
                    XMPNode sourceProp = (XMPNode)ic.Next();
                    if (doAllProperties || !Utils.IsInternalProperty(sourceSchema.GetName(), sourceProp.GetName()))
                    {
                        AppendSubtree(dest, sourceProp, destSchema, replaceOldValues, deleteEmptyValues);
                    }
                }
                if (!destSchema.HasChildren() && (createdSchema || deleteEmptyValues))
                {
                    // Don't create an empty schema / remove empty schema.
                    dest.GetRoot().RemoveChild(destSchema);
                }
            }
        }
示例#2
0
        // EMPTY
        /// <seealso cref="Com.Adobe.Xmp.XMPUtils.CatenateArrayItems(Com.Adobe.Xmp.XMPMeta, string, string, string, string, bool)"/>
        /// <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="Com.Adobe.Xmp.XMPException">Forwards the Exceptions from the metadata processing</exception>
        public static string CatenateArrayItems(XMPMeta xmp, string schemaNS, string arrayName, string separator, string quotes, bool allowCommas)
        {
            ParameterAsserts.AssertSchemaNS(schemaNS);
            ParameterAsserts.AssertArrayName(arrayName);
            ParameterAsserts.AssertImplementation(xmp);
            if (separator == null || separator.Length == 0)
            {
                separator = "; ";
            }
            if (quotes == null || quotes.Length == 0)
            {
                quotes = "\"";
            }
            XMPMetaImpl xmpImpl   = (XMPMetaImpl)xmp;
            XMPNode     arrayNode = null;
            XMPNode     currItem  = null;
            // Return an empty result if the array does not exist,
            // hurl if it isn't the right form.
            XMPPath arrayPath = XMPPathParser.ExpandXPath(schemaNS, arrayName);

            arrayNode = XMPNodeUtils.FindNode(xmpImpl.GetRoot(), arrayPath, false, null);
            if (arrayNode == null)
            {
                return(string.Empty);
            }
            else
            {
                if (!arrayNode.GetOptions().IsArray() || arrayNode.GetOptions().IsArrayAlternate())
                {
                    throw new XMPException("Named property must be non-alternate array", XMPErrorConstants.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 (Iterator it = arrayNode.IterateChildren(); it.HasNext();)
            {
                currItem = (XMPNode)it.Next();
                if (currItem.GetOptions().IsCompositeProperty())
                {
                    throw new XMPException("Array items must be simple", XMPErrorConstants.Badparam);
                }
                string str = ApplyQuotes(currItem.GetValue(), openQuote, closeQuote, allowCommas);
                catinatedString.Append(str);
                if (it.HasNext())
                {
                    catinatedString.Append(separator);
                }
            }
            return(catinatedString.ToString());
        }
示例#3
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 _enclosing, XMPNode parentNode, string parentPath)
     : base(_enclosing)
 {
     this._enclosing = _enclosing;
     if (parentNode.GetOptions().IsSchemaNode())
     {
         this._enclosing.SetBaseNS(parentNode.GetName());
     }
     this.parentPath       = this.AccumulatePath(parentNode, parentPath, 1);
     this.childrenIterator = parentNode.IterateChildren();
 }
示例#4
0
 /// <summary>
 /// Remove all schema children according to the flag
 /// <code>doAllProperties</code>.
 /// </summary>
 /// <remarks>
 /// Remove all schema children according to the flag
 /// <code>doAllProperties</code>. Empty schemas are automatically remove
 /// by <code>XMPNode</code>
 /// </remarks>
 /// <param name="schemaNode">a schema node</param>
 /// <param name="doAllProperties">flag if all properties or only externals shall be removed.</param>
 /// <returns>Returns true if the schema is empty after the operation.</returns>
 private static bool RemoveSchemaChildren(XMPNode schemaNode, bool doAllProperties)
 {
     for (Iterator it = schemaNode.IterateChildren(); it.HasNext();)
     {
         XMPNode currProp = (XMPNode)it.Next();
         if (doAllProperties || !Utils.IsInternalProperty(schemaNode.GetName(), currProp.GetName()))
         {
             it.Remove();
         }
     }
     return(!schemaNode.HasChildren());
 }
 /// <summary>Remove all empty schemas from the metadata tree that were generated during the rdf parsing.</summary>
 /// <param name="tree">the root of the metadata tree</param>
 private static void DeleteEmptySchemas(XMPNode tree)
 {
     // Delete empty schema nodes. Do this last, other cleanup can make empty
     // schema.
     for (Iterator it = tree.IterateChildren(); it.HasNext();)
     {
         XMPNode schema = (XMPNode)it.Next();
         if (!schema.HasChildren())
         {
             it.Remove();
         }
     }
 }
 /// <summary>The outermost call is special.</summary>
 /// <remarks>
 /// The outermost call is special. The names almost certainly differ. The
 /// qualifiers (and hence options) will differ for an alias to the x-default
 /// item of a langAlt array.
 /// </remarks>
 /// <param name="aliasNode">the alias node</param>
 /// <param name="baseNode">the base node of the alias</param>
 /// <param name="outerCall">marks the outer call of the recursion</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards XMP errors</exception>
 private static void CompareAliasedSubtrees(XMPNode aliasNode, XMPNode baseNode, bool outerCall)
 {
     if (!aliasNode.GetValue().Equals(baseNode.GetValue()) || aliasNode.GetChildrenLength() != baseNode.GetChildrenLength())
     {
         throw new XMPException("Mismatch between alias and base nodes", XMPErrorConstants.Badxmp);
     }
     if (!outerCall && (!aliasNode.GetName().Equals(baseNode.GetName()) || !aliasNode.GetOptions().Equals(baseNode.GetOptions()) || aliasNode.GetQualifierLength() != baseNode.GetQualifierLength()))
     {
         throw new XMPException("Mismatch between alias and base nodes", XMPErrorConstants.Badxmp);
     }
     for (Iterator an = aliasNode.IterateChildren(), bn = baseNode.IterateChildren(); an.HasNext() && bn.HasNext();)
     {
         XMPNode aliasChild = (XMPNode)an.Next();
         XMPNode baseChild  = (XMPNode)bn.Next();
         CompareAliasedSubtrees(aliasChild, baseChild, false);
     }
     for (Iterator an_1 = aliasNode.IterateQualifier(), bn = baseNode.IterateChildren(); an_1.HasNext() && bn.HasNext();)
     {
         XMPNode aliasQual = (XMPNode)an_1.Next();
         XMPNode baseQual  = (XMPNode)bn.Next();
         CompareAliasedSubtrees(aliasQual, baseQual, false);
     }
 }
示例#7
0
 /// <summary>See if an array is an alt-text array.</summary>
 /// <remarks>
 /// See if an array is an alt-text array. If so, make sure the x-default item
 /// is first.
 /// </remarks>
 /// <param name="arrayNode">the array node to check if its an alt-text array</param>
 internal static void DetectAltText(XMPNode arrayNode)
 {
     if (arrayNode.GetOptions().IsArrayAlternate() && arrayNode.HasChildren())
     {
         bool isAltText = false;
         for (Iterator it = arrayNode.IterateChildren(); it.HasNext();)
         {
             XMPNode child = (XMPNode)it.Next();
             if (child.GetOptions().GetHasLanguage())
             {
                 isAltText = true;
                 break;
             }
         }
         if (isAltText)
         {
             arrayNode.GetOptions().SetArrayAltText(true);
             NormalizeLangArray(arrayNode);
         }
     }
 }
 /// <summary>Make sure that the array is well-formed AltText.</summary>
 /// <remarks>
 /// Make sure that the array is well-formed AltText. Each item must be simple
 /// and have an "xml:lang" qualifier. If repairs are needed, keep simple
 /// non-empty items by adding the "xml:lang" with value "x-repair".
 /// </remarks>
 /// <param name="arrayNode">the property node of the array to repair.</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards unexpected exceptions.</exception>
 private static void RepairAltText(XMPNode arrayNode)
 {
     if (arrayNode == null || !arrayNode.GetOptions().IsArray())
     {
         // Already OK or not even an array.
         return;
     }
     // fix options
     arrayNode.GetOptions().SetArrayOrdered(true).SetArrayAlternate(true).SetArrayAltText(true);
     for (Iterator it = arrayNode.IterateChildren(); it.HasNext();)
     {
         XMPNode currChild = (XMPNode)it.Next();
         if (currChild.GetOptions().IsCompositeProperty())
         {
             // Delete non-simple children.
             it.Remove();
         }
         else
         {
             if (!currChild.GetOptions().GetHasLanguage())
             {
                 string childValue = currChild.GetValue();
                 if (childValue == null || childValue.Length == 0)
                 {
                     // Delete empty valued children that have no xml:lang.
                     it.Remove();
                 }
                 else
                 {
                     // Add an xml:lang qualifier with the value "x-repair".
                     XMPNode repairLang = new XMPNode(XMPConstConstants.XmlLang, "x-repair", null);
                     currChild.AddQualifier(repairLang);
                 }
             }
         }
     }
 }
		/// <summary>
		/// Recursively handles the "value" for a node that must be written as an RDF
		/// property element.
		/// </summary>
		/// <remarks>
		/// Recursively handles the "value" for a node that must be written as an RDF
		/// property element. It does not matter if it is a top level property, a
		/// field of a struct, or an item of an array. The indent is that for the
		/// property element. The patterns bwlow ignore attribute qualifiers such as
		/// xml:lang, they don't affect the output form.
		/// <blockquote>
		/// <pre>
		/// &lt;ns:UnqualifiedStructProperty-1
		/// ... The fields as attributes, if all are simple and unqualified
		/// /&gt;
		/// &lt;ns:UnqualifiedStructProperty-2 rdf:parseType=&quot;Resource&quot;&gt;
		/// ... The fields as elements, if none are simple and unqualified
		/// &lt;/ns:UnqualifiedStructProperty-2&gt;
		/// &lt;ns:UnqualifiedStructProperty-3&gt;
		/// &lt;rdf:Description
		/// ... The simple and unqualified fields as attributes
		/// &gt;
		/// ... The compound or qualified fields as elements
		/// &lt;/rdf:Description&gt;
		/// &lt;/ns:UnqualifiedStructProperty-3&gt;
		/// &lt;ns:UnqualifiedArrayProperty&gt;
		/// &lt;rdf:Bag&gt; or Seq or Alt
		/// ... Array items as rdf:li elements, same forms as top level properties
		/// &lt;/rdf:Bag&gt;
		/// &lt;/ns:UnqualifiedArrayProperty&gt;
		/// &lt;ns:QualifiedProperty rdf:parseType=&quot;Resource&quot;&gt;
		/// &lt;rdf:value&gt; ... Property &quot;value&quot;
		/// following the unqualified forms ... &lt;/rdf:value&gt;
		/// ... Qualifiers looking like named struct fields
		/// &lt;/ns:QualifiedProperty&gt;
		/// </pre>
		/// </blockquote>
		/// *** Consider numbered array items, but has compatibility problems.
		/// Consider qualified form with rdf:Description and attributes.
		/// </remarks>
		/// <param name="parentNode">the parent node</param>
		/// <param name="indent">the current indent level</param>
		/// <exception cref="System.IO.IOException">Forwards writer exceptions</exception>
		/// <exception cref="Com.Adobe.Xmp.XMPException">If qualifier and element fields are mixed.</exception>
		private void SerializeCompactRDFElementProps(XMPNode parentNode, int indent)
		{
			for (Iterator it = parentNode.IterateChildren(); it.HasNext(); )
			{
				XMPNode node = (XMPNode)it.Next();
				if (CanBeRDFAttrProp(node))
				{
					continue;
				}
				bool emitEndTag = true;
				bool indentEndTag = true;
				// Determine the XML element name, write the name part of the start tag. Look over the
				// qualifiers to decide on "normal" versus "rdf:value" form. Emit the attribute
				// qualifiers at the same time.
				string elemName = node.GetName();
				if (XMPConstConstants.ArrayItemName.Equals(elemName))
				{
					elemName = "rdf:li";
				}
				WriteIndent(indent);
				Write('<');
				Write(elemName);
				bool hasGeneralQualifiers = false;
				bool hasRDFResourceQual = false;
				for (Iterator iq = node.IterateQualifier(); iq.HasNext(); )
				{
					XMPNode qualifier = (XMPNode)iq.Next();
					if (!RdfAttrQualifier.Contains(qualifier.GetName()))
					{
						hasGeneralQualifiers = true;
					}
					else
					{
						hasRDFResourceQual = "rdf:resource".Equals(qualifier.GetName());
						Write(' ');
						Write(qualifier.GetName());
						Write("=\"");
						AppendNodeValue(qualifier.GetValue(), true);
						Write('"');
					}
				}
				// Process the property according to the standard patterns.
				if (hasGeneralQualifiers)
				{
					SerializeCompactRDFGeneralQualifier(indent, node);
				}
				else
				{
					// This node has only attribute qualifiers. Emit as a property element.
					if (!node.GetOptions().IsCompositeProperty())
					{
						object[] result = SerializeCompactRDFSimpleProp(node);
						emitEndTag = ((bool)result[0]);
						indentEndTag = ((bool)result[1]);
					}
					else
					{
						if (node.GetOptions().IsArray())
						{
							SerializeCompactRDFArrayProp(node, indent);
						}
						else
						{
							emitEndTag = SerializeCompactRDFStructProp(node, indent, hasRDFResourceQual);
						}
					}
				}
				// Emit the property element end tag.
				if (emitEndTag)
				{
					if (indentEndTag)
					{
						WriteIndent(indent);
					}
					Write("</");
					Write(elemName);
					Write('>');
					WriteNewline();
				}
			}
		}
示例#10
0
        /// <seealso cref="AppendProperties(Com.Adobe.Xmp.XMPMeta, Com.Adobe.Xmp.XMPMeta, bool, bool, bool)"/>
        /// <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="Com.Adobe.Xmp.XMPException"/>
        private static void AppendSubtree(XMPMetaImpl destXMP, XMPNode sourceNode, XMPNode destParent, bool replaceOldValues, bool deleteEmptyValues)
        {
            XMPNode destNode     = XMPNodeUtils.FindChildNode(destParent, sourceNode.GetName(), false);
            bool    valueIsEmpty = false;

            if (deleteEmptyValues)
            {
                valueIsEmpty = sourceNode.GetOptions().IsSimple() ? sourceNode.GetValue() == null || sourceNode.GetValue().Length == 0 : !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.GetValue(), sourceNode.GetOptions(), 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.GetOptions();
                        PropertyOptions destForm   = destNode.GetOptions();
                        if (sourceForm != destForm)
                        {
                            return;
                        }
                        if (sourceForm.IsStruct())
                        {
                            // 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 (Iterator it = sourceNode.IterateChildren(); it.HasNext();)
                            {
                                XMPNode sourceField = (XMPNode)it.Next();
                                AppendSubtree(destXMP, sourceField, destNode, replaceOldValues, deleteEmptyValues);
                                if (deleteEmptyValues && !destNode.HasChildren())
                                {
                                    destParent.RemoveChild(destNode);
                                }
                            }
                        }
                        else
                        {
                            if (sourceForm.IsArrayAltText())
                            {
                                // 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 (Iterator it = sourceNode.IterateChildren(); it.HasNext();)
                                {
                                    XMPNode sourceItem = (XMPNode)it.Next();
                                    if (!sourceItem.HasQualifier() || !XMPConstConstants.XmlLang.Equals(sourceItem.GetQualifier(1).GetName()))
                                    {
                                        continue;
                                    }
                                    int destIndex = XMPNodeUtils.LookupLanguageItem(destNode, sourceItem.GetQualifier(1).GetValue());
                                    if (deleteEmptyValues && (sourceItem.GetValue() == null || sourceItem.GetValue().Length == 0))
                                    {
                                        if (destIndex != -1)
                                        {
                                            destNode.RemoveChild(destIndex);
                                            if (!destNode.HasChildren())
                                            {
                                                destParent.RemoveChild(destNode);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (destIndex == -1)
                                        {
                                            // Not replacing, keep the existing item.
                                            if (!XMPConstConstants.XDefault.Equals(sourceItem.GetQualifier(1).GetValue()) || !destNode.HasChildren())
                                            {
                                                sourceItem.CloneSubtree(destNode);
                                            }
                                            else
                                            {
                                                XMPNode destItem = new XMPNode(sourceItem.GetName(), sourceItem.GetValue(), sourceItem.GetOptions());
                                                sourceItem.CloneSubtree(destItem);
                                                destNode.AddChild(1, destItem);
                                            }
                                        }
                                    }
                                }
                            }
                            else
                            {
                                if (sourceForm.IsArray())
                                {
                                    // 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 (Iterator @is = sourceNode.IterateChildren(); @is.HasNext();)
                                    {
                                        XMPNode sourceItem = (XMPNode)@is.Next();
                                        bool    match      = false;
                                        for (Iterator id = destNode.IterateChildren(); id.HasNext();)
                                        {
                                            XMPNode destItem = (XMPNode)id.Next();
                                            if (ItemValuesMatch(sourceItem, destItem))
                                            {
                                                match = true;
                                            }
                                        }
                                        if (!match)
                                        {
                                            destNode = (XMPNode)sourceItem.Clone();
                                            destParent.AddChild(destNode);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
		/// <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="System.IO.IOException">Forwards the writer exceptions.</exception>
		/// <exception cref="Com.Adobe.Xmp.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 (Iterator ic = node.IterateChildren(); ic.HasNext(); )
			{
				XMPNode field = (XMPNode)ic.Next();
				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", XMPErrorConstants.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(RdfStructStart);
						SerializeCompactRDFAttrProps(node, indent + 2);
						Write(">");
						WriteNewline();
						SerializeCompactRDFElementProps(node, indent + 1);
						WriteIndent(indent + 1);
						Write(RdfStructEnd);
						WriteNewline();
					}
				}
			}
			return emitEndTag;
		}
		/// <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.
		/// </summary>
		/// <remarks>
		/// 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>
		/// </remarks>
		/// <param name="schemaNode">a schema node</param>
		/// <param name="level"></param>
		/// <exception cref="System.IO.IOException">Forwarded writer exceptions</exception>
		/// <exception cref="Com.Adobe.Xmp.XMPException"></exception>
		private void SerializeCanonicalRDFSchema(XMPNode schemaNode, int level)
		{
			// Write each of the schema's actual properties.
			for (Iterator it = schemaNode.IterateChildren(); it.HasNext(); )
			{
				XMPNode propNode = (XMPNode)it.Next();
				SerializeCanonicalRDFProperty(propNode, options.GetUseCanonicalFormat(), false, level + 2);
			}
		}
		/// <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="Com.Adobe.Xmp.XMPException">Forwards exceptions to the calling method.</exception>
		private static bool ItemValuesMatch(XMPNode leftNode, XMPNode rightNode)
		{
			PropertyOptions leftForm = leftNode.GetOptions();
			PropertyOptions rightForm = rightNode.GetOptions();
			if (leftForm.Equals(rightForm))
			{
				return false;
			}
			if (leftForm.GetOptions() == 0)
			{
				// Simple nodes, check the values and xml:lang qualifiers.
				if (!leftNode.GetValue().Equals(rightNode.GetValue()))
				{
					return false;
				}
				if (leftNode.GetOptions().GetHasLanguage() != rightNode.GetOptions().GetHasLanguage())
				{
					return false;
				}
				if (leftNode.GetOptions().GetHasLanguage() && !leftNode.GetQualifier(1).GetValue().Equals(rightNode.GetQualifier(1).GetValue()))
				{
					return false;
				}
			}
			else
			{
				if (leftForm.IsStruct())
				{
					// Struct nodes, see if all fields match, ignoring order.
					if (leftNode.GetChildrenLength() != rightNode.GetChildrenLength())
					{
						return false;
					}
					for (Iterator it = leftNode.IterateChildren(); it.HasNext(); )
					{
						XMPNode leftField = (XMPNode)it.Next();
						XMPNode rightField = XMPNodeUtils.FindChildNode(rightNode, leftField.GetName(), 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.
					System.Diagnostics.Debug.Assert(leftForm.IsArray());
					for (Iterator il = leftNode.IterateChildren(); il.HasNext(); )
					{
						XMPNode leftItem = (XMPNode)il.Next();
						bool match = false;
						for (Iterator ir = rightNode.IterateChildren(); ir.HasNext(); )
						{
							XMPNode rightItem = (XMPNode)ir.Next();
							if (ItemValuesMatch(leftItem, rightItem))
							{
								match = true;
								break;
							}
						}
						if (!match)
						{
							return false;
						}
					}
				}
			}
			return true;
		}
 /// <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 _enclosing, XMPNode parentNode, string parentPath)
     : base(_enclosing)
 {
     this._enclosing = _enclosing;
     if (parentNode.GetOptions().IsSchemaNode())
     {
         this._enclosing.SetBaseNS(parentNode.GetName());
     }
     this.parentPath = this.AccumulatePath(parentNode, parentPath, 1);
     this.childrenIterator = parentNode.IterateChildren();
 }
        /// <summary>Visit all of the top level nodes looking for aliases.</summary>
        /// <remarks>
        /// Visit all of the top level nodes looking for aliases. If there is
        /// no base, transplant the alias subtree. If there is a base and strict
        /// aliasing is on, make sure the alias and base subtrees match.
        /// </remarks>
        /// <param name="tree">the root of the metadata tree</param>
        /// <param name="options">th parsing options</param>
        /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards XMP errors</exception>
        private static void MoveExplicitAliases(XMPNode tree, ParseOptions options)
        {
            if (!tree.GetHasAliases())
            {
                return;
            }
            tree.SetHasAliases(false);
            bool strictAliasing = options.GetStrictAliasing();

            for (Iterator schemaIt = tree.GetUnmodifiableChildren().Iterator(); schemaIt.HasNext();)
            {
                XMPNode currSchema = (XMPNode)schemaIt.Next();
                if (!currSchema.GetHasAliases())
                {
                    continue;
                }
                for (Iterator propertyIt = currSchema.IterateChildren(); propertyIt.HasNext();)
                {
                    XMPNode currProp = (XMPNode)propertyIt.Next();
                    if (!currProp.IsAlias())
                    {
                        continue;
                    }
                    currProp.SetAlias(false);
                    // Find the base path, look for the base schema and root node.
                    XMPAliasInfo info = XMPMetaFactory.GetSchemaRegistry().FindAlias(currProp.GetName());
                    if (info != null)
                    {
                        // find or create schema
                        XMPNode baseSchema = XMPNodeUtils.FindSchemaNode(tree, info.GetNamespace(), null, true);
                        baseSchema.SetImplicit(false);
                        XMPNode baseNode = XMPNodeUtils.FindChildNode(baseSchema, info.GetPrefix() + info.GetPropName(), false);
                        if (baseNode == null)
                        {
                            if (info.GetAliasForm().IsSimple())
                            {
                                // A top-to-top alias, transplant the property.
                                // change the alias property name to the base name
                                string qname = info.GetPrefix() + info.GetPropName();
                                currProp.SetName(qname);
                                baseSchema.AddChild(currProp);
                                // remove the alias property
                                propertyIt.Remove();
                            }
                            else
                            {
                                // An alias to an array item,
                                // create the array and transplant the property.
                                baseNode = new XMPNode(info.GetPrefix() + info.GetPropName(), info.GetAliasForm().ToPropertyOptions());
                                baseSchema.AddChild(baseNode);
                                TransplantArrayItemAlias(propertyIt, currProp, baseNode);
                            }
                        }
                        else
                        {
                            if (info.GetAliasForm().IsSimple())
                            {
                                // The base node does exist and this is a top-to-top alias.
                                // Check for conflicts if strict aliasing is on.
                                // Remove and delete the alias subtree.
                                if (strictAliasing)
                                {
                                    CompareAliasedSubtrees(currProp, baseNode, true);
                                }
                                propertyIt.Remove();
                            }
                            else
                            {
                                // This is an alias to an array item and the array exists.
                                // Look for the aliased item.
                                // Then transplant or check & delete as appropriate.
                                XMPNode itemNode = null;
                                if (info.GetAliasForm().IsArrayAltText())
                                {
                                    int xdIndex = XMPNodeUtils.LookupLanguageItem(baseNode, XMPConstConstants.XDefault);
                                    if (xdIndex != -1)
                                    {
                                        itemNode = baseNode.GetChild(xdIndex);
                                    }
                                }
                                else
                                {
                                    if (baseNode.HasChildren())
                                    {
                                        itemNode = baseNode.GetChild(1);
                                    }
                                }
                                if (itemNode == null)
                                {
                                    TransplantArrayItemAlias(propertyIt, currProp, baseNode);
                                }
                                else
                                {
                                    if (strictAliasing)
                                    {
                                        CompareAliasedSubtrees(currProp, itemNode, true);
                                    }
                                    propertyIt.Remove();
                                }
                            }
                        }
                    }
                }
                currSchema.SetHasAliases(false);
            }
        }
		/// <summary>Write each of the parent's simple unqualified properties as an attribute.</summary>
		/// <remarks>
		/// Write each of the parent's simple unqualified properties as an attribute. Returns true if all
		/// of the properties are written as attributes.
		/// </remarks>
		/// <param name="parentNode">the parent property node</param>
		/// <param name="indent">the current indent level</param>
		/// <returns>Returns true if all properties can be rendered as RDF attribute.</returns>
		/// <exception cref="System.IO.IOException"/>
		private bool SerializeCompactRDFAttrProps(XMPNode parentNode, int indent)
		{
			bool allAreAttrs = true;
			for (Iterator it = parentNode.IterateChildren(); it.HasNext(); )
			{
				XMPNode prop = (XMPNode)it.Next();
				if (CanBeRDFAttrProp(prop))
				{
					WriteNewline();
					WriteIndent(indent);
					Write(prop.GetName());
					Write("=\"");
					AppendNodeValue(prop.GetValue(), true);
					Write('"');
				}
				else
				{
					allAreAttrs = false;
				}
			}
			return allAreAttrs;
		}
 /// <summary>Remove all empty schemas from the metadata tree that were generated during the rdf parsing.</summary>
 /// <param name="tree">the root of the metadata tree</param>
 private static void DeleteEmptySchemas(XMPNode tree)
 {
     // Delete empty schema nodes. Do this last, other cleanup can make empty
     // schema.
     for (Iterator it = tree.IterateChildren(); it.HasNext(); )
     {
         XMPNode schema = (XMPNode)it.Next();
         if (!schema.HasChildren())
         {
             it.Remove();
         }
     }
 }
 /// <summary>
 /// <ol>
 /// <li>Look for an exact match with the specific language.
 /// </summary>
 /// <remarks>
 /// <ol>
 /// <li>Look for an exact match with the specific language.
 /// <li>If a generic language is given, look for partial matches.
 /// <li>Look for an "x-default"-item.
 /// <li>Choose the first item.
 /// </ol>
 /// </remarks>
 /// <param name="arrayNode">the alt text array node</param>
 /// <param name="genericLang">the generic language</param>
 /// <param name="specificLang">the specific language</param>
 /// <returns>
 /// Returns the kind of match as an Integer and the found node in an
 /// array.
 /// </returns>
 /// <exception cref="Com.Adobe.Xmp.XMPException"/>
 internal static object[] ChooseLocalizedText(XMPNode arrayNode, string genericLang, string specificLang)
 {
     // See if the array has the right form. Allow empty alt arrays,
     // that is what parsing returns.
     if (!arrayNode.GetOptions().IsArrayAltText())
     {
         throw new XMPException("Localized text array is not alt-text", XMPErrorConstants.Badxpath);
     }
     else
     {
         if (!arrayNode.HasChildren())
         {
             return new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltNoValues, null };
         }
     }
     int foundGenericMatches = 0;
     XMPNode resultNode = null;
     XMPNode xDefault = null;
     // Look for the first partial match with the generic language.
     for (Iterator it = arrayNode.IterateChildren(); it.HasNext(); )
     {
         XMPNode currItem = (XMPNode)it.Next();
         // perform some checks on the current item
         if (currItem.GetOptions().IsCompositeProperty())
         {
             throw new XMPException("Alt-text array item is not simple", XMPErrorConstants.Badxpath);
         }
         else
         {
             if (!currItem.HasQualifier() || !XMPConstConstants.XmlLang.Equals(currItem.GetQualifier(1).GetName()))
             {
                 throw new XMPException("Alt-text array item has no language qualifier", XMPErrorConstants.Badxpath);
             }
         }
         string currLang = currItem.GetQualifier(1).GetValue();
         // Look for an exact match with the specific language.
         if (specificLang.Equals(currLang))
         {
             return new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltSpecificMatch, currItem };
         }
         else
         {
             if (genericLang != null && currLang.StartsWith(genericLang))
             {
                 if (resultNode == null)
                 {
                     resultNode = currItem;
                 }
                 // ! Don't return/break, need to look for other matches.
                 foundGenericMatches++;
             }
             else
             {
                 if (XMPConstConstants.XDefault.Equals(currLang))
                 {
                     xDefault = currItem;
                 }
             }
         }
     }
     // evaluate loop
     if (foundGenericMatches == 1)
     {
         return new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltSingleGeneric, resultNode };
     }
     else
     {
         if (foundGenericMatches > 1)
         {
             return new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltMultipleGeneric, resultNode };
         }
         else
         {
             if (xDefault != null)
             {
                 return new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltXdefault, xDefault };
             }
             else
             {
                 // Everything failed, choose the first item.
                 return new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltFirstItem, arrayNode.GetChild(1) };
             }
         }
     }
 }
 /// <summary>See if an array is an alt-text array.</summary>
 /// <remarks>
 /// See if an array is an alt-text array. If so, make sure the x-default item
 /// is first.
 /// </remarks>
 /// <param name="arrayNode">the array node to check if its an alt-text array</param>
 internal static void DetectAltText(XMPNode arrayNode)
 {
     if (arrayNode.GetOptions().IsArrayAlternate() && arrayNode.HasChildren())
     {
         bool isAltText = false;
         for (Iterator it = arrayNode.IterateChildren(); it.HasNext(); )
         {
             XMPNode child = (XMPNode)it.Next();
             if (child.GetOptions().GetHasLanguage())
             {
                 isAltText = true;
                 break;
             }
         }
         if (isAltText)
         {
             arrayNode.GetOptions().SetArrayAltText(true);
             NormalizeLangArray(arrayNode);
         }
     }
 }
		/// <summary>Writes all used namespaces of the subtree in node to the output.</summary>
		/// <remarks>
		/// Writes all used namespaces of the subtree in node to the output.
		/// The subtree is recursivly traversed.
		/// </remarks>
		/// <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="System.IO.IOException">Forwards all writer exceptions.</exception>
        private void DeclareUsedNamespaces(XMPNode node, HashSet<string> usedPrefixes, int indent)
		{
			if (node.GetOptions().IsSchemaNode())
			{
				// The schema node name is the URI, the value is the prefix.
				string prefix = Sharpen.Runtime.Substring(node.GetValue(), 0, node.GetValue().Length - 1);
				DeclareNamespace(prefix, node.GetName(), usedPrefixes, indent);
			}
			else
			{
				if (node.GetOptions().IsStruct())
				{
					for (Iterator it = node.IterateChildren(); it.HasNext(); )
					{
						XMPNode field = (XMPNode)it.Next();
						DeclareNamespace(field.GetName(), null, usedPrefixes, indent);
					}
				}
			}
			for (Iterator it_1 = node.IterateChildren(); it_1.HasNext(); )
			{
				XMPNode child = (XMPNode)it_1.Next();
				DeclareUsedNamespaces(child, usedPrefixes, indent);
			}
			for (Iterator it_2 = node.IterateQualifier(); it_2.HasNext(); )
			{
				XMPNode qualifier = (XMPNode)it_2.Next();
				DeclareNamespace(qualifier.GetName(), null, usedPrefixes, indent);
				DeclareUsedNamespaces(qualifier, usedPrefixes, indent);
			}
		}
示例#21
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="Com.Adobe.Xmp.XMPException">Forwards exceptions to the calling method.</exception>
        private static bool ItemValuesMatch(XMPNode leftNode, XMPNode rightNode)
        {
            PropertyOptions leftForm  = leftNode.GetOptions();
            PropertyOptions rightForm = rightNode.GetOptions();

            if (leftForm.Equals(rightForm))
            {
                return(false);
            }
            if (leftForm.GetOptions() == 0)
            {
                // Simple nodes, check the values and xml:lang qualifiers.
                if (!leftNode.GetValue().Equals(rightNode.GetValue()))
                {
                    return(false);
                }
                if (leftNode.GetOptions().GetHasLanguage() != rightNode.GetOptions().GetHasLanguage())
                {
                    return(false);
                }
                if (leftNode.GetOptions().GetHasLanguage() && !leftNode.GetQualifier(1).GetValue().Equals(rightNode.GetQualifier(1).GetValue()))
                {
                    return(false);
                }
            }
            else
            {
                if (leftForm.IsStruct())
                {
                    // Struct nodes, see if all fields match, ignoring order.
                    if (leftNode.GetChildrenLength() != rightNode.GetChildrenLength())
                    {
                        return(false);
                    }
                    for (Iterator it = leftNode.IterateChildren(); it.HasNext();)
                    {
                        XMPNode leftField  = (XMPNode)it.Next();
                        XMPNode rightField = XMPNodeUtils.FindChildNode(rightNode, leftField.GetName(), 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.
                    System.Diagnostics.Debug.Assert(leftForm.IsArray());
                    for (Iterator il = leftNode.IterateChildren(); il.HasNext();)
                    {
                        XMPNode leftItem = (XMPNode)il.Next();
                        bool    match    = false;
                        for (Iterator ir = rightNode.IterateChildren(); ir.HasNext();)
                        {
                            XMPNode rightItem = (XMPNode)ir.Next();
                            if (ItemValuesMatch(leftItem, rightItem))
                            {
                                match = true;
                                break;
                            }
                        }
                        if (!match)
                        {
                            return(false);
                        }
                    }
                }
            }
            return(true);
        }
		/// <summary>
		/// Remove all schema children according to the flag
		/// <code>doAllProperties</code>.
		/// </summary>
		/// <remarks>
		/// Remove all schema children according to the flag
		/// <code>doAllProperties</code>. Empty schemas are automatically remove
		/// by <code>XMPNode</code>
		/// </remarks>
		/// <param name="schemaNode">a schema node</param>
		/// <param name="doAllProperties">flag if all properties or only externals shall be removed.</param>
		/// <returns>Returns true if the schema is empty after the operation.</returns>
		private static bool RemoveSchemaChildren(XMPNode schemaNode, bool doAllProperties)
		{
			for (Iterator it = schemaNode.IterateChildren(); it.HasNext(); )
			{
				XMPNode currProp = (XMPNode)it.Next();
				if (doAllProperties || !Utils.IsInternalProperty(schemaNode.GetName(), currProp.GetName()))
				{
					it.Remove();
				}
			}
			return !schemaNode.HasChildren();
		}
		/// <seealso cref="AppendProperties(Com.Adobe.Xmp.XMPMeta, Com.Adobe.Xmp.XMPMeta, bool, bool, bool)"/>
		/// <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="Com.Adobe.Xmp.XMPException"/>
		private static void AppendSubtree(XMPMetaImpl destXMP, XMPNode sourceNode, XMPNode destParent, bool replaceOldValues, bool deleteEmptyValues)
		{
			XMPNode destNode = XMPNodeUtils.FindChildNode(destParent, sourceNode.GetName(), false);
			bool valueIsEmpty = false;
			if (deleteEmptyValues)
			{
				valueIsEmpty = sourceNode.GetOptions().IsSimple() ? sourceNode.GetValue() == null || sourceNode.GetValue().Length == 0 : !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.GetValue(), sourceNode.GetOptions(), 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.GetOptions();
						PropertyOptions destForm = destNode.GetOptions();
						if (sourceForm != destForm)
						{
							return;
						}
						if (sourceForm.IsStruct())
						{
							// 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 (Iterator it = sourceNode.IterateChildren(); it.HasNext(); )
							{
								XMPNode sourceField = (XMPNode)it.Next();
								AppendSubtree(destXMP, sourceField, destNode, replaceOldValues, deleteEmptyValues);
								if (deleteEmptyValues && !destNode.HasChildren())
								{
									destParent.RemoveChild(destNode);
								}
							}
						}
						else
						{
							if (sourceForm.IsArrayAltText())
							{
								// 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 (Iterator it = sourceNode.IterateChildren(); it.HasNext(); )
								{
									XMPNode sourceItem = (XMPNode)it.Next();
									if (!sourceItem.HasQualifier() || !XMPConstConstants.XmlLang.Equals(sourceItem.GetQualifier(1).GetName()))
									{
										continue;
									}
									int destIndex = XMPNodeUtils.LookupLanguageItem(destNode, sourceItem.GetQualifier(1).GetValue());
									if (deleteEmptyValues && (sourceItem.GetValue() == null || sourceItem.GetValue().Length == 0))
									{
										if (destIndex != -1)
										{
											destNode.RemoveChild(destIndex);
											if (!destNode.HasChildren())
											{
												destParent.RemoveChild(destNode);
											}
										}
									}
									else
									{
										if (destIndex == -1)
										{
											// Not replacing, keep the existing item.						
											if (!XMPConstConstants.XDefault.Equals(sourceItem.GetQualifier(1).GetValue()) || !destNode.HasChildren())
											{
												sourceItem.CloneSubtree(destNode);
											}
											else
											{
												XMPNode destItem = new XMPNode(sourceItem.GetName(), sourceItem.GetValue(), sourceItem.GetOptions());
												sourceItem.CloneSubtree(destItem);
												destNode.AddChild(1, destItem);
											}
										}
									}
								}
							}
							else
							{
								if (sourceForm.IsArray())
								{
									// 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 (Iterator @is = sourceNode.IterateChildren(); @is.HasNext(); )
									{
										XMPNode sourceItem = (XMPNode)@is.Next();
										bool match = false;
										for (Iterator id = destNode.IterateChildren(); id.HasNext(); )
										{
											XMPNode destItem = (XMPNode)id.Next();
											if (ItemValuesMatch(sourceItem, destItem))
											{
												match = true;
											}
										}
										if (!match)
										{
											destNode = (XMPNode)sourceItem.Clone();
											destParent.AddChild(destNode);
										}
									}
								}
							}
						}
					}
				}
			}
		}
 /// <summary>Make sure that the array is well-formed AltText.</summary>
 /// <remarks>
 /// Make sure that the array is well-formed AltText. Each item must be simple
 /// and have an "xml:lang" qualifier. If repairs are needed, keep simple
 /// non-empty items by adding the "xml:lang" with value "x-repair".
 /// </remarks>
 /// <param name="arrayNode">the property node of the array to repair.</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards unexpected exceptions.</exception>
 private static void RepairAltText(XMPNode arrayNode)
 {
     if (arrayNode == null || !arrayNode.GetOptions().IsArray())
     {
         // Already OK or not even an array.
         return;
     }
     // fix options
     arrayNode.GetOptions().SetArrayOrdered(true).SetArrayAlternate(true).SetArrayAltText(true);
     for (Iterator it = arrayNode.IterateChildren(); it.HasNext(); )
     {
         XMPNode currChild = (XMPNode)it.Next();
         if (currChild.GetOptions().IsCompositeProperty())
         {
             // Delete non-simple children.
             it.Remove();
         }
         else
         {
             if (!currChild.GetOptions().GetHasLanguage())
             {
                 string childValue = currChild.GetValue();
                 if (childValue == null || childValue.Length == 0)
                 {
                     // Delete empty valued children that have no xml:lang.
                     it.Remove();
                 }
                 else
                 {
                     // Add an xml:lang qualifier with the value "x-repair".
                     XMPNode repairLang = new XMPNode(XMPConstConstants.XmlLang, "x-repair", null);
                     currChild.AddQualifier(repairLang);
                 }
             }
         }
     }
 }
		/// <summary>Recursively handles the "value" for a node.</summary>
		/// <remarks>
		/// 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>
		/// </remarks>
		/// <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="System.IO.IOException">Forwards all writer exceptions.</exception>
		/// <exception cref="Com.Adobe.Xmp.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.GetName();
			if (emitAsRDFValue)
			{
				elemName = "rdf:value";
			}
			else
			{
				if (XMPConstConstants.ArrayItemName.Equals(elemName))
				{
					elemName = "rdf:li";
				}
			}
			WriteIndent(indent);
			Write('<');
			Write(elemName);
			bool hasGeneralQualifiers = false;
			bool hasRDFResourceQual = false;
			for (Iterator it = node.IterateQualifier(); it.HasNext(); )
			{
				XMPNode qualifier = (XMPNode)it.Next();
				if (!RdfAttrQualifier.Contains(qualifier.GetName()))
				{
					hasGeneralQualifiers = true;
				}
				else
				{
					hasRDFResourceQual = "rdf:resource".Equals(qualifier.GetName());
					if (!emitAsRDFValue)
					{
						Write(' ');
						Write(qualifier.GetName());
						Write("=\"");
						AppendNodeValue(qualifier.GetValue(), 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", XMPErrorConstants.Badrdf);
				}
				// Change serialization to canonical format with inner rdf:Description-tag
				// depending on option
				if (useCanonicalRDF)
				{
					Write(">");
					WriteNewline();
					indent++;
					WriteIndent(indent);
					Write(RdfStructStart);
					Write(">");
				}
				else
				{
					Write(" rdf:parseType=\"Resource\">");
				}
				WriteNewline();
				SerializeCanonicalRDFProperty(node, useCanonicalRDF, true, indent + 1);
				for (Iterator it_1 = node.IterateQualifier(); it_1.HasNext(); )
				{
					XMPNode qualifier = (XMPNode)it_1.Next();
					if (!RdfAttrQualifier.Contains(qualifier.GetName()))
					{
						SerializeCanonicalRDFProperty(qualifier, useCanonicalRDF, false, indent + 1);
					}
				}
				if (useCanonicalRDF)
				{
					WriteIndent(indent);
					Write(RdfStructEnd);
					WriteNewline();
					indent--;
				}
			}
			else
			{
				// This node has no general qualifiers. Emit using an unqualified form.
				if (!node.GetOptions().IsCompositeProperty())
				{
					// This is a simple property.
					if (node.GetOptions().IsURI())
					{
						Write(" rdf:resource=\"");
						AppendNodeValue(node.GetValue(), true);
						Write("\"/>");
						WriteNewline();
						emitEndTag = false;
					}
					else
					{
						if (node.GetValue() == null || string.Empty.Equals(node.GetValue()))
						{
							Write("/>");
							WriteNewline();
							emitEndTag = false;
						}
						else
						{
							Write('>');
							AppendNodeValue(node.GetValue(), false);
							indentEndTag = false;
						}
					}
				}
				else
				{
					if (node.GetOptions().IsArray())
					{
						// This is an array.
						Write('>');
						WriteNewline();
						EmitRDFArrayTag(node, true, indent + 1);
						if (node.GetOptions().IsArrayAltText())
						{
							XMPNodeUtils.NormalizeLangArray(node);
						}
						for (Iterator it_1 = node.IterateChildren(); it_1.HasNext(); )
						{
							XMPNode child = (XMPNode)it_1.Next();
							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(RdfEmptyStruct);
								}
								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(RdfStructStart);
									Write(">");
								}
								else
								{
									Write(" rdf:parseType=\"Resource\">");
								}
								WriteNewline();
								for (Iterator it_1 = node.IterateChildren(); it_1.HasNext(); )
								{
									XMPNode child = (XMPNode)it_1.Next();
									SerializeCanonicalRDFProperty(child, useCanonicalRDF, false, indent + 1);
								}
								if (useCanonicalRDF)
								{
									WriteIndent(indent);
									Write(RdfStructEnd);
									WriteNewline();
									indent--;
								}
							}
						}
						else
						{
							// This is a struct with an rdf:resource attribute, use the
							// "empty property element" form.
							for (Iterator it_1 = node.IterateChildren(); it_1.HasNext(); )
							{
								XMPNode child = (XMPNode)it_1.Next();
								if (!CanBeRDFAttrProp(child))
								{
									throw new XMPException("Can't mix rdf:resource and complex fields", XMPErrorConstants.Badrdf);
								}
								WriteNewline();
								WriteIndent(indent + 1);
								Write(' ');
								Write(child.GetName());
								Write("=\"");
								AppendNodeValue(child.GetValue(), true);
								Write('"');
							}
							Write("/>");
							WriteNewline();
							emitEndTag = false;
						}
					}
				}
			}
			// Emit the property element end tag.
			if (emitEndTag)
			{
				if (indentEndTag)
				{
					WriteIndent(indent);
				}
				Write("</");
				Write(elemName);
				Write('>');
				WriteNewline();
			}
		}
示例#26
0
        /// <summary>
        /// <ol>
        /// <li>Look for an exact match with the specific language.
        /// </summary>
        /// <remarks>
        /// <ol>
        /// <li>Look for an exact match with the specific language.
        /// <li>If a generic language is given, look for partial matches.
        /// <li>Look for an "x-default"-item.
        /// <li>Choose the first item.
        /// </ol>
        /// </remarks>
        /// <param name="arrayNode">the alt text array node</param>
        /// <param name="genericLang">the generic language</param>
        /// <param name="specificLang">the specific language</param>
        /// <returns>
        /// Returns the kind of match as an Integer and the found node in an
        /// array.
        /// </returns>
        /// <exception cref="Com.Adobe.Xmp.XMPException"/>
        internal static object[] ChooseLocalizedText(XMPNode arrayNode, string genericLang, string specificLang)
        {
            // See if the array has the right form. Allow empty alt arrays,
            // that is what parsing returns.
            if (!arrayNode.GetOptions().IsArrayAltText())
            {
                throw new XMPException("Localized text array is not alt-text", XMPErrorConstants.Badxpath);
            }
            else
            {
                if (!arrayNode.HasChildren())
                {
                    return(new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltNoValues, null });
                }
            }
            int     foundGenericMatches = 0;
            XMPNode resultNode          = null;
            XMPNode xDefault            = null;

            // Look for the first partial match with the generic language.
            for (Iterator it = arrayNode.IterateChildren(); it.HasNext();)
            {
                XMPNode currItem = (XMPNode)it.Next();
                // perform some checks on the current item
                if (currItem.GetOptions().IsCompositeProperty())
                {
                    throw new XMPException("Alt-text array item is not simple", XMPErrorConstants.Badxpath);
                }
                else
                {
                    if (!currItem.HasQualifier() || !XMPConstConstants.XmlLang.Equals(currItem.GetQualifier(1).GetName()))
                    {
                        throw new XMPException("Alt-text array item has no language qualifier", XMPErrorConstants.Badxpath);
                    }
                }
                string currLang = currItem.GetQualifier(1).GetValue();
                // Look for an exact match with the specific language.
                if (specificLang.Equals(currLang))
                {
                    return(new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltSpecificMatch, currItem });
                }
                else
                {
                    if (genericLang != null && currLang.StartsWith(genericLang))
                    {
                        if (resultNode == null)
                        {
                            resultNode = currItem;
                        }
                        // ! Don't return/break, need to look for other matches.
                        foundGenericMatches++;
                    }
                    else
                    {
                        if (XMPConstConstants.XDefault.Equals(currLang))
                        {
                            xDefault = currItem;
                        }
                    }
                }
            }
            // evaluate loop
            if (foundGenericMatches == 1)
            {
                return(new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltSingleGeneric, resultNode });
            }
            else
            {
                if (foundGenericMatches > 1)
                {
                    return(new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltMultipleGeneric, resultNode });
                }
                else
                {
                    if (xDefault != null)
                    {
                        return(new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltXdefault, xDefault });
                    }
                    else
                    {
                        // Everything failed, choose the first item.
                        return(new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltFirstItem, arrayNode.GetChild(1) });
                    }
                }
            }
        }
 /// <summary>The outermost call is special.</summary>
 /// <remarks>
 /// The outermost call is special. The names almost certainly differ. The
 /// qualifiers (and hence options) will differ for an alias to the x-default
 /// item of a langAlt array.
 /// </remarks>
 /// <param name="aliasNode">the alias node</param>
 /// <param name="baseNode">the base node of the alias</param>
 /// <param name="outerCall">marks the outer call of the recursion</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards XMP errors</exception>
 private static void CompareAliasedSubtrees(XMPNode aliasNode, XMPNode baseNode, bool outerCall)
 {
     if (!aliasNode.GetValue().Equals(baseNode.GetValue()) || aliasNode.GetChildrenLength() != baseNode.GetChildrenLength())
     {
         throw new XMPException("Mismatch between alias and base nodes", XMPErrorConstants.Badxmp);
     }
     if (!outerCall && (!aliasNode.GetName().Equals(baseNode.GetName()) || !aliasNode.GetOptions().Equals(baseNode.GetOptions()) || aliasNode.GetQualifierLength() != baseNode.GetQualifierLength()))
     {
         throw new XMPException("Mismatch between alias and base nodes", XMPErrorConstants.Badxmp);
     }
     for (Iterator an = aliasNode.IterateChildren(), bn = baseNode.IterateChildren(); an.HasNext() && bn.HasNext(); )
     {
         XMPNode aliasChild = (XMPNode)an.Next();
         XMPNode baseChild = (XMPNode)bn.Next();
         CompareAliasedSubtrees(aliasChild, baseChild, false);
     }
     for (Iterator an_1 = aliasNode.IterateQualifier(), bn_1 = baseNode.IterateQualifier(); an_1.HasNext() && bn_1.HasNext(); )
     {
         XMPNode aliasQual = (XMPNode)an_1.Next();
         XMPNode baseQual = (XMPNode)bn_1.Next();
         CompareAliasedSubtrees(aliasQual, baseQual, false);
     }
 }