13
13
package scala
14
14
package xml
15
15
16
+ import scala .annotation .tailrec
16
17
import scala .collection .mutable
17
18
import scala .language .implicitConversions
18
19
import scala .collection .Seq
@@ -191,9 +192,8 @@ object Utility extends AnyRef with parsing.TokenTests {
191
192
decodeEntities : Boolean = true ,
192
193
preserveWhitespace : Boolean = false ,
193
194
minimizeTags : Boolean = false
194
- ): StringBuilder = {
195
+ ): StringBuilder =
195
196
serialize(x, pscope, sb, stripComments, decodeEntities, preserveWhitespace, if (minimizeTags) MinimizeMode .Always else MinimizeMode .Never )
196
- }
197
197
198
198
/**
199
199
* Serialize an XML Node to a StringBuilder.
@@ -212,32 +212,64 @@ object Utility extends AnyRef with parsing.TokenTests {
212
212
preserveWhitespace : Boolean = false ,
213
213
minimizeTags : MinimizeMode .Value = MinimizeMode .Default
214
214
): StringBuilder = {
215
- x match {
216
- case c : Comment => if (! stripComments) c.buildString(sb); sb
217
- case s : SpecialNode => s.buildString(sb)
218
- case g : Group =>
219
- for (c <- g.nodes) serialize(c, g.scope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags); sb
220
- case el : Elem =>
221
- // print tag with namespace declarations
222
- sb.append('<' )
223
- el.nameToString(sb)
224
- if (el.attributes.ne(null )) el.attributes.buildString(sb)
225
- el.scope.buildString(sb, pscope)
226
- if (el.child.isEmpty &&
227
- (minimizeTags == MinimizeMode .Always ||
228
- (minimizeTags == MinimizeMode .Default && el.minimizeEmpty))) {
229
- // no children, so use short form: <xyz .../>
230
- sb.append(" />" )
231
- } else {
232
- // children, so use long form: <xyz ...>...</xyz>
233
- sb.append('>' )
234
- sequenceToXML(el.child, el.scope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags)
215
+ serializeImpl(List (x), pscope, false , stripComments, minimizeTags, sb)
216
+ sb
217
+ }
218
+
219
+ private def serializeImpl (
220
+ ns : Seq [Node ],
221
+ pscope : NamespaceBinding ,
222
+ spaced : Boolean ,
223
+ stripComments : Boolean ,
224
+ minimizeTags : MinimizeMode .Value ,
225
+ sb : StringBuilder
226
+ ): Unit = {
227
+ @ tailrec def ser (nss : List [Seq [Node ]], pscopes : List [NamespaceBinding ], spaced : List [Boolean ], toClose : List [Node ]): Unit = nss match {
228
+ case List (ns) if ns.isEmpty =>
229
+ case ns :: rests if ns.isEmpty =>
230
+ if (toClose.head != null ) {
235
231
sb.append(" </" )
236
- el .nameToString(sb)
232
+ toClose.head .nameToString(sb)
237
233
sb.append('>' )
238
234
}
239
- case _ => throw new IllegalArgumentException (" Don't know how to serialize a " + x.getClass.getName)
235
+ ser(rests, pscopes.tail, spaced.tail, toClose.tail)
236
+ case ns1 :: r =>
237
+ val (n, ns) = (ns1.head, ns1.tail)
238
+ def sp (): Unit = if (ns.nonEmpty && spaced.head) sb.append(' ' )
239
+ n match {
240
+ case c : Comment =>
241
+ if (! stripComments) {
242
+ c.buildString(sb)
243
+ sp()
244
+ }
245
+ ser(ns :: r, pscopes, spaced, toClose)
246
+ case s : SpecialNode =>
247
+ s.buildString(sb)
248
+ sp()
249
+ ser(ns :: r, pscopes, spaced, toClose)
250
+ case g : Group =>
251
+ ser(g.nodes :: ns :: r, g.scope :: pscopes, false :: spaced, null :: toClose)
252
+ case e : Elem =>
253
+ sb.append('<' )
254
+ e.nameToString(sb)
255
+ if (e.attributes.ne(null )) e.attributes.buildString(sb)
256
+ e.scope.buildString(sb, pscopes.head)
257
+ if (e.child.isEmpty &&
258
+ (minimizeTags == MinimizeMode .Always ||
259
+ (minimizeTags == MinimizeMode .Default && e.minimizeEmpty))) {
260
+ // no children, so use short form: <xyz .../>
261
+ sb.append(" />" )
262
+ sp()
263
+ ser(ns :: r, pscopes, spaced, toClose)
264
+ } else {
265
+ sb.append('>' )
266
+ val csp = e.child.forall(isAtomAndNotText)
267
+ ser(e.child :: ns :: r, e.scope :: pscopes, csp :: spaced, e :: toClose)
268
+ }
269
+ case n => throw new IllegalArgumentException (" Don't know how to serialize a " + n.getClass.getName)
270
+ }
240
271
}
272
+ ser(List (ns), List (pscope), List (spaced), Nil )
241
273
}
242
274
243
275
def sequenceToXML (
@@ -248,18 +280,9 @@ object Utility extends AnyRef with parsing.TokenTests {
248
280
decodeEntities : Boolean = true ,
249
281
preserveWhitespace : Boolean = false ,
250
282
minimizeTags : MinimizeMode .Value = MinimizeMode .Default
251
- ): Unit = {
252
- if (children.isEmpty) ()
253
- else if (children.forall(isAtomAndNotText)) { // add space
254
- val it : Iterator [Node ] = children.iterator
255
- val f : Node = it.next()
256
- serialize(f, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags)
257
- while (it.hasNext) {
258
- val x : Node = it.next()
259
- sb.append(' ' )
260
- serialize(x, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags)
261
- }
262
- } else children.foreach { serialize(_, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) }
283
+ ): Unit = if (children.nonEmpty) {
284
+ val spaced = children.forall(isAtomAndNotText)
285
+ serializeImpl(children, pscope, spaced, stripComments, minimizeTags, sb)
263
286
}
264
287
265
288
def splitName (name : String ): (Option [String ], String ) = {
0 commit comments