Click here to Skip to main content
15,886,795 members
Articles / Programming Languages / C#

Customize an application with XML fragments

Rate me:
Please Sign up or sign in to vote.
1.44/5 (3 votes)
22 May 20073 min read 28.2K   124   10  
How to customize an application using XML fragments
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using Aquin.Serialization.Core;
using System.Diagnostics;
using System.Reflection;
using System.IO;
using Aquin.Serialization.Serialization;

namespace Aquin.Serialization.Core
{

    [Flags]
    public enum ElementState
    {
        New = 0x01,
        ThisModified = 0x02,
        ChildrenModified = 0x04,
        Changed = New | ThisModified | ChildrenModified,

        MaskChanges = New | ThisModified | ChildrenModified,

        Public = 0x00,
        Protected = 0x10,
        Internal = 0x20,
        Private = 0x40,

        MaskAccess = Public | Protected | Internal | Private,

        Hidden = 0x100,
        Empty = 0x200,
        HideIfEmpty = 0x400,

        MaskDesign = Hidden | Empty | HideIfEmpty
    }

    public enum ElementAccess
    {
        Public, // full access
        Protected, // element is readonly (thus cannot delete)
        Internal, // element and its children are read-only
        Private // invisible and read-only
    }

    public class ElementBase : IEnumerable
    {
        public const string AccessName = "Access";
        public const string AuthorName = "Author";

        public const string ContainerName = "Container";
        public const string DeletedName = "Deleted";
        public const string DescriptionName = "Description";
        public const string ElementName = "Name";
        public const string GUIDName = "ID";


        protected internal ElementState _state = ElementState.New;
        protected internal List<ElementBase> containerElements = new List<ElementBase>();

        protected virtual void AddElement(ElementBase element)
        {
            containerElements.Add(element);
        }

        protected internal virtual void AddInnerElement(ElementBase element)
        {
            element.Container = this;
            element._state = element._state & ~ElementState.MaskAccess;
        }

        protected internal Dictionary<string, object> _changes = new Dictionary<string, object>();
        public Dictionary<string, object> Changes
        {
            get { return _changes; }
        }

        private ElementBase _container;

        [Serialization(), Category("Advanced"), Browsable(false), ReadOnly(true), TypeConverter(typeof(TypeConverter))]
        public ElementBase Container
        {
            get
            {
                return _container;
            }
            set
            {
                if (_container != value)
                {
                    ElementBase oldOwner = _container;

                    OnOwnerChanging(oldOwner, value);

                    if (_container != null)
                        _container.RemoveElement(this);

                    if (value != null)
                        value.AddElement(this);

                    _container = value;

                    OnOwnerChanged();
                }
            }
        }

        [Serialization()]
        [Category("Design"), DefaultValue(false), Description("Indicates that object is deleted")]
        public bool Deleted
        {
            get
            {
                return (ElementSpace != null) && ElementSpace.IsDeleted(this);
            }
            set
            {
                bool oldValue = Deleted;

                if (value != oldValue)
                {
                    if (ElementSpace == null)
                    {
                        if (value) return;
                        throw new Exception("Unsupported Operation: ElementSpace required");
                    }

                    LogChange(oldValue, value);

                    if (value)
                        ElementSpace.Delete(this);
                    else
                        ElementSpace.Undelete(this);
                }
            }
        }

        public virtual string DisplayText
        {
            get { return String.IsNullOrEmpty(_name) ? PermanentName : _name; }
        }

        protected virtual void DoWrite(Writer writer)
        {
            foreach (PropertyInfo prop in this.GetType().GetProperties())
            {
                SerializationAttribute attr = Attribute.GetCustomAttribute(prop, typeof(SerializationAttribute)) as SerializationAttribute;
                if (attr != null)
                {
                    string serialName = GetSerializationName(prop);
                    if (serialName == ElementName)
                    {
                        if (writer.IdentifyByID || (_name != PermanentName))
                            writer.WriteObject(ElementName, Name, false);
                        continue;
                    }

                    if (serialName == GUIDName)
                    {
                        if (!writer.IdentifyByID)
                            continue;
                    }

                    Type propType = prop.PropertyType;
                    bool nullable = propType.IsGenericType && (propType.GetGenericTypeDefinition() == typeof(Nullable<>));
                    if (nullable)
                        propType = propType.GetGenericArguments()[0];
                    object propValue = prop.GetValue(this, null);

                    if (propType == typeof(bool))
                        writer.WriteBoolean(serialName, propValue as bool?, false);
                    else if (propType == typeof(DateTime))
                        writer.WriteDateTime(serialName, propValue as DateTime?, false);
                    else if (propType == typeof(string))
                        writer.WriteString(serialName, propValue as string, false, (attr.Hints & SerializationSettings.Encrypt) != 0);
                    else
                    {
                        if (propType.BaseType == typeof(System.ValueType) || (propType.BaseType.BaseType == typeof(System.ValueType)))
                        {
                            writer.WriteObject(serialName, propValue, false);
                        }
                    }
                }
            }
            //if (writer.IdentifyByID || (_name != PermanentName))
            //    writer.WriteAttr(ElementName, Name, false);

        }

