XmlNameContext.cs
119 lines
| 4.3 KiB
| text/x-csharp
|
CSharpLexer
cin
|
r289 | using System; | ||
using System.Collections.Generic; | ||||
using System.Linq; | ||||
using System.Text; | ||||
using System.Threading.Tasks; | ||||
using System.Xml; | ||||
namespace Implab.Xml { | ||||
class XmlNameContext { | ||||
public const string XmlnsNamespace = "http://www.w3.org/2000/xmlns/"; | ||||
public const string XmlnsPrefix = "xmlns"; | ||||
public const string XmlNamespace = "http://www.w3.org/XML/1998/namespace"; | ||||
public const string XmlPrefix = "xml"; | ||||
public const string XsiNamespace = "http://www.w3.org/2001/XMLSchema-instance"; | ||||
public const string XsiPrefix = "xsi"; | ||||
readonly static char[] _qNameDelim = new[] { ':' }; | ||||
Dictionary<string, string> m_ns2prefix; | ||||
Dictionary<string, string> m_prefix2ns; | ||||
int m_nextPrefix = 1; | ||||
string m_lastNs; | ||||
string m_lastPrefix; | ||||
public XmlNameContext ParentContext { get; private set; } | ||||
public int Depth { get; private set; } | ||||
public XmlNameContext(XmlNameContext parent, int depth) { | ||||
ParentContext = parent; | ||||
Depth = depth; | ||||
if (parent == null) { | ||||
DefinePrefixNoCheck(XmlnsNamespace, XmlnsPrefix); | ||||
DefinePrefixNoCheck(XmlNamespace, XmlPrefix); | ||||
} else { | ||||
m_nextPrefix = parent.m_nextPrefix; | ||||
} | ||||
} | ||||
public bool LookupNamespacePrefix(string ns, out string prefix) { | ||||
if (ns == null) | ||||
ns = string.Empty; | ||||
if (ns == m_lastNs) { | ||||
prefix = m_lastPrefix; | ||||
return true; | ||||
} | ||||
prefix = null; | ||||
for (var ctx = this; ctx != null; ctx = ctx.ParentContext) { | ||||
if (ctx.m_ns2prefix != null && ctx.m_ns2prefix.TryGetValue(ns, out prefix)) { | ||||
m_lastNs = ns; | ||||
m_lastPrefix = prefix; | ||||
return true; | ||||
} | ||||
} | ||||
return false; | ||||
} | ||||
public string CreateNamespacePrefix(string ns) { | ||||
var prefix = $"p{m_nextPrefix++}"; | ||||
DefinePrefixNoCheck(ns, prefix); | ||||
return prefix; | ||||
} | ||||
void DefinePrefixNoCheck(string ns, string prefix) { | ||||
if (ns == null) | ||||
ns = string.Empty; | ||||
if (prefix == null) | ||||
prefix = string.Empty; | ||||
if (m_ns2prefix == null) | ||||
m_ns2prefix = new Dictionary<string, string>(); | ||||
m_ns2prefix[ns] = prefix; | ||||
if (m_prefix2ns == null) | ||||
m_prefix2ns = new Dictionary<string, string>(); | ||||
m_prefix2ns[prefix] = ns; | ||||
} | ||||
public void DefinePrefix(string ns, string prefix) { | ||||
// according to https://www.w3.org/TR/xml-names/#ns-decl | ||||
// It MUST NOT be declared . Other prefixes MUST NOT be bound to this namespace name, and it MUST NOT be declared as the default namespace | ||||
if (ns == XmlnsNamespace) | ||||
throw new Exception($"Attempt to define xmlns:{prefix}='{ns}'"); | ||||
// It MAY, but need not, be declared, and MUST NOT be bound to any other namespace name | ||||
if (ns == XmlNamespace && prefix != XmlPrefix) | ||||
throw new Exception($"Attempt to define xmlns:{prefix}='{ns}'"); | ||||
// add mapping | ||||
DefinePrefixNoCheck(ns, prefix); | ||||
} | ||||
public string ResolvePrefix(string prefix) { | ||||
if (prefix == null) | ||||
prefix = string.Empty; | ||||
string ns = null; | ||||
for(var ctx = this; ctx != null; ctx = ctx.ParentContext) { | ||||
if (ctx.m_prefix2ns != null && ctx.m_prefix2ns.TryGetValue(prefix, out ns) == true) | ||||
return ns; | ||||
} | ||||
return null; | ||||
} | ||||
public XmlQualifiedName Resolve(string name) { | ||||
Safe.ArgumentNotEmpty(name, nameof(name)); | ||||
var parts = name.Split(_qNameDelim, 2, StringSplitOptions.RemoveEmptyEntries); | ||||
if (parts.Length == 2) { | ||||
return new XmlQualifiedName(parts[1], ResolvePrefix(parts[0])); | ||||
} else { | ||||
return new XmlQualifiedName(parts[0], ResolvePrefix(string.Empty)); | ||||
} | ||||
} | ||||
} | ||||
} | ||||