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); } } }