/// <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 XMPMeta Process(XmpMetaImpl xmp, ParseOptions options) { XmpNode tree = xmp.Root; TouchUpDataModel(xmp); MoveExplicitAliases(tree, options); TweakOldXmp(tree); DeleteEmptySchemas(tree); return xmp; }
/// <summary> /// Parses the input source into an XMP metadata object, including /// de-aliasing and normalisation. /// </summary> /// <param name="input"> the input can be an <code>InputStream</code>, a <code>String</code> or /// a byte buffer containing the XMP packet. </param> /// <param name="options"> the parse options </param> /// <returns> Returns the resulting XMP metadata object </returns> /// <exception cref="XmpException"> Thrown if parsing or normalisation fails. </exception> public static XMPMeta Parse(object input, ParseOptions options) { ParameterAsserts.AssertNotNull(input); options = options ?? new ParseOptions(); XmlDocument document = ParseXml(input, options); bool xmpmetaRequired = options.RequireXmpMeta; object[] result = new object[3]; result = FindRootNode(document, xmpmetaRequired, result); if (result != null && result[1] == XmpRdf) { XmpMetaImpl xmp = ParseRdf.Parse((XmlNode) result[0]); xmp.PacketHeader = (string) result[2]; // Check if the XMP object shall be normalized if (!options.OmitNormalization) { return XmpNormalizer.Process(xmp, options); } return xmp; } // no appropriate root node found, return empty metadata object return new XmpMetaImpl(); }
/// <seealso cref= XMPMeta#normalize(ParseOptions) </seealso> public virtual void Normalize(ParseOptions options) { if (options == null) { options = new ParseOptions(); } XmpNormalizer.Process(this, options); }
/// <summary> /// Parses XML from a <seealso cref="string"/>, /// fixing the illegal control character optionally. /// </summary> /// <param name="input"> a <code>String</code> containing the XMP packet </param> /// <param name="options"> the parsing options </param> /// <returns> Returns an XML DOM-Document. </returns> /// <exception cref="XmpException"> Thrown when the parsing fails. </exception> private static XmlDocument ParseXmlFromString(string input, ParseOptions options) { try { XmlDocument doc = new XmlDocument(); doc.Load(new StringReader(input)); return doc; } catch (XmpException e) { if (e.ErrorCode == XmpError.BADXML && options.FixControlChars) { XmlDocument doc = new XmlDocument(); doc.Load(new FixAsciiControlsReader(new StringReader(input))); return doc; } throw e; } }
/// <summary> /// Parses XML from a byte buffer, /// fixing the encoding (Latin-1 to UTF-8) and illegal control character optionally. /// </summary> /// <param name="buffer"> a byte buffer containing the XMP packet </param> /// <param name="options"> the parsing options </param> /// <returns> Returns an XML DOM-Document. </returns> /// <exception cref="XmpException"> Thrown when the parsing fails. </exception> private static XmlDocument ParseXmlFromBytebuffer(ByteBuffer buffer, ParseOptions options) { try { XmlDocument doc = new XmlDocument(); doc.Load(buffer.ByteStream); return doc; } catch (XmlException e) { XmlDocument doc = new XmlDocument(); if (options.AcceptLatin1) { buffer = Latin1Converter.Convert(buffer); } if (options.FixControlChars) { try { StreamReader streamReader = new StreamReader(buffer.ByteStream, Encoding.GetEncoding(buffer.Encoding)); FixAsciiControlsReader fixReader = new FixAsciiControlsReader(streamReader); doc.Load(fixReader); return doc; } catch (Exception) { // can normally not happen as the encoding is provided by a util function throw new XmpException("Unsupported Encoding", XmpError.INTERNALFAILURE, e); } } doc.Load(buffer.ByteStream); return doc; } }
/// <summary> /// Parses XML from an <seealso cref="Stream"/>, /// fixing the encoding (Latin-1 to UTF-8) and illegal control character optionally. /// </summary> /// <param name="stream"> an <code>InputStream</code> </param> /// <param name="options"> the parsing options </param> /// <returns> Returns an XML DOM-Document. </returns> /// <exception cref="XmpException"> Thrown when the parsing fails. </exception> private static XmlDocument ParseXmlFromInputStream(Stream stream, ParseOptions options) { if (!options.AcceptLatin1 && !options.FixControlChars) { XmlDocument doc = new XmlDocument(); doc.Load(stream); return doc; } // load stream into bytebuffer try { ByteBuffer buffer = new ByteBuffer(stream); return ParseXmlFromBytebuffer(buffer, options); } catch (IOException e) { throw new XmpException("Error reading the XML-file", XmpError.BADSTREAM, e); } }
/// <summary> /// Parses the raw XML metadata packet considering the parsing options. /// Latin-1/ISO-8859-1 can be accepted when the input is a byte stream /// (some old toolkits versions such packets). The stream is /// then wrapped in another stream that converts Latin-1 to UTF-8. /// <p> /// If control characters shall be fixed, a reader is used that fixes the chars to spaces /// (if the input is a byte stream is has to be read as character stream). /// <p> /// Both options reduce the performance of the parser. /// </summary> /// <param name="input"> the input can be an <code>InputStream</code>, a <code>String</code> or /// a byte buffer containing the XMP packet. </param> /// <param name="options"> the parsing options </param> /// <returns> Returns the parsed XML document or an exception. </returns> /// <exception cref="XmpException"> Thrown if the parsing fails for different reasons </exception> private static XmlDocument ParseXml(object input, ParseOptions options) { if (input is Stream) { return ParseXmlFromInputStream((Stream) input, options); } if (input is byte[]) { return ParseXmlFromBytebuffer(new ByteBuffer((byte[]) input), options); } return ParseXmlFromString((string) input, options); }
/// <summary> /// Visit all of the top level nodes looking for aliases. If there is /// no base, transplant the alias subtree. If there is a base and strict /// aliasing is on, make sure the alias and base subtrees match. /// </summary> /// <param name="tree"> the root of the metadata tree </param> /// <param name="options"> th parsing options </param> /// <exception cref="XmpException"> Forwards XMP errors </exception> private static void MoveExplicitAliases(XmpNode tree, ParseOptions options) { if (!tree.HasAliases) { return; } tree.HasAliases = false; bool strictAliasing = options.StrictAliasing; IEnumerator schemaIt = tree.UnmodifiableChildren.GetEnumerator(); while (schemaIt.MoveNext()) { XmpNode currSchema = (XmpNode) schemaIt.Current; if (currSchema == null) continue; if (!currSchema.HasAliases) { continue; } ArrayList currPropsToRemove = new ArrayList(); IEnumerator propertyIt = currSchema.IterateChildren(); while (propertyIt.MoveNext()) { XmpNode currProp = (XmpNode) propertyIt.Current; if (currProp == null) continue; if (!currProp.Alias) { continue; } currProp.Alias = false; // Find the base path, look for the base schema and root node. XMPAliasInfo info = XMPMetaFactory.SchemaRegistry.FindAlias(currProp.Name); if (info != null) { // find or create schema XmpNode baseSchema = XmpNodeUtils.FindSchemaNode(tree, info.Namespace, null, true); baseSchema.Implicit = false; XmpNode baseNode = XmpNodeUtils.FindChildNode(baseSchema, info.Prefix + info.PropName, false); if (baseNode == null) { if (info.AliasForm.Simple) { // A top-to-top alias, transplant the property. // change the alias property name to the base name string qname = info.Prefix + info.PropName; currProp.Name = qname; baseSchema.AddChild(currProp); } else { // An alias to an array item, // create the array and transplant the property. baseNode = new XmpNode(info.Prefix + info.PropName, info.AliasForm.ToPropertyOptions()); baseSchema.AddChild(baseNode); TransplantArrayItemAlias(currProp, baseNode); } currPropsToRemove.Add(currProp); } else if (info.AliasForm.Simple) { // The base node does exist and this is a top-to-top alias. // Check for conflicts if strict aliasing is on. // Remove and delete the alias subtree. if (strictAliasing) { CompareAliasedSubtrees(currProp, baseNode, true); } currPropsToRemove.Add(currProp); } else { // This is an alias to an array item and the array exists. // Look for the aliased item. // Then transplant or check & delete as appropriate. XmpNode itemNode = null; if (info.AliasForm.ArrayAltText) { int xdIndex = XmpNodeUtils.LookupLanguageItem(baseNode, XmpConst.X_DEFAULT); if (xdIndex != -1) { itemNode = baseNode.GetChild(xdIndex); } } else if (baseNode.HasChildren()) { itemNode = baseNode.GetChild(1); } if (itemNode == null) { TransplantArrayItemAlias(currProp, baseNode); } else { if (strictAliasing) { CompareAliasedSubtrees(currProp, itemNode, true); } } currPropsToRemove.Add(currProp); } } } foreach (object o in currPropsToRemove) currSchema.Children.Remove(o); currPropsToRemove.Clear(); currSchema.HasAliases = false; } }
/// <summary> /// These functions support parsing serialized RDF into an XMP object, and serailizing an XMP /// object into RDF. The input for parsing may be any valid Unicode /// encoding. ISO Latin-1 is also recognized, but its use is strongly discouraged. Serialization /// is always as UTF-8. /// <p/> /// <code>parseFromBuffer()</code> parses RDF from an <code>InputStream</code>. The encoding /// is recognized automatically. /// </summary> /// <param name="in"> an <code>InputStream</code> </param> /// <param name="options"> Options controlling the parsing.<br> /// The available options are: /// <ul> /// <li> XMP_REQUIRE_XMPMETA - The <x:xmpmeta> XML element is required around /// <tt><rdf:RDF></tt>. /// <li> XMP_STRICT_ALIASING - Do not reconcile alias differences, throw an exception. /// </ul> /// <em>Note:</em>The XMP_STRICT_ALIASING option is not yet implemented. </param> /// <returns> Returns the <code>XMPMeta</code>-object created from the input. </returns> /// <exception cref="XmpException"> If the file is not well-formed XML or if the parsing fails. </exception> public static IXmpMeta Parse(Stream @in, ParseOptions options) { return XmpMetaParser.Parse(@in, options); }
/// <summary> /// Creates an <code>XMPMeta</code>-object from a byte-buffer. /// </summary> /// <param name="buffer"> a String contain an XMP-file. </param> /// <param name="options"> Options controlling the parsing. </param> /// <returns> Returns the <code>XMPMeta</code>-object created from the input. </returns> /// <exception cref="XmpException"> If the file is not well-formed XML or if the parsing fails. </exception> /// <seealso cref= XMPMetaFactory#parse(InputStream, ParseOptions) </seealso> public static IXmpMeta ParseFromBuffer(byte[] buffer, ParseOptions options) { return XmpMetaParser.Parse(buffer, options); }
/// <summary> /// Creates an <code>XMPMeta</code>-object from a string. /// </summary> /// <param name="packet"> a String contain an XMP-file. </param> /// <param name="options"> Options controlling the parsing. </param> /// <returns> Returns the <code>XMPMeta</code>-object created from the input. </returns> /// <exception cref="XmpException"> If the file is not well-formed XML or if the parsing fails. </exception> /// <seealso cref= XMPMetaFactory#parseFromString(String, ParseOptions) </seealso> public static IXmpMeta ParseFromString(string packet, ParseOptions options) { return XmpMetaParser.Parse(packet, options); }