#region Disclaimer / License
// Copyright (C) 2009, Kenneth Skovhede
// http://www.hexad.dk, opensource@hexad.dk
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 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 GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
#endregion Disclaimer / License
using OSGeo.MapGuide.MaestroAPI.Exceptions;
using OSGeo.MapGuide.ObjectModels;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Schema;
namespace OSGeo.MapGuide.MaestroAPI
{
///
/// Class that makes XSD validation
///
public class XmlValidator
{
private List warnings = new List();
private List errors = new List();
///
/// Gets the validation warnings.
///
public ReadOnlyCollection ValidationWarnings => this.warnings.AsReadOnly();
///
/// Gets the validation errors.
///
public ReadOnlyCollection ValidationErrors => this.errors.AsReadOnly();
///
/// Validates the specified XML.
///
/// The XML.
/// The array of objects to validate against.
public void Validate(Stream xml, XmlSchema[] xsds)
{
this.warnings.Clear();
this.errors.Clear();
var config = new XmlReaderSettings();
if (xsds != null && xsds.Length > 0)
{
foreach (var xsd in xsds)
config.Schemas.Add(xsd);
}
config.ValidationType = ValidationType.Schema;
config.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
config.ValidationFlags |= XmlSchemaValidationFlags.ProcessInlineSchema;
config.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation;
//This will trap all the errors and warnings that are raised
config.ValidationEventHandler += (s, e) =>
{
var ex = e.Exception;
if (e.Severity == XmlSeverityType.Warning)
{
this.warnings.Add(string.Format(Strings.XmlValidationIssueTemplate, ex.LineNumber, ex.LinePosition, ex.Message));
}
else
{
this.errors.Add(string.Format(Strings.XmlValidationIssueTemplate, ex.LineNumber, ex.LinePosition, ex.Message));
}
};
using (var reader = XmlReader.Create(xml, config))
{
while (reader.Read()) { } //Trigger the validation
}
}
private static XmlSchema GetXsd(string xsdPath, string xsdFile)
{
string path = xsdFile;
if (!string.IsNullOrEmpty(xsdPath))
path = Path.Combine(xsdPath, xsdFile);
if (File.Exists(path))
{
ValidationEventHandler handler = (s, e) =>
{
};
return XmlSchema.Read(File.OpenRead(path), handler);
}
return null;
}
///
/// Validates the content of the resource XML.
///
/// Content of the XML.
/// The XSD path.
/// The errors.
/// The warnings.
public static void ValidateResourceXmlContent(string xmlContent, string xsdPath, out string[] errors, out string[] warnings)
{
errors = new string[0];
warnings = new string[0];
List err = new List();
List warn = new List();
//Test for well-formedness
try
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlContent);
}
catch (XmlException ex)
{
err.Add(ex.Message);
}
IResource res = null;
//Test for serializablility
try
{
//Use original resource type to determine how to deserialize
res = ObjectFactory.DeserializeXml(xmlContent);
}
catch (Exception ex)
{
if (!(ex is SerializationException))
err.Add(ex.Message);
else
res = null;
}
if (res != null)
{
//Finally verify the content itself
var xml = xmlContent;
var xsds = new Dictionary();
var xsd = GetXsd(xsdPath, res.ValidatingSchema);
if (xsd != null)
xsds.Add(res.ValidatingSchema, xsd);
else
return; //One or more schemas is not found. Cannot proceed. Let MG figure it out
//HACK: Yes this is hard-coded because XmlSchema's dependency resolution sucks!
//Nearly all relevant xsds include this anyway so add it to the set
var pc = GetXsd(xsdPath, "PlatformCommon-1.0.0.xsd"); //NOXLATE
if (pc != null)
xsds.Add("PlatformCommon-1.0.0.xsd", pc); //NOXLATE
if (res.ResourceType == ResourceTypes.LayerDefinition.ToString())
{
string version = res.ResourceVersion.ToString();
if (version.StartsWith("1.1.0")) //NOXLATE
{
var sym = GetXsd(xsdPath, "SymbolDefinition-1.0.0.xsd"); //NOXLATE
if (sym != null)
xsds.Add("SymbolDefinition-1.0.0.xsd", sym); //NOXLATE
}
else if (version.StartsWith("1.2.0") || version.StartsWith("1.3.0")) //NOXLATE
{
var sym = GetXsd(xsdPath, "SymbolDefinition-1.1.0.xsd"); //NOXLATE
if (sym != null)
xsds.Add("SymbolDefinition-1.1.0.xsd", sym); //NOXLATE
}
else if (version.StartsWith("2.3.0")) //NOXLATE
{
var wmd = GetXsd(xsdPath, "WatermarkDefinition-2.3.0.xsd"); //NOXLATE
if (wmd != null)
xsds.Add("WatermarkDefinition-2.3.0.xsd", wmd); //NOXLATE
}
else if (version.StartsWith("2.4.0")) //NOXLATE
{
var sym = GetXsd(xsdPath, "SymbolDefinition-2.4.0.xsd"); //NOXLATE
if (sym != null)
xsds.Add("SymbolDefinition-2.4.0.xsd", sym); //NOXLATE
var wmd = GetXsd(xsdPath, "WatermarkDefinition-2.4.0.xsd"); //NOXLATE
if (wmd != null)
xsds.Add("WatermarkDefinition-2.4.0.xsd", wmd); //NOXLATE
}
}
if (res.ResourceType == ResourceTypes.WatermarkDefinition.ToString())
{
string version = res.ResourceVersion.ToString();
if (version.StartsWith("2.3.0")) //NOXLATE
{
var sym = GetXsd(xsdPath, "SymbolDefinition-1.1.0.xsd"); //NOXLATE
if (sym != null)
xsds.Add("SymbolDefinition-1.1.0.xsd", sym); //NOXLATE
}
else if (version.StartsWith("2.4.0"))
{
var sym = GetXsd(xsdPath, "SymbolDefinition-2.4.0.xsd"); //NOXLATE
if (sym != null)
xsds.Add("SymbolDefinition-2.4.0.xsd", sym); //NOXLATE
}
}
if (res.ResourceType == ResourceTypes.MapDefinition.ToString())
{
string version = res.ResourceVersion.ToString();
if (version.StartsWith("2.3.0"))
{
var sym = GetXsd(xsdPath, "SymbolDefinition-1.1.0.xsd"); //NOXLATE
if (sym != null)
xsds.Add("SymbolDefinition-1.1.0.xsd", sym); //NOXLATE
var wmd = GetXsd(xsdPath, "WatermarkDefinition-2.3.0.xsd"); //NOXLATE
if (wmd != null)
xsds.Add("WatermarkDefinition-2.3.0.xsd", wmd); //NOXLATE
}
else if (version.StartsWith("2.4.0") || version.StartsWith("3.0.0"))
{
var sym = GetXsd(xsdPath, "SymbolDefinition-2.4.0.xsd"); //NOXLATE
if (sym != null)
xsds.Add("SymbolDefinition-2.4.0.xsd", sym); //NOXLATE
var wmd = GetXsd(xsdPath, "WatermarkDefinition-2.4.0.xsd"); //NOXLATE
if (wmd != null)
xsds.Add("WatermarkDefinition-2.4.0.xsd", wmd); //NOXLATE
}
}
string xsdName = res.ResourceType.ToString() + "-" + res.ResourceVersion.ToString() + ".xsd"; //NOXLATE
if (!xsds.ContainsKey(xsdName))
{
var schemaObj = GetXsd(xsdPath, xsdName);
if (schemaObj != null)
xsds.Add(xsdName, schemaObj);
}
var validator = new XmlValidator();
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
{
validator.Validate(ms, new List(xsds.Values).ToArray());
}
err.AddRange(validator.ValidationErrors);
warn.AddRange(validator.ValidationWarnings);
}
errors = err.ToArray();
warnings = warn.ToArray();
}
}
}