Click here to Skip to main content
15,885,365 members
Articles / Programming Languages / XSLT

Customize Applications with XML Fragments: Part 2

Rate me:
Please Sign up or sign in to vote.
3.00/5 (2 votes)
19 Jun 20073 min read 21.9K   111   9  
An advanced discussion of customizing applications with XML fragments
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;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.


Written By
Web Developer
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions