File: System\Xml\Xsl\Runtime\XsltConvert.cs
Project: ndp\fx\src\XmlUtils\System.Data.SqlXml.csproj (System.Data.SqlXml)
//------------------------------------------------------------------------------
// <copyright file="XsltConvert.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
// <owner current="true" primary="true">Microsoft</owner>
//------------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Xml.XPath;
using System.Xml.Xsl;
using System.Xml.Schema;
using System.Diagnostics;
using System.ComponentModel;
 
namespace System.Xml.Xsl.Runtime {
    using Res           = System.Xml.Utils.Res;
 
    /// <summary>
    /// Contains conversion routines used by Xslt.  These conversions fall into several categories:
    ///   1. Internal type to internal type: These are conversions from one of the five Xslt types to another
    ///      of the five types.
    ///   2. External type to internal type: These are conversions from any of the Xsd types to one of the five
    ///      Xslt types.
    ///   3. Internal type to external type: These are conversions from one of the five Xslt types to any of
    ///      of the Xsd types.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public static class XsltConvert {
        internal static readonly Type BooleanType = typeof(bool);
        internal static readonly Type ByteArrayType = typeof(byte[]);
        internal static readonly Type ByteType = typeof(byte);
        internal static readonly Type DateTimeType = typeof(DateTime);
        internal static readonly Type DecimalType = typeof(decimal);
        internal static readonly Type DoubleType = typeof(double);
        internal static readonly Type ICollectionType = typeof(ICollection);
        internal static readonly Type IEnumerableType = typeof(IEnumerable);
        internal static readonly Type IListType = typeof(IList);
        internal static readonly Type Int16Type = typeof(short);
        internal static readonly Type Int32Type = typeof(int);
        internal static readonly Type Int64Type = typeof(long);
        internal static readonly Type IXPathNavigableType = typeof(IXPathNavigable);
        internal static readonly Type ObjectType = typeof(object);
        internal static readonly Type SByteType = typeof(sbyte);
        internal static readonly Type SingleType = typeof(float);
        internal static readonly Type StringType = typeof(string);
        internal static readonly Type TimeSpanType = typeof(TimeSpan);
        internal static readonly Type UInt16Type = typeof(ushort);
        internal static readonly Type UInt32Type = typeof(uint);
        internal static readonly Type UInt64Type = typeof(ulong);
        internal static readonly Type UriType = typeof(Uri);
        internal static readonly Type VoidType = typeof(void);
        internal static readonly Type XmlAtomicValueType = typeof(XmlAtomicValue);
        internal static readonly Type XmlQualifiedNameType = typeof(XmlQualifiedName);
        internal static readonly Type XPathItemType = typeof(XPathItem);
        internal static readonly Type XPathNavigatorArrayType = typeof(XPathNavigator[]);
        internal static readonly Type XPathNavigatorType = typeof(XPathNavigator);
        internal static readonly Type XPathNodeIteratorType = typeof(XPathNodeIterator);
 
 
        //------------------------------------------------------------------------
        // ToBoolean (internal type to internal type)
        //------------------------------------------------------------------------
 
        public static bool ToBoolean(XPathItem item) {
            XsltLibrary.CheckXsltValue(item);
 
            if (item.IsNode)
                return true;
 
            Type itemType = item.ValueType;
 
            if (itemType == StringType) {
                return item.Value.Length != 0;
            }
            else if (itemType == DoubleType) {
                // (x < 0 || 0 < x)  ==  (x != 0) && !Double.IsNaN(x)
                double dbl = item.ValueAsDouble;
                return dbl < 0 || 0 < dbl;
            }
            else {
                Debug.Assert(itemType == BooleanType, "Unexpected type of atomic sequence " + itemType.ToString());
                return item.ValueAsBoolean;
            }
        }
 
        public static bool ToBoolean(IList<XPathItem> listItems) {
            XsltLibrary.CheckXsltValue(listItems);
 
            if (listItems.Count == 0)
                return false;
 
            return ToBoolean(listItems[0]);
        }
 
 
        //------------------------------------------------------------------------
        // ToDouble (internal type to internal type)
        //------------------------------------------------------------------------
 
        public static double ToDouble(string value) {
            return XPathConvert.StringToDouble(value);
        }
 
        public static double ToDouble(XPathItem item) {
            XsltLibrary.CheckXsltValue(item);
 
            if (item.IsNode)
                return XPathConvert.StringToDouble(item.Value);
 
            Type itemType = item.ValueType;
 
            if (itemType == StringType) {
                return XPathConvert.StringToDouble(item.Value);
            }
            else if (itemType == DoubleType) {
                return item.ValueAsDouble;
            }
            else {
                Debug.Assert(itemType == BooleanType, "Unexpected type of atomic sequence " + itemType.ToString());
                return item.ValueAsBoolean ? 1d : 0d;
            }
        }
 
