#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.IO;
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