summaryrefslogtreecommitdiff
path: root/src/library/scala/xml/pull/XMLEventReader.scala
blob: e2e874e1916d5633c9d7ea8c5d2e1aac3f3f89fe (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/*                     __                                               *\
**     ________ ___   / /  ___     Scala API                            **
**    / __/ __// _ | / /  / _ |    (c) 2003-2009, LAMP/EPFL             **
**  __\ \/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **
** /____/\___/_/ |_/____/_/ | |                                         **
**                          |/                                          **
\*                                                                      */

// $Id$


package scala.xml.pull


import java.lang.{Runnable, Thread}

import scala.io.Source
import scala.xml.parsing.{ExternalSources, MarkupHandler, MarkupParser}

/** <p>
 *   A pull parser that offers to view an XML document as a series of events.
 *   Please note that this API might change. Here's how to use this class
 *  </p><pre>
 *  <b>import</b> scala.xml._
 *  <b>import</b> scala.xml.pull._
 *  <b>import</b> scala.io.Source
 *
 *  <b>object</b> reader {
 *    <b>val</b> src = Source.fromString("<hello><world/></hello>")
 *    <b>val</b> er = new XMLEventReader().initialize(src)
 *
 *    <b>def</b> main(args: Array[String]) {
 *      Console.println(er.next)
 *      Console.println(er.next)
 *    }
 *  }
 *  </pre>
 *
 *  @author Burak Emir
 */
class XMLEventReader extends Iterator[XMLEvent] {

  var src:Source = null
  def getSource = this.src
  def initialize(src: Source): this.type = {
    this.src = src
    this.parserThread = new Thread(new Parser())
    this.parserThread.start()
    this
  }

  // -- this part of the class is for communication with the thread
  var xmlEvent: XMLEvent = null
  var continue: Boolean = true

  def myresume = synchronized {
    while (continue) {
      wait()
    }
    continue = true
    notify()
  }
  def getAndClearEvent: XMLEvent = synchronized {
    while (xmlEvent eq null) {
      wait()
    }
    val r = xmlEvent
    xmlEvent = null
    r
  }
  def setEvent(e: XMLEvent) {
    xmlEvent = e
  }

  def doNotify(): NodeSeq = synchronized {
    XMLEventReader.this.continue = false
    notify()
    while (!XMLEventReader.this.continue) {
      try { wait() } catch {
       case _: java.lang.InterruptedException => /* ignore */
	  }
	}
    NodeSeq.Empty
  }

  // iterator methods

  def next: XMLEvent = {
    myresume
    val r = getAndClearEvent
    r
  }

  def hasNext = true

  // After calling stop, one must call initialize to be able to get new events.
  def stop = {
    continue = true;
    parserThread.interrupt();
    parserThread = null;
  }

  var parserThread: Thread = null

  class Parser extends MarkupHandler with MarkupParser with ExternalSources with Runnable {

    val preserveWS = true
    lazy val input = XMLEventReader.this.getSource
	// document must contain one element - avoid spurious syntax error
    final val ignore_node = <ignore/>

    override def elemStart(pos: Int, pre: String, label: String, attrs: MetaData, scope: NamespaceBinding) {
      setEvent(EvElemStart(pre, label, attrs, scope)); doNotify
    }

    override def elemEnd(pos: Int, pre: String, label: String) {
      setEvent(EvElemEnd(pre, label)); doNotify
    }

    final def elem(pos: Int, pre: String, label: String, attrs: MetaData, pscope: NamespaceBinding, nodes: NodeSeq): NodeSeq =
      ignore_node

    def procInstr(pos: Int, target: String, txt: String): NodeSeq = {
      setEvent(EvProcInstr(target, txt)); doNotify
    }

    def comment(pos: Int, txt: String): NodeSeq = {
      setEvent(EvComment(txt)); doNotify
    }

    def entityRef(pos: Int, n: String): NodeSeq = {
      setEvent(EvEntityRef(n)); doNotify
    }

    def text(pos: Int, txt:String): NodeSeq = {
      setEvent(EvText(txt)); doNotify
    }

    override def run() {
      curInput = input
      this.nextch
      doNotify()
      this.document()
    }
  }
}