<PackageReference Include="Castle.Core" Version="3.2.0" />

XmlAdapter

using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Xml; using System.Xml.Serialization; namespace Castle.Components.DictionaryAdapter.Xml { public class XmlAdapter : DictionaryBehaviorAttribute, IDictionaryInitializer, IDictionaryPropertyGetter, IDictionaryPropertySetter, IDictionaryBehavior, IDictionaryCreateStrategy, IDictionaryCopyStrategy, IDictionaryReferenceManager, IVirtual, IXmlNodeSource { private IXmlNode node; private object source; private XmlReferenceManager references; private XmlMetadata primaryXmlMeta; private Dictionary<Type, XmlMetadata> secondaryXmlMetas; private readonly bool isRoot; public bool IsReal { get { if (node != null) return node.IsReal; return false; } } public IXmlNode Node => node; internal XmlReferenceManager References => references; public event EventHandler Realized; public XmlAdapter() : this(new XmlDocument()) { } public XmlAdapter(XmlNode node) { if (node == null) throw Error.ArgumentNull("node"); source = node; isRoot = true; } public XmlAdapter(IXmlNode node, XmlReferenceManager references) { if (node == null) throw Error.ArgumentNull("node"); if (references == null) throw Error.ArgumentNull("references"); this.node = node; this.references = references; } object IDictionaryCreateStrategy.Create(IDictionaryAdapter parent, Type type, IDictionary dictionary) { XmlAdapter adapter = new XmlAdapter(new XmlDocument()); return parent.CreateChildAdapter(type, adapter, dictionary); } void IDictionaryInitializer.Initialize(IDictionaryAdapter dictionaryAdapter, object[] behaviors) { DictionaryAdapterMeta meta = dictionaryAdapter.Meta; if (primaryXmlMeta == null) InitializePrimary(meta, dictionaryAdapter); else InitializeSecondary(meta); InitializeBaseTypes(meta); InitializeStrategies(dictionaryAdapter); InitializeReference(dictionaryAdapter); } private void InitializePrimary(DictionaryAdapterMeta meta, IDictionaryAdapter dictionaryAdapter) { RequireXmlMeta(meta); primaryXmlMeta = meta.GetXmlMeta(); if (node == null) node = GetBaseNode(); if (!node.IsReal) node.Realized += HandleNodeRealized; if (references == null) references = new XmlReferenceManager(node, DefaultXmlReferenceFormat.Instance); InitializeReference(this); } private void InitializeSecondary(DictionaryAdapterMeta meta) { AddSecondaryXmlMeta(meta); } private void InitializeBaseTypes(DictionaryAdapterMeta meta) { Type[] interfaces = meta.Type.GetInterfaces(); foreach (Type type in interfaces) { string namespace = type.Namespace; if (!(namespace == "Castle.Components.DictionaryAdapter") && !(namespace == "System.ComponentModel")) { DictionaryAdapterMeta adapterMeta = meta.GetAdapterMeta(type); AddSecondaryXmlMeta(adapterMeta); } } } private void InitializeStrategies(IDictionaryAdapter dictionaryAdapter) { DictionaryAdapterInstance this = dictionaryAdapter.This; if (this.CreateStrategy == null) { this.CreateStrategy = this; this.AddCopyStrategy(this); } } private void InitializeReference(object value) { if (isRoot) references.Add(node, this, value, true); } private void AddSecondaryXmlMeta(DictionaryAdapterMeta meta) { if (secondaryXmlMetas == null) secondaryXmlMetas = new Dictionary<Type, XmlMetadata>(); else if (secondaryXmlMetas.ContainsKey(meta.Type)) { return; } RequireXmlMeta(meta); secondaryXmlMetas[meta.Type] = meta.GetXmlMeta(); } private static void RequireXmlMeta(DictionaryAdapterMeta meta) { if (!meta.HasXmlMeta()) throw Error.XmlMetadataNotAvailable(meta.Type); } bool IDictionaryCopyStrategy.Copy(IDictionaryAdapter source, IDictionaryAdapter target, ref Func<PropertyDescriptor, bool> selector) { if (selector == null) selector = ((PropertyDescriptor property) => HasProperty(property.PropertyName, source)); return false; } object IDictionaryPropertyGetter.GetPropertyValue(IDictionaryAdapter dictionaryAdapter, string key, object storedValue, PropertyDescriptor property, bool ifExists) { if (TryGetAccessor(key, property, null != storedValue, out XmlAccessor accessor)) { storedValue = accessor.GetPropertyValue(node, dictionaryAdapter, references, !ifExists); if (storedValue != null) { AttachObservers(storedValue, dictionaryAdapter, property); dictionaryAdapter.StoreProperty(property, key, storedValue); } } return storedValue; } bool IDictionaryPropertySetter.SetPropertyValue(IDictionaryAdapter dictionaryAdapter, string key, ref object value, PropertyDescriptor property) { if (TryGetAccessor(key, property, false, out XmlAccessor accessor)) { if (value != null && dictionaryAdapter.ShouldClearProperty(property, value)) value = null; object oldValue = dictionaryAdapter.ReadProperty(key); accessor.SetPropertyValue(node, dictionaryAdapter, references, oldValue, ref value); } return true; } private static string EnsureKey(string key, PropertyDescriptor property) { if (!string.IsNullOrEmpty(key)) return key; return property.PropertyName; } private IXmlNode GetBaseNode() { IXmlNode sourceNode = GetSourceNode(); if (sourceNode.IsElement) return sourceNode; if (sourceNode.IsAttribute) throw Error.NotSupported(); IXmlCursor xmlCursor = primaryXmlMeta.SelectBase(sourceNode); if (!xmlCursor.MoveNext()) return xmlCursor; return xmlCursor.Save(); } private IXmlNode GetSourceNode() { XmlNode xmlNode = source as XmlNode; if (xmlNode != null) return new SysXmlNode(xmlNode, primaryXmlMeta.ClrType, primaryXmlMeta.Context); throw Error.NotSupported(); } private bool TryGetAccessor(string key, PropertyDescriptor property, bool requireVolatile, out XmlAccessor accessor) { accessor = (property.HasAccessor() ? property.GetAccessor() : CreateAccessor(key, property)); if (accessor.IsIgnored) return Try.Failure(out accessor); if (requireVolatile && !accessor.IsVolatile) return Try.Failure(out accessor); return true; } private XmlAccessor CreateAccessor(string key, PropertyDescriptor property) { XmlAccessor accessor = null; bool isVolatile = false; bool isReference = false; if (string.IsNullOrEmpty(key)) accessor = CreateAccessor(key, property, XmlSelfAccessor.Factory); object[] annotations = property.Annotations; foreach (object behavior in annotations) { if (IsIgnoreBehavior(behavior)) return XmlIgnoreBehaviorAccessor.Instance; if (IsVolatileBehavior(behavior)) isVolatile = true; else if (IsReferenceBehavior(behavior)) { isReference = true; } else { TryApplyBehavior(key, property, behavior, ref accessor); } } if (accessor == null) accessor = CreateAccessor(key, property, XmlDefaultBehaviorAccessor.Factory); accessor.ConfigureVolatile(isVolatile); accessor.ConfigureReference(isReference); accessor.Prepare(); property.SetAccessor(accessor); return accessor; } private bool TryApplyBehavior(string key, PropertyDescriptor property, object behavior, ref XmlAccessor accessor) { if (!TryApplyBehavior<XmlElementAttribute, XmlElementBehaviorAccessor>(key, property, behavior, ref accessor, XmlElementBehaviorAccessor.Factory) && !TryApplyBehavior<XmlArrayAttribute, XmlArrayBehaviorAccessor>(key, property, behavior, ref accessor, XmlArrayBehaviorAccessor.Factory) && !TryApplyBehavior<XmlArrayItemAttribute, XmlArrayBehaviorAccessor>(key, property, behavior, ref accessor, XmlArrayBehaviorAccessor.Factory) && !TryApplyBehavior<XmlAttributeAttribute, XmlAttributeBehaviorAccessor>(key, property, behavior, ref accessor, XmlAttributeBehaviorAccessor.Factory) && !TryApplyBehavior<XPathAttribute, XPathBehaviorAccessor>(key, property, behavior, ref accessor, XPathBehaviorAccessor.Factory) && !TryApplyBehavior<XPathVariableAttribute, XPathBehaviorAccessor>(key, property, behavior, ref accessor, XPathBehaviorAccessor.Factory)) return TryApplyBehavior<XPathFunctionAttribute, XPathBehaviorAccessor>(key, property, behavior, ref accessor, XPathBehaviorAccessor.Factory); return true; } private bool TryApplyBehavior<TBehavior, TAccessor>(string key, PropertyDescriptor property, object behavior, ref XmlAccessor accessor, XmlAccessorFactory<TAccessor> factory) where TBehavior : class where TAccessor : XmlAccessor, IConfigurable<TBehavior> { TBehavior val = behavior as TBehavior; if (val == null) return false; if (accessor == null) accessor = CreateAccessor(key, property, factory); TAccessor val2 = accessor as TAccessor; if (val2 == null) throw Error.AttributeConflict(key); ((IConfigurable<TBehavior>)val2).Configure(val); return true; } private TAccessor CreateAccessor<TAccessor>(string key, PropertyDescriptor property, XmlAccessorFactory<TAccessor> factory) where TAccessor : XmlAccessor { XmlMetadata xmlMetadata = GetXmlMetadata(property.Property.DeclaringType); TAccessor result = factory(key, property.PropertyType, xmlMetadata.Context); if (xmlMetadata.IsNullable.HasValue) result.ConfigureNillable(xmlMetadata.IsNullable.Value); if (xmlMetadata.IsReference.HasValue) result.ConfigureReference(xmlMetadata.IsReference.Value); return result; } private XmlMetadata GetXmlMetadata(Type type) { if (type == primaryXmlMeta.ClrType) return primaryXmlMeta; if (secondaryXmlMetas.TryGetValue(type, out XmlMetadata value)) return value; throw Error.XmlMetadataNotAvailable(type); } private static bool IsIgnoreBehavior(object behavior) { return behavior is XmlIgnoreAttribute; } private static bool IsVolatileBehavior(object behavior) { return behavior is VolatileAttribute; } private static bool IsReferenceBehavior(object behavior) { return behavior is ReferenceAttribute; } void IVirtual.Realize() { throw new NotSupportedException("XmlAdapter does not support realization ssvia IVirtual.Realize()."); } protected virtual void OnRealized() { if (this.Realized != null) this.Realized(this, EventArgs.Empty); } private void HandleNodeRealized(object sender, EventArgs e) { OnRealized(); } private void AttachObservers(object value, IDictionaryAdapter dictionaryAdapter, PropertyDescriptor property) { IBindingList bindingList = value as IBindingList; if (bindingList != null) bindingList.ListChanged += delegate(object s, ListChangedEventArgs e) { HandleListChanged(s, e, dictionaryAdapter, property); }; } private void HandleListChanged(object value, ListChangedEventArgs args, IDictionaryAdapter dictionaryAdapter, PropertyDescriptor property) { ListChangedType listChangedType = args.ListChangedType; if ((listChangedType == ListChangedType.ItemAdded || listChangedType == ListChangedType.ItemDeleted || listChangedType == ListChangedType.ItemMoved || listChangedType == ListChangedType.Reset) && dictionaryAdapter.ShouldClearProperty(property, value)) { value = null; dictionaryAdapter.SetProperty(property.PropertyName, ref value); } } bool IDictionaryReferenceManager.IsReferenceProperty(IDictionaryAdapter dictionaryAdapter, string propertyName) { XmlAdapter xmlAdapter = For(dictionaryAdapter, false); if (xmlAdapter == null) return false; DictionaryAdapterInstance this = dictionaryAdapter.This; if (!this.Properties.TryGetValue(propertyName, out PropertyDescriptor value)) return false; string key = value.GetKey(dictionaryAdapter, propertyName, this.Descriptor); if (xmlAdapter.TryGetAccessor(key, value, false, out XmlAccessor accessor)) return accessor.IsReference; return false; } bool IDictionaryReferenceManager.TryGetReference(object keyObject, out object inGraphObject) { return references.TryGet(keyObject, out inGraphObject); } void IDictionaryReferenceManager.AddReference(object keyObject, object relatedObject, bool isInGraph) { references.Add(null, keyObject, relatedObject, isInGraph); } public override IDictionaryBehavior Copy() { return null; } public static XmlAdapter For(object obj) { return For(obj, true); } public static XmlAdapter For(object obj, bool required) { if (obj == null) { if (!required) return null; throw Error.ArgumentNull("obj"); } IDictionaryAdapter dictionaryAdapter = obj as IDictionaryAdapter; if (dictionaryAdapter == null) { if (!required) return null; throw Error.NotDictionaryAdapter("obj"); } PropertyDescriptor descriptor = dictionaryAdapter.This.Descriptor; if (descriptor == null) { if (!required) return null; throw Error.NoInstanceDescriptor("obj"); } IEnumerable<IDictionaryPropertyGetter> getters = descriptor.Getters; if (getters == null) { if (!required) return null; throw Error.NoXmlAdapter("obj"); } foreach (IDictionaryPropertyGetter item in getters) { XmlAdapter result; if ((result = (item as XmlAdapter)) != null) return result; } if (!required) return null; throw Error.NoXmlAdapter("obj"); } public static bool IsPropertyDefined(string propertyName, IDictionaryAdapter dictionaryAdapter) { return For(dictionaryAdapter, true)?.HasProperty(propertyName, dictionaryAdapter) ?? false; } public bool HasProperty(string propertyName, IDictionaryAdapter dictionaryAdapter) { string key = dictionaryAdapter.GetKey(propertyName); if (key == null) return false; if (dictionaryAdapter.This.Properties.TryGetValue(propertyName, out PropertyDescriptor value) && TryGetAccessor(key, value, false, out XmlAccessor accessor)) return accessor.IsPropertyDefined(node); return false; } } }