// Author: Gockner, Simon
// Created: 2020-02-12
// Copyright(c) 2020 SimonG. All Rights Reserved.
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using GBase.DataHandling.Exceptions;
using GBase.Interfaces.DataHandling;
using GBase.Interfaces.DataHandling.Xml;
namespace GBase.DataHandling
{
///
/// A that writes to a xml file
///
public class XmlDataWriter : IXmlDataWriter
{
///
/// Initialize the
///
///
///
/// A to cancel the async operation
/// Returns true if successful, false if not
/// No root element found
public async Task InitFile(FileStream file, string rootElementName, CancellationToken cancellationToken)
{
//if the xml file isn't empty, return
if (file.Length > 3) //> 3 because of BOM
return true; //TODO: Don't return bool?
XDocument xmlDocument = new XDocument();
xmlDocument.Add(new XElement(rootElementName));
file.Seek(0, SeekOrigin.Begin); //reset stream to it's beginning to be able to save it
await xmlDocument.SaveAsync(file, SaveOptions.OmitDuplicateNamespaces, cancellationToken);
return true;
}
///
/// Write the data of a property
///
/// The
/// The of the property
///
/// The name of the property
/// The value of the property
/// If true an existing value is overwritten, if false an additional value is added
///
/// A to await
public async Task Write(FileStream file, string propertyName, string value, bool overwrite, CancellationToken cancellationToken) =>
await Write(file, propertyName, value, typeof(TProperty), overwrite, cancellationToken);
///
/// Write the data of a property
///
/// The that implements the property
///
/// The name of the property
/// The value of the property
///
/// If true an existing value is overwritten, if false an additional value is added
///
/// A to await
///
/// No root element is set
public async Task Write(FileStream file, string propertyName, string value, Type propertyType, bool overwrite, CancellationToken cancellationToken)
{
string typeName = typeof(T).FullName;
if (typeName == null)
throw new ArgumentNullException(nameof(typeName));
file.Seek(0, SeekOrigin.Begin); //reset stream to it's beginning to be able to save it
XDocument xmlDocument = await XDocument.LoadAsync(file, LoadOptions.None, cancellationToken);
file.Seek(0, SeekOrigin.Begin); //reset stream to it's beginning to be able to save it
XElement rootElement = xmlDocument.Root;
if (rootElement == null)
throw new InvalidXmlFileException("No root element is set.");
XElement propertyElement = rootElement.Element(propertyName);
if (propertyElement != null) //property element already exists
{
XElement valueElement = propertyElement.Element(XmlDataHandler.VALUE_ELEMENT_NAME);
if (valueElement != null && overwrite) //value element exists and overwrite is true
{
valueElement.Value = value; //overwrite existing value
}
else if (!overwrite && propertyElement.Elements(XmlDataHandler.VALUE_ELEMENT_NAME).Any(v => v.Value.Equals(value))) //no overwrite and same value exists already
{
XElement sameValueElement = propertyElement.Elements(XmlDataHandler.VALUE_ELEMENT_NAME).First(v => v.Value.Equals(value));
sameValueElement.Remove(); //remove the already existing value from its current position
propertyElement.AddFirst(sameValueElement); //add it as the first element again
}
else //no value element exists or overwrite is false
propertyElement.AddFirst(new XElement(XmlDataHandler.VALUE_ELEMENT_NAME) { Value = value }); //add a new value element
}
else //property element doesn't exist
{
propertyElement = new XElement(propertyName, new XElement(XmlDataHandler.VALUE_ELEMENT_NAME) { Value = value }); //create the new property element with the value element
propertyElement.SetAttributeValue(XmlDataHandler.VALUE_TYPE_ATTRIBUTE_NAME, propertyType.FullName); //add the property type attribute
rootElement.Add(propertyElement); //create new property element with the value element
}
//TODO: check if whole file is overwritten (probably) -> performance issues for large files?
await xmlDocument.SaveAsync(file, SaveOptions.OmitDuplicateNamespaces, cancellationToken); //save the document with the added elements
}
///
/// Remove the value for the given property
///
/// The of the property
/// The of the property
///
/// The name of the property
/// The value to set
///
/// A to await
///
/// No root element is set
public async Task Remove(FileStream file, string propertyName, string value, CancellationToken cancellationToken)
{
string typeName = typeof(T).FullName;
if (typeName == null)
throw new ArgumentNullException(nameof(typeName));
XDocument xmlDocument = await XDocument.LoadAsync(file, LoadOptions.None, cancellationToken);
file.Seek(0, SeekOrigin.Begin); //reset stream to it's beginning to be able to save it
XElement rootElement = xmlDocument.Root;
if (rootElement == null)
throw new InvalidXmlFileException("No root element is set.");
XElement typeElement = rootElement.Element(typeName);
XElement propertyElement = typeElement?.Element(propertyName);
XElement valueElement = propertyElement?.Elements(XmlDataHandler.VALUE_ELEMENT_NAME).FirstOrDefault(e => e.Value.Equals(value));
if (valueElement == null)
return;
valueElement.Remove();
//TODO: check if whole file is overwritten (probably) -> performance issues for large files?
await xmlDocument.SaveAsync(file, SaveOptions.OmitDuplicateNamespaces, cancellationToken); //save the document with the added elements
}
}
}