        protected internal virtual void DoWriteContainerElements(Writer writer)
        {
            bool prevId = writer.SetIdentifyByID(true);
            foreach (ElementBase element in containerElements)
            {
                element.Write(writer);
            }
            writer.SetIdentifyByID(prevId);
        }

        public ElementBase()
        {
            _name = PermanentName;

            if (_name == null)
                GUID = Guid.NewGuid();
        }
        [Serialization()]
        [Browsable(false), Category("Design"), DefaultValue(ElementState.Public)]
        public ElementAccess ElementAccess
        {
            get
            {
                switch (_state & ElementState.MaskAccess)
                {
                    case ElementState.Private:
                        return ElementAccess.Private;

                    case ElementState.Internal:
                        return ElementAccess.Internal;

                    case ElementState.Protected:
                        return ElementAccess.Protected;

                    default:
                        return ElementAccess.Public;
                }
            }
            set
            {
                if (ElementAccess != value)
                {
                    LogChange(ElementAccess, value);

                    switch (value)
                    {
                        case ElementAccess.Public:
                            _state = (_state & ~ElementState.MaskAccess) | ElementState.Public;
                            break;

                        case ElementAccess.Protected:
                            _state = (_state & ~ElementState.MaskAccess) | ElementState.Protected;
                            break;

                        case ElementAccess.Internal:
                            _state = (_state & ~ElementState.MaskAccess) | ElementState.Internal;
                            break;

                        case ElementAccess.Private:
                            _state = (_state & ~ElementState.MaskAccess) | ElementState.Private;
                            break;
                    }
                }
            }
        }

        public bool ElementChanged
        {
            get { return ((_state & ElementState.MaskChanges) > 0) || (Changes.Count > 0); }
        }

        protected internal ElementSpace _elementSpace;
        public ElementSpace ElementSpace
        {
            get { return _elementSpace; }
        }

        public TParent GetOwner<TParent>() where TParent : ElementBase
        {
            for (ElementBase scan = this; scan != null; scan = scan._container)
            {
                TParent result = scan as TParent;
                if (result != null)
                    return result;
            }
            return null;
        }

        [Category("Advanced"), Browsable(false), Description("Indicates that object is deleted or located in deleted parent")]
        public bool GlobalDeleted
        {
            get
            {
                return ((_container != null) && _container.GlobalDeleted) || Deleted;
            }
        }

        [Category("Design"), DisplayName("GUID"), Description("Global object path")]
        public virtual string GlobalPath
        {
            get
            {
                if (GUID != null)
                    return GUID.ToString();

                string result = "/" + _name;

                if (_container != null)
                    result = _container.GlobalPath + result;

                return result;
            }
        }

        private Guid? _guid = null;

        [Serialization(GUIDName)]
        [Browsable(false)]
        public Guid? GUID
        {
            get
            {
                return _guid;
            }
            protected internal set
            {
                if (_guid != value)
                {
                    LogChange(_guid, value);

                    if (ElementSpace != null)
                    {
                        if (_guid != null)
                            ElementSpace.UnregisterElement(this);

                        _guid = value;

                        if (_guid != null)
                            ElementSpace.RegisterElement(this);
                    }
                    else
                        _guid = value;
                }
            }
        }

        public string GetSerializationName(PropertyInfo propInfo)
        {
            string SerialName = propInfo.Name;
            SerializationAttribute Attr = Attribute.GetCustomAttribute(propInfo, typeof(SerializationAttribute)) as SerializationAttribute;

            if ((Attr != null) && (!string.IsNullOrEmpty(Attr.Name)))
                SerialName = Attr.Name;

            return SerialName;
        }

        protected internal void LogChange(object oldValue, object newValue)
        {
            StackTrace st = new StackTrace();
            StackFrame sf = st.GetFrame(1);
            MethodBase mb = sf.GetMethod();
            string MethodName = mb.Name;

            if (MethodName.StartsWith("set_"))
            {
                string PropertyName = MethodName.Substring(4);
                PropertyInfo propInfo = this.GetType().GetProperty(PropertyName);

                LogChange(GetSerializationName(propInfo), oldValue, newValue);
            }
        }

