Click here to Skip to main content
15,894,343 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.3K   124   10  
How to customize an application using XML fragments
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Text;
using Bulasoft.Common.Elements;
using Bulasoft.Common.Serialization;
using Bulasoft.Common.Utils;

namespace Bulasoft.Common.Elements
{
    [Flags]
    public enum ElementState
    {
        New = 0x01,
        ThisModified = 0x02,
        ChildrenModified = 0x04,
        Changed = New | ThisModified | ChildrenModified,

        MaskChanges = New | ThisModified | ChildrenModified,

        Public = 0x00,
        ElementReadOnly = 0x10,
        ElementAndChildReadOnly = 0x20,
        Private = 0x40,

        MaskAccess = Public | ElementReadOnly | ElementAndChildReadOnly | Private
    }

    public enum ElementAccess
    {
        Public,
        ElementReadOnly,
        ElementAndChildrenReadOnly,
        Private
    }

    [ToolboxItem(false)]
    public class Element : DefaultElement, IElement, IEnumerable, IComparer<IElement>, IComponent, ISite, ICustomTypeDescriptor, IComparable<IElement>, IComparable, INotifyPropertyChanged 
    {
        public const string AuthorName = "Author";
        public const string ContainerName = "Container";
        public const string DeletedName = "Deleted";
        public const string DescriptionName = "Description";
        public const string ReferenceName = "Ref";
        public const string ElementAccessName = "ElementAccess";

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

        public virtual IElement Find(Guid id)
        {
            return containerElements.Find(delegate(IElement elem) { return elem.ID == id; });
        }

        private string _author;
        [Serialization(AuthorName)]
        [Category("Design"), ReadOnly(true), Description("Author name")]
        public string Author
        {
            get { return _author; }
            set
            {
                if (value != _author)
                {
                    LogChange(AuthorName, _author, value);
                    _author = value;
                    NotifyPropertyChange(AuthorName);
                }
            }
        }

        [Browsable(false)]
        IElement IElement.Container
        {
            get { return base.Container; }
            set
            {
                if (Container != value)
                {
                    ValidateNewContainer(value);
                    if (Container != null)
                        Container.RemoveElement(this);

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

                    base.Container = value;

                    SetElementSpace(GetContainer<ElementSpace>());
                }
            }
        }

        [Browsable(false)]
        public new Element Container
        {
            get { return base.Container as Element; }
        }

        public virtual void Validate(Logger log) { }

        public virtual bool CanDelete(out Logger log)
        {
            log = new Logger();
            return true;
        }

        protected virtual void ValidateNewContainer(IElement container)
        { }

        [Browsable(false)]
        public virtual IEnumerable ImplicitList
        {
            get { return null; }
        }

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

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

                    LogChange(DeletedName, oldValue, value);

                    if (value)
                        ElementSpace.DeleteElement(this);
                    else
                        ElementSpace.UndeleteElement(this);

                    NotifyPropertyChange(DeletedName);
                }
            }
        }

        private string _description;
        [Serialization(DescriptionName)]
        [Category("Design")] 
        [Description("Description of the object")]
        public virtual string Description
        {
            get { return _description; }
            set
            {
                if (_description != value)
                {
                    LogChange(DescriptionName, _description, value);
                    _description = value;
                    NotifyPropertyChange(DescriptionName);
                }
            }
        }

        [Browsable(false)]
        public virtual string QualifiedName
        {
            get
            {
                if (this is ElementSpace)
                    return Name;
                else
                {
                    StringBuilder sb = new StringBuilder();
                    IElement el = this;

                    while ((el != null) && !(el is ElementSpace))
                    {
                        sb.Insert(0, "." + el.Name);
                        el = el.Container;
                    }
                    sb.Remove(0, 1);

                    return sb.ToString();
                }
            }
        }

        [Serialization()]
        [Browsable(false)] 
        [Category("Design")] 
        [DefaultValue(ElementAccess.Public)]
        public ElementAccess ElementAccess
        {
            get
            {
                switch (State & ElementState.MaskAccess)
                {
                    case ElementState.Private:
                        return ElementAccess.Private;

                    case ElementState.ElementAndChildReadOnly:
                        return ElementAccess.ElementAndChildrenReadOnly;

                    case ElementState.ElementReadOnly:
                        return ElementAccess.ElementReadOnly;

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

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

                        case ElementAccess.ElementReadOnly:
                            State = (State & ~ElementState.MaskAccess) | ElementState.ElementReadOnly;
                            break;

                        case ElementAccess.ElementAndChildrenReadOnly:
                            State = (State & ~ElementState.MaskAccess) | ElementState.ElementAndChildReadOnly;
                            break;

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

        [Browsable(false)]
        public bool IsPrivate
        {
            get { return ElementAccess == ElementAccess.Private; }
        }

        private ElementSpace _elementSpace;
        [Browsable(false)]
        public ElementSpace ElementSpace
        {
            get { return _elementSpace; }
        }

        public TContainer GetContainer<TContainer>() where TContainer : Element
        {
            IElement container = this;
            while (container != null)
            {
                TContainer result = container as TContainer;
                if (result != null)
                    return result;
                else
                    container = container.Container;
            }
            return null;
        }

        [Browsable(false)]
        public bool IsToBeDeleted
        {
            get { return (IsDeleted || ((Container != null) &&Container.IsToBeDeleted)); }
        }

        [Category("Design"), DisplayName("ID"), Description("Object identification")]
        public virtual string QualifiedID
        {
            get
            {
                if (ID != null)
                    return ID.ToString();
                else
                    return Name;
            }
        }

        [Serialization(IDName)]
        [Browsable(false)]
        public override Guid? ID
        {
            get { return base.ID; }
            set
            {
                if (ID != value)
                {
                    LogChange(IDName, ID, value);

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

                        base.ID = value;

                        if (ID != null)
                            ElementSpace.RegisterElement(this);
                    }
                    else
                        base.ID = value;
                    NotifyPropertyChange(IDName);
                }
            }
        }

        [Browsable(false)]
        public bool IsNew
        {
            get { return (State & ElementState.New) != 0; }
        }

        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)
        {
            if (!TrackChanges())
                return;
            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 = GetType().GetProperty(PropertyName);

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

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

        public event EventHandler<ElementArgs> OnElemenChanged;

        protected override void LogChange(string propertyName, object orgValue, object newValue)
        {
            if (OnElemenChanged != null)
                OnElemenChanged(this, new ElementArgs(this, propertyName));            

            base.LogChange(propertyName, orgValue, newValue);
        }

        public void NotifyPropertyChange(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        [Serialization(ElementName, SerializationSettings.SerializeAsAttribute)]
        [Category("Design")]
        public override string Name
        {
            get { return base.Name; }
            set
            {
                if (!String.Equals(Name, value))
                {
                    ValidateName(value);
                    LogChange(ElementName, Name, value);
                    base.Name = value;
                    NotifyPropertyChange(ElementName);
                    NameChanged();
                }
            }
        }

        protected virtual void NameChanged()
        { }

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

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

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

                _elementSpace = value;

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

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

        protected override bool TrackChanges()
        {
            if (ElementSpace != null)
                return ElementSpace.ChangesTracked;
            else
                return base.TrackChanges();
        }

        public override void AcceptChanges()
        {
            if (OnElemenChanged != null)
                OnElemenChanged(this, null);

            State &= ~ElementState.MaskChanges;            

            foreach (Element element in this)
                element.AcceptChanges();

            base.AcceptChanges();
        }

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

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

        protected override void MarkModified(ElementState addedFlags)
        {
            if ((base.Container != null) && ((base.Container.State & ElementState.ChildrenModified) == 0))
            {
                if (OnElemenChanged != null)
                    OnElemenChanged(this, null);

                if (base.Container is Element)
                    ((Element) base.Container).MarkModified(ElementState.ChildrenModified);
            }

            State |= addedFlags;
        }

        protected virtual void ValidateName(string value)
        {
            if ((FixedName != null) && (value != FixedName))
                throw new Exception("Cannot change a fix name");

            if (string.IsNullOrEmpty(value))
                throw new Exception("Empty name is not valid");

            if (!(Char.IsLetter(value[0]) || (value[0] == '_')))
                throw new Exception(string.Format("Invalid character \"{0}\" in name at position 1", value[0]));

            for (int i = 0; i < value.Length; i++)
            {
                char ch = value[i];
                if (!(Char.IsLetterOrDigit(ch) || (ch == '_')))
                    throw new Exception(string.Format("Invalid character \"{0}\" in name at position {1}", ch, i + 1));
            }

            if ((Container != null) &&
                    ReflectionHelper.ImplementsGenericInterface(Container.GetType(), typeof(ICollection<>)))
                foreach (Element element in Container)
                {
                    if ((element != this) && (element.Name == value))
                        throw new Exception(string.Format("Name {0} alreday used", element.Name));
                }
        }

        public int Compare(IElement x, IElement y)
        {
            string xQualifiedName = null;
            string yQualifiedName = null;
            Element xElement = x as Element;
            if (xElement != null)
                xQualifiedName = xElement.QualifiedName;
            Element yElement = y as Element;
            if (yElement != null)
                yQualifiedName = yElement.QualifiedName;

            return string.Compare(xQualifiedName, yQualifiedName, true);
        }

        public virtual void Sort()
        {
            containerElements.Sort(this);
            foreach (Element element in this)
                element.Sort();
        }

        #region IComponent Members

        public event EventHandler Disposed;

        [Browsable(false)]
        public ISite Site
        {
            get
            {
                return this;
            }
            set
            {
                throw new Exception("The method or operation is not implemented.");
            }
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                lock (this)
                {
                    if (Container != null)
                        Container.RemoveElement(this);

                    foreach (Element element in this)
                        element.Dispose();

                    if (Disposed != null)
                        Disposed(this, EventArgs.Empty);
                }
            }
        }

        #endregion

        #region ISite Members

        [Browsable(false)]
        public IComponent Component
        {
            get { return this; }
        }

        [Browsable(false)]
        IContainer ISite.Container
        {
            get { return null; }
        }

        [Browsable(false)]
        public bool DesignMode
        {
            get { return true; }
        }

        #endregion

        #region IServiceProvider Members

        public object GetService(Type serviceType)
        {
            if (serviceType == typeof(ISite))
                return this;
            else
                return null;
        }

        #endregion

        #region ICustomTypeDescriptor Members

        AttributeCollection ICustomTypeDescriptor.GetAttributes()
        {
            return TypeDescriptor.GetAttributes(this, true);
        }

        string ICustomTypeDescriptor.GetClassName()
        {
            return TypeDescriptor.GetClassName(this, true);
        }

        string ICustomTypeDescriptor.GetComponentName()
        {
            return TypeDescriptor.GetComponentName(this, true);
        }

        TypeConverter ICustomTypeDescriptor.GetConverter()
        {
            return TypeDescriptor.GetConverter(this, true);
        }

        EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
        {
            return TypeDescriptor.GetDefaultEvent(this, true);
        }

        PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
        {
            return TypeDescriptor.GetDefaultProperty(this, true);
        }

        object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
        {
            return TypeDescriptor.GetEditor(this, editorBaseType, true);
        }

        EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
        {
            return TypeDescriptor.GetEvents(this, attributes, true);
        }

        EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
        {
            return TypeDescriptor.GetEvents(this, true);
        }

        PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
        {
            return Filter(TypeDescriptor.GetProperties(this, attributes, true));
        }

        PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
        {
            return Filter(TypeDescriptor.GetProperties(this, true));
        }

        protected virtual PropertyDescriptorCollection Filter(PropertyDescriptorCollection original)
        {
            return original;
        }

        protected virtual PropertyDescriptorCollection Filter(PropertyDescriptorCollection original, List<PropertyDescriptor> forbiddenProperties)
        {
            if ((forbiddenProperties == null) || (forbiddenProperties.Count == 0))
                return original;
            else
            {

                List<PropertyDescriptor> result = new List<PropertyDescriptor>();
                foreach (PropertyDescriptor pd in original)
                {
                    if (forbiddenProperties.IndexOf(pd) == -1)
                        result.Add(pd);
                }
                return new PropertyDescriptorCollection(result.ToArray());
            }
        }

        object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
        {
            return this;
        }

        #endregion

        #region IComparable<IElement> Members

        public int CompareTo(IElement other)
        {
            return Compare(this, other);
        }

        #endregion

        #region IComparable Members

        public int CompareTo(object obj)
        {
            return CompareTo(obj as IElement);
        }

        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #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