Code development platform for open source projects from the European Union institutions

Skip to content
Snippets Groups Projects
XMLFileSelector.cs 6.17 KiB
Newer Older
using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
using System.Xml;
using System.Xml.Schema;
using HashingTool.Helper;
using HashingTool.Util;

namespace HashingTool.ViewModel.UserControl
{
	public enum XmlFileStatus
	{
		Unknown, // no file selected,				outline
		Invalid, // reading failed, no xml, etc.		red
		InvalidXML, // does not validate against known schemas
		IncorrectContent, // content validation failed			
		ValidXML //									green
	}

	public interface IErrorLogger
	{
		void LogError(string message);
	}

	public class XMLFileSelector : ObservableObject, IErrorLogger
	{
		private string _source;

		private bool _busy;

		private readonly bool _validate;
		private XmlDocument _document;
		private readonly Func<XmlDocument, IErrorLogger, bool?> _contentVerification;
		private RelayCommand _browseFileCommand;
		public XMLFileSelector(IOService ioservice, string prefix, bool validate = false,
			Func<XmlDocument, IErrorLogger, bool?> contentCheck = null)
			_validate = validate;
			_browseFileCommand = new RelayCommand(BrowseXMLFile, () => !_busy);
			XMLValidationErrors = new ObservableCollection<string>();
			HasContentValidation = contentCheck != null;
			_contentVerification = contentCheck ?? ((x, c) => null);
			Source = "";
			RaisePropertyChanged("ValidateInput");
			RaisePropertyChanged("HasContentValidation");
		}

		public XmlDocument Document
		{
			get { return _document; }
			private set {
				if (_document == value) {
					return;
				}
				_document = value;
				RaisePropertyChanged("Document");
			}
		}

		public string Source
		{
			get { return _source; }
			private set {
				if (_source == value) {
					return;
				}
				_source = value;
				RaisePropertyChanged("Source");
			}
		}

		public bool ValidateInput
		{
			get { return _validate; }
		}

		{
			get { return _isValid; }
			private set {
				if (_isValid == value) {
					return;
				}
				_isValid = value;
				RaisePropertyChanged("IsValid");
		private void SetToolTip(XmlFileStatus value)
		{
			var toolTip = "";
			switch (value) {
				case XmlFileStatus.Unknown:
					toolTip = "Select XML File file";
					break;
				case XmlFileStatus.Invalid:
					toolTip = "Invalid file";
					break;
				case XmlFileStatus.InvalidXML:
					toolTip = "XML Schema validation failed";
					break;
				case XmlFileStatus.IncorrectContent:
					toolTip = "Incorrect XML content";
					break;
				case XmlFileStatus.ValidXML:
					toolTip = "OK";
					break;
			}
			if (ToolTip == toolTip) {
				return;
			}
			ToolTip = toolTip;
			RaisePropertyChanged("ToolTip");
		}

		public string ToolTip { get; internal set; }

		public ObservableCollection<string> XMLValidationErrors { get; set; }

		public ICommand BrowseFileCommand
		{
			get { return new RelayCommand<string>(SetXMLFile, f => !_busy); }
		private async void SetXMLFile(string fileName)
		{
			if (!File.Exists(fileName)) {
				Document = null;
				XMLValidationErrors.Clear();
				ContentValid = null;
			using (var stream = File.OpenRead(fileName)) {
				await LoadXMLFile(stream);
			}
			using (var stream = IoService.OpenFileDialog(null, ".xml", "VECTO XML file|*.xml", out filename)) {
				if (stream == null) {
					return;
				}
				await LoadXMLFile(stream);
				Source = filename;
			}
			XMLValidationErrors.Clear();
			var fileValid = XmlFileStatus.ValidXML;
			bool? contentValid = null;
			try {
				var ms = new MemoryStream();
				if (_validate) {
					// copy stream beforehand if validation is needed later on
					await stream.CopyToAsync(ms);
					ms.Seek(0, SeekOrigin.Begin);
					stream.Seek(0, SeekOrigin.Begin);
				}

				var document = new XmlDocument();
				var reader = XmlReader.Create(stream);
				document.Load(reader);
				Document = document;
				var xmlValid = true;
				if (_validate) {
					xmlValid = await Validate(XmlReader.Create(ms));
					if (!xmlValid) {
						fileValid = XmlFileStatus.InvalidXML;
					}
				}
				if (HasContentValidation) {
					contentValid = _contentVerification(document, this);
					if (xmlValid && (contentValid == null || !contentValid.Value)) {
						fileValid = XmlFileStatus.IncorrectContent;
					}
				}
			} catch (Exception e) {
				fileValid = XmlFileStatus.Invalid;
			} finally {
				IsValid = fileValid;
				ContentValid = contentValid;
				_busy = false;
				_browseFileCommand.RaiseCanExecuteChanged();
Markus Quaritsch's avatar
Markus Quaritsch committed
				RaisePropertyChanged(GeneralUpdate);
		public void LogError(string message) 
		{
			XMLValidationErrors.Add(String.Format("{0}: {1}", _prefix, message));
		}

		public bool HasContentValidation { get; private set; }

		public bool? ContentValid
		{
			get { return _contentValid; }
			set {
				if (_contentValid == value) {
					return;
				}
				_contentValid = value;
				RaisePropertyChanged("ContentValid");
			}
		}

		private async Task<bool> Validate(XmlReader xml)
				var validator = new XMLValidator(r => { valid = r; },
						Application.Current.Dispatcher.Invoke(
							() =>
								LogError(string.Format("Validation {0} Line {2}: {1}",
									s == XmlSeverityType.Warning ? "WARNING" : "ERROR",
									e.ValidationEventArgs == null
										? e.Exception.Message +
										(e.Exception.InnerException != null ? Environment.NewLine + e.Exception.InnerException.Message : "")
										: e.ValidationEventArgs.Message,
									e.ValidationEventArgs == null ? 0 : e.ValidationEventArgs.Exception.LineNumber)));
					});
				await validator.ValidateXML(xml);
			} catch (Exception e) {