#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
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