using System; using System.Collections.Generic; using System.Xml; using System.Xml.XPath; using System.Xml.Xsl; // source: mvpxml.codeplex.com namespace Umbraco.Cms.Core.Xml { /// /// Provides the evaluation context for fast execution and custom /// variables resolution. /// /// /// This class is responsible for resolving variables during dynamic expression execution. /// Discussed in http://weblogs.asp.net/cazzu/archive/2003/10/07/30888.aspx /// Author: Daniel Cazzulino, blog /// public class DynamicContext : XsltContext { #region Private vars readonly IDictionary _variables = new Dictionary(); #endregion Private #region Constructors & Initialization /// /// Initializes a new instance of the class. /// public DynamicContext() : base(new NameTable()) { } /// /// Initializes a new instance of the /// class with the specified . /// /// The NameTable to use. public DynamicContext(NameTable table) : base(table) { } /// /// Initializes a new instance of the class. /// /// A previously filled context with the namespaces to use. public DynamicContext(XmlNamespaceManager context) : this(context, new NameTable()) { } /// /// Initializes a new instance of the class. /// /// A previously filled context with the namespaces to use. /// The NameTable to use. public DynamicContext(XmlNamespaceManager context, NameTable table) : base(table) { object xml = table.Add(XmlNamespaces.Xml); object xmlns = table.Add(XmlNamespaces.XmlNs); if (context == null) return; foreach (string prefix in context) { var uri = context.LookupNamespace(prefix); // Use fast object reference comparison to omit forbidden namespace declarations. if (Equals(uri, xml) || Equals(uri, xmlns)) continue; if (uri == null) continue; base.AddNamespace(prefix, uri); } } #endregion Constructors & Initialization #region Common Overrides /// /// Implementation equal to . /// public override int CompareDocument(string baseUri, string nextbaseUri) { return String.Compare(baseUri, nextbaseUri, false, System.Globalization.CultureInfo.InvariantCulture); } /// /// Same as . /// public override string LookupNamespace(string prefix) { var key = NameTable.Get(prefix); return key == null ? null : base.LookupNamespace(key); } /// /// Same as . /// public override string LookupPrefix(string uri) { var key = NameTable.Get(uri); return key == null ? null : base.LookupPrefix(key); } /// /// Same as . /// public override bool PreserveWhitespace(XPathNavigator node) { return true; } /// /// Same as . /// public override bool Whitespace { get { return true; } } #endregion Common Overrides #region Public Members /// /// Shortcut method that compiles an expression using an empty navigator. /// /// The expression to compile /// A compiled . public static XPathExpression Compile(string xpath) { return new XmlDocument().CreateNavigator().Compile(xpath); } #endregion Public Members #region Variable Handling Code /// /// Adds the variable to the dynamic evaluation context. /// /// The name of the variable to add to the context. /// The value of the variable to add to the context. /// /// Value type conversion for XPath evaluation is as follows: /// /// /// CLR Type /// XPath type /// /// /// System.String /// XPathResultType.String /// /// /// System.Double (or types that can be converted to) /// XPathResultType.Number /// /// /// System.Boolean /// XPathResultType.Boolean /// /// /// System.Xml.XPath.XPathNavigator /// XPathResultType.Navigator /// /// /// System.Xml.XPath.XPathNodeIterator /// XPathResultType.NodeSet /// /// /// Others /// XPathResultType.Any /// /// /// See the topic "Compile, Select, Evaluate, and Matches with /// XPath and XPathExpressions" in MSDN documentation for additional information. /// /// The is null. public void AddVariable(string name, object value) { if (value == null) throw new ArgumentNullException("value"); _variables[name] = new DynamicVariable(name, value); } /// /// See . Not used in our implementation. /// public override IXsltContextFunction ResolveFunction(string prefix, string name, XPathResultType[] argTypes) { return null; } /// /// Resolves the dynamic variables added to the context. See . /// public override IXsltContextVariable ResolveVariable(string prefix, string name) { IXsltContextVariable var; _variables.TryGetValue(name, out var); return var; } #endregion Variable Handling Code #region Internal DynamicVariable class /// /// Represents a variable during dynamic expression execution. /// internal class DynamicVariable : IXsltContextVariable { readonly string _name; readonly object _value; #region Public Members public string Name { get { return _name; } } /// /// Initializes a new instance of the class. /// /// The name of the variable. /// The value of the variable. public DynamicVariable(string name, object value) { _name = name; _value = value; if (value is string) _type = XPathResultType.String; else if (value is bool) _type = XPathResultType.Boolean; else if (value is XPathNavigator) _type = XPathResultType.Navigator; else if (value is XPathNodeIterator) _type = XPathResultType.NodeSet; else { // Try to convert to double (native XPath numeric type) if (value is double) { _type = XPathResultType.Number; } else { if (value is IConvertible) { try { _value = Convert.ToDouble(value); // We succeeded, so it's a number. _type = XPathResultType.Number; } catch (FormatException) { _type = XPathResultType.Any; } catch (OverflowException) { _type = XPathResultType.Any; } } else { _type = XPathResultType.Any; } } } } #endregion Public Members #region IXsltContextVariable Implementation XPathResultType IXsltContextVariable.VariableType { get { return _type; } } readonly XPathResultType _type; object IXsltContextVariable.Evaluate(XsltContext context) { return _value; } bool IXsltContextVariable.IsLocal { get { return false; } } bool IXsltContextVariable.IsParam { get { return false; } } #endregion IXsltContextVariable Implementation } #endregion Internal DynamicVariable class } }