1 "This is a port of the Smalltalk YAXO XML parser. It was originally written
4 prototypes ensureNamespace: #XML.
6 XML define: #Node &parents: {Cloneable}.
8 n@(XML Node traits) contentsDo: block
11 n@(XML Node traits) elementsDo: block
14 n@(XML Node traits) firstTagNamed: name
15 "Return the first encountered node with the specified tag.
18 n elementsDo: [| :each | (answer := each firstTagNamed: name)
19 ifNotNil: [^ answer]].
23 n@(XML Node traits) firstTagNamed: name with: testBlock
24 "Return the first encountered node with the specified tag for which the block
25 returns True. Pass the message on."
27 n elementsDo: [| :each | (answer := each firstTagNamed: name with: testBlock)
28 ifNotNil: [^ answer]].
32 n@(XML Node traits) tagsNamed: name childrenDo: block
33 "Evaluate the block on all children that match."
35 n elementsDo: [| :each | each tagsNamed: name ifReceiverDo: block]
38 n@(XML Node traits) tagsNamed: name childrenDoAndRecurse: block
39 "Evaluate the block on all children that match and recurse."
41 n elementsDo: [| :each | each tagsNamed: name ifReceiverDoAndRecurse: block]
44 n@(XML Node traits) tagsNamed: name contentsDo: block
45 "Evaluate the block on all contents that match."
47 n elementsDo: [| :each | each tagsNamed: name contentsDo: block]
50 n@(XML Node traits) tagsNamed: name do: block
51 "Evaluate the block on all tags that match."
53 n elementsDo: [| :each | each tagsNamed: name do: block]
56 n@(XML Node traits) tagsNamed: name ifReceiverDo: block
57 "Default handler; only XML TagNode should handle this."
60 n@(XML Node traits) tagsNamed: name ifReceiverDoAndRecurse: block
61 "Recurse all children."
63 n elementsDo: [| :each | each tagsNamed: name ifReceiverDoAndRecurse: block]
66 n@(XML Node traits) tagsNamed: name ifReceiverOrChildDo: block
67 "Recurse all children."
69 n elementsDo: [| :each | each tagsNamed: name ifReceiverDo: block]
72 n@(XML Node traits) printOn: stream
73 "This is for normal printing compatibility."
75 n printXMLOn: (XML PrintStream newOn: stream)
78 n@(XML Node traits) printXMLOn: _
82 XML define: #NodeWithElements &parents: {XML Node}
83 &slots: {#elements -> ExtensibleArray new}.
85 n@(XML NodeWithElements traits) copy
86 [n clone `setting: #{#elements} to: {n elements copy}].
88 n@(XML NodeWithElements traits) printXMLOn: writer
90 n elements do: [| :each | each printXMLOn: writer]
93 XML define: #Document &parents: {XML NodeWithElements} &slots:
97 #requiredMarkup -> ''}.
99 d@(XML Document traits) printXMLOn: writer
101 n version ifNotNil: [writer xmlDeclaration: n version].
105 d@(XML Document traits) printCanonicalOn: stream
107 writer := XML PrintStream on: stream.
108 writer canonical := True.
112 XML define: #Element &parents: {XML NodeWithElements} &slots:
114 #contents -> ExtensibleArray new.
115 #attributes -> ExtensibleArray new}.
117 e@(XML Element traits) firstTagNamed: name
118 "Return the first node with the tag, or pass it on."
125 e@(XML Element traits) firstTagNamed: name with: testBlock
126 "Return the first node with the tag and that passes the test, or pass it on."
128 e name == name /\ [testBlock applyWith: e]
133 e@(XML Element traits) tagsNamed: name contentsDo: block
134 "Call the block on all contents if the element's tag matches the given name,
137 e name == name ifTrue: [e contentsDo: block].
141 e@(XML Element traits) tagsNamed: name do: block
142 "Call the block on the element if its tag matches the given name,
145 e name == name ifTrue: [block applyWith: e].
149 e@(XML Element traits) tagsNamed: name ifReceiverDo: block
150 "Call the block on the element if the name matches."
152 e name == name ifTrue: [block applyWith: e]
155 e@(XML Element traits) tagsNamed: name ifReceiverDoAndRecurse: block
156 "Call the block on the element if the name matches. Then recurse through the
159 e name == name ifTrue: [block applyWith: e].
163 e@(XML Element traits) tagsNamed: name ifReceiverOrChildDo: block
164 "Call the block on the element if the name matches, and do the same for the
165 direct children only."
167 e name == name ifTrue: [block applyWith: e].
171 e@(XML Element traits) contentsDo: block
176 e@(XML Element traits) contentsString
178 e contents size == 1 /\ [e contents first is: XML StringNode]
179 ifTrue: [e contents first string]
183 e@(XML Element traits) contentsStringAt: name
185 (e elements at: name ifAbsent: [^ '']) string
188 e@(XML Element traits) printXMLOn: writer
190 writer startElement: e name attributeList: e attributes.
191 writer canonical not /\ [e isEmpty] /\ [e attributes isEmpty not]
192 ifTrue: [writer endEmptyTag: e name]
195 e contentsDo: [| :content | content printXMLOn: writer].
197 writer endTag: e name]
200 e@(XML Element traits) isEmpty
201 "Treat the element as the joining of its elements and contents."
203 e elements isEmpty /\ [e contents isEmpty]
206 XML define: #ProcessingInstruction &parents: {XML Node} &slots:
207 {#target -> ''. #data -> ''}.
209 pi@(XML ProcessingInstruction traits) newForTarget: name data: string
210 [pi clone `setting: #{#target. #data} to: {name. string}].
212 "printXMLOn: defined after XML PrintStream is defined."
214 XML define: #StringNode &parents: {XML Node} &slots: {#string -> ''}.
216 sn@(XML StringNode traits) copy
217 [sn clone `setting: #{#string} to: {sn string copy}].
219 sn@(XML StringNode traits) newFor: string
220 [sn clone `setting: #{#string} to: {string}].
222 sn@(XML StringNode traits) printXMLOn: writer
224 writer pcData: sn string
227 (XML Translation ::= Dictionary new &capacity: 30) addAll: {
238 XML define: #PrintStream &parents: {Cloneable} &slots: {
245 w@(XML PrintStream traits) newOn: stream
246 [w clone `setting: #{#stack. #stream} to: {w stack new. stream}].
248 w@(XML PrintStream traits) attribute: name value: value
250 w stream ; ' ' ; name printString ; '="'.
252 w stream nextPut: $\".
256 w@(XML PrintStream traits) cdata: string
258 w stream ; '<![CDATA[' ; string ; ']]>'
261 w@(XML PrintStream traits) pcData: c
263 w stream ; (XML Translation at: c ifAbsent: [c as: String])
266 w@(XML PrintStream traits) comment: string
268 w stream ; '<!-- ' ; string ; ' -->'
271 pi@(XML ProcessingInstruction traits) printXMLOn: w@(XML PrintStream traits)
273 w stream ; '<?' ; pi target ; ' ' ; pi data ; '?>'
276 w@(XML PrintStream traits) pushTag: name
281 w@(XML PrintStream traits) popTag: name
283 top := w stack isEmpty
285 ifFalse: [w stack last].
287 ifTrue: [w stack pop]
288 ifFalse: [w error: 'Closing tag ' ; name ; ' doesn\'t match ' ; top]
291 w@(XML PrintStream traits) startTag: name
293 w stream ; '<' ; name.
294 "w canonical ifTrue: [w stream ; ' ']."
298 w@(XML PrintStream traits) endTag
300 w stream nextPut: $>.
304 w@(XML PrintStream traits) endTag: name
307 w stream ; '</' ; name.
308 w stream nextPut: $>.
312 w@(XML PrintStream traits) endEmptyTag: name
316 w canonical ifFalse: [w stream nextPut: $\s].
320 w@(XML PrintStream traits) startElement: name attributes: attribs
322 w canonical ifFalse: [w stream nextPut: $\r].
324 (attribs keySet as: SortedArray) do: [| :key |
325 w attribute: key value: (attribs at: key)].
329 w@(XML PrintStream traits) xmlDeclaration: versionString
331 w canonical ifFalse: [
333 w attribute: 'version' value: versionString.