#region CPL License /* Nuclex Framework Copyright (C) 2002-2017 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion using System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Xml; #if !USE_XMLDOCUMENT using System.Xml.Linq; #endif using System.Xml.Schema; namespace Nuclex.Support { /// Helper routines for handling XML code public static class XmlHelper { #if !NO_XMLSCHEMA #region class ValidationEventProcessor /// Handles any events occurring when an XML schema is loaded private class ValidationEventProcessor { /// /// Callback for notifications being sent by the XmlSchema.Read() method /// /// Not used /// Contains the notification being sent public void OnValidationEvent(object sender, ValidationEventArgs arguments) { // We're only interested in the first error, so if we already got // an exception on record, skip it! if(this.OccurredException != null) { return; } // Only errors count as reasons to assume loading has failed if(arguments.Severity == XmlSeverityType.Error) { // This code uses the ternary operator because I don't know how to // stimlate a validation error that has no exception and couldn't achieve // 100% test coverage otherwise. MSDN does not tell whether a validation // error without an exception can even occur. this.OccurredException = (arguments.Exception != null) ? arguments.Exception : new XmlSchemaValidationException("Unspecified schema validation error"); } } /// Exception that has occurred during schema loading public Exception OccurredException; } #endregion // class ValidationEventProcessor /// Loads a schema from a file /// Path to the file containing the schema /// The loaded schema public static XmlSchema LoadSchema(string schemaPath) { using(FileStream schemaStream = openFileForSharedReading(schemaPath)) { return LoadSchema(schemaStream); } } /// Loads a schema from the provided stream /// Stream containing the schema that will be loaded /// The loaded schema public static XmlSchema LoadSchema(Stream schemaStream) { return LoadSchema(new StreamReader(schemaStream)); } /// Loads a schema from a text reader /// Text reader through which the schema can be read /// The loaded schema public static XmlSchema LoadSchema(TextReader schemaReader) { ValidationEventProcessor eventProcessor = new ValidationEventProcessor(); XmlSchema schema = XmlSchema.Read(schemaReader, eventProcessor.OnValidationEvent); if(eventProcessor.OccurredException != null) { throw eventProcessor.OccurredException; } return schema; } /// Attempts to load a schema from a file /// Path to the file containing the schema /// Output parameter that will receive the loaded schema /// True if the schema was loaded successfully, otherwise false public static bool TryLoadSchema(string schemaPath, out XmlSchema schema) { FileStream schemaStream; if(!tryOpenFileForSharedReading(schemaPath, out schemaStream)) { schema = null; return false; } using(schemaStream) { return TryLoadSchema(schemaStream, out schema); } } /// Attempts to load a schema from the provided stream /// Stream containing the schema that will be loaded /// Output parameter that will receive the loaded schema /// True if the schema was loaded successfully, otherwise false public static bool TryLoadSchema(Stream schemaStream, out XmlSchema schema) { return TryLoadSchema(new StreamReader(schemaStream), out schema); } /// Attempts to load a schema from the provided text reader /// Reader from which the schema can be read /// Output parameter that will receive the loaded schema /// True if the schema was loaded successfully, otherwise false public static bool TryLoadSchema(TextReader schemaReader, out XmlSchema schema) { try { ValidationEventProcessor eventProcessor = new ValidationEventProcessor(); schema = XmlSchema.Read(schemaReader, eventProcessor.OnValidationEvent); if(eventProcessor.OccurredException == null) { return true; } } catch(Exception) { // Munch! } schema = null; return false; } /// Loads an XML document from a file /// Schema to use for validating the XML document /// /// Path to the file containing the XML document that will be loaded /// /// The loaded XML document public static XDocument LoadDocument(XmlSchema schema, string documentPath) { using(FileStream documentStream = openFileForSharedReading(documentPath)) { return LoadDocument(schema, documentStream); } } /// Loads an XML document from a stream /// Schema to use for validating the XML document /// /// Stream from which the XML document will be read /// /// The loaded XML document public static XDocument LoadDocument(XmlSchema schema, Stream documentStream) { XmlReaderSettings settings = new XmlReaderSettings(); settings.Schemas.Add(schema); using (XmlReader reader = XmlReader.Create(documentStream, settings)) { var document = XDocument.Load(reader, LoadOptions.None); // Create a schema set because the Validate() method only accepts // schemas in a schemaset var schemas = new XmlSchemaSet(); schemas.Add(schema); // Perform the validation and report the first validation error // encountered to the caller var validationEventProcessor = new ValidationEventProcessor(); document.Validate(schemas, validationEventProcessor.OnValidationEvent); if (validationEventProcessor.OccurredException != null) { throw validationEventProcessor.OccurredException; } return document; } } /// Opens a file for shared reading /// Path to the file that will be opened /// The opened file's stream private static FileStream openFileForSharedReading(string path) { return new FileStream( path, FileMode.Open, FileAccess.Read, FileShare.Read ); } /// Opens a file for shared reading /// Path to the file that will be opened /// /// Output parameter that receives the opened file's stream /// /// True if the file was opened successfully private static bool tryOpenFileForSharedReading(string path, out FileStream fileStream) { try { fileStream = new FileStream( path, FileMode.Open, FileAccess.Read, FileShare.Read ); return true; } catch(Exception) { // Munch! } fileStream = null; return false; } #endif // !NO_XMLSCHEMA } } // namespace Nuclex.Support