        public static double ToDouble(IList<XPathItem> listItems) {
            XsltLibrary.CheckXsltValue(listItems);
 
            if (listItems.Count == 0)
                return Double.NaN;
 
            return ToDouble(listItems[0]);
        }
 
 
        //------------------------------------------------------------------------
        // ToNode (internal type to internal type)
        //------------------------------------------------------------------------
        [SuppressMessage("Microsoft.Security.Xml", "CA3059:UseXmlReaderForXPathDocument", Justification="Use internal APIs of XPathDocument to build a document and not to read it")]
        public static XPathNavigator ToNode(XPathItem item) {
            XsltLibrary.CheckXsltValue(item);
 
            if (!item.IsNode) {
                // Create Navigator over text node containing string value of item
                XPathDocument doc = new XPathDocument();
                XmlRawWriter writer = doc.LoadFromWriter(XPathDocument.LoadFlags.AtomizeNames, string.Empty);
                writer.WriteString(ToString(item));
                writer.Close();
                return doc.CreateNavigator();
            }
 
            RtfNavigator rtf = item as RtfNavigator;
            if (rtf != null)
                return rtf.ToNavigator();
 
            return (XPathNavigator) item;
        }
 
        public static XPathNavigator ToNode(IList<XPathItem> listItems) {
            XsltLibrary.CheckXsltValue(listItems);
 
            if (listItems.Count == 1)
                return ToNode(listItems[0]);
 
            throw new XslTransformException(Res.Xslt_NodeSetNotNode, string.Empty);
        }
 
 
        //------------------------------------------------------------------------
        // ToNodes (internal type to internal type)
        //------------------------------------------------------------------------
 
        public static IList<XPathNavigator> ToNodeSet(XPathItem item) {
            return new XmlQueryNodeSequence(ToNode(item));
        }
 
        public static IList<XPathNavigator> ToNodeSet(IList<XPathItem> listItems) {
            XsltLibrary.CheckXsltValue(listItems);
 
            if (listItems.Count == 1)
                return new XmlQueryNodeSequence(ToNode(listItems[0]));
 
            return XmlILStorageConverter.ItemsToNavigators(listItems);
        }
 
 
        //------------------------------------------------------------------------
        // ToString (internal type to internal type)
        //------------------------------------------------------------------------
 
        public static string ToString(double value) {
            return XPathConvert.DoubleToString(value);
        }
 
        public static string ToString(XPathItem item) {
            XsltLibrary.CheckXsltValue(item);
 
            // Use XPath 1.0 rules to convert double to string
            if (!item.IsNode && item.ValueType == DoubleType)
                return XPathConvert.DoubleToString(item.ValueAsDouble);
 
            return item.Value;
        }
 
        public static string ToString(IList<XPathItem> listItems) {
            XsltLibrary.CheckXsltValue(listItems);
 
            if (listItems.Count == 0)
                return string.Empty;
 
            return ToString(listItems[0]);
        }
 
 
        //------------------------------------------------------------------------
        // External type to internal type
        //------------------------------------------------------------------------
 
        public static string ToString(DateTime value) {
            return (new XsdDateTime(value, XsdDateTimeFlags.DateTime)).ToString();
        }
 
        public static double ToDouble(decimal value) {
            return (double) value;
        }
 
        public static double ToDouble(int value) {
            return (double) value;
        }
 
        public static double ToDouble(long value) {
            return (double) value;
        }
 
 
        //------------------------------------------------------------------------
        // Internal type to external type
        //------------------------------------------------------------------------
 
        public static decimal ToDecimal(double value) {
            checked { return (decimal) value; }
        }
 
        public static int ToInt(double value) {
            checked { return (int) value; }
        }
 
        public static long ToLong(double value) {
            checked { return (long) value; }
        }
 
        public static DateTime ToDateTime(string value) {
            return (DateTime)(new XsdDateTime(value, XsdDateTimeFlags.AllXsd));
        }
 
 
        //------------------------------------------------------------------------
        // External type to external type
        //------------------------------------------------------------------------
 
