/*                     __                                               *\
**     ________ ___   / /  ___     Scala API                            **
**    / __/ __// _ | / /  / _ |    (c) 2003-2009, LAMP/EPFL             **
**  __\ \/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **
** /____/\___/_/ |_/____/_/ | |                                         **
**                          |/                                          **
\*                                                                      */

// $Id: Node.scala 18210 2009-07-05 14:46:37Z extempore $


package scala.xml

/**
 * This object provides methods ...
 *
 * @author  Burak Emir
 * @version 1.0
 */
object Node {

  /** the constant empty attribute sequence */
  final def NoAttributes: MetaData = Null

  /** the empty namespace */
  val EmptyNamespace = ""

  def unapplySeq(n: Node) = Some((n.label, n.attributes, n.child))

}

/**
 * An abstract class representing XML with nodes of a labelled tree.
 * This class contains an implementation of a subset of XPath for navigation.
 *
 * @author  Burak Emir and others
 * @version 1.1
 */
abstract class Node extends NodeSeq {

  /** prefix of this node */
  def prefix: String = null

  /** label of this node. I.e. "foo" for <foo/>) */
  def label: String

  /** used internally. Atom/Molecule = -1 PI = -2 Comment = -3 EntityRef = -5
   */ 
  def isAtom = this.isInstanceOf[Atom[_]]
  
  /** The logic formerly found in typeTag$, as best I could infer it. */
  def doCollectNamespaces = true  // if (tag >= 0) DO collect namespaces
  def doTransform         = true  // if (tag < 0) DO NOT transform

  /**
   *  method returning the namespace bindings of this node. by default, this
   *  is TopScope, which means there are no namespace bindings except the
   *  predefined one for "xml".
   */
  def scope: NamespaceBinding = TopScope

  /**
   *  convenience, same as <code>getNamespace(this.prefix)</code>
   */
  def namespace = getNamespace(this.prefix)

  /**
   * Convenience method, same as <code>scope.getURI(pre)</code> but additionally
   * checks if scope is <code>null</code>.
   *
   * @param pre the prefix whose namespace name we would like to obtain
   * @return    the namespace if <code>scope != null</code> and prefix was
   *            found, else <code>null</code>
   */
  def getNamespace(pre: String): String = if (scope eq null) null else scope.getURI(pre)

  /**
   * Convenience method, looks up an unprefixed attribute in attributes of this node.
   * Same as <code>attributes.getValue(key)</code>
   *
   * @param  key of queried attribute.
   * @return value of <code>UnprefixedAttribute</code> with given key
   *         in attributes, if it exists, otherwise <code>null</code>.
   */
  final def attribute(key: String): Option[Seq[Node]] = attributes.get(key)

  /**
   * Convenience method, looks up a prefixed attribute in attributes of this node.
   * Same as <code>attributes.getValue(uri, this, key)</code>
   *
   * @param  uri namespace of queried attribute (may not be null).
   * @param  key of queried attribute.
   * @return value of <code>PrefixedAttribute</code> with given namespace
   *         and given key, otherwise <code>null</code>.
   */
  final def attribute(uri: String, key: String): Option[Seq[Node]] =
    attributes.get(uri, this, key)

  /**
   * Returns attribute meaning all attributes of this node, prefixed and
   * unprefixed, in no particular order. In class <code>Node</code>, this
   * defaults to <code>Null</code> (the empty attribute list).
   *
   * @return all attributes of this node
   */
  def attributes: MetaData = Null

  /**
   * Returns child axis i.e. all children of this node.
   *
   * @return all children of this node
   */
  def child: Seq[Node]

  /**
   * Descendant axis (all descendants of this node, not including node itself) 
   * includes all text nodes, element nodes, comments and processing instructions.
   */
  def descendant: List[Node] =
    child.toList.flatMap { x => x::x.descendant }

  /**
   * Descendant axis (all descendants of this node, including thisa node) 
   * includes all text nodes, element nodes, comments and processing instructions.
   */
  def descendant_or_self: List[Node] = this :: descendant

  /**
   * Returns true if x is structurally equal to this node. Compares prefix,
   * label, attributes and children.
   *
   * @param x ...
   * @return  <code>true</code> if ..
   */
  override def equals(x: Any): Boolean = x match {
    case g: Group   => false
    case that: Node =>
      this.prefix == that.prefix &&
      this.label == that.label && 
      this.attributes == that.attributes &&
      equalChildren(that)
    case _          => false
  }

  // children comparison has to be done carefully - see bug #1773.
  // It would conceivably be a better idea for a scala block which
  // generates the empty string not to generate a child rather than
  // our having to filter it later, but that approach would be more
  // delicate to implement.    
  private def equalChildren(that: Node) = {
    def noEmpties(xs: Seq[Node]) = xs filter (_.toString() != "")
    noEmpties(this.child) sameElements noEmpties(that.child)
  }

  /** <p>
   *    Returns a hashcode. The default implementation here calls only
   *    super.hashcode (which is the same as for objects). A more useful
   *    implementation can be invoked by calling 
   *  <code>Utility.hashCode(pre, label, attributes.hashCode(), child)</code>.
   *  </p>
   */
  override def hashCode(): Int = super.hashCode

  // implementations of NodeSeq methods

  /**
   *  returns a sequence consisting of only this node
   */
  def theSeq: Seq[Node] = this :: Nil

  /**
   * String representation of this node
   *
   * @param stripComment if true, strips comment nodes from result
   * @return ...
   */
  def buildString(stripComments: Boolean): String =
    Utility.toXML(this, stripComments = stripComments).toString

  /**
   * Same as <code>toString(false)</code>.
   *
   * @see <code><a href="#toString">toString(Boolean)</a></code>
   */
  override def toString(): String = buildString(false)

  /**
   * Appends qualified name of this node to <code>StringBuilder</code>.
   *
   * @param sb ...
   * @return   ...
   */
  def nameToString(sb: StringBuilder): StringBuilder = {
    if (null != prefix) {
      sb.append(prefix)
      sb.append(':')
    }
    sb.append(label)
  }

  /**
   * Returns a type symbol (e.g. DTD, XSD), default <code>null</code>.
   */
  def xmlType(): TypeSymbol = null

  /**
   * Returns a text representation of this node. Note that this is not equivalent to
   * the XPath node-test called text(), it is rather an implementation of the
   * XPath function string()
   *  Martin to Burak: to do: if you make this method abstract, the compiler will now
   *  complain if there's no implementation in a subclass. Is this what we want? Note that
   *  this would break doc/DocGenator and doc/ModelToXML, with an error message like:
doc\DocGenerator.scala:1219: error: object creation impossible, since there is a deferred declaration of method text in class Node of type => String which is not implemented in a subclass
    new SpecialNode {
    ^
   */
  override def text: String = super.text
}