// 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; }