99package scala
1010package xml
1111
12+ import scala .annotation .tailrec
1213import scala .collection .mutable
1314import scala .language .implicitConversions
1415import scala .collection .Seq
@@ -187,9 +188,7 @@ object Utility extends AnyRef with parsing.TokenTests {
187188 decodeEntities : Boolean = true ,
188189 preserveWhitespace : Boolean = false ,
189190 minimizeTags : Boolean = false ): StringBuilder =
190- {
191191 serialize(x, pscope, sb, stripComments, decodeEntities, preserveWhitespace, if (minimizeTags) MinimizeMode .Always else MinimizeMode .Never )
192- }
193192
194193 /**
195194 * Serialize an XML Node to a StringBuilder.
@@ -206,35 +205,67 @@ object Utility extends AnyRef with parsing.TokenTests {
206205 stripComments : Boolean = false ,
207206 decodeEntities : Boolean = true ,
208207 preserveWhitespace : Boolean = false ,
209- minimizeTags : MinimizeMode .Value = MinimizeMode .Default ): StringBuilder =
210- {
211- x match {
212- case c : Comment => if (! stripComments) c buildString sb; sb
213- case s : SpecialNode => s buildString sb
214- case g : Group =>
215- for (c <- g.nodes) serialize(c, g.scope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags); sb
216- case el : Elem =>
217- // print tag with namespace declarations
218- sb.append('<' )
219- el.nameToString(sb)
220- if (el.attributes ne null ) el.attributes.buildString(sb)
221- el.scope.buildString(sb, pscope)
222- if (el.child.isEmpty &&
223- (minimizeTags == MinimizeMode .Always ||
224- (minimizeTags == MinimizeMode .Default && el.minimizeEmpty))) {
225- // no children, so use short form: <xyz .../>
226- sb.append(" />" )
227- } else {
228- // children, so use long form: <xyz ...>...</xyz>
229- sb.append('>' )
230- sequenceToXML(el.child, el.scope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags)
231- sb.append(" </" )
232- el.nameToString(sb)
233- sb.append('>' )
234- }
235- case _ => throw new IllegalArgumentException (" Don't know how to serialize a " + x.getClass.getName)
236- }
208+ minimizeTags : MinimizeMode .Value = MinimizeMode .Default
209+ ): StringBuilder = {
210+ serializeImpl(List (x), pscope, false , stripComments, minimizeTags, sb)
211+ sb
212+ }
213+
214+ private def serializeImpl (
215+ ns : Seq [Node ],
216+ pscope : NamespaceBinding ,
217+ spaced : Boolean ,
218+ stripComments : Boolean ,
219+ minimizeTags : MinimizeMode .Value ,
220+ sb : StringBuilder
221+ ): Unit = {
222+ @ tailrec def ser (nss : List [Seq [Node ]], pscopes : List [NamespaceBinding ], spaced : List [Boolean ], toClose : List [Node ]): Unit = nss match {
223+ case List (ns) if ns.isEmpty =>
224+ case ns :: rests if ns.isEmpty =>
225+ if (toClose.head != null ) {
226+ sb.append(" </" )
227+ toClose.head.nameToString(sb)
228+ sb.append('>' )
229+ }
230+ ser(rests, pscopes.tail, spaced.tail, toClose.tail)
231+ case ns1 :: r =>
232+ val (n, ns) = (ns1.head, ns1.tail)
233+ def sp (): Unit = if (ns.nonEmpty && spaced.head) sb.append(' ' )
234+ n match {
235+ case c : Comment =>
236+ if (! stripComments) {
237+ c.buildString(sb)
238+ sp()
239+ }
240+ ser(ns :: r, pscopes, spaced, toClose)
241+ case s : SpecialNode =>
242+ s.buildString(sb)
243+ sp()
244+ ser(ns :: r, pscopes, spaced, toClose)
245+ case g : Group =>
246+ ser(g.nodes :: ns :: r, g.scope :: pscopes, false :: spaced, null :: toClose)
247+ case e : Elem =>
248+ sb.append('<' )
249+ e.nameToString(sb)
250+ if (e.attributes.ne(null )) e.attributes.buildString(sb)
251+ e.scope.buildString(sb, pscopes.head)
252+ if (e.child.isEmpty &&
253+ (minimizeTags == MinimizeMode .Always ||
254+ (minimizeTags == MinimizeMode .Default && e.minimizeEmpty))) {
255+ // no children, so use short form: <xyz .../>
256+ sb.append(" />" )
257+ sp()
258+ ser(ns :: r, pscopes, spaced, toClose)
259+ } else {
260+ sb.append('>' )
261+ val csp = e.child.forall(isAtomAndNotText)
262+ ser(e.child :: ns :: r, e.scope :: pscopes, csp :: spaced, e :: toClose)
263+ }
264+ case n => throw new IllegalArgumentException (" Don't know how to serialize a " + n.getClass.getName)
265+ }
237266 }
267+ ser(List (ns), List (pscope), List (spaced), Nil )
268+ }
238269
239270 def sequenceToXML (
240271 children : Seq [Node ],
@@ -243,20 +274,11 @@ object Utility extends AnyRef with parsing.TokenTests {
243274 stripComments : Boolean = false ,
244275 decodeEntities : Boolean = true ,
245276 preserveWhitespace : Boolean = false ,
246- minimizeTags : MinimizeMode .Value = MinimizeMode .Default ): Unit =
247- {
248- if (children.isEmpty) return
249- else if (children forall isAtomAndNotText) { // add space
250- val it = children.iterator
251- val f = it.next()
252- serialize(f, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags)
253- while (it.hasNext) {
254- val x = it.next()
255- sb.append(' ' )
256- serialize(x, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags)
257- }
258- } else children foreach { serialize(_, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) }
259- }
277+ minimizeTags : MinimizeMode .Value = MinimizeMode .Default
278+ ): Unit = if (children.nonEmpty) {
279+ val spaced = children.forall(isAtomAndNotText)
280+ serializeImpl(children, pscope, spaced, stripComments, minimizeTags, sb)
281+ }
260282
261283 /**
262284 * Returns prefix of qualified name if any.
0 commit comments