XmlMetadata
public class XmlMetadata : IXmlKnownType, IXmlIdentity, IXmlKnownTypeMap, IXmlIncludedType, IXmlIncludedTypeMap
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;
namespace Castle.Components.DictionaryAdapter.Xml
{
public class XmlMetadata : IXmlKnownType, IXmlIdentity, IXmlKnownTypeMap, IXmlIncludedType, IXmlIncludedTypeMap
{
private const CursorFlags RootFlags = CursorFlags.Elements | CursorFlags.Mutable;
private readonly Type clrType;
private readonly bool? qualified;
private readonly bool? isNullable;
private readonly bool? isReference;
private readonly string rootLocalName;
private readonly string rootNamespaceUri;
private readonly string childNamespaceUri;
private readonly string typeLocalName;
private readonly string typeNamespaceUri;
private readonly HashSet<string> reservedNamespaceUris;
private List<Type> pendingIncludes;
private readonly XmlIncludedTypeSet includedTypes;
private readonly XmlContext context;
private readonly DictionaryAdapterMeta source;
private readonly CompiledXPath path;
protected static readonly StringComparer NameComparer = StringComparer.OrdinalIgnoreCase;
public Type ClrType => clrType;
public bool? Qualified => qualified;
public bool? IsNullable => isNullable;
public bool? IsReference => isReference;
public XmlName Name => new XmlName(rootLocalName, rootNamespaceUri);
public XmlName XsiType => new XmlName(typeLocalName, typeNamespaceUri);
XmlName IXmlIdentity.XsiType {
get {
return XmlName.Empty;
}
}
public string ChildNamespaceUri => childNamespaceUri;
public IEnumerable<string> ReservedNamespaceUris => reservedNamespaceUris.ToArray();
public XmlIncludedTypeSet IncludedTypes {
get {
ProcessPendingIncludes();
return includedTypes;
}
}
public IXmlContext Context => context;
public CompiledXPath Path => path;
IXmlKnownType IXmlKnownTypeMap.Default {
get {
return this;
}
}
IXmlIncludedType IXmlIncludedTypeMap.Default {
get {
return this;
}
}
public XmlMetadata(DictionaryAdapterMeta meta, IEnumerable<string> reservedNamespaceUris)
{
if (meta == null)
throw Error.ArgumentNull("meta");
if (reservedNamespaceUris == null)
throw Error.ArgumentNull("reservedNamespaceUris");
source = meta;
clrType = meta.Type;
context = new XmlContext(this);
includedTypes = new XmlIncludedTypeSet();
this.reservedNamespaceUris = ((reservedNamespaceUris as HashSet<string>) ?? new HashSet<string>(reservedNamespaceUris));
XmlRootAttribute result = null;
XmlTypeAttribute result2 = null;
XmlDefaultsAttribute result3 = null;
XmlIncludeAttribute result4 = null;
XmlNamespaceAttribute result5 = null;
ReferenceAttribute result6 = null;
XPathAttribute result7 = null;
XPathVariableAttribute result8 = null;
XPathFunctionAttribute result9 = null;
object[] behaviors = meta.Behaviors;
foreach (object obj in behaviors) {
if (!TryCast(obj, ref result) && !TryCast(obj, ref result2) && !TryCast(obj, ref result3)) {
if (TryCast(obj, ref result4))
AddPendingInclude(result4);
else if (TryCast(obj, ref result5)) {
context.AddNamespace(result5);
} else if (!TryCast(obj, ref result6) && !TryCast(obj, ref result7)) {
if (TryCast(obj, ref result8))
context.AddVariable(result8);
else if (TryCast(obj, ref result9)) {
context.AddFunction(result9);
}
}
}
}
if (result3 != null) {
qualified = result3.Qualified;
isNullable = result3.IsNullable;
}
if (result6 != null)
isReference = true;
typeLocalName = XmlConvert.EncodeLocalName(((!meta.HasXmlType()) ? null : meta.GetXmlType().NonEmpty()) ?? result2?.TypeName.NonEmpty() ?? GetDefaultTypeLocalName(clrType));
rootLocalName = XmlConvert.EncodeLocalName(result?.ElementName.NonEmpty() ?? typeLocalName);
typeNamespaceUri = result2?.Namespace;
rootNamespaceUri = result?.Namespace;
childNamespaceUri = (typeNamespaceUri ?? rootNamespaceUri);
if (result7 != null) {
path = result7.GetPath;
path.SetContext(context);
}
}
public bool IsReservedNamespaceUri(string namespaceUri)
{
return reservedNamespaceUris.Contains(namespaceUri);
}
public IXmlCursor SelectBase(IXmlNode node)
{
if (path != null)
return node.Select(path, this, context, CursorFlags.Elements | CursorFlags.Mutable);
return node.SelectChildren(this, context, CursorFlags.Elements | CursorFlags.Mutable);
}
private bool IsMatch(IXmlIdentity xmlIdentity)
{
XmlName name = xmlIdentity.Name;
if (NameComparer.Equals(rootLocalName, name.LocalName)) {
if (rootNamespaceUri != null)
return NameComparer.Equals(rootNamespaceUri, name.NamespaceUri);
return true;
}
return false;
}
private bool IsMatch(Type clrType)
{
return clrType == this.clrType;
}
public bool TryGet(IXmlIdentity xmlIdentity, out IXmlKnownType knownType)
{
if (!IsMatch(xmlIdentity))
return Try.Failure(out knownType);
return Try.Success(out knownType, this);
}
public bool TryGet(Type clrType, out IXmlKnownType knownType)
{
if (!IsMatch(clrType))
return Try.Failure(out knownType);
return Try.Success(out knownType, this);
}
public bool TryGet(XmlName xsiType, out IXmlIncludedType includedType)
{
if (!(xsiType == XmlName.Empty) && !(xsiType == XsiType))
return Try.Failure(out includedType);
return Try.Success(out includedType, this);
}
public bool TryGet(Type clrType, out IXmlIncludedType includedType)
{
if (!(clrType == this.clrType))
return Try.Failure(out includedType);
return Try.Success(out includedType, this);
}
private void AddPendingInclude(XmlIncludeAttribute attribute)
{
if (pendingIncludes == null)
pendingIncludes = new List<Type>();
pendingIncludes.Add(attribute.Type);
}
private void ProcessPendingIncludes()
{
List<Type> list = pendingIncludes;
pendingIncludes = null;
if (list != null) {
foreach (Type item in list) {
XmlName defaultXsiType = GetDefaultXsiType(item);
XmlIncludedType includedType = new XmlIncludedType(defaultXsiType, item);
includedTypes.Add(includedType);
}
}
}
public XmlName GetDefaultXsiType(Type clrType)
{
if (clrType == this.clrType)
return XsiType;
if (includedTypes.TryGet(clrType, out IXmlIncludedType includedType))
return includedType.XsiType;
switch (XmlTypeSerializer.For(clrType).Kind) {
case XmlTypeKind.Complex:
if (System.Reflection.IntrospectionExtensions.GetTypeInfo(clrType).IsInterface)
return GetXmlMetadata(clrType).XsiType;
break;
case XmlTypeKind.Collection: {
Type collectionItemType = clrType.GetCollectionItemType();
return new XmlName("ArrayOf" + GetDefaultXsiType(collectionItemType).LocalName, null);
}
}
return new XmlName(clrType.Name, null);
}
public IEnumerable<IXmlIncludedType> GetIncludedTypes(Type baseType)
{
Queue<XmlMetadata> queue = new Queue<XmlMetadata>();
HashSet<Type> visited = new HashSet<Type> {
baseType
};
if (TryGetXmlMetadata(baseType, out XmlMetadata metadata))
queue.Enqueue(metadata);
metadata = this;
while (true) {
foreach (IXmlIncludedType includedType in metadata.IncludedTypes) {
Type clrType = includedType.ClrType;
if (baseType != clrType && baseType.IsAssignableFrom(clrType) && visited.Add(clrType)) {
yield return includedType;
if (TryGetXmlMetadata(clrType, out metadata))
queue.Enqueue(metadata);
}
}
if (queue.Count == 0)
break;
metadata = queue.Dequeue();
}
}
private bool TryGetXmlMetadata(Type clrType, out XmlMetadata metadata)
{
XmlTypeKind kind = XmlTypeSerializer.For(clrType).Kind;
if (kind != XmlTypeKind.Complex || !System.Reflection.IntrospectionExtensions.GetTypeInfo(clrType).IsInterface)
return Try.Failure(out metadata);
return Try.Success(out metadata, GetXmlMetadata(clrType));
}
private XmlMetadata GetXmlMetadata(Type clrType)
{
return source.GetAdapterMeta(clrType).GetXmlMeta();
}
private string GetDefaultTypeLocalName(Type clrType)
{
string name = clrType.Name;
if (!IsInterfaceName(name))
return name;
return name.Substring(1);
}
private static bool IsInterfaceName(string name)
{
if (name.Length > 1 && name[0] == 'I')
return char.IsUpper(name, 1);
return false;
}
private static bool TryCast<T>(object obj, ref T result) where T : class
{
T val = obj as T;
if (val == null)
return false;
result = val;
return true;
}
}
}