You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
151 lines
8.1 KiB
151 lines
8.1 KiB
// 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
|
|
{
|
|
/// <summary>
|
|
/// A <see cref="IDataWriter"/> that writes to a xml file
|
|
/// </summary>
|
|
public class XmlDataWriter : IXmlDataWriter
|
|
{
|
|
/// <summary>
|
|
/// Initialize the <see cref="XmlDataWriter"/>
|
|
/// </summary>
|
|
/// <param name="file"></param>
|
|
/// <param name="rootElementName"></param>
|
|
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to cancel the async operation</param>
|
|
/// <returns>Returns true if successful, false if not</returns>
|
|
/// <exception cref="Exception">No root element found</exception>
|
|
public async Task<bool> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write the data of a property
|
|
/// </summary>
|
|
/// <typeparam name="T">The <see cref="Type"/></typeparam>
|
|
/// <typeparam name="TProperty">The <see cref="Type"/> of the property</typeparam>
|
|
/// <param name="file"></param>
|
|
/// <param name="propertyName">The name of the property</param>
|
|
/// <param name="value">The value of the property</param>
|
|
/// <param name="overwrite">If true an existing value is overwritten, if false an additional value is added</param>
|
|
/// <param name="cancellationToken"></param>
|
|
/// <returns>A <see cref="Task"/> to await</returns>
|
|
public async Task Write<T, TProperty>(FileStream file, string propertyName, string value, bool overwrite, CancellationToken cancellationToken) =>
|
|
await Write<T>(file, propertyName, value, typeof(TProperty), overwrite, cancellationToken);
|
|
|
|
/// <summary>
|
|
/// Write the data of a property
|
|
/// </summary>
|
|
/// <typeparam name="T">The <see cref="Type"/> that implements the property</typeparam>
|
|
/// <param name="file"></param>
|
|
/// <param name="propertyName">The name of the property</param>
|
|
/// <param name="value">The value of the property</param>
|
|
/// <param name="propertyType"></param>
|
|
/// <param name="overwrite">If true an existing value is overwritten, if false an additional value is added</param>
|
|
/// <param name="cancellationToken"></param>
|
|
/// <returns>A <see cref="Task"/> to await</returns>
|
|
/// <exception cref="ArgumentNullException"><paramref name="propertyName"/></exception>
|
|
/// <exception cref="InvalidXmlFileException">No root element is set</exception>
|
|
public async Task Write<T>(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
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove the value for the given property
|
|
/// </summary>
|
|
/// <typeparam name="T">The <see cref="Type"/> of the property</typeparam>
|
|
/// <typeparam name="TProperty">The <see cref="Type"/> of the property</typeparam>
|
|
/// <param name="file"></param>
|
|
/// <param name="propertyName">The name of the property</param>
|
|
/// <param name="value">The value to set</param>
|
|
/// <param name="cancellationToken"></param>
|
|
/// <returns>A <see cref="Task"/> to await</returns>
|
|
/// <exception cref="ArgumentNullException"><paramref name="propertyName"/></exception>
|
|
/// <exception cref="InvalidXmlFileException">No root element is set</exception>
|
|
public async Task Remove<T, TProperty>(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
|
|
}
|
|
}
|
|
} |