        protected internal void LogChange(string Name, object key, object orgValue, object newValue)
        {
            if (key == null)
                LogChange(Name, orgValue, newValue);
            else
                LogChange(string.Format("{0}-{1}", Name, key), orgValue, newValue);
        }

        protected internal void LogChange(string Name, object orgValue, object newValue)
        {
            if ((_state & ElementState.New) != 0)
                UpdateState(ElementState.ThisModified);
            else
                if (TrackChanges())
                {
                    object TempValue;
                    if (Changes.TryGetValue(Name, out TempValue) && (TempValue == newValue))
                    {
                        Changes.Remove(Name);
                        _state &= ~ElementState.ThisModified;
                    }
                    else
                        Changes[Name] = orgValue;
                }
        }

        private string _name;
        [Serialization(ElementName)]
        [Category("Design"), ParenthesizePropertyName(true), Description("Specifies the name of the object used in expressions (if applicable)"), RefreshProperties(RefreshProperties.Repaint)]
        public virtual string Name
        {
            get
            {
                return _name;
            }
            set
            {
                if (!String.Equals(_name, value))
                {
                    ValidateName(value);

                    LogChange(_name, value);

                    _name = value;

                    NameChanged();
                }
            }
        }

        protected virtual void NameChanged()
        { }

        protected internal virtual void OnOwnerChanging(ElementBase oldOwner, ElementBase newOwner)
        {
            LogChange(ContainerName, oldOwner, null);
        }

        protected internal virtual void OnOwnerChanged()
        {
            SetElementSpace(GetOwner<ElementSpace>());
            LogChange(ContainerName, null, _container);
            UpdateState(0);
        }

        protected virtual string PermanentName
        {
            get
            {
                return null;
            }
        }

        protected internal void Register()
        {
            if (_guid != null)
                ElementSpace.RegisterElement(this);

            foreach (ElementBase element in this)
                element.Register();
        }

        protected virtual bool RemoveElement(ElementBase element)
        {
            return containerElements.Remove(element);
        }

        protected void SetElementSpace(ElementSpace value)
        {
            if (ElementSpace != value)
            {
                if ((_guid != null) && (ElementSpace != null))
                    ElementSpace.UnregisterElement(this);

                _elementSpace = value;

                if ((_guid != null) && (value != null))
                    value.RegisterElement(this);

                foreach (ElementBase element in this)
                    element.SetElementSpace(value);
            }
        }

        public override string ToString()
        {
            return DisplayText ?? base.ToString();
        }

        protected internal bool TrackChanges()
        {
            if (ElementSpace == null)
                return false;

            if (!ElementSpace.ChangesTracked)
                return false;

            UpdateState(ElementState.ThisModified);

            return true;
        }

        protected internal void Unregister()
        {
            if (_guid != null)
                ElementSpace.UnregisterElement(this);

            foreach (ElementBase element in this)
                element.Unregister();
        }


        protected void UpdateState(ElementState addedFlags)
        {
            if ((_container != null) && ((_container._state & ElementState.ChildrenModified) == 0))
                _container.UpdateState(ElementState.ChildrenModified);

            _state |= addedFlags;
        }

        protected virtual void ValidateName(string value)
        {
            string defName = PermanentName;

            if ((defName != null) && (value != defName))
                throw new Exception("Cannot change name for permanent named object");

            if (value == null)
                throw new NullReferenceException();

            if ((value.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0) || (value.IndexOfAny(Path.GetInvalidPathChars()) >= 0))
                throw new Exception("Invalid characters in Name");
        }

        public virtual void Write(Writer writer)
        {
            if (Deleted && !writer.WriteChanges)
                return;

            if (writer.IsElementFiltered(this))
                return;// null;

            string elementName = ElementTypesCache.GetName(this.GetType());

            if (string.IsNullOrEmpty(elementName))
                return;

            writer.SaveContext();
            bool prevId = writer.SetIdentifyByID(true);
            try
            {
                SerializationContext ctx = writer.NewElement(this);

                if (writer.WriteChanges)
                {
                    if (writer._completeWrite || ElementChanged)
                        DoWrite(writer);

                    DoWriteContainerElements(writer);

                    writer.FixCurrentElement();
                }
                else
                {
                    DoWrite(writer);
                    DoWriteContainerElements(writer);
                }
            }
            finally
            {
                writer.SetIdentifyByID(prevId);
                writer.RestoreContext();
            }
        }

        #region IEnumerable Members

        public IEnumerator GetEnumerator()
        {
            return containerElements.GetEnumerator();
        }

        #endregion
    }
}

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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


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