// // XmlBinaryDictionaryReader.cs // // Author: // Atsushi Enomoto // // Copyright (C) 2005, 2007 Novell, Inc. http://www.novell.com // // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Collections; using System.Collections.Specialized; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Text; using QName = System.Xml.XmlQualifiedName; using BF = System.Xml.XmlBinaryFormat; namespace System.Xml { // FIXME: // - native value data (7B-82, 8D-A0) are not implemented. // - support XmlDictionaryReaderQuotas. // - support XmlBinaryReaderSession. // - handle namespaces as expected. internal class XmlBinaryDictionaryReader : XmlDictionaryReader, IXmlNamespaceResolver { internal interface ISource { int Position { get; } int ReadByte (); int Read (byte [] data, int offset, int count); BinaryReader Reader { get; } } internal class StreamSource : ISource { BinaryReader stream; int position; public StreamSource (Stream stream) { this.stream = new BinaryReader (stream); } public int Position { get { return position - 1; } } public BinaryReader Reader { get { return stream; } } public int ReadByte () { if (stream.PeekChar () < 0) return -1; position++; return stream.ReadByte (); } public int Read (byte [] data, int offset, int count) { int ret = stream.Read (data, offset, count); position += ret; return ret; } } class NodeInfo { public NodeInfo () { } public NodeInfo (bool isAttr) { IsAttributeValue = isAttr; } public bool IsAttributeValue; public int Position; public string Prefix; public XmlDictionaryString DictLocalName; public XmlDictionaryString DictNS; public XmlNodeType NodeType; public object TypedValue; public byte ValueType; // -1 for nothing, // -2 for that of element (only for attribute), // 0 or more to fill later public int NSSlot; string name; string local_name; string ns; string value; public string LocalName { get { return DictLocalName != null ? DictLocalName.Value : local_name; } set { DictLocalName = null; local_name = value; } } public string NS { get { return DictNS != null ? DictNS.Value : ns; } set { DictNS = null; ns = value; } } public string Name { get { if (name == null) name = Prefix.Length > 0 ? String.Concat (Prefix, ":", LocalName) : LocalName; return name; } } public string Value { get { if (BF.AttrString <= ValueType && ValueType <= BF.GlobalAttrIndexInElemNS) return value; // attribute switch (ValueType) { case 0: case BF.Comment: case BF.Text: case BF.EmptyText: return value; case BF.Zero: case BF.One: return XmlConvert.ToString ((int) TypedValue); case BF.Int8: return XmlConvert.ToString ((byte) TypedValue); case BF.Int16: return XmlConvert.ToString ((short) TypedValue); case BF.Int32: return XmlConvert.ToString ((int) TypedValue); case BF.Int64: return XmlConvert.ToString ((long) TypedValue); case BF.Single: return XmlConvert.ToString ((float) TypedValue); case BF.Double: return XmlConvert.ToString ((double) TypedValue); case BF.DateTime: return XmlConvert.ToString ((DateTime) TypedValue, XmlDateTimeSerializationMode.RoundtripKind); case BF.TimeSpan: return XmlConvert.ToString ((TimeSpan) TypedValue); case BF.Guid: return XmlConvert.ToString ((Guid) TypedValue); case BF.UniqueIdFromGuid: return TypedValue.ToString (); case BF.Base64: case BF.Base64Fixed: return Convert.ToBase64String ((byte []) TypedValue); default: throw new NotImplementedException ("ValueType " + ValueType + " on node " + NodeType); } } set { this.value = value; } } public virtual void Reset () { Position = 0; DictLocalName = DictNS = null; LocalName = NS = Prefix = Value = String.Empty; NodeType = XmlNodeType.None; TypedValue = null; ValueType = 0; NSSlot = -1; } } class AttrNodeInfo : NodeInfo { public int ValueIndex; public override void Reset () { base.Reset (); ValueIndex = -1; } } ISource source; IXmlDictionary dictionary; XmlDictionaryReaderQuotas quota; XmlBinaryReaderSession session; OnXmlDictionaryReaderClose on_close; XmlParserContext context; ReadState state = ReadState.Initial; NodeInfo node; NodeInfo current; List attributes = new List (); List attr_values = new List (); List node_stack = new List (); List ns_store = new List (); Dictionary ns_dict_store = new Dictionary (); int attr_count; int attr_value_count; int current_attr = -1; int depth = 0; // used during Read() int ns_slot; // next byte in the source (one byte token ahead always // happens because there is no "end of start element" mark). int next = -1; bool is_next_end_element; // temporary buffer for utf8enc.GetString() byte [] tmp_buffer = new byte [128]; UTF8Encoding utf8enc = new UTF8Encoding (); public XmlBinaryDictionaryReader (byte [] buffer, int offset, int count, IXmlDictionary dictionary, XmlDictionaryReaderQuotas quota, XmlBinaryReaderSession session, OnXmlDictionaryReaderClose onClose) { source = /*new ArraySource (buffer, offset, count);*/ new StreamSource (new MemoryStream (buffer, offset, count)); Initialize (dictionary, quota, session, onClose); } public XmlBinaryDictionaryReader (Stream stream, IXmlDictionary dictionary, XmlDictionaryReaderQuotas quota, XmlBinaryReaderSession session, OnXmlDictionaryReaderClose onClose) { source = new StreamSource (stream); Initialize (dictionary, quota, session, onClose); } private void Initialize (IXmlDictionary dictionary, XmlDictionaryReaderQuotas quotas, XmlBinaryReaderSession session, OnXmlDictionaryReaderClose onClose) { if (quotas == null) throw new ArgumentNullException ("quotas"); if (dictionary == null) dictionary = new XmlDictionary (); this.dictionary = dictionary; this.quota = quotas; if (session == null) session = new XmlBinaryReaderSession (); this.session = session; on_close = onClose; NameTable nt = new NameTable (); this.context = new XmlParserContext (nt, new XmlNamespaceManager (nt), null, XmlSpace.None); current = node = new NodeInfo (); current.Reset (); } public override int AttributeCount { get { return attr_count; } } public override string BaseURI { get { return context.BaseURI; } } public override int Depth { get { return depth; } } public override bool EOF { get { return state == ReadState.EndOfFile || state == ReadState.Error; } } public override bool HasValue { get { return current.Value.Length > 0; } } public override bool IsEmptyElement { get { return false; } } public override XmlNodeType NodeType { get { return current.NodeType; } } public override string Prefix { get { return current.Prefix; } } public override string LocalName { get { return current.LocalName; } } public override string NamespaceURI { get { return current.NS; } } public override XmlNameTable NameTable { get { return context.NameTable; } } public override XmlDictionaryReaderQuotas Quotas { get { return quota; } } public override ReadState ReadState { get { return state; } } public override string Value { get { return current.Value; } } public override void Close () { if (on_close != null) on_close (this); } public override string GetAttribute (int i) { if (i >= attr_count) throw new ArgumentOutOfRangeException (String.Format ("Specified attribute index is {0} and should be less than {1}", i, attr_count)); return attributes [i].Value; } public override string GetAttribute (string name) { for (int i = 0; i < attributes.Count; i++) if (attributes [i].Name == name) return attributes [i].Value; return null; } public override string GetAttribute (string localName, string ns) { for (int i = 0; i < attributes.Count; i++) if (attributes [i].LocalName == localName && attributes [i].NS == ns) return attributes [i].Value; return null; } public IDictionary GetNamespacesInScope ( XmlNamespaceScope scope) { return context.NamespaceManager.GetNamespacesInScope (scope); } public string LookupPrefix (string ns) { return context.NamespaceManager.LookupPrefix (NameTable.Get (ns)); } public override string LookupNamespace (string prefix) { return context.NamespaceManager.LookupNamespace ( NameTable.Get (prefix)); } public override bool MoveToElement () { bool ret = current_attr >= 0; current_attr = -1; current = node; return ret; } public override bool MoveToFirstAttribute () { if (attr_count == 0) return false; current_attr = 0; current = attributes [current_attr]; return true; } public override bool MoveToNextAttribute () { if (++current_attr < attr_count) { current = attributes [current_attr]; return true; } else { --current_attr; return false; } } public override void MoveToAttribute (int i) { if (i >= attr_count) throw new ArgumentOutOfRangeException (String.Format ("Specified attribute index is {0} and should be less than {1}", i, attr_count)); current_attr = i; current = attributes [i]; } public override bool MoveToAttribute (string name) { for (int i = 0; i < attributes.Count; i++) { if (attributes [i].Name == name) { MoveToAttribute (i); return true; } } return false; } public override bool MoveToAttribute (string localName, string ns) { for (int i = 0; i < attributes.Count; i++) { if (attributes [i].LocalName == localName && attributes [i].NS == ns) { MoveToAttribute (i); return true; } } return false; } public override bool ReadAttributeValue () { if (current_attr < 0) return false; int start = attributes [current_attr].ValueIndex; int end = current_attr + 1 == attr_count ? attr_value_count : attributes [current_attr + 1].ValueIndex; if (start == end) return false; if (!current.IsAttributeValue) { current = attr_values [start]; return true; } for (int i = start; i < end; i++) { if (current == attr_values [i] && i + 1 < end) { current = attr_values [i + 1]; return true; } } return false; } public override bool Read () { switch (state) { case ReadState.Closed: case ReadState.EndOfFile: case ReadState.Error: return false; } // clear. state = ReadState.Interactive; attr_count = 0; attr_value_count = 0; ns_slot = 0; current = node; if (node.NodeType == XmlNodeType.Element) { // push element scope depth++; if (node_stack.Count <= depth) { node_stack.Add (node); node = new NodeInfo (); } else node = node_stack [depth]; node.Reset (); current = node; } if (is_next_end_element) { is_next_end_element = false; ProcessEndElement (); return true; } node.Reset (); int ident = next >= 0 ? next : source.ReadByte (); next = -1; // check end of source. if (ident < 0) { state = ReadState.EndOfFile; current.Reset (); return false; } is_next_end_element = ident > 0x80 && (ident & 1) == 1; ident -= is_next_end_element ? 1 : 0; /* if (0x3F <= ident && ident <= 0x42) ReadElementBinary ((byte) ident); else { switch (ident) { case 0x3C: // end element ProcessEndElement (); break; case 0x3D: // comment node.Value = ReadUTF8 (); node.NodeType = XmlNodeType.Comment; break; default: ReadTextOrValue ((byte) ident, node, false); break; } } */ switch (ident) { case BF.EndElement: ProcessEndElement (); break; case BF.Comment: node.Value = ReadUTF8 (); node.ValueType = BF.Comment; node.NodeType = XmlNodeType.Comment; break; case BF.ElemString: case BF.ElemStringPrefix: case BF.ElemIndex: case BF.ElemIndexPrefix: ReadElementBinary ((byte) ident); break; default: ReadTextOrValue ((byte) ident, node, false); break; } return true; } private void ProcessEndElement () { if (depth == 0) throw new XmlException ("Unexpected end of element while there is no element started."); current = node = node_stack [--depth]; node.NodeType = XmlNodeType.EndElement; context.NamespaceManager.PopScope (); } private void ReadElementBinary (int ident) { // element node.NodeType = XmlNodeType.Element; node.Prefix = String.Empty; context.NamespaceManager.PushScope (); switch (ident) { case BF.ElemString: node.LocalName = ReadUTF8 (); break; case BF.ElemStringPrefix: node.Prefix = ReadUTF8 (); node.NSSlot = ns_slot++; goto case BF.ElemString; case BF.ElemIndex: node.DictLocalName = ReadDictName (); break; case BF.ElemIndexPrefix: node.Prefix = ReadUTF8 (); node.NSSlot = ns_slot++; goto case BF.ElemIndex; } bool loop = true; do { ident = next < 0 ? ReadByteOrError () : next; next = -1; switch (ident) { case BF.AttrString: case BF.AttrStringPrefix: case BF.AttrIndex: case BF.AttrIndexPrefix: case BF.GlobalAttrIndex: case BF.GlobalAttrIndexInElemNS: ReadAttribute ((byte) ident); break; case BF.DefaultNSString: case BF.PrefixNSString: case BF.DefaultNSIndex: case BF.PrefixNSIndex: ReadNamespace ((byte) ident); break; default: next = ident; loop = false; break; } /* if (ident < 4) { // attributes if (attributes.Count == attr_count) attributes.Add (new AttrNodeInfo ()); AttrNodeInfo a = attributes [attr_count++]; a.Reset (); a.Position = source.Position; switch (ident) { case 0: a.LocalName = ReadUTF8 (); break; case 1: a.Prefix = ReadUTF8 (); goto case 0; case 2: a.DictLocalName = ReadDictName (); break; case 3: a.Prefix = ReadUTF8 (); goto case 2; } ReadAttributeValueBinary (a); } else if (ident < 6) { // namespaces string prefix = ident == 4 ? String.Empty : ReadUTF8 (); string ns = ReadUTF8 (); ns_store.Add (new QName (prefix, ns)); context.NamespaceManager.AddNamespace (prefix, ns); } else if (0x22 <= ident && ident < 0x3C) { // attributes with predefined ns index if (attributes.Count == attr_count) attributes.Add (new AttrNodeInfo ()); AttrNodeInfo a = attributes [attr_count++]; a.Reset (); a.Position = source.Position; a.NSSlot = ident - 0x22; a.LocalName = ReadUTF8 (); ReadAttributeValueBinary (a); } else { next = ident; break; } */ } while (loop); #if true node.NS = context.NamespaceManager.LookupNamespace (node.Prefix) ?? String.Empty; foreach (AttrNodeInfo a in attributes) if (a.Prefix.Length > 0) a.NS = context.NamespaceManager.LookupNamespace (a.Prefix); //Console.WriteLine ("[{0}-{1}->{3}/{2:X02}]", node.Prefix, node.LocalName, ident, node.NS); #else if (node.Prefix.Length == 0) foreach (QName q in ns_store) if (q.Name.Length == 0) { node.NS = q.Namespace; break; } else if (node.NSSlot >= 0) FillNamespaceBySlot (node); foreach (AttrNodeInfo a in attributes) { if (a.NSSlot >= 0) { /* if (a.NSSlot >= ns_store.Count) throw new XmlException (String.Format ("Binary XML data is not valid. An attribute node has an invalid index at position {0}. Index is {1}.", a.Position, a.NSSlot)); a.NS = ns_store [a.NSSlot].Namespace; a.Prefix = ns_store [a.NSSlot].Name; */ FillNamespaceBySlot (a); } else if (a.NSSlot == -2) { a.NS = node.NS; a.Prefix = node.Prefix; } } #endif ns_store.Clear (); ns_dict_store.Clear (); } void FillNamespaceBySlot (NodeInfo n) { if (n.NSSlot >= ns_store.Count) throw new XmlException (String.Format ("Binary XML data is not valid. The '{2}' node has an invalid index. Index is {1}. The position in the stream is at {0}.", n.Position, n.NSSlot, n.NodeType)); n.NS = ns_store [n.NSSlot].Namespace; //n.Prefix = ns_store [n.NSSlot].Name; } private void ReadAttribute (byte ident) { if (attributes.Count == attr_count) attributes.Add (new AttrNodeInfo ()); AttrNodeInfo a = attributes [attr_count++]; a.Reset (); a.Position = source.Position; switch (ident) { case BF.AttrString: a.LocalName = ReadUTF8 (); break; case BF.AttrStringPrefix: a.Prefix = ReadUTF8 (); a.NSSlot = ns_slot++; goto case BF.AttrString; case BF.AttrIndex: a.DictLocalName = ReadDictName (); break; case BF.AttrIndexPrefix: a.Prefix = ReadUTF8 (); a.NSSlot = ns_slot++; goto case BF.AttrIndex; case BF.GlobalAttrIndex: a.NSSlot = ns_slot++; a.DictLocalName = ReadDictName (); // FIXME: retrieve namespace break; case BF.GlobalAttrIndexInElemNS: a.Prefix = node.Prefix; a.DictLocalName = ReadDictName (); a.NSSlot = -2; break; } ReadAttributeValueBinary (a); } private void ReadNamespace (byte ident) { string prefix = null, ns = null; XmlDictionaryString dns; switch (ident) { case BF.DefaultNSString: prefix = String.Empty; ns = ReadUTF8 (); break; case BF.PrefixNSString: prefix = ReadUTF8 (); ns = ReadUTF8 (); break; case BF.DefaultNSIndex: prefix = String.Empty; dns = ReadDictName (); ns_dict_store.Add (ns_store.Count, dns); ns = dns.Value; break; case BF.PrefixNSIndex: prefix = ReadUTF8 (); dns = ReadDictName (); ns_dict_store.Add (ns_store.Count, dns); ns = dns.Value; break; } ns_store.Add (new QName (prefix, ns)); context.NamespaceManager.AddNamespace (prefix, ns); } private void ReadAttributeValueBinary (AttrNodeInfo a) { a.ValueIndex = attr_value_count; do { if (attr_value_count == attr_values.Count) attr_values.Add (new NodeInfo (true)); NodeInfo v = attr_values [attr_value_count++]; v.Reset (); int ident = ReadByteOrError (); is_next_end_element = ident > 0x80 && (ident & 1) == 1; ident -= is_next_end_element ? 1 : 0; if (!ReadTextOrValue ((byte) ident, v, true) || is_next_end_element) break; } while (true); } private bool ReadTextOrValue (byte ident, NodeInfo node, bool canSkip) { node.Value = null; node.ValueType = ident; node.NodeType = XmlNodeType.Text; switch (ident) { case BF.Zero: node.TypedValue = 0; break; case BF.One: node.TypedValue = 1; break; case BF.BoolFalse: node.TypedValue = false; break; case BF.BoolTrue: node.TypedValue = true; break; case BF.Int8: node.TypedValue = ReadByteOrError (); break; case BF.Int16: node.TypedValue = source.Reader.ReadInt16 (); break; case BF.Int32: node.TypedValue = source.Reader.ReadInt32 (); break; case BF.Int64: node.TypedValue = source.Reader.ReadInt64 (); break; case BF.Single: node.TypedValue = source.Reader.ReadSingle (); break; case BF.Double: node.TypedValue = source.Reader.ReadDouble (); break; case BF.Decimal: int [] bits = new int [4]; bits [3] = source.Reader.ReadInt32 (); bits [2] = source.Reader.ReadInt32 (); bits [0] = source.Reader.ReadInt32 (); bits [1] = source.Reader.ReadInt32 (); node.TypedValue = new Decimal (bits); break; case BF.DateTime: node.TypedValue = new DateTime (source.Reader.ReadInt64 ()); break; //case BF.UniqueId: // identical to .Text case BF.Base64: byte [] base64 = new byte [ReadVariantSize ()]; source.Reader.Read (base64, 0, base64.Length); node.TypedValue = base64; break; case BF.Base64Fixed: base64 = new byte [source.Reader.ReadInt16 ()]; source.Reader.Read (base64, 0, base64.Length); node.TypedValue = base64; break; case BF.TimeSpan: node.TypedValue = new TimeSpan (source.Reader.ReadInt64 ()); break; case BF.UniqueIdFromGuid: byte [] guid = new byte [16]; source.Reader.Read (guid, 0, guid.Length); node.TypedValue = new UniqueId (new Guid (guid)); break; case BF.Guid: guid = new byte [16]; source.Reader.Read (guid, 0, guid.Length); node.TypedValue = new Guid (guid); break; case BF.Text: node.Value = ReadUTF8 (); node.NodeType = XmlNodeType.Text; break; case BF.EmptyText: node.Value = String.Empty; node.NodeType = XmlNodeType.Text; break; default: if (!canSkip) throw new ArgumentException (String.Format ("Unexpected binary XML data at position {1}: {0:X}", ident + (is_next_end_element ? 1 : 0), source.Position)); next = ident; return false; } return true; /* if (ident == 0x8B) { // empty text node.Value = String.Empty; node.NodeType = XmlNodeType.Text; } else if (0x83 <= ident && ident <= 0x85 || 0x9D <= ident && ident <= 0x9F) { // text int sizeSpec = ident > 0x90 ? ident - 0x9D : ident - 0x83; node.Value = ReadUTF8 (sizeSpec); node.NodeType = XmlNodeType.Text; is_next_end_element = ident > 0x90; } else { switch (ident) { case 0x7B: // byte case 0x7C: // short case 0x7D: // int case 0x7E: // long case 0x7F: // float case 0x80: // double case 0x81: // decimal case 0x82: // DateTime case 0x8D: // UniqueId case 0x8E: // TimeSpan case 0x8F: // Guid case 0xA0: // base64Binary Console.WriteLine ("At position {0}({0:X})", source.Position); throw new NotImplementedException (); default: if (!canSkip) throw new ArgumentException (String.Format ("Unexpected binary XML data at position {1}: {0:X}", ident, source.Position)); next = ident; return false; } } return true; */ } private int ReadVariantSize () { int size = 0; // If sizeSpec < 0, then it is variant size specifier. // Otherwise it is fixed size s = sizeSpec + 1 byte(s). do { size <<= 7; byte got = ReadByteOrError (); size += got; if (got < 0x80) break; size -= 0x80; } while (true); return size; } private string ReadUTF8 () { int size = ReadVariantSize (); if (size == 0) return String.Empty; if (tmp_buffer.Length < size) { int extlen = tmp_buffer.Length * 2; tmp_buffer = new byte [size < extlen ? extlen : size]; } size = source.Read (tmp_buffer, 0, size); return utf8enc.GetString (tmp_buffer, 0, size); } private XmlDictionaryString ReadDictName () { int key = ReadVariantSize (); XmlDictionaryString s; if ((key & 1) == 1) { if (session.TryLookup (key >> 1, out s)) return s; } else { if (dictionary.TryLookup (key >> 1, out s)) return s; } throw new XmlException (String.Format ("Input XML binary stream is invalid. No matching XML dictionary string entry at {0}. Binary stream position at {1}", key, source.Position)); } private byte ReadByteOrError () { int ret = source.ReadByte (); if (ret < 0) throw new XmlException (String.Format ("Unexpected end of binary stream. Position is at {0}", source.Position)); return (byte) ret; } public override void ResolveEntity () { throw new NotSupportedException ("this XmlReader does not support ResolveEntity."); } public override bool TryGetBase64ContentLength (out int length) { length = 0; if (current.ValueType != BF.Base64 && current.ValueType != BF.Base64Fixed) return false; length = ((byte []) current.TypedValue).Length; return true; } public override string ReadContentAsString () { string value = Value; do { switch (NodeType) { case XmlNodeType.Element: case XmlNodeType.EndElement: return value; case XmlNodeType.Text: value += Value; break; } } while (Read ()); return value; } #region read typed content public override int ReadContentAsInt () { int ret = GetIntValue (); Read (); return ret; } int GetIntValue () { switch (node.ValueType) { case BF.Zero: return 0; case BF.One: return 1; case BF.Int8: return (byte) current.TypedValue; case BF.Int16: return (short) current.TypedValue; case BF.Int32: return (int) current.TypedValue; } throw new InvalidOperationException ("Current content is not an integer"); } public override long ReadContentAsLong () { if (node.ValueType == BF.Int64) { long v = (long) current.TypedValue; Read (); return v; } return ReadContentAsInt (); } public override float ReadContentAsFloat () { if (node.ValueType != BF.Single) throw new InvalidOperationException ("Current content is not a single"); float v = (float) current.TypedValue; Read (); return v; } public override double ReadContentAsDouble () { if (node.ValueType != BF.Double) throw new InvalidOperationException ("Current content is not a double"); double v = (double) current.TypedValue; Read (); return v; } // FIXME: this is not likely to consume sequential base64 nodes. public override byte [] ReadContentAsBase64 () { byte [] ret = null; if (node.ValueType != BF.Base64 && node.ValueType != BF.Base64Fixed) throw new InvalidOperationException ("Current content is not base64"); while (NodeType == XmlNodeType.Text && (node.ValueType == BF.Base64 || node.ValueType == BF.Base64Fixed)) { if (ret == null) ret = (byte []) node.TypedValue; else { byte [] tmp = (byte []) node.TypedValue; byte [] tmp2 = new byte [ret.Length + tmp.Length]; Array.Copy (ret, tmp2, ret.Length); Array.Copy (tmp, 0, tmp2, ret.Length, tmp.Length); ret = tmp2; } Read (); //MoveToContent (); } return ret; } public override Guid ReadContentAsGuid () { if (node.ValueType != BF.Guid) throw new InvalidOperationException ("Current content is not a Guid"); Guid ret = (Guid) node.TypedValue; Read (); return ret; } public override UniqueId ReadContentAsUniqueId () { switch (node.ValueType) { case BF.Text: UniqueId ret = new UniqueId (node.Value); Read (); return ret; case BF.UniqueIdFromGuid: ret = (UniqueId) node.TypedValue; Read (); return ret; default: throw new InvalidOperationException ("Current content is not a UniqueId"); } } #endregion } }