namespace DotNetOAuth { using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Xml; using System.Xml.Linq; /// /// An XmlReader-looking object that actually reads from a dictionary. /// class DictionaryXmlReader { /// /// Creates an XmlReader that reads data out of a dictionary instead of XML. /// /// The name of the root XML element. /// The dictionary to read data from. /// The XmlReader that will read the data out of the given dictionary. internal static XmlReader Create(XName rootElement, IDictionary fields) { if (fields == null) { throw new ArgumentNullException("fields"); } return CreateRoundtripReader(rootElement, fields); // The pseudo reader MAY be more efficient, but it's buggy. //return CreatePseudoReader(rootElement, fields); } private static XmlReader CreateRoundtripReader(XName rootElement, IDictionary fields) { MemoryStream stream = new MemoryStream(); XmlWriter writer = XmlWriter.Create(stream); SerializeDictionaryToXml(writer, rootElement, fields); writer.Flush(); stream.Seek(0, SeekOrigin.Begin); // For debugging purposes. StreamReader sr = new StreamReader(stream); Trace.WriteLine(sr.ReadToEnd()); stream.Seek(0, SeekOrigin.Begin); return XmlReader.Create(stream); } static XmlReader CreatePseudoReader(XName rootElement, IDictionary fields) { return new PseudoXmlReader(fields); } static void SerializeDictionaryToXml(XmlWriter writer, XName rootElement, IDictionary fields) { if (writer == null) { throw new ArgumentNullException("writer"); } if (fields == null) { throw new ArgumentNullException("fields"); } writer.WriteStartElement(rootElement.LocalName, rootElement.NamespaceName); // The elements must be serialized in alphabetical order so the DataContractSerializer will see them. foreach (var pair in fields.OrderBy(pair => pair.Key, StringComparer.Ordinal)) { writer.WriteStartElement(pair.Key, rootElement.NamespaceName); writer.WriteValue(pair.Value); writer.WriteEndElement(); } writer.WriteEndElement(); } /// /// Reads in a dictionary as if it were XML. /// class PseudoXmlReader : XmlReader { IDictionary dictionary; IEnumerator keyEnumerator; bool isFinished; int depth; XmlNodeType currentNode = XmlNodeType.None; internal PseudoXmlReader(IDictionary dictionary) { this.dictionary = dictionary; keyEnumerator = dictionary.Keys.OrderBy(key => key, StringComparer.Ordinal).GetEnumerator(); } public override int AttributeCount { get { return 0; } } public override bool IsEmptyElement { get { if (keyEnumerator.Current == null) { return isFinished; } string value; bool result = !dictionary.TryGetValue(keyEnumerator.Current, out value) || String.IsNullOrEmpty(value); return result; } } public override string LocalName { get { return keyEnumerator.Current; } } public override bool MoveToElement() { Trace.WriteLine("MoveToElement()"); bool result = currentNode != XmlNodeType.Element && depth > 0; currentNode = depth > 0 ? XmlNodeType.Element : XmlNodeType.None; return result; } public override bool MoveToNextAttribute() { Trace.WriteLine("MoveToNextAttribute()"); if (depth == 1 && currentNode != XmlNodeType.Attribute) { currentNode = XmlNodeType.Attribute; return true; } else { return false; } } public override string NamespaceURI { get { string result = depth == 1 && currentNode == XmlNodeType.Attribute ? "http://www.w3.org/2000/xmlns/" : Protocol.DataContractNamespace; return result; } } public override XmlNodeType NodeType { get { return currentNode; } } public override bool Read() { Trace.WriteLine("Read()"); if (isFinished) { if (depth > 0) { depth--; } return depth > 0; } switch (depth) { case 0: // moving to root node depth++; // -> 1 currentNode = XmlNodeType.Element; return true; case 1: // moving to first content node depth++; // -> 2 isFinished = !keyEnumerator.MoveNext(); currentNode = isFinished ? XmlNodeType.EndElement : XmlNodeType.Element; return true; case 2: // content node switch (currentNode) { case XmlNodeType.Element: currentNode = XmlNodeType.Text; return true; case XmlNodeType.Text: currentNode = XmlNodeType.EndElement; return true; case XmlNodeType.EndElement: bool result = keyEnumerator.MoveNext(); if (!result) { isFinished = true; depth--; currentNode = XmlNodeType.EndElement; } else { currentNode = XmlNodeType.Element; } return true; } break; } throw new InvalidOperationException(); } public override string Value { get { return dictionary[keyEnumerator.Current]; } } #region Unimplemented methods public override string BaseURI { get { throw new NotImplementedException(); } } public override void Close() { throw new NotImplementedException(); } public override int Depth { get { Trace.WriteLine("Depth: " + (depth - 1)); return depth - 1; } } public override bool EOF { get { throw new NotImplementedException(); } } public override string GetAttribute(int i) { throw new NotImplementedException(); } public override string GetAttribute(string name, string namespaceURI) { throw new NotImplementedException(); } public override string GetAttribute(string name) { throw new NotImplementedException(); } public override bool HasValue { get { throw new NotImplementedException(); } } public override string LookupNamespace(string prefix) { throw new NotImplementedException(); } public override bool MoveToAttribute(string name, string ns) { throw new NotImplementedException(); } public override bool MoveToAttribute(string name) { throw new NotImplementedException(); } public override bool MoveToFirstAttribute() { throw new NotImplementedException(); } public override XmlNameTable NameTable { get { throw new NotImplementedException(); } } public override string Prefix { get { throw new NotImplementedException(); } } public override ReadState ReadState { get { Trace.WriteLine("ReadState"); return ReadState.Interactive; } } public override bool ReadAttributeValue() { throw new NotImplementedException(); } public override void ResolveEntity() { throw new NotImplementedException(); } #endregion } } }