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