using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using System.Xml.XPath;
using Ringtoets.Piping.Data;
using Ringtoets.Piping.IO.Builders;
using Ringtoets.Piping.IO.Properties;
namespace Ringtoets.Piping.IO.SoilProfile
{
///
/// This class is responsible for reading an array of bytes and interpret this as a XML document, which contains information about
/// the geometry of a .
///
internal class SoilLayer2DReader
{
private const string outerLoopElementName = "OuterLoop";
private const string innerLoopElementName = "InnerLoop";
private const string endPointElementName = "EndPoint";
private const string headPointElementName = "HeadPoint";
private const string geometryCurveElementName = "GeometryCurve";
private const string xElementName = "X";
private const string zElementName = "Z";
private readonly XmlSchemaSet schema;
///
/// Constructs an instance of .
///
/// Thrown when the XML-schema
/// could not be loaded.
internal SoilLayer2DReader()
{
schema = LoadXmlSchema();
}
///
/// Reads a new using the as the source of the
/// geometry for a .
///
/// An of which contains the information
/// of a in an XML document.
/// A new with information taken from the XML document.
/// Thrown when is null.
/// Thrown when:
///
/// - is not valid XML.
/// - does not pass schema validation.
///
///
///
internal SoilLayer2D Read(byte[] geometry)
{
if (geometry == null)
{
throw new ArgumentNullException("geometry", Resources.SoilLayer2DReader_Geometry_is_null);
}
try
{
return Read(XDocument.Load(new MemoryStream(geometry)));
}
catch (XmlException e)
{
throw new SoilLayer2DConversionException(Resources.SoilLayer2DReader_Geometry_contains_no_valid_xml, e);
}
}
///
/// Reads a new using the as the source of the
/// geometry for a .
///
/// An which contains the information of a
/// in an XML document.
/// A new with information taken from the XML document.
/// Thrown when is null.
/// Thrown when:
///
/// - is not valid XML.
/// - does not pass schema validation.
///
///
///
internal SoilLayer2D Read(XDocument geometry)
{
ValidateToSchema(geometry);
return ParseLayer(geometry);
}
///
/// Validates the to the .
///
/// The to validate.
/// Thrown when the validation failed.
///
private void ValidateToSchema(XDocument document)
{
try
{
document.Validate(schema, null);
}
catch (XmlSchemaValidationException e)
{
throw new SoilLayer2DConversionException(Resources.SoilLayer2DReader_Geometry_contains_no_valid_xml, e);
}
}
private XmlSchemaSet LoadXmlSchema()
{
var schemaFile = GetType().Assembly.GetManifestResourceStream("Ringtoets.Piping.IO.SoilProfile.XmlGeometrySchema.xsd");
var xmlSchema = new XmlSchemaSet();
xmlSchema.Add(XmlSchema.Read(schemaFile, null));
return xmlSchema;
}
///
/// Parses the XML element to create a 2D soil layer.
///
/// The geometry.
/// XML for inner or outer geometry loops is invalid.
private SoilLayer2D ParseLayer(XDocument geometry)
{
var pipingSoilLayer = new SoilLayer2D();
var xmlOuterLoop = geometry.XPathSelectElement(string.Format("//{0}", outerLoopElementName));
var xmlInnerLoops = geometry.XPathSelectElements(string.Format("//{0}", innerLoopElementName));
if (xmlOuterLoop != null)
{
pipingSoilLayer.OuterLoop = ParseGeometryLoop(xmlOuterLoop);
}
foreach (XElement loop in xmlInnerLoops)
{
pipingSoilLayer.AddInnerLoop(ParseGeometryLoop(loop));
}
return pipingSoilLayer;
}
///
/// Parses the XML element to create a collection of describing
/// a geometric loop.
///
/// The geometric loop element.
/// XML for any geometry curve is invalid.
private IEnumerable ParseGeometryLoop(XElement loop)
{
var loops = new Collection();
var curves = loop.XPathSelectElements(string.Format("//{0}", geometryCurveElementName));
foreach (XElement curve in curves)
{
loops.Add(ParseGeometryCurve(curve));
}
return loops;
}
///
/// Parses the XML element to create a .
///
/// The geometry curve element.
/// XML for geometry curve is invalid.
private Segment2D ParseGeometryCurve(XElement curve)
{
var headDefinition = curve.Element(headPointElementName);
var endDefinition = curve.Element(endPointElementName);
if (headDefinition != null && endDefinition != null)
{
return new Segment2D(
ParsePoint(headDefinition),
ParsePoint(endDefinition)
);
}
throw new SoilLayer2DConversionException(Resources.SoilLayer2DReader_Geometry_contains_no_valid_xml);
}
///
/// Parses the XML element to create a .
///
/// The 2D point element.
/// When any of the following occurs:
///
/// - A coordinate value cannot be parsed.
/// - XML for 2D point is invalid.
///
private Point2D ParsePoint(XElement point)
{
var x = point.Element(xElementName);
var y = point.Element(zElementName);
if (x != null && y != null)
{
try
{
return new Point2D
{
X = double.Parse(x.Value, CultureInfo.InvariantCulture),
Y = double.Parse(y.Value, CultureInfo.InvariantCulture)
};
}
catch (ArgumentNullException e)
{
throw new SoilLayer2DConversionException(Resources.SoilLayer2DReader_Could_not_parse_point_location, e);
}
catch (FormatException e)
{
throw new SoilLayer2DConversionException(Resources.SoilLayer2DReader_Could_not_parse_point_location, e);
}
catch (OverflowException e)
{
throw new SoilLayer2DConversionException(Resources.SoilLayer2DReader_Could_not_parse_point_location, e);
}
}
throw new SoilLayer2DConversionException(Resources.SoilLayer2DReader_Geometry_contains_no_valid_xml);
}
}
}