package scala.xml
package parsing
import java.io.{ InputStream, Reader, File, FileDescriptor, FileInputStream }
import scala.collection.{ mutable, Iterator }
import org.xml.sax.Attributes
import org.xml.sax.helpers.DefaultHandler
trait ConsoleErrorHandler extends DefaultHandler {
  
  override def warning(ex: SAXParseException): Unit = { }
  override def error(ex: SAXParseException): Unit = printError("Error", ex) 
  override def fatalError(ex: SAXParseException): Unit = printError("Fatal Error", ex)
  protected def printError(errtype: String, ex: SAXParseException): Unit =
    Console.withOut(Console.err) {
      val s = "[%s]:%d:%d: %s".format(
        errtype, ex.getLineNumber, ex.getColumnNumber, ex.getMessage)
      Console.println(s)
      Console.flush
    }
}
abstract class FactoryAdapter extends DefaultHandler with factory.XMLLoader[Node] {
  var rootElem: Node = null
  
  val buffer      = new StringBuilder()
  val attribStack = new mutable.Stack[MetaData]
  val hStack      = new mutable.Stack[Node]   
  val tagStack    = new mutable.Stack[String]
  var scopeStack  = new mutable.Stack[NamespaceBinding]
  var curTag : String = null
  var capture: Boolean = false
  
  
  def nodeContainsText(localName: String): Boolean 
  
  
  def createNode(pre: String, elemName: String, attribs: MetaData,
                 scope: NamespaceBinding, chIter: List[Node]): Node 
  
  
  def createText(text: String): Text 
  
  def createProcInstr(target: String, data: String): Seq[ProcInstr] 
    
  
  
  
  
  val normalizeWhitespace = false
  
  override def characters(ch: Array[Char], offset: Int, length: Int): Unit = {
    if (!capture) return
    
    else if (!normalizeWhitespace) buffer.appendAll(ch, offset, length)
    
    else {
      var it = ch.slice(offset, offset + length).iterator
      while (it.hasNext) {
        val c = it.next
        val isSpace = c.isWhitespace
        buffer append (if (isSpace) ' ' else c)
        if (isSpace)
          it = it dropWhile (_.isWhitespace)
      }
    }
  }
  private def splitName(s: String) = {
    val idx = s indexOf ':'
    if (idx < 0) (null, s)
    else (s take idx, s drop (idx + 1))
  }
  
  
  override def startElement(
    uri: String,
    _localName: String,
    qname: String,
    attributes: Attributes): Unit =
  {
    captureText()
    tagStack push curTag
    curTag = qname
    val localName = splitName(qname)._2
    capture = nodeContainsText(localName)
    hStack push null
    var m: MetaData = Null
    var scpe: NamespaceBinding = 
      if (scopeStack.isEmpty) TopScope
      else scopeStack.top
    
    for (i <- 0 until attributes.getLength()) {
      val qname = attributes getQName i
      val value = attributes getValue i
      val (pre, key) = splitName(qname)
      def nullIfEmpty(s: String) = if (s == "") null else s
      
      if (pre == "xmlns" || (pre == null && qname == "xmlns")) {
        val arg = if (pre == null) null else key
        scpe = new NamespaceBinding(arg, nullIfEmpty(value), scpe)
      }
      else
        m = Attribute(Option(pre), key, Text(value), m)
    }
    
    scopeStack push scpe
    attribStack push m
  }
  
  
  def captureText(): Unit = {
    if (capture && buffer.length > 0)
      hStack push createText(buffer.toString)
    
    buffer.clear()
  }
  
  
  override def endElement(uri: String , _localName: String, qname: String): Unit = {  
    captureText()
    val metaData = attribStack.pop
  
    
    val v = (Iterator continually hStack.pop takeWhile (_ != null)).toList.reverse
    val (pre, localName) = splitName(qname)
    val scp = scopeStack.pop
  
    
    rootElem = createNode(pre, localName, metaData, scp, v)
    hStack push rootElem
    curTag = tagStack.pop
    capture = curTag != null && nodeContainsText(curTag) 
  }
  
  
  override def processingInstruction(target: String, data: String) {
    hStack pushAll createProcInstr(target, data)
  }
}