// 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 } } }