1313package scala
1414package xml
1515
16+ import scala .annotation .tailrec
1617import scala .collection .mutable
1718import scala .language .implicitConversions
1819import scala .collection .Seq
@@ -191,9 +192,8 @@ object Utility extends AnyRef with parsing.TokenTests {
191192 decodeEntities : Boolean = true ,
192193 preserveWhitespace : Boolean = false ,
193194 minimizeTags : Boolean = false
194- ): StringBuilder = {
195+ ): StringBuilder =
195196 serialize(x, pscope, sb, stripComments, decodeEntities, preserveWhitespace, if (minimizeTags) MinimizeMode .Always else MinimizeMode .Never )
196- }
197197
198198 /**
199199 * Serialize an XML Node to a StringBuilder.
@@ -212,32 +212,63 @@ object Utility extends AnyRef with parsing.TokenTests {
212212 preserveWhitespace : Boolean = false ,
213213 minimizeTags : MinimizeMode .Value = MinimizeMode .Default
214214 ): 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 [List [Node ]], pscopes : List [NamespaceBinding ], spaced : List [Boolean ], toClose : List [Node ]): Unit = nss match {
228+ case List (Nil ) =>
229+ case Nil :: rests =>
230+ if (toClose.head != null ) {
235231 sb.append(" </" )
236- el .nameToString(sb)
232+ toClose.head .nameToString(sb)
237233 sb.append('>' )
238234 }
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 (n :: ns) :: r =>
237+ def sp (): Unit = if (ns.nonEmpty && spaced.head) sb.append(' ' )
238+ n match {
239+ case c : Comment =>
240+ if (! stripComments) {
241+ c.buildString(sb)
242+ sp()
243+ }
244+ ser(ns :: r, pscopes, spaced, toClose)
245+ case s : SpecialNode =>
246+ s.buildString(sb)
247+ sp()
248+ ser(ns :: r, pscopes, spaced, toClose)
249+ case g : Group =>
250+ ser(g.nodes.toList :: ns :: r, g.scope :: pscopes, false :: spaced, null :: toClose)
251+ case e : Elem =>
252+ sb.append('<' )
253+ e.nameToString(sb)
254+ if (e.attributes.ne(null )) e.attributes.buildString(sb)
255+ e.scope.buildString(sb, pscopes.head)
256+ if (e.child.isEmpty &&
257+ (minimizeTags == MinimizeMode .Always ||
258+ (minimizeTags == MinimizeMode .Default && e.minimizeEmpty))) {
259+ // no children, so use short form: <xyz .../>
260+ sb.append(" />" )
261+ sp()
262+ ser(ns :: r, pscopes, spaced, toClose)
263+ } else {
264+ sb.append('>' )
265+ val csp = e.child.forall(isAtomAndNotText)
266+ ser(e.child.toList :: ns :: r, e.scope :: pscopes, csp :: spaced, e :: toClose)
267+ }
268+ case n => throw new IllegalArgumentException (" Don't know how to serialize a " + n.getClass.getName)
269+ }
240270 }
271+ ser(List (ns.toList), List (pscope), List (spaced), Nil )
241272 }
242273
243274 def sequenceToXML (
@@ -248,18 +279,9 @@ object Utility extends AnyRef with parsing.TokenTests {
248279 decodeEntities : Boolean = true ,
249280 preserveWhitespace : Boolean = false ,
250281 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) }
282+ ): Unit = if (children.nonEmpty) {
283+ val spaced = children.forall(isAtomAndNotText)
284+ serializeImpl(children, pscope, spaced, stripComments, minimizeTags, sb)
263285 }
264286
265287 def splitName (name : String ): (Option [String ], String ) = {
0 commit comments