context: /* --------------------------------------------------------------------------
   * Copyrights
   *
   * Portions created by or assigned to Cursive Systems, Inc. are
   * Copyright (c) 2002-2008 Cursive Systems, Inc.  All Rights Reserved.  Contact
   * information for Cursive Systems, Inc. is available at
   * http://www.cursive.net/.
   *
   * License
   *
   * Jabber-Net is licensed under the LGPL.
   * See LICENSE.txt for details.
   * --------------------------------------------------------------------------*/
  using System;
  using System.Collections;
  using System.Diagnostics;
  using System.Reflection;
  using System.Security.Cryptography;
  using System.Text;
  using System.Text.RegularExpressions;
  using System.Xml;
  
  using bedrock.util;
  
  namespace jabber.protocol
  {
      /// <summary>
      /// An enum that should translate "_" into "-" for use externally.
      /// </summary>
      [SVN(@"$Id$")]
      public class DashAttribute : Attribute
      {
          /// <summary>
          /// This is just a flag attribute.
          /// </summary>
          public DashAttribute()
          {
          }
      }
  
      /// <summary>
      /// An XmlElement with type-safe accessors.  This class is not much use by itself,
      /// but provides a number of utility functions for its descendants.
      /// </summary>
      [SVN(@"$Id$")]
      public class Element : XmlElement
      {
          /// <summary>
          /// UTF-8 encoding used throughout.
          /// </summary>
          protected static readonly Encoding ENCODING = Encoding.UTF8;
  
          /// <summary>
          /// Fix up bad namespaces that don't need to be sent on XML streams.
          /// jabber:client and jabber:component:accept are removed from the root element,
          /// and empty namespace declarations are removed throughout.
          /// </summary>
          private static readonly Regex s_RemoveNS = 
              new Regex("(?:(?<=^[^>]*)( xmlns=\"(?:jabber:client|jabber:component:accept)\")| xmlns=\"\")",
                        RegexOptions.Compiled);
  
          /// <summary>
          ///
          /// </summary>
          /// <param name="prefix"></param>
          /// <param name="qname"></param>
          /// <param name="doc"></param>
          public Element(string prefix, XmlQualifiedName qname, XmlDocument doc) :
              base(prefix, qname.Name, qname.Namespace, doc)
          {
          }
          /// <summary>
          ///
          /// </summary>
          /// <param name="localName"></param>
          /// <param name="doc"></param>
          public Element(string localName, XmlDocument doc) :
              base("", localName, "", doc)
          {
          }
          /// <summary>
          ///
          /// </summary>
          /// <param name="localName"></param>
          /// <param name="namespaceURI"></param>
          /// <param name="doc"></param>
          public Element(string localName, string namespaceURI, XmlDocument doc) :
              base("", localName, namespaceURI, doc)
          {
          }
  
          /// <summary>
          /// Returns the first child element with the given type.
          /// 
          /// You might expect this to be slower than this["name", "uri"], but it's 
          /// probably actually faster, since that code has to check several different
          /// things, and this code can just do a type comparison.
          /// </summary>
          /// <typeparam name="T">The type of child to search for</typeparam>
          /// <returns>The first child with the given type, or null if none found</returns>
          public T GetChildElement<T>()
              where T : XmlElement
          {
              for (XmlNode node = this.FirstChild; node != null; node = node.NextSibling)
              {
                  if (node is T)
                      return (T)node;
              }
              return null;
          }
  
          /// <summary>
          /// The xml:lang of this element.
          /// </summary>
          public string Lang
          {
              get
              {
                  if (!HasAttribute("lang", URI.XML))
                      return null;
                  return GetAttribute("lang", URI.XML);
              }
              set
              {
                  if (HasAttribute("lang", URI.XML))
                      RemoveAttribute("lang", URI.XML);
                  if (value != null)
                  {
                      XmlAttribute attr = OwnerDocument.CreateAttribute("xml:lang", URI.XML);
                      attr.Value = value;
                      this.Attributes.Append(attr);
                  }
              }
          }
  
          /// <summary>
          /// Add a child element.  The element can be from a different document.
          /// </summary>
          /// <param name="value"></param>
          public void AddChild(XmlElement value)
          {
              if (this.OwnerDocument == value.OwnerDocument)
              {
                  this.AppendChild(value);
              }
              else
              {
                  this.AppendChild(this.OwnerDocument.ImportNode(value, true));
              }
          }
  
          /// <summary>
          /// Get a string representation of this element and its children, with the default
          /// namespace stripped off if and only if it is jabber:client or jabber:component:accept.
          /// </summary>
          public override string OuterXml
          {
              get
              {
                  return s_RemoveNS.Replace(base.OuterXml, "");
              }
          }
  
          /// <summary>
          /// The implementation of OuterXml from XmlElement, without removing the jabber:client
          /// namespace.  Needed for Stream.
          /// </summary>
          protected string OriginalOuterXml
          {
              get { return base.OuterXml; }
          }
  
          /// <summary>
          /// Returns an XmlNodeList containing a list of child elements that match the specified localname and namespace URI.
          /// </summary>
          /// <param name="localName"></param>
          /// <param name="namespaceURI"></param>
          /// <returns></returns>
          public override XmlNodeList GetElementsByTagName(string localName, string namespaceURI)
          {
              return new ElementList(this, localName, namespaceURI);
          }
  
          /// <summary>
          /// Returns an XmlNodeList containing a list of child elements that match the specified localname.
          /// </summary>
          /// <param name="localName"></param>
          /// <returns></returns>
          public override XmlNodeList GetElementsByTagName(string localName)
          {
              return new ElementList(this, localName);
          }
  
          /// <summary>
          /// Get a list of child elements that have the specified type.
          /// </summary>
          /// <typeparam name="T">The type of element to search for</typeparam>
          /// <returns>A typed element list</returns>
          public TypedElementList<T> GetElements<T>()
              where T : XmlElement
          {
              return new TypedElementList<T>(this);
          }
  
          /// <summary>
          /// Gett he text contents of the first sub-element with the specified type
          /// </summary>
          /// <typeparam name="T"></typeparam>
          /// <returns></returns>
          protected string GetElem<T>()
              where T : Element
          {
              T e = GetChildElement<T>();
              if (e == null)
                  return null;
              if (!e.HasChildNodes)
                  return null;
              return e.InnerText;
          }
  
          /// <summary>
          /// Get the text contents of a sub-element.
          /// </summary>
          /// <param name="name"></param>
          /// <returns></returns>
          protected string GetElem(string name)
          {
              XmlElement e = this[name];
              if (e == null)
                  return null;
              if (!e.HasChildNodes)
                  return null;
              return e.InnerText;
          }
  
          /// <summary>
          /// Sets the text contents of a sub-element with a specified type.
          /// Creates the element if it doesn't exist.
          /// </summary>
          /// <typeparam name="T"></typeparam>
          /// <param name="value"></param>
          /// <returns></returns>
          public T SetElem<T>(string value)
              where T : Element
          {
              T result = GetOrCreateElement<T>();
              result.RemoveAll();
              if (value != null)
                  result.InnerText = value;
              return result;
          }
  
          /// <summary>
          /// Sets the text contents of a sub-element.
          /// Note: Do not use this if you want the sub-element to have a type that is not XmlElement.
          /// Instead use <see cref="SetElem(string,string,Type)"/>
          /// </summary>
          /// <param name="name">The element tag.</param>
          /// <param name="value">The inner text of the element.</param>
          protected void SetElem(string name, string value)
          {
              XmlElement e = GetOrCreateElement(name, null, null);
              e.RemoveAll();
  
              if (value != null)
                  e.InnerText = value;
          }
  
          /// <summary>
          /// Sets the text contents of a sub-element.
          /// </summary>
          /// <param name="name">The element tag.</param>
          /// <param name="value">The inner text of the element.</param>
          /// <param name="typeToCreate">If the element doesn't exist, create it with this type.  If null, then just use an XmlElement.</param>
          protected void SetElem(string name, string value, Type typeToCreate)
          {
              XmlElement e = GetOrCreateElement(name, null, typeToCreate);
              e.RemoveAll();
  
              if (value != null)
                  e.InnerText = value;
          }
  
          /// <summary>
          /// Create an element that is a child of this element, of the specified type.
          /// </summary>
          /// <typeparam name="T"></typeparam>
          /// <returns></returns>
          protected T CreateChildElement<T>()
              where T : Element
          {
              // Note: It would be cool to just do new T(OwnerDocument), but you can only call
              // parameter-less constructors in generic-land.
              ConstructorInfo constructor = typeof(T).GetConstructor(new Type[] { typeof(XmlDocument) });
              Debug.Assert(constructor != null, "Type " + typeof(T).ToString() + " does not have a constructor taking an XmlDocument");
              T c = (T)constructor.Invoke(new object[] { this.OwnerDocument });
              AppendChild(c);
              return c;
          }
  
          /// <summary>
          /// If a child element exists with the given type, return it.  Otherwise,
          /// gin up a new instance of the given type, add it as a child, 
          /// and return the result.
          /// 
          /// This should not have the performance impact of GetOrCreateElement.
          /// </summary>
          /// <typeparam name="T"></typeparam>
          /// <returns></returns>
          protected T GetOrCreateElement<T>()
              where T : Element
          {
              T c = GetChildElement<T>();
              if (c == null)
                  c = CreateChildElement<T>();
              return c;
          }
  
          /// <summary>
          /// If the named element exists as a child, return it.  Otherwise, gin up
          /// a new instance of the given class (which must be a subclass of XmlElement)
          /// add it as a child, and return the result.  Will often be paired with
          /// ReplaceChild as the setter.
          /// </summary>
          /// <remarks>
          /// This seems kind of around-the-barn.  Wish there was an easier way to do this,
          /// rather than having to get the constructor, and whatnot.  Hopefully it won't
          /// be called all that often, so the speed issue won't be too bad.
          /// </remarks>
          /// <param name="name"></param>
          /// <param name="xmlns">Namespace URI.  Null to use parent's.</param>
          /// <param name="typeToCreate">If the element doesn't exist, create it with this type.  If null, then just use an XmlElement.</param>
          /// <returns></returns>
          protected XmlElement GetOrCreateElement(string name, string xmlns, Type typeToCreate)
          {
              string ns = (xmlns!=null) ? xmlns : NamespaceURI;
              XmlElement child = this[name, ns];
              if (child != null)
                  return child;
  
              if (typeToCreate == null)
                  child = this.OwnerDocument.CreateElement(name, ns);
              else
              {
                  Debug.Assert(typeToCreate.IsSubclassOf(typeof(XmlElement)));
  
                  ConstructorInfo constructor = typeToCreate.GetConstructor(new Type[] { typeof(XmlDocument) });
                  Debug.Assert(constructor != null);
                  child = constructor.Invoke(new object[] { this.OwnerDocument }) as XmlElement;
                  Debug.Assert(child != null);
              }
  
              this.AppendChild(child);
              return child;
          }
  
          /// <summary>
          /// Replaces the first element that has the specified type.
          /// </summary>
          /// <typeparam name="T">The type of sub-element to find</typeparam>
          /// <param name="elem">The element to replace; if this is null, the old element is just deleted</param>
          /// <returns>The replaced element</returns>
          protected T ReplaceChild<T>(T elem)
              where T : Element
          {
              T old = GetChildElement<T>();
              if (old != null)
                  this.RemoveChild(old);
              if (elem != null)
                  AddChild(elem);
              return old;
          }
  
          /// <summary>
          /// Replaces the first element that has the same name
          /// with the passed in element.
          /// </summary>
          /// <param name="elem">The new element</param>
          /// <returns>The replaced element</returns>
          protected XmlElement ReplaceChild(XmlElement elem)
          {
              XmlElement old = this[elem.Name, elem.NamespaceURI];
              if (old != null)
              {
                  this.RemoveChild(old);
              }
              if (elem != null)
                  AddChild(elem);
              return old;
          }
  
          /// <summary>
          /// Remove a child element of the specified type
          /// </summary>
          /// <typeparam name="T"></typeparam>
          /// <returns></returns>
          protected T RemoveElem<T>()
              where T : Element
          {
              T e = GetChildElement<T>();
              if (e != null)
                  this.RemoveChild(e);
              return e;
          }
  
          /// <summary>
          /// Remove a child element
          /// </summary>
          /// <param name="name"></param>
          /// <returns>The old element, or null if it didn't exist.</returns>
          protected XmlElement RemoveElem(string name)
          {
              XmlElement e = this[name];
              if (e != null)
                  this.RemoveChild(e);
              return e;
          }
  
          /// <summary>
          /// Remove each of the child elements with the specified type.
          /// </summary>
          /// <typeparam name="T"></typeparam>
          protected void RemoveElems<T>()
              where T : Element
          {
  
gt:             foreach (T child in GetElements<T>())