/// <summary>Visit all schemas to do general fixes and handle special cases.</summary> /// <param name="xmp">the metadata object implementation</param> /// <exception cref="XmpException">Thrown if the normalisation fails.</exception> private static void TouchUpDataModel(XmpMeta 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(), XmpConstants.NsDC, true); // Do the special case fixes within each schema. for (var it = xmp.GetRoot().IterateChildren(); it.HasNext();) { var currSchema = (XmpNode)it.Next(); switch (currSchema.Name) { case XmpConstants.NsDC: { NormalizeDcArrays(currSchema); break; } case XmpConstants.NsExif: { // Do a special case fix for exif:GPSTimeStamp. FixGpsTimeStamp(currSchema); var arrayNode = XmpNodeUtils.FindChildNode(currSchema, "exif:UserComment", false); if (arrayNode != null) { RepairAltText(arrayNode); } break; } case XmpConstants.NsDm: { // Do a special case migration of xmpDM:copyright to // dc:rights['x-default']. var dmCopyright = XmpNodeUtils.FindChildNode(currSchema, "xmpDM:copyright", false); if (dmCopyright != null) { MigrateAudioCopyright(xmp, dmCopyright); } break; } case XmpConstants.NsXmpRights: { var arrayNode = XmpNodeUtils.FindChildNode(currSchema, "xmpRights:UsageTerms", false); if (arrayNode != null) { RepairAltText(arrayNode); } break; } } } }
/// <summary>Constructor with optional initial values.</summary> /// <remarks>If <c>propName</c> is provided, <c>schemaNS</c> 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 reduced to this property within the <c>schemaNS</c></param> /// <param name="options">advanced iteration options, see <see cref="IteratorOptions"/></param> /// <exception cref="XmpException">If the node defined by the parameters is not existing.</exception> public XmpIterator(XmpMeta xmp, string schemaNs, string propPath, IteratorOptions options) { // make sure that options is defined at least with defaults Options = options ?? new IteratorOptions(); // the start node of the iteration depending on the schema and property filter XmpNode startNode = null; string initialPath = null; var baseSchema = !string.IsNullOrEmpty(schemaNs); var baseProperty = !string.IsNullOrEmpty(propPath); if (!baseSchema && !baseProperty) { // complete tree will be iterated startNode = xmp.GetRoot(); } else { if (baseSchema && baseProperty) { // Schema and property node provided var path = XmpPathParser.ExpandXPath(schemaNs, propPath); // base path is the prop path without the property leaf var basePath = new XmpPath(); for (var i = 0; i < path.Size() - 1; i++) { basePath.Add(path.GetSegment(i)); } startNode = XmpNodeUtils.FindNode(xmp.GetRoot(), path, false, null); BaseNamespace = 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", XmpErrorCode.BadSchema); } } } // create iterator _nodeIterator = startNode != null ? (IIterator)(!Options.IsJustChildren ? new NodeIterator(this, startNode, initialPath, 1) : new NodeIteratorChildren(this, startNode, initialPath)) : Enumerable.Empty <object>().Iterator(); }
/// <summary>Constructor with optional initial values.</summary> /// <remarks>If <c>propName</c> is provided, <c>schemaNS</c> 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 reduced to this property within the <c>schemaNS</c></param> /// <param name="options">advanced iteration options, see <see cref="IteratorOptions"/></param> /// <exception cref="XmpException">If the node defined by the parameters is not existing.</exception> public XmpIterator(XmpMeta xmp, string schemaNs, string propPath, IteratorOptions options) { // make sure that options is defined at least with defaults Options = options ?? new IteratorOptions(); // the start node of the iteration depending on the schema and property filter XmpNode startNode = null; string initialPath = null; var baseSchema = !string.IsNullOrEmpty(schemaNs); var baseProperty = !string.IsNullOrEmpty(propPath); if (!baseSchema && !baseProperty) { // complete tree will be iterated startNode = xmp.GetRoot(); } else { if (baseSchema && baseProperty) { // Schema and property node provided var path = XmpPathParser.ExpandXPath(schemaNs, propPath); // base path is the prop path without the property leaf var basePath = new XmpPath(); for (var i = 0; i < path.Size() - 1; i++) { basePath.Add(path.GetSegment(i)); } startNode = XmpNodeUtils.FindNode(xmp.GetRoot(), path, false, null); BaseNamespace = 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", XmpErrorCode.BadSchema); } } } // create iterator _nodeIterator = startNode != null ? (IIterator)(!Options.IsJustChildren ? new NodeIterator(this, startNode, initialPath, 1) : new NodeIteratorChildren(this, startNode, initialPath)) : Enumerable.Empty<object>().Iterator(); }
/// <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="XmpException">Collects all severe processing errors.</exception> internal static IXmpMeta Process(XmpMeta xmp, ParseOptions options) { var tree = xmp.GetRoot(); TouchUpDataModel(xmp); MoveExplicitAliases(tree, options); TweakOldXmp(tree); DeleteEmptySchemas(tree); return xmp; }
/// <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="XmpException">Collects all severe processing errors.</exception> internal static IXmpMeta Process(XmpMeta xmp, ParseOptions options) { var tree = xmp.GetRoot(); TouchUpDataModel(xmp); MoveExplicitAliases(tree, options); TweakOldXmp(tree); DeleteEmptySchemas(tree); return(xmp); }
/// <summary>Utility to find or create the array used by <c>separateArrayItems()</c>.</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="XmpException">Forwards exceptions</exception> private static XmpNode SeparateFindCreateArray(string schemaNs, string arrayName, PropertyOptions arrayOptions, XmpMeta xmp) { arrayOptions = XmpNodeUtils.VerifySetOptions(arrayOptions, null); if (!arrayOptions.IsOnlyArrayOptions) { throw new XmpException("Options can only provide array form", XmpErrorCode.BadOptions); } // Find the array node, make sure it is OK. Move the current children // aside, to be readded later if kept. var arrayPath = XmpPathParser.ExpandXPath(schemaNs, arrayName); var 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. var arrayForm = arrayNode.Options; if (!arrayForm.IsArray || arrayForm.IsArrayAlternate) { throw new XmpException("Named property must be non-alternate array", XmpErrorCode.BadXPath); } if (arrayOptions.EqualArrayTypes(arrayForm)) { throw new XmpException("Mismatch of specified and existing array form", XmpErrorCode.BadXPath); } } else { // *** Right error? // The array does not exist, try to create it. // don't modify the options handed into the method arrayOptions.IsArray = true; arrayNode = XmpNodeUtils.FindNode(xmp.GetRoot(), arrayPath, true, arrayOptions); if (arrayNode == null) { throw new XmpException("Failed to create named array", XmpErrorCode.BadXPath); } } return(arrayNode); }
/// <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="XmpException">Collects all severe processing errors.</exception> internal static IXmpMeta Process(XmpMeta xmp, ParseOptions options) { var tree = xmp.GetRoot(); TouchUpDataModel(xmp); // FfF: collect the aliases to prevent browsing the tree again MoveExplicitAliases(tree, options); TweakOldXmp(tree); DeleteEmptySchemas(tree); return(xmp); }
/// <summary>Serializes the metadata in pretty-printed manner.</summary> /// <param name="level">indent level</param> /// <exception cref="System.IO.IOException">Forwarded writer exceptions</exception> /// <exception cref="XmpException"></exception> private void SerializeCanonicalRdfSchemas(int level) { if (_xmp.GetRoot().GetChildrenLength() > 0) { StartOuterRdfDescription(_xmp.GetRoot(), level); for (var it = _xmp.GetRoot().IterateChildren(); it.HasNext();) { var currSchema = (XmpNode)it.Next(); SerializeCanonicalRdfSchema(currSchema, level); } EndOuterRdfDescription(level); } else { WriteIndent(level + 1); Write(RdfSchemaStart); // Special case an empty XMP object. WriteTreeName(); Write("/>"); WriteNewline(); } }
/// <summary>Visit all schemas to do general fixes and handle special cases.</summary> /// <param name="xmp">the metadata object implementation</param> /// <exception cref="XmpException">Thrown if the normalisation fails.</exception> private static void TouchUpDataModel(XmpMeta 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(), XmpConstants.NsDC, true); // Do the special case fixes within each schema. for (var it = xmp.GetRoot().IterateChildren(); it.HasNext(); ) { var currSchema = (XmpNode)it.Next(); if (XmpConstants.NsDC.Equals(currSchema.Name)) { NormalizeDcArrays(currSchema); } else { if (XmpConstants.NsExif.Equals(currSchema.Name)) { // Do a special case fix for exif:GPSTimeStamp. FixGpsTimeStamp(currSchema); var arrayNode = XmpNodeUtils.FindChildNode(currSchema, "exif:UserComment", false); if (arrayNode != null) { RepairAltText(arrayNode); } } else { if (XmpConstants.NsDm.Equals(currSchema.Name)) { // Do a special case migration of xmpDM:copyright to // dc:rights['x-default']. var dmCopyright = XmpNodeUtils.FindChildNode(currSchema, "xmpDM:copyright", false); if (dmCopyright != null) { MigrateAudioCopyright(xmp, dmCopyright); } } else { if (XmpConstants.NsXmpRights.Equals(currSchema.Name)) { var arrayNode = XmpNodeUtils.FindChildNode(currSchema, "xmpRights:UsageTerms", false); if (arrayNode != null) { RepairAltText(arrayNode); } } } } } } }
/// <summary>Visit all schemas to do general fixes and handle special cases.</summary> /// <param name="xmp">the metadata object implementation</param> /// <exception cref="XmpException">Thrown if the normalisation fails.</exception> private static void TouchUpDataModel(XmpMeta 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(), XmpConstants.NsDC, true); // Do the special case fixes within each schema. for (var it = xmp.GetRoot().IterateChildren(); it.HasNext();) { var currSchema = (XmpNode)it.Next(); switch (currSchema.Name) { case XmpConstants.NsDC: { NormalizeDcArrays(currSchema); break; } case XmpConstants.NsExif: { // Do a special case fix for exif:GPSTimeStamp. FixGpsTimeStamp(currSchema); /*var arrayNode = XmpNodeUtils.FindChildNode(currSchema, "exif:UserComment", false); * if (arrayNode != null) * { * RepairAltText(arrayNode); * }*/ var userComment = XmpNodeUtils.FindChildNode(currSchema, "exif:UserComment", false); if (userComment != null) { if (userComment.Options.IsSimple) { XmpNode newNode = new XmpNode(XmpConstants.ArrayItemName, userComment.Value, userComment.Options); newNode.Parent = userComment; int QualNo = userComment.GetQualifierLength(); while (QualNo > 0) { newNode.AddQualifier(userComment.GetQualifier(userComment.GetQualifierLength() - QualNo)); --QualNo; } userComment.RemoveQualifiers(); if (!newNode.Options.HasLanguage) { var po = new PropertyOptions(); po.SetOption(PropertyOptions.HasQualifiersFlag, true); XmpNode langQual = new XmpNode("xml:lang", "x-default", po); newNode.AddQualifier(langQual); newNode.Options.SetOption(PropertyOptions.HasQualifiersFlag, true); newNode.Options.SetOption(PropertyOptions.HasLanguageFlag, true); } userComment.AddChild(newNode); userComment.Options = new PropertyOptions(PropertyOptions.ArrayFlag | PropertyOptions.ArrayOrderedFlag | PropertyOptions.ArrayAltTextFlag | PropertyOptions.ArrayAlternateFlag); userComment.Value = ""; } RepairAltText(userComment); } break; } case XmpConstants.NsDm: { // Do a special case migration of xmpDM:copyright to // dc:rights['x-default']. var dmCopyright = XmpNodeUtils.FindChildNode(currSchema, "xmpDM:copyright", false); if (dmCopyright != null) { MigrateAudioCopyright(xmp, dmCopyright); } break; } case XmpConstants.NsXmpRights: { var arrayNode = XmpNodeUtils.FindChildNode(currSchema, "xmpRights:UsageTerms", false); if (arrayNode != null) { RepairAltText(arrayNode); } break; } } } }
/// <summary>Utility to find or create the array used by <c>separateArrayItems()</c>.</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="XmpException">Forwards exceptions</exception> private static XmpNode SeparateFindCreateArray(string schemaNs, string arrayName, PropertyOptions arrayOptions, XmpMeta xmp) { arrayOptions = XmpNodeUtils.VerifySetOptions(arrayOptions, null); if (!arrayOptions.IsOnlyArrayOptions) throw new XmpException("Options can only provide array form", XmpErrorCode.BadOptions); // Find the array node, make sure it is OK. Move the current children // aside, to be readded later if kept. var arrayPath = XmpPathParser.ExpandXPath(schemaNs, arrayName); var 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. var arrayForm = arrayNode.Options; if (!arrayForm.IsArray || arrayForm.IsArrayAlternate) throw new XmpException("Named property must be non-alternate array", XmpErrorCode.BadXPath); if (arrayOptions.EqualArrayTypes(arrayForm)) throw new XmpException("Mismatch of specified and existing array form", XmpErrorCode.BadXPath); } else { // *** Right error? // The array does not exist, try to create it. // don't modify the options handed into the method arrayOptions.IsArray = true; arrayNode = XmpNodeUtils.FindNode(xmp.GetRoot(), arrayPath, true, arrayOptions); if (arrayNode == null) throw new XmpException("Failed to create named array", XmpErrorCode.BadXPath); } return arrayNode; }
/// <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="XmpException">thrown on parsing errors</exception> internal static void Rdf_RDF(XmpMeta xmp, XElement rdfRdfNode) { if (rdfRdfNode.Attributes().Count() > 0) { Rdf_NodeElementList(xmp, xmp.GetRoot(), rdfRdfNode); } else { throw new XmpException("Invalid attributes of rdf:RDF element", XmpErrorCode.BadRdf); } }
private static XmpNode AddChildNode(XmpMeta xmp, XmpNode xmpParent, XName nodeName, string nodeNamespacePrefix, string value, bool isTopLevel) { var registry = XmpMetaFactory.SchemaRegistry; var ns = nodeName.NamespaceName; string childName; if (ns != string.Empty) { if (XmpConstants.NsDcDeprecated.Equals(ns)) { // Fix a legacy DC namespace ns = XmpConstants.NsDC; } var prefix = registry.GetNamespacePrefix(ns); if (prefix == null) { prefix = nodeNamespacePrefix ?? DefaultPrefix; prefix = registry.RegisterNamespace(ns, prefix); } childName = prefix + nodeName.LocalName; } else { throw new XmpException("XML namespace required for all elements and attributes", XmpErrorCode.BadRdf); } // create schema node if not already there var childOptions = new PropertyOptions(); var isAlias = false; if (isTopLevel) { // Lookup the schema node, adjust the XMP parent pointer. // Incoming parent must be the tree root. var schemaNode = XmpNodeUtils.FindSchemaNode(xmp.GetRoot(), ns, DefaultPrefix, true); schemaNode.IsImplicit = 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().HasAliases = true; schemaNode.HasAliases = true; } } // Make sure that this is not a duplicate of a named node. var isArrayItem = "rdf:li".Equals(childName); var isValueNode = "rdf:value".Equals(childName); // Create XMP node and so some checks var newChild = new XmpNode(childName, value, childOptions) { IsAlias = isAlias }; // Add the new child to the XMP parent node, a value node first. if (!isValueNode) { xmpParent.AddChild(newChild); } else { xmpParent.AddChild(1, newChild); } if (isValueNode) { if (isTopLevel || !xmpParent.Options.IsStruct) { throw new XmpException("Misplaced rdf:value element", XmpErrorCode.BadRdf); } xmpParent.HasValueChild = true; } if (isArrayItem) { if (!xmpParent.Options.IsArray) { throw new XmpException("Misplaced rdf:li element", XmpErrorCode.BadRdf); } newChild.Name = XmpConstants.ArrayItemName; } return newChild; }