// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using Microsoft.Xml; using System.Globalization; using System.Collections.Generic; namespace System.Runtime.Serialization { #if USE_REFEMIT || NET_NATIVE public class XmlReaderDelegator #else internal class XmlReaderDelegator #endif { protected XmlReader reader; protected XmlDictionaryReader dictionaryReader; protected bool isEndOfEmptyElement = false; public XmlReaderDelegator(XmlReader reader) { XmlObjectSerializer.CheckNull(reader, "reader"); this.reader = reader; this.dictionaryReader = reader as XmlDictionaryReader; } internal XmlReader UnderlyingReader { get { return reader; } } internal int AttributeCount { get { return isEndOfEmptyElement ? 0 : reader.AttributeCount; } } internal string GetAttribute(string name) { return isEndOfEmptyElement ? null : reader.GetAttribute(name); } internal string GetAttribute(string name, string namespaceUri) { return isEndOfEmptyElement ? null : reader.GetAttribute(name, namespaceUri); } internal string GetAttribute(int i) { if (isEndOfEmptyElement) throw /*System.Runtime.Serialization.*/DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("i", string.Format(SRSerialization.XmlElementAttributes))); return reader.GetAttribute(i); } internal bool IsEmptyElement { get { return false; } } internal bool IsNamespaceURI(string ns) { if (dictionaryReader == null) return ns == reader.NamespaceURI; else return dictionaryReader.IsNamespaceUri(ns); } internal bool IsLocalName(string localName) { if (dictionaryReader == null) return localName == reader.LocalName; else return dictionaryReader.IsLocalName(localName); } internal bool IsNamespaceUri(XmlDictionaryString ns) { if (dictionaryReader == null) return ns.Value == reader.NamespaceURI; else return dictionaryReader.IsNamespaceUri(ns); } internal bool IsLocalName(XmlDictionaryString localName) { if (dictionaryReader == null) return localName.Value == reader.LocalName; else return dictionaryReader.IsLocalName(localName); } internal int IndexOfLocalName(XmlDictionaryString[] localNames, XmlDictionaryString ns) { if (dictionaryReader != null) return dictionaryReader.IndexOfLocalName(localNames, ns); if (reader.NamespaceURI == ns.Value) { string localName = this.LocalName; for (int i = 0; i < localNames.Length; i++) { if (localName == localNames[i].Value) { return i; } } } return -1; } #if USE_REFEMIT public bool IsStartElement() #else internal bool IsStartElement() #endif { return !isEndOfEmptyElement && reader.IsStartElement(); } internal bool IsStartElement(string localname, string ns) { return !isEndOfEmptyElement && reader.IsStartElement(localname, ns); } #if USE_REFEMIT public bool IsStartElement(XmlDictionaryString localname, XmlDictionaryString ns) #else internal bool IsStartElement(XmlDictionaryString localname, XmlDictionaryString ns) #endif { if (dictionaryReader == null) return !isEndOfEmptyElement && reader.IsStartElement(localname.Value, ns.Value); else return !isEndOfEmptyElement && dictionaryReader.IsStartElement(localname, ns); } internal bool MoveToAttribute(string name) { return isEndOfEmptyElement ? false : reader.MoveToAttribute(name); } internal bool MoveToAttribute(string name, string ns) { return isEndOfEmptyElement ? false : reader.MoveToAttribute(name, ns); } internal void MoveToAttribute(int i) { if (isEndOfEmptyElement) throw /*System.Runtime.Serialization.*/DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("i", string.Format(SRSerialization.XmlElementAttributes))); reader.MoveToAttribute(i); } internal bool MoveToElement() { return isEndOfEmptyElement ? false : reader.MoveToElement(); } internal bool MoveToFirstAttribute() { return isEndOfEmptyElement ? false : reader.MoveToFirstAttribute(); } internal bool MoveToNextAttribute() { return isEndOfEmptyElement ? false : reader.MoveToNextAttribute(); } #if USE_REFEMIT public XmlNodeType NodeType #else internal XmlNodeType NodeType #endif { get { return isEndOfEmptyElement ? XmlNodeType.EndElement : reader.NodeType; } } internal bool Read() { //reader.MoveToFirstAttribute(); //if (NodeType == XmlNodeType.Attribute) reader.MoveToElement(); if (!reader.IsEmptyElement) return reader.Read(); if (isEndOfEmptyElement) { isEndOfEmptyElement = false; return reader.Read(); } isEndOfEmptyElement = true; return true; } internal XmlNodeType MoveToContent() { if (isEndOfEmptyElement) return XmlNodeType.EndElement; return reader.MoveToContent(); } internal bool ReadAttributeValue() { return isEndOfEmptyElement ? false : reader.ReadAttributeValue(); } #if USE_REFEMIT public void ReadEndElement() #else internal void ReadEndElement() #endif { if (isEndOfEmptyElement) Read(); else reader.ReadEndElement(); } private void ThrowConversionException(string value, string type) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(XmlObjectSerializer.TryAddLineInfo(this, string.Format(SRSerialization.XmlInvalidConversion, value, type)))); } private void ThrowNotAtElement() { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(string.Format(SRSerialization.XmlStartElementExpected, "EndElement"))); } #if USE_REFEMIT public virtual char ReadElementContentAsChar() #else internal virtual char ReadElementContentAsChar() #endif { return ToChar(ReadElementContentAsInt()); } private char ToChar(int value) { if (value < char.MinValue || value > char.MaxValue) { ThrowConversionException(value.ToString(NumberFormatInfo.CurrentInfo), "Char"); } return (char)value; } #if USE_REFEMIT public string ReadElementContentAsString() #else internal string ReadElementContentAsString() #endif { if (isEndOfEmptyElement) ThrowNotAtElement(); return reader.ReadElementContentAsString(); } internal string ReadContentAsString() { return isEndOfEmptyElement ? String.Empty : reader.ReadContentAsString(); } #if USE_REFEMIT public bool ReadElementContentAsBoolean() #else internal bool ReadElementContentAsBoolean() #endif { if (isEndOfEmptyElement) ThrowNotAtElement(); return reader.ReadElementContentAsBoolean(); } internal bool ReadContentAsBoolean() { if (isEndOfEmptyElement) ThrowConversionException(string.Empty, "Boolean"); return reader.ReadContentAsBoolean(); } #if USE_REFEMIT public float ReadElementContentAsFloat() #else internal float ReadElementContentAsFloat() #endif { if (isEndOfEmptyElement) ThrowNotAtElement(); return reader.ReadElementContentAsFloat(); } #if USE_REFEMIT public double ReadElementContentAsDouble() #else internal double ReadElementContentAsDouble() #endif { if (isEndOfEmptyElement) ThrowNotAtElement(); return reader.ReadElementContentAsDouble(); } #if USE_REFEMIT public decimal ReadElementContentAsDecimal() #else internal decimal ReadElementContentAsDecimal() #endif { if (isEndOfEmptyElement) ThrowNotAtElement(); return reader.ReadElementContentAsDecimal(); } #if USE_REFEMIT public virtual byte[] ReadElementContentAsBase64() #else internal virtual byte[] ReadElementContentAsBase64() #endif { if (isEndOfEmptyElement) ThrowNotAtElement(); if (dictionaryReader == null) { return ReadContentAsBase64(reader.ReadElementContentAsString()); } else { return dictionaryReader.ReadElementContentAsBase64(); } } internal byte[] ReadContentAsBase64(string str) { if (str == null) return null; str = str.Trim(); if (str.Length == 0) return Array.Empty<byte>(); try { return Convert.FromBase64String(str); } catch (ArgumentException exception) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(str, "byte[]", exception)); } catch (FormatException exception) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(str, "byte[]", exception)); } } #if USE_REFEMIT public virtual DateTime ReadElementContentAsDateTime() #else internal virtual DateTime ReadElementContentAsDateTime() #endif { if (isEndOfEmptyElement) ThrowNotAtElement(); return XmlConvert.ToDateTime(reader.ReadElementContentAsString(), XmlDateTimeSerializationMode.RoundtripKind); } #if USE_REFEMIT public int ReadElementContentAsInt() #else internal int ReadElementContentAsInt() #endif { if (isEndOfEmptyElement) ThrowNotAtElement(); return reader.ReadElementContentAsInt(); } internal int ReadContentAsInt() { if (isEndOfEmptyElement) ThrowConversionException(string.Empty, "Int32"); return reader.ReadContentAsInt(); } #if USE_REFEMIT public long ReadElementContentAsLong() #else internal long ReadElementContentAsLong() #endif { if (isEndOfEmptyElement) ThrowNotAtElement(); return reader.ReadElementContentAsLong(); } #if USE_REFEMIT public short ReadElementContentAsShort() #else internal short ReadElementContentAsShort() #endif { return ToShort(ReadElementContentAsInt()); } private short ToShort(int value) { if (value < short.MinValue || value > short.MaxValue) { ThrowConversionException(value.ToString(NumberFormatInfo.CurrentInfo), "Int16"); } return (short)value; } #if USE_REFEMIT public byte ReadElementContentAsUnsignedByte() #else internal byte ReadElementContentAsUnsignedByte() #endif { return ToByte(ReadElementContentAsInt()); } private byte ToByte(int value) { if (value < byte.MinValue || value > byte.MaxValue) { ThrowConversionException(value.ToString(NumberFormatInfo.CurrentInfo), "Byte"); } return (byte)value; } #if USE_REFEMIT [CLSCompliant(false)] public SByte ReadElementContentAsSignedByte() #else internal SByte ReadElementContentAsSignedByte() #endif { return ToSByte(ReadElementContentAsInt()); } private SByte ToSByte(int value) { if (value < SByte.MinValue || value > SByte.MaxValue) { ThrowConversionException(value.ToString(NumberFormatInfo.CurrentInfo), "SByte"); } return (SByte)value; } #if USE_REFEMIT [CLSCompliant(false)] public UInt32 ReadElementContentAsUnsignedInt() #else internal UInt32 ReadElementContentAsUnsignedInt() #endif { return ToUInt32(ReadElementContentAsLong()); } private UInt32 ToUInt32(long value) { if (value < UInt32.MinValue || value > UInt32.MaxValue) { ThrowConversionException(value.ToString(NumberFormatInfo.CurrentInfo), "UInt32"); } return (UInt32)value; } #if USE_REFEMIT [CLSCompliant(false)] public virtual UInt64 ReadElementContentAsUnsignedLong() #else internal virtual UInt64 ReadElementContentAsUnsignedLong() #endif { if (isEndOfEmptyElement) ThrowNotAtElement(); string str = reader.ReadElementContentAsString(); if (str == null || str.Length == 0) ThrowConversionException(string.Empty, "UInt64"); return XmlConverter.ToUInt64(str); } #if USE_REFEMIT [CLSCompliant(false)] public UInt16 ReadElementContentAsUnsignedShort() #else internal UInt16 ReadElementContentAsUnsignedShort() #endif { return ToUInt16(ReadElementContentAsInt()); } private UInt16 ToUInt16(int value) { if (value < UInt16.MinValue || value > UInt16.MaxValue) { ThrowConversionException(value.ToString(NumberFormatInfo.CurrentInfo), "UInt16"); } return (UInt16)value; } #if USE_REFEMIT public TimeSpan ReadElementContentAsTimeSpan() #else internal TimeSpan ReadElementContentAsTimeSpan() #endif { if (isEndOfEmptyElement) ThrowNotAtElement(); string str = reader.ReadElementContentAsString(); return XmlConverter.ToTimeSpan(str); } #if USE_REFEMIT public Guid ReadElementContentAsGuid() #else internal Guid ReadElementContentAsGuid() #endif { if (isEndOfEmptyElement) ThrowNotAtElement(); string str = reader.ReadElementContentAsString(); try { return new Guid(str); } catch (ArgumentException exception) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(str, "Guid", exception)); } catch (FormatException exception) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(str, "Guid", exception)); } catch (OverflowException exception) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(str, "Guid", exception)); } } #if USE_REFEMIT public Uri ReadElementContentAsUri() #else internal Uri ReadElementContentAsUri() #endif { if (isEndOfEmptyElement) ThrowNotAtElement(); string str = ReadElementContentAsString(); try { return new Uri(str, UriKind.RelativeOrAbsolute); } catch (ArgumentException exception) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(str, "Uri", exception)); } catch (FormatException exception) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(str, "Uri", exception)); } } #if USE_REFEMIT public XmlQualifiedName ReadElementContentAsQName() #else internal XmlQualifiedName ReadElementContentAsQName() #endif { Read(); XmlQualifiedName obj = ReadContentAsQName(); ReadEndElement(); return obj; } internal virtual XmlQualifiedName ReadContentAsQName() { return ParseQualifiedName(ReadContentAsString()); } private XmlQualifiedName ParseQualifiedName(string str) { string name, ns, prefix; if (str == null || str.Length == 0) name = ns = String.Empty; else XmlObjectSerializerReadContext.ParseQualifiedName(str, this, out name, out ns, out prefix); return new XmlQualifiedName(name, ns); } private void CheckExpectedArrayLength(XmlObjectSerializerReadContext context, int arrayLength) { context.IncrementItemCount(arrayLength); } protected int GetArrayLengthQuota(XmlObjectSerializerReadContext context) { return Math.Min(context.RemainingItemCount, int.MaxValue); } private void CheckActualArrayLength(int expectedLength, int actualLength, XmlDictionaryString itemName, XmlDictionaryString itemNamespace) { if (expectedLength != actualLength) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(string.Format(SRSerialization.ArrayExceededSizeAttribute, expectedLength, itemName.Value, itemNamespace.Value))); } #if USE_REFEMIT public bool TryReadBooleanArray(XmlObjectSerializerReadContext context, #else internal bool TryReadBooleanArray(XmlObjectSerializerReadContext context, #endif XmlDictionaryString itemName, XmlDictionaryString itemNamespace, int arrayLength, out bool[] array) { if (dictionaryReader == null) { array = null; return false; } if (arrayLength != -1) { CheckExpectedArrayLength(context, arrayLength); array = new bool[arrayLength]; int read = 0, offset = 0; while ((read = dictionaryReader.ReadArray(itemName, itemNamespace, array, offset, arrayLength - offset)) > 0) { offset += read; } CheckActualArrayLength(arrayLength, offset, itemName, itemNamespace); } else { array = BooleanArrayHelperWithDictionaryString.Instance.ReadArray( dictionaryReader, itemName, itemNamespace, GetArrayLengthQuota(context)); context.IncrementItemCount(array.Length); } return true; } #if USE_REFEMIT public bool TryReadDateTimeArray(XmlObjectSerializerReadContext context, #else internal bool TryReadDateTimeArray(XmlObjectSerializerReadContext context, #endif XmlDictionaryString itemName, XmlDictionaryString itemNamespace, int arrayLength, out DateTime[] array) { if (dictionaryReader == null) { array = null; return false; } if (arrayLength != -1) { CheckExpectedArrayLength(context, arrayLength); array = new DateTime[arrayLength]; int read = 0, offset = 0; while ((read = dictionaryReader.ReadArray(itemName, itemNamespace, array, offset, arrayLength - offset)) > 0) { offset += read; } CheckActualArrayLength(arrayLength, offset, itemName, itemNamespace); } else { array = DateTimeArrayHelperWithDictionaryString.Instance.ReadArray( dictionaryReader, itemName, itemNamespace, GetArrayLengthQuota(context)); context.IncrementItemCount(array.Length); } return true; } #if USE_REFEMIT public bool TryReadDecimalArray(XmlObjectSerializerReadContext context, #else internal bool TryReadDecimalArray(XmlObjectSerializerReadContext context, #endif XmlDictionaryString itemName, XmlDictionaryString itemNamespace, int arrayLength, out decimal[] array) { if (dictionaryReader == null) { array = null; return false; } if (arrayLength != -1) { CheckExpectedArrayLength(context, arrayLength); array = new decimal[arrayLength]; int read = 0, offset = 0; while ((read = dictionaryReader.ReadArray(itemName, itemNamespace, array, offset, arrayLength - offset)) > 0) { offset += read; } CheckActualArrayLength(arrayLength, offset, itemName, itemNamespace); } else { array = DecimalArrayHelperWithDictionaryString.Instance.ReadArray( dictionaryReader, itemName, itemNamespace, GetArrayLengthQuota(context)); context.IncrementItemCount(array.Length); } return true; } #if USE_REFEMIT public bool TryReadInt32Array(XmlObjectSerializerReadContext context, #else internal bool TryReadInt32Array(XmlObjectSerializerReadContext context, #endif XmlDictionaryString itemName, XmlDictionaryString itemNamespace, int arrayLength, out int[] array) { if (dictionaryReader == null) { array = null; return false; } if (arrayLength != -1) { CheckExpectedArrayLength(context, arrayLength); array = new int[arrayLength]; int read = 0, offset = 0; while ((read = dictionaryReader.ReadArray(itemName, itemNamespace, array, offset, arrayLength - offset)) > 0) { offset += read; } CheckActualArrayLength(arrayLength, offset, itemName, itemNamespace); } else { array = Int32ArrayHelperWithDictionaryString.Instance.ReadArray( dictionaryReader, itemName, itemNamespace, GetArrayLengthQuota(context)); context.IncrementItemCount(array.Length); } return true; } #if USE_REFEMIT public bool TryReadInt64Array(XmlObjectSerializerReadContext context, #else internal bool TryReadInt64Array(XmlObjectSerializerReadContext context, #endif XmlDictionaryString itemName, XmlDictionaryString itemNamespace, int arrayLength, out long[] array) { if (dictionaryReader == null) { array = null; return false; } if (arrayLength != -1) { CheckExpectedArrayLength(context, arrayLength); array = new long[arrayLength]; int read = 0, offset = 0; while ((read = dictionaryReader.ReadArray(itemName, itemNamespace, array, offset, arrayLength - offset)) > 0) { offset += read; } CheckActualArrayLength(arrayLength, offset, itemName, itemNamespace); } else { array = Int64ArrayHelperWithDictionaryString.Instance.ReadArray( dictionaryReader, itemName, itemNamespace, GetArrayLengthQuota(context)); context.IncrementItemCount(array.Length); } return true; } #if USE_REFEMIT public bool TryReadSingleArray(XmlObjectSerializerReadContext context, #else internal bool TryReadSingleArray(XmlObjectSerializerReadContext context, #endif XmlDictionaryString itemName, XmlDictionaryString itemNamespace, int arrayLength, out float[] array) { if (dictionaryReader == null) { array = null; return false; } if (arrayLength != -1) { CheckExpectedArrayLength(context, arrayLength); array = new float[arrayLength]; int read = 0, offset = 0; while ((read = dictionaryReader.ReadArray(itemName, itemNamespace, array, offset, arrayLength - offset)) > 0) { offset += read; } CheckActualArrayLength(arrayLength, offset, itemName, itemNamespace); } else { array = SingleArrayHelperWithDictionaryString.Instance.ReadArray( dictionaryReader, itemName, itemNamespace, GetArrayLengthQuota(context)); context.IncrementItemCount(array.Length); } return true; } #if USE_REFEMIT public bool TryReadDoubleArray(XmlObjectSerializerReadContext context, #else internal bool TryReadDoubleArray(XmlObjectSerializerReadContext context, #endif XmlDictionaryString itemName, XmlDictionaryString itemNamespace, int arrayLength, out double[] array) { if (dictionaryReader == null) { array = null; return false; } if (arrayLength != -1) { CheckExpectedArrayLength(context, arrayLength); array = new double[arrayLength]; int read = 0, offset = 0; while ((read = dictionaryReader.ReadArray(itemName, itemNamespace, array, offset, arrayLength - offset)) > 0) { offset += read; } CheckActualArrayLength(arrayLength, offset, itemName, itemNamespace); } else { array = DoubleArrayHelperWithDictionaryString.Instance.ReadArray( dictionaryReader, itemName, itemNamespace, GetArrayLengthQuota(context)); context.IncrementItemCount(array.Length); } return true; } internal IDictionary<string, string> GetNamespacesInScope(XmlNamespaceScope scope) { return (reader is IXmlNamespaceResolver) ? ((IXmlNamespaceResolver)reader).GetNamespacesInScope(scope) : null; } // IXmlLineInfo members internal bool HasLineInfo() { IXmlLineInfo iXmlLineInfo = reader as IXmlLineInfo; return (iXmlLineInfo == null) ? false : iXmlLineInfo.HasLineInfo(); } internal int LineNumber { get { IXmlLineInfo iXmlLineInfo = reader as IXmlLineInfo; return (iXmlLineInfo == null) ? 0 : iXmlLineInfo.LineNumber; } } internal int LinePosition { get { IXmlLineInfo iXmlLineInfo = reader as IXmlLineInfo; return (iXmlLineInfo == null) ? 0 : iXmlLineInfo.LinePosition; } } // IXmlTextParser members // delegating properties and methods internal string Name { get { return reader.Name; } } internal string LocalName { get { return reader.LocalName; } } internal string NamespaceURI { get { return reader.NamespaceURI; } } internal string Value { get { return reader.Value; } } internal Type ValueType { get { return reader.ValueType; } } internal int Depth { get { return reader.Depth; } } internal string LookupNamespace(string prefix) { return reader.LookupNamespace(prefix); } internal bool EOF { get { return reader.EOF; } } internal void Skip() { reader.Skip(); isEndOfEmptyElement = false; } } }