        internal static XmlAtomicValue ConvertToType(XmlAtomicValue value, XmlQueryType destinationType) {
            Debug.Assert(destinationType.IsStrict && destinationType.IsAtomicValue, "Can only convert to strict atomic type.");
 
            // This conversion matrix should match the one in XmlILVisitor.GetXsltConvertMethod
            switch (destinationType.TypeCode) {
                case XmlTypeCode.Boolean:
                    switch (value.XmlType.TypeCode) {
                        case XmlTypeCode.Boolean:
                        case XmlTypeCode.Double:
                        case XmlTypeCode.String:
                            return new XmlAtomicValue(destinationType.SchemaType, ToBoolean(value));
                    }
                    break;
 
                case XmlTypeCode.DateTime:
                    if (value.XmlType.TypeCode == XmlTypeCode.String)
                        return new XmlAtomicValue(destinationType.SchemaType, ToDateTime(value.Value));
                    break;
 
                case XmlTypeCode.Decimal:
                    if (value.XmlType.TypeCode == XmlTypeCode.Double)
                        return new XmlAtomicValue(destinationType.SchemaType, ToDecimal(value.ValueAsDouble));
                    break;
 
                case XmlTypeCode.Double:
                    switch (value.XmlType.TypeCode) {
                        case XmlTypeCode.Boolean:
                        case XmlTypeCode.Double:
                        case XmlTypeCode.String:
                            return new XmlAtomicValue(destinationType.SchemaType, ToDouble(value));
 
                        case XmlTypeCode.Decimal:
                            return new XmlAtomicValue(destinationType.SchemaType, ToDouble((decimal) value.ValueAs(DecimalType, null)));
 
                        case XmlTypeCode.Int:
                        case XmlTypeCode.Long:
                            return new XmlAtomicValue(destinationType.SchemaType, ToDouble(value.ValueAsLong));
                    }
                    break;
 
                case XmlTypeCode.Int:
                case XmlTypeCode.Long:
                    if (value.XmlType.TypeCode == XmlTypeCode.Double)
                        return new XmlAtomicValue(destinationType.SchemaType, ToLong(value.ValueAsDouble));
                    break;
 
                case XmlTypeCode.String:
                    switch (value.XmlType.TypeCode) {
                        case XmlTypeCode.Boolean:
                        case XmlTypeCode.Double:
                        case XmlTypeCode.String:
                            return new XmlAtomicValue(destinationType.SchemaType, ToString(value));
 
                        case XmlTypeCode.DateTime:
                            return new XmlAtomicValue(destinationType.SchemaType, ToString(value.ValueAsDateTime));
                    }
                    break;
            }
 
            Debug.Fail("Conversion from " + value.XmlType.QualifiedName.Name + " to " + destinationType + " is not supported.");
            return value;
        }
 
 
        //------------------------------------------------------------------------
        // EnsureXXX methods (TreatAs)
        //------------------------------------------------------------------------
 
        public static IList<XPathNavigator> EnsureNodeSet(IList<XPathItem> listItems) {
            XsltLibrary.CheckXsltValue(listItems);
 
            if (listItems.Count == 1) {
                XPathItem item = listItems[0];
                if (!item.IsNode)
                    throw new XslTransformException(Res.XPath_NodeSetExpected, string.Empty);
 
                if (item is RtfNavigator)
                    throw new XslTransformException(Res.XPath_RtfInPathExpr, string.Empty);
            }
 
            return XmlILStorageConverter.ItemsToNavigators(listItems);
        }
 
 
        //------------------------------------------------------------------------
        // InferXsltType
        //------------------------------------------------------------------------
 
        /// <summary>
        /// Infer one of the Xslt types from "clrType" -- Boolean, Double, String, Node, Node*, Item*.
        /// </summary>
        internal static XmlQueryType InferXsltType(Type clrType) {
            if (clrType == BooleanType) return XmlQueryTypeFactory.BooleanX;
            if (clrType == ByteType) return XmlQueryTypeFactory.DoubleX;
            if (clrType == DecimalType) return XmlQueryTypeFactory.DoubleX;
            if (clrType == DateTimeType) return XmlQueryTypeFactory.StringX;
            if (clrType == DoubleType) return XmlQueryTypeFactory.DoubleX;
            if (clrType == Int16Type) return XmlQueryTypeFactory.DoubleX;
            if (clrType == Int32Type) return XmlQueryTypeFactory.DoubleX;
            if (clrType == Int64Type) return XmlQueryTypeFactory.DoubleX;
            if (clrType == IXPathNavigableType) return XmlQueryTypeFactory.NodeNotRtf;
            if (clrType == SByteType) return XmlQueryTypeFactory.DoubleX;
            if (clrType == SingleType) return XmlQueryTypeFactory.DoubleX;
            if (clrType == StringType) return XmlQueryTypeFactory.StringX;
            if (clrType == UInt16Type) return XmlQueryTypeFactory.DoubleX;
            if (clrType == UInt32Type) return XmlQueryTypeFactory.DoubleX;
            if (clrType == UInt64Type) return XmlQueryTypeFactory.DoubleX;
            if (clrType == XPathNavigatorArrayType) return XmlQueryTypeFactory.NodeSDod;
            if (clrType == XPathNavigatorType) return XmlQueryTypeFactory.NodeNotRtf;
            if (clrType == XPathNodeIteratorType) return XmlQueryTypeFactory.NodeSDod;
            if (clrType.IsEnum) return XmlQueryTypeFactory.DoubleX;
            if (clrType == VoidType) return XmlQueryTypeFactory.Empty;
 
            return XmlQueryTypeFactory.ItemS;
        }
    }
}