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
}
}