using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Xml;
using Bulasoft.Common.Elements;
using Bulasoft.Common.Utils;
using ICSharpCode.SharpZipLib.Zip;
namespace Bulasoft.Common.Serialization
{
public class Reader : ReaderWriterBase
{
private bool _readChanges;
public bool ReadChanges
{
get { return _readChanges; }
}
private Dictionary<IElement, Dictionary<string, string>> Refs = new Dictionary<IElement, Dictionary<string, string>>();
public const string EmptyFile = "The file is empty";
private static bool initialized = false;
private static void RegisterElement(IElement element)
{
if (!initialized)
{
foreach (Type type in Assembly.GetAssembly(element.GetType()).GetTypes())
{
if (!type.IsGenericType && !type.IsAbstract && (type.GetInterface(typeof(IElement).Name) != null))
Cache.RegisterType(type);
}
initialized = true;
}
}
public static void Read(XmlDocument doc, IElement element, Logger logger)
{
RegisterElement(element);
new Reader(doc, element, logger);
}
public static void Read(string fileName, IElement element, Logger logger)
{
XmlDocument doc = new XmlDocument();
doc.Load(fileName);
RegisterElement(element);
new Reader(doc, element, logger);
}
public static void ReadZip(string fileName, IElement element, Logger logger, string password)
{
RegisterElement(element);
XmlDocument doc = new XmlDocument();
using (ZipInputStream zipStream = new ZipInputStream(File.OpenRead(fileName)))
{
zipStream.Password = password;
zipStream.GetNextEntry();
doc.Load(zipStream);
}
new Reader(doc, element, logger);
}
Logger _logger;
private Reader(XmlDocument doc, IElement element, Logger logger)
: base(doc)
{
_logger = logger;
SaveContext();
CurrentContext.Element = element;
CurrentContext.XmlElement = doc.DocumentElement;
if (doc.DocumentElement == null)
{
LogError(element, EmptyFile, EmptyFile);
return;
}
ReadElement(element, doc.DocumentElement);
foreach (KeyValuePair<IElement, Dictionary<string, string>> pair in Refs)
{
Dictionary<string, string> attrs = pair.Value;
IElement elem = pair.Key;
CacheEntry entry = Cache.GetElementEntry(elem);
foreach (ElementTypeCachePropertyEntry propEntry in entry.GetReferenceProperties())
{
string guidStr;
if (attrs.TryGetValue(propEntry.Name, out guidStr))
{
if (string.IsNullOrEmpty(guidStr))
propEntry.SetValue(element, null);
else
{
try
{
Guid elementRef;
elementRef = new Guid(guidStr);
IElement referedElement;
referedElement = FindReference(element, elementRef);
if (referedElement != null)
propEntry.SetValue(elem, referedElement);
else
LogError(elem, propEntry.PropertyInfo, "Reference: {0} not found", guidStr);
}
catch
{
LogError(elem, propEntry.PropertyInfo, "Invalid reference {0}", guidStr);
}
}
}
}
}
}
private static IElement FindReference(IElement element, Guid elementRef)
{
IElement result = null;
if (element is IElementSpace)
result = ((IElementSpace)element).Find(elementRef);
else
{
foreach (IElement el in element)
if (el.ID == elementRef)
{
result = el;
break;
}
else
{
if ((result = FindReference(el, elementRef)) != null)
break;
}
}
return result;
}
private void LogError(IElement element, PropertyInfo propInfo, string message, params string[] param)
{
if (_logger != null) _logger.AddEntry(LoggerEntryKind.Error, element, propInfo, message, param);
}
private void LogError(IElement element, string message, params string[] param)
{
if (_logger != null) _logger.LogError(element, message, param);
}
private void LogWarning(IElement element, string message, params string[] param)
{
if (_logger != null) _logger.LogWarning(element, message, param);
}
public IElement ConstructElement(string name)
{
return (IElement)Cache.NewInstance(name);
}
private void ReadElement(IElement element, XmlElement xmlElement)
{
SaveContext();
CurrentContext.Element = element;
CurrentContext.XmlElement = xmlElement;
CurrentContext.CacheEntry = Cache.GetElementEntry(element);
if (element is ElementSpace)
{
_readChanges = xmlElement.GetAttributeNode("xmlns:" + XmlHelpers.xmlVersioningNamespacePrefix) != null;
}
if (ReadChanges)
{
foreach (ElementTypeCachePropertyEntry entry in CurrentContext.CacheEntry.Properties)
{
string oldValue = null;
string newValue = null;
if (ReflectionHelper.IsBasedOn(entry.PropertyType, typeof(StringDictionary)))
{
XmlElement e = XmlHelpers.GetChildNode(CurrentXmlElement, entry.Name, XmlHelpers.xmlAttributeNamespace);
if (e != null)
ReadElement(element, entry, e);
continue;
}
if (((entry.PropertyType == typeof(string)) && ((entry.SerializationSettings & SerializationSettings.SerializeAsAttribute) == 0)))
{
XmlElement e = XmlHelpers.GetChildNode(CurrentXmlElement, entry.Name, XmlHelpers.xmlAttributeNamespace);
if (e != null)
newValue = e.InnerText;
e = XmlHelpers.GetChildNode(CurrentXmlElement, entry.Name, XmlHelpers.xmlVersioningNamespace);
if (e != null)
oldValue = e.InnerText;
}
else
{
XmlAttribute attr = CurrentXmlElement.GetAttributeNode(entry.Name, XmlHelpers.xmlAttributeNamespace);
if (attr != null)
newValue = attr.Value;
attr = CurrentXmlElement.GetAttributeNode(entry.Name, XmlHelpers.xmlVersioningNamespace);
if (attr != null)
oldValue = attr.Value;
}
if (!string.IsNullOrEmpty(oldValue) || !string.IsNullOrEmpty(newValue))
{
if (oldValue != null)
{
object originalValue = entry.GetValue(element);
CheckOldValue(element, originalValue, entry, oldValue);
}
ReadElement(element, entry, newValue);
}
}
}
else
{
foreach (XmlAttribute attr in CurrentXmlElement.Attributes)
if (attr.NamespaceURI == XmlHelpers.xmlAttributeNamespace)
ReadElement(element, attr.LocalName, attr.Value);
foreach (XmlElement xmlElem in CurrentXmlElement.ChildNodes)
if (xmlElem.NamespaceURI == XmlHelpers.xmlAttributeNamespace)
ReadElement(element, xmlElem.LocalName, xmlElem);
}
ReadSubElements();
RestoreContext();
}
private static void CheckOldValue(IElement element, object originalValue, ElementTypeCachePropertyEntry entry, string oldValue)
{
object readValue;
string errorMessage;
if (entry.ConvertFromStringHandler(oldValue, entry.PropertyType, out readValue, out errorMessage))
return;
}
private void AddReference(IElement element, string attrName, string value)
{
Dictionary<string, string> attrs;
if (!Refs.TryGetValue(element, out attrs))
{
attrs = new Dictionary<string, string>();
Refs.Add(element, attrs);
}
attrs.Add(attrName, value);
}
private void ReadElement(IElement element, string attrName, XmlElement xmlElement)
{
ElementTypeCachePropertyEntry entry = CurrentContext.CacheEntry.GetPropertyEntry(attrName);
if (entry == null)
{
LogError(element, "Unknown attribute %s", attrName);
return;
}
ReadElement(element, entry, xmlElement);
}
private void ReadElement(IElement element, ElementTypeCachePropertyEntry entry, XmlElement xmlElement)
{
if (entry.PropertyType == typeof(string))
ReadElement(element, entry, xmlElement.InnerText);
if (ReflectionHelper.IsBasedOn(entry.PropertyType, typeof(StringDictionary)))
{
Element elem = element as Element;
StringDictionary dictionary = new StringDictionary(null, entry.Name);
foreach (XmlElement xmlElem in xmlElement.ChildNodes)
if (xmlElem.NamespaceURI == XmlHelpers.xmlAttributeNamespace)
{
dictionary.Add(xmlElem.Name, xmlElem.InnerText);
}
entry.SetValue(element, dictionary);
}
}
private void ReadElement(IElement element, string attrName, string value)
{
ElementTypeCachePropertyEntry entry = CurrentContext.CacheEntry.GetPropertyEntry(attrName);
if (entry == null)
{
LogError(element, "Unknown attribute %s", attrName);
return;
}
ReadElement(element, entry, value);
}
private void ReadElement(IElement element, ElementTypeCachePropertyEntry entry, string value)
{
if ((entry.SerializationSettings & SerializationSettings.Reference) > 0)
{
AddReference(element, entry.Name, value);
return;
}
if ((entry.SerializationSettings & SerializationSettings.Encrypt) > 0)
value = XmlHelpers.Decrypt(value);
object propValue;
string errorMessage;
if (entry.ConvertFromStringHandler(value, entry.PropertyType, out propValue, out errorMessage))
{
if ((propValue != null) || entry.Nullable)
entry.SetValue(element, propValue);
}
else
LogError(element, entry.PropertyInfo, errorMessage);
}
private void ReadSubElements()
{
if (ReflectionHelper.ImplementsGenericInterface(CurrentContext.Element.GetType(), typeof(ICollection<>)))
{
foreach (XmlElement xmlElement in CurrentXmlElement.ChildNodes)
if (xmlElement.NamespaceURI == XmlHelpers.xmlElementNamespace)
{
IElement elem = null;
XmlAttribute attr = xmlElement.GetAttributeNode(DefaultElement.IDName, XmlHelpers.xmlAttributeNamespace);
if (attr != null)
{
string newValue = attr.Value;
if (!string.IsNullOrEmpty(newValue))
{
try
{
Guid id = new Guid(newValue);
foreach (IElement ielem in CurrentContext.Element)
{
if (ielem.ID == id)
{
elem = ielem;
break;
}
}
}
catch { };
}
}
if (elem == null)
{
elem = ConstructElement(xmlElement.LocalName);
if (elem == null)
continue;
elem.Container = CurrentElement;
}
ReadElement(elem, xmlElement);
}
}
else
{
foreach (XmlElement xmlElement in CurrentXmlElement.ChildNodes)
if (xmlElement.NamespaceURI == XmlHelpers.xmlElementNamespace)
{
foreach (IElement elem in CurrentContext.Element)
{
if (Cache.GetName(elem.GetType()) == xmlElement.LocalName)
{
ReadElement(elem, xmlElement);
break;
}
}
}
}
}
public string ReadString(string attr, string oldValue)
{
return oldValue;
}
}
}