// EMPTY
 /// <summary>Normalizes a raw parsed XMPMeta-Object</summary>
 /// <param name="xmp">the raw metadata object</param>
 /// <param name="options">the parsing options</param>
 /// <returns>Returns the normalized metadata object</returns>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Collects all severe processing errors.</exception>
 internal static XMPMeta Process(XMPMetaImpl xmp, ParseOptions options)
 {
     XMPNode tree = xmp.GetRoot();
     TouchUpDataModel(xmp);
     MoveExplicitAliases(tree, options);
     TweakOldXMP(tree);
     DeleteEmptySchemas(tree);
     return xmp;
 }
		/// <summary>Utility to find or create the array used by <code>SeparateArrayItems()</code>.</summary>
		/// <param name="schemaNS">a the namespace fo the array</param>
		/// <param name="arrayName">the name of the array</param>
		/// <param name="arrayOptions">the options for the array if newly created</param>
		/// <param name="xmp">the xmp object</param>
		/// <returns>Returns the array node.</returns>
		/// <exception cref="Com.Adobe.Xmp.XMPException">Forwards exceptions</exception>
		private static XMPNode SeparateFindCreateArray(string schemaNS, string arrayName, PropertyOptions arrayOptions, XMPMetaImpl xmp)
		{
			arrayOptions = XMPNodeUtils.VerifySetOptions(arrayOptions, null);
			if (!arrayOptions.IsOnlyArrayOptions())
			{
				throw new XMPException("Options can only provide array form", XMPErrorConstants.Badoptions);
			}
			// Find the array node, make sure it is OK. Move the current children
			// aside, to be readded later if kept.
			XMPPath arrayPath = XMPPathParser.ExpandXPath(schemaNS, arrayName);
			XMPNode arrayNode = XMPNodeUtils.FindNode(xmp.GetRoot(), arrayPath, false, null);
			if (arrayNode != null)
			{
				// The array exists, make sure the form is compatible. Zero
				// arrayForm means take what exists.
				PropertyOptions arrayForm = arrayNode.GetOptions();
				if (!arrayForm.IsArray() || arrayForm.IsArrayAlternate())
				{
					throw new XMPException("Named property must be non-alternate array", XMPErrorConstants.Badxpath);
				}
				if (arrayOptions.EqualArrayTypes(arrayForm))
				{
					throw new XMPException("Mismatch of specified and existing array form", XMPErrorConstants.Badxpath);
				}
			}
			else
			{
				// *** Right error?
				// The array does not exist, try to create it.
				// don't modify the options handed into the method
				arrayNode = XMPNodeUtils.FindNode(xmp.GetRoot(), arrayPath, true, arrayOptions.SetArray(true));
				if (arrayNode == null)
				{
					throw new XMPException("Failed to create named array", XMPErrorConstants.Badxpath);
				}
			}
			return arrayNode;
		}
 /// <summary>Visit all schemas to do general fixes and handle special cases.</summary>
 /// <param name="xmp">the metadata object implementation</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Thrown if the normalisation fails.</exception>
 private static void TouchUpDataModel(XMPMetaImpl xmp)
 {
     // make sure the DC schema is existing, because it might be needed within the normalization
     // if not touched it will be removed by removeEmptySchemas
     XMPNodeUtils.FindSchemaNode(xmp.GetRoot(), XMPConstConstants.NsDc, true);
     // Do the special case fixes within each schema.
     for (Iterator it = xmp.GetRoot().IterateChildren(); it.HasNext(); )
     {
         XMPNode currSchema = (XMPNode)it.Next();
         if (XMPConstConstants.NsDc.Equals(currSchema.GetName()))
         {
             NormalizeDCArrays(currSchema);
         }
         else
         {
             if (XMPConstConstants.NsExif.Equals(currSchema.GetName()))
             {
                 // Do a special case fix for exif:GPSTimeStamp.
                 FixGPSTimeStamp(currSchema);
                 XMPNode arrayNode = XMPNodeUtils.FindChildNode(currSchema, "exif:UserComment", false);
                 if (arrayNode != null)
                 {
                     RepairAltText(arrayNode);
                 }
             }
             else
             {
                 if (XMPConstConstants.NsDm.Equals(currSchema.GetName()))
                 {
                     // Do a special case migration of xmpDM:copyright to
                     // dc:rights['x-default'].
                     XMPNode dmCopyright = XMPNodeUtils.FindChildNode(currSchema, "xmpDM:copyright", false);
                     if (dmCopyright != null)
                     {
                         MigrateAudioCopyright(xmp, dmCopyright);
                     }
                 }
                 else
                 {
                     if (XMPConstConstants.NsXmpRights.Equals(currSchema.GetName()))
                     {
                         XMPNode arrayNode = XMPNodeUtils.FindChildNode(currSchema, "xmpRights:UsageTerms", false);
                         if (arrayNode != null)
                         {
                             RepairAltText(arrayNode);
                         }
                     }
                 }
             }
         }
     }
 }
 /// <summary>Constructor with optionsl initial values.</summary>
 /// <remarks>
 /// Constructor with optionsl initial values. If <code>propName</code> is provided,
 /// <code>schemaNS</code> has also be provided.
 /// </remarks>
 /// <param name="xmp">the iterated metadata object.</param>
 /// <param name="schemaNS">the iteration is reduced to this schema (optional)</param>
 /// <param name="propPath">the iteration is redurce to this property within the <code>schemaNS</code></param>
 /// <param name="options">
 /// advanced iteration options, see
 /// <see cref="Com.Adobe.Xmp.Options.IteratorOptions"/>
 /// </param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">If the node defined by the paramters is not existing.</exception>
 public XMPIteratorImpl(XMPMetaImpl xmp, string schemaNS, string propPath, IteratorOptions options)
 {
     // make sure that options is defined at least with defaults
     this.options = options != null ? options : new IteratorOptions();
     // the start node of the iteration depending on the schema and property filter
     XMPNode startNode = null;
     string initialPath = null;
     bool baseSchema = schemaNS != null && schemaNS.Length > 0;
     bool baseProperty = propPath != null && propPath.Length > 0;
     if (!baseSchema && !baseProperty)
     {
         // complete tree will be iterated
         startNode = xmp.GetRoot();
     }
     else
     {
         if (baseSchema && baseProperty)
         {
             // Schema and property node provided
             XMPPath path = XMPPathParser.ExpandXPath(schemaNS, propPath);
             // base path is the prop path without the property leaf
             XMPPath basePath = new XMPPath();
             for (int i = 0; i < path.Size() - 1; i++)
             {
                 basePath.Add(path.GetSegment(i));
             }
             startNode = XMPNodeUtils.FindNode(xmp.GetRoot(), path, false, null);
             baseNS = schemaNS;
             initialPath = basePath.ToString();
         }
         else
         {
             if (baseSchema && !baseProperty)
             {
                 // Only Schema provided
                 startNode = XMPNodeUtils.FindSchemaNode(xmp.GetRoot(), schemaNS, false);
             }
             else
             {
                 // !baseSchema  &&  baseProperty
                 // No schema but property provided -> error
                 throw new XMPException("Schema namespace URI is required", XMPErrorConstants.Badschema);
             }
         }
     }
     // create iterator
     if (startNode != null)
     {
         if (!this.options.IsJustChildren())
         {
             nodeIterator = new XMPIteratorImpl.NodeIterator(this, startNode, initialPath, 1);
         }
         else
         {
             nodeIterator = new XMPIteratorImpl.NodeIteratorChildren(this, startNode, initialPath);
         }
     }
     else
     {
         // create null iterator
         nodeIterator = Sharpen.Collections.EmptyList().Iterator();
     }
 }
 /// <summary>
 /// Each of these parsing methods is responsible for recognizing an RDF
 /// syntax production and adding the appropriate structure to the XMP tree.
 /// </summary>
 /// <remarks>
 /// Each of these parsing methods is responsible for recognizing an RDF
 /// syntax production and adding the appropriate structure to the XMP tree.
 /// They simply return for success, failures will throw an exception.
 /// </remarks>
 /// <param name="xmp">the xmp metadata object that is generated</param>
 /// <param name="rdfRdfNode">the top-level xml node</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">thown on parsing errors</exception>
 internal static void Rdf_RDF(XMPMetaImpl xmp, XmlNode rdfRdfNode)
 {
     if (rdfRdfNode.HasAttributes())
     {
         Rdf_NodeElementList(xmp, xmp.GetRoot(), rdfRdfNode);
     }
     else
     {
         throw new XMPException("Invalid attributes of rdf:RDF element", XMPErrorConstants.Badrdf);
     }
 }
 /// <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="Com.Adobe.Xmp.XMPException">thown on parsing errors</exception>
 private static XMPNode AddChildNode(XMPMetaImpl xmp, XMPNode xmpParent, XmlNode xmlNode, string value, bool isTopLevel)
 {
     XMPSchemaRegistry registry = XMPMetaFactory.GetSchemaRegistry();
     string @namespace = xmlNode.NamespaceURI;
     string childName;
     if (@namespace != null)
     {
         if (XMPConstConstants.NsDcDeprecated.Equals(@namespace))
         {
             // Fix a legacy DC namespace
             @namespace = XMPConstConstants.NsDc;
         }
         string prefix = registry.GetNamespacePrefix(@namespace);
         if (prefix == null)
         {
             prefix = xmlNode.Prefix != null ? xmlNode.Prefix : DefaultPrefix;
             prefix = registry.RegisterNamespace(@namespace, prefix);
         }
         childName = prefix + xmlNode.LocalName;
     }
     else
     {
         throw new XMPException("XML namespace required for all elements and attributes", XMPErrorConstants.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.GetRoot(), @namespace, DefaultPrefix, true);
         schemaNode.SetImplicit(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.GetRoot().SetHasAliases(true);
             schemaNode.SetHasAliases(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.SetAlias(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.GetOptions().IsStruct())
         {
             throw new XMPException("Misplaced rdf:value element", XMPErrorConstants.Badrdf);
         }
         xmpParent.SetHasValueChild(true);
     }
     if (isArrayItem)
     {
         if (!xmpParent.GetOptions().IsArray())
         {
             throw new XMPException("Misplaced rdf:li element", XMPErrorConstants.Badrdf);
         }
         newChild.SetName(XMPConstConstants.ArrayItemName);
     }
     return newChild;
 }