Click here to Skip to main content
15,886,110 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;
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)
                throw new Exception("Cannot change a fix name");

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

            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.


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