diff options
author | Alex Cruise <alex@cluonflux.com> | 2012-03-08 23:41:58 -0800 |
---|---|---|
committer | Alex Cruise <alex@metaforsoftware.com> | 2012-03-15 14:31:08 -0700 |
commit | e7ea29cc0b3c55d7880b767f8c442d8bf3cd0dea (patch) | |
tree | 09008ce8463b33b068210128cdaf508c91b08195 /src/library | |
parent | 24d8760030ea9379131104561929d19e437a9793 (diff) | |
download | scala-e7ea29cc0b3c55d7880b767f8c442d8bf3cd0dea.tar.gz scala-e7ea29cc0b3c55d7880b767f8c442d8bf3cd0dea.tar.bz2 scala-e7ea29cc0b3c55d7880b767f8c442d8bf3cd0dea.zip |
SI-1118 WIP
Diffstat (limited to 'src/library')
-rwxr-xr-x[-rw-r--r--] | src/library/scala/xml/Elem.scala | 35 | ||||
-rwxr-xr-x[-rw-r--r--] | src/library/scala/xml/Node.scala | 2 | ||||
-rwxr-xr-x[-rw-r--r--] | src/library/scala/xml/PrettyPrinter.scala | 2 | ||||
-rwxr-xr-x[-rw-r--r--] | src/library/scala/xml/Utility.scala | 66 | ||||
-rwxr-xr-x[-rw-r--r--] | src/library/scala/xml/XML.scala | 19 | ||||
-rwxr-xr-x[-rw-r--r--] | src/library/scala/xml/factory/Binder.scala | 10 | ||||
-rwxr-xr-x[-rw-r--r--] | src/library/scala/xml/parsing/ConstructingHandler.scala | 4 | ||||
-rwxr-xr-x[-rw-r--r--] | src/library/scala/xml/parsing/DefaultMarkupHandler.scala | 2 | ||||
-rwxr-xr-x[-rw-r--r--] | src/library/scala/xml/parsing/MarkupHandler.scala | 3 | ||||
-rwxr-xr-x[-rw-r--r--] | src/library/scala/xml/parsing/MarkupParser.scala | 2 | ||||
-rwxr-xr-x | src/library/scala/xml/pull/XMLEventReader.scala | 2 |
11 files changed, 107 insertions, 40 deletions
diff --git a/src/library/scala/xml/Elem.scala b/src/library/scala/xml/Elem.scala index df52b34f87..605f3cb7fb 100644..100755 --- a/src/library/scala/xml/Elem.scala +++ b/src/library/scala/xml/Elem.scala @@ -17,8 +17,18 @@ package scala.xml * @author Burak Emir <bqe@google.com> */ object Elem { - def apply(prefix: String,label: String, attributes: MetaData, scope: NamespaceBinding, child: Node*) = - new Elem(prefix, label, attributes, scope, child:_*) + /** Build an Elem, setting its minimizeEmpty property to <code>true</code> if it has no children. Note that this + * default may not be exactly what you want, as some XML dialects don't permit some elements to be minimized. + * + * @deprecated This factory method is retained for backward compatibility; please use the other one, with which you + * can specify your own preference for minimizeEmpty. + */ + @deprecated + def apply(prefix: String,label: String, attributes: MetaData, scope: NamespaceBinding, child: Node*): Elem = + apply(prefix, label, attributes, scope, child.isEmpty, child: _*) + + def apply(prefix: String,label: String, attributes: MetaData, scope: NamespaceBinding, minimizeEmpty: Boolean, child: Node*): Elem = + new Elem(prefix,label,attributes,scope, minimizeEmpty, child:_*) def unapplySeq(n: Node) = n match { case _: SpecialNode | _: Group => None @@ -29,11 +39,13 @@ object Elem { /** The case class `Elem` extends the `Node` class, * providing an immutable data object representing an XML element. * - * @param prefix namespace prefix (may be null, but not the empty string) - * @param label the element name - * @param attribute the attribute map - * @param scope the scope containing the namespace bindings - * @param child the children of this node + * @param prefix namespace prefix (may be null, but not the empty string) + * @param label the element name + * @param attributes1 the attribute map + * @param scope the scope containing the namespace bindings + * @param minimizeEmpty `true` if this element should be serialized as minimized (i.e. "<el/>") when + * empty; `false` if it should be written out in long form. + * @param child the children of this node * * Copyright 2008 Google Inc. All Rights Reserved. * @author Burak Emir <bqe@google.com> @@ -43,9 +55,15 @@ class Elem( val label: String, attributes1: MetaData, override val scope: NamespaceBinding, + val minimizeEmpty: Boolean, val child: Node*) extends Node with Serializable { + @deprecated(since="2.10", message="this constructor is retained for backward compatibility. Please use the primary constructor, which lets you specify your own preference for `minimizeEmpty`.") + def this(prefix: String, label: String, attributes: MetaData, scope: NamespaceBinding, child: Node*) = { + this(prefix, label, attributes, scope, child.isEmpty, child: _*) + } + final override def doCollectNamespaces = true final override def doTransform = true @@ -83,8 +101,9 @@ extends Node with Serializable label: String = this.label, attributes: MetaData = this.attributes, scope: NamespaceBinding = this.scope, + minimizeEmpty: Boolean = this.minimizeEmpty, child: Seq[Node] = this.child.toSeq - ): Elem = Elem(prefix, label, attributes, scope, child: _*) + ): Elem = Elem(prefix, label, attributes, scope, minimizeEmpty, child: _*) /** Returns concatenation of `text(n)` for each child `n`. */ diff --git a/src/library/scala/xml/Node.scala b/src/library/scala/xml/Node.scala index 2ead18fe08..02e34e1bdc 100644..100755 --- a/src/library/scala/xml/Node.scala +++ b/src/library/scala/xml/Node.scala @@ -159,7 +159,7 @@ abstract class Node extends NodeSeq { * @return ... */ def buildString(stripComments: Boolean): String = - Utility.toXML(this, stripComments = stripComments).toString + Utility.serialize(this, stripComments = stripComments).toString /** * Same as `toString('''false''')`. diff --git a/src/library/scala/xml/PrettyPrinter.scala b/src/library/scala/xml/PrettyPrinter.scala index ea39b51352..64dbd00f2f 100644..100755 --- a/src/library/scala/xml/PrettyPrinter.scala +++ b/src/library/scala/xml/PrettyPrinter.scala @@ -161,7 +161,7 @@ class PrettyPrinter(width: Int, step: Int) { case _ => val test = { val sb = new StringBuilder() - Utility.toXML(node, pscope, sb, false) + Utility.serialize(node, pscope, sb, false) if (doPreserve(node)) sb.toString else TextBuffer.fromString(sb.toString).toText(0).data } diff --git a/src/library/scala/xml/Utility.scala b/src/library/scala/xml/Utility.scala index fc20b892b9..e5644acac8 100644..100755 --- a/src/library/scala/xml/Utility.scala +++ b/src/library/scala/xml/Utility.scala @@ -181,6 +181,14 @@ object Utility extends AnyRef with parsing.TokenTests { // sb.toString() // } + /** + * Serialize the provided Node to the provided StringBuilder. + * <p/> + * Note that calling this source-compatible method will result in the same old, arguably almost universally unwanted, + * behaviour. + * @deprecated please use {@link #serialize} instead and specify a minimizeTags parameter. + */ + @deprecated def toXML( x: Node, pscope: NamespaceBinding = TopScope, @@ -190,29 +198,53 @@ object Utility extends AnyRef with parsing.TokenTests { preserveWhitespace: Boolean = false, minimizeTags: Boolean = false): StringBuilder = { + serialize(x, pscope, sb, stripComments, decodeEntities, preserveWhitespace, if (minimizeTags) MinimizeMode.Always else Mini + } + + /** + * Serialize an XML Node to a StringBuilder. + * + * This is essentially a minor rework of {@link #toXML} that can't have the same name due to an unfortunate + * combination of named/default arguments and overloading. + * + * @todo seriously consider just changing the default to {@link MinimizeMode#Default} so that the serialization is + * transparent by default + * @todo use a Writer instead + */ + def serialize( + x: Node, + pscope: NamespaceBinding = TopScope, + sb: StringBuilder = new StringBuilder, + stripComments: Boolean = false, + decodeEntities: Boolean = true, + preserveWhitespace: Boolean = false, + minimizeTags: MinimizeMode.Value = MinimizeMode.Default): StringBuilder = + { x match { - case c: Comment => if (!stripComments) c buildString sb else sb - case x: SpecialNode => x buildString sb - case g: Group => - g.nodes foreach {toXML(_, x.scope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags)} - sb - case _ => + case c: Comment if !stripComments => c buildString sb + case s: SpecialNode => s buildString sb + case g: Group => for (c <- g.nodes) serialize(c, g.scope, sb, minimizeTags = minimizeTags) ; sb + case el: Elem => // print tag with namespace declarations sb.append('<') - x.nameToString(sb) - if (x.attributes ne null) x.attributes.buildString(sb) - x.scope.buildString(sb, pscope) - if (x.child.isEmpty && minimizeTags) { + el.nameToString(sb) + if (el.attributes ne null) el.attributes.buildString(sb) + el.scope.buildString(sb, pscope) + if (el.child.isEmpty && + (minimizeTags == MinimizeMode.Always || + (minimizeTags == MinimizeMode.Default && el.minimizeEmpty))) + { // no children, so use short form: <xyz .../> - sb.append(" />") + sb.append("/>") } else { // children, so use long form: <xyz ...>...</xyz> sb.append('>') - sequenceToXML(x.child, x.scope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) + sequenceToXML(el.child, el.scope, sb, stripComments) sb.append("</") - x.nameToString(sb) + el.nameToString(sb) sb.append('>') } + case _ => throw new IllegalArgumentException("Don't know how to serialize a " + x.getClass.getName) } } @@ -223,20 +255,20 @@ object Utility extends AnyRef with parsing.TokenTests { stripComments: Boolean = false, decodeEntities: Boolean = true, preserveWhitespace: Boolean = false, - minimizeTags: Boolean = false): Unit = + minimizeTags: MinimizeMode.Value = MinimizeMode.Default): Unit = { if (children.isEmpty) return else if (children forall isAtomAndNotText) { // add space val it = children.iterator val f = it.next - toXML(f, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) + serialize(f, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) while (it.hasNext) { val x = it.next sb.append(' ') - toXML(x, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) + serialize(x, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) } } - else children foreach { toXML(_, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) } + else children foreach { serialize(_, pscope, sb, stripComments, decodeEntities, preserveWhitespace, minimizeTags) } } /** diff --git a/src/library/scala/xml/XML.scala b/src/library/scala/xml/XML.scala index bc3033d081..dfdd71a9ae 100644..100755 --- a/src/library/scala/xml/XML.scala +++ b/src/library/scala/xml/XML.scala @@ -26,6 +26,21 @@ object Source def fromSysId(sysID: String) = new InputSource(sysID) def fromString(string: String) = fromReader(new StringReader(string)) } + +/** + * Governs how empty elements (i.e. those without children) should be serialized. + */ +object MinimizeMode extends Enumeration { + /** Minimize empty tags if they were originally empty when parsed, or if they were constructed with {@link Elem#minimizeEmpty} == true */ + val Default = Value + + /** Always minimize empty tags. Note that this may be problematic for XHTML, in which case {@link Xhtml#toXhtml} should be used instead. */ + val Always = Value + + /** Never minimize empty tags. */ + val Never = Value +} + import Source._ /** The object `XML` provides constants, and functions to load @@ -83,10 +98,10 @@ object XML extends XMLLoader[Elem] * @param xmlDecl if true, write xml declaration * @param doctype if not null, write doctype declaration */ - final def write(w: java.io.Writer, node: Node, enc: String, xmlDecl: Boolean, doctype: dtd.DocType) { + final def write(w: java.io.Writer, node: Node, enc: String, xmlDecl: Boolean, doctype: dtd.DocType, minimizeTags: MinimizeMode.Value = MinimizeMode.Default) { /* TODO: optimize by giving writer parameter to toXML*/ if (xmlDecl) w.write("<?xml version='1.0' encoding='" + enc + "'?>\n") if (doctype ne null) w.write( doctype.toString() + "\n") - w.write(Utility.toXML(node).toString) + w.write(Utility.serialize(node, minimizeTags = minimizeTags).toString) } } diff --git a/src/library/scala/xml/factory/Binder.scala b/src/library/scala/xml/factory/Binder.scala index a8b0ed585b..b4fe153bd8 100644..100755 --- a/src/library/scala/xml/factory/Binder.scala +++ b/src/library/scala/xml/factory/Binder.scala @@ -43,13 +43,13 @@ abstract class Binder(val preserveWS: Boolean) extends ValidatingMarkupHandler { result &+ text(0, x.data) case x:EntityRef => result &+ entityRef(0, x.entityName) - case _ => - elemStart(0, n.prefix, n.label, n.attributes, n.scope) + case x:Elem => + elemStart(0, x.prefix, x.label, x.attributes, x.scope) val old = result result = new NodeBuffer() - for (m <- n.child) traverse(m) - result = old &+ elem(0, n.prefix, n.label, n.attributes, n.scope, NodeSeq.fromSeq(result)).toList; - elemEnd(0, n.prefix, n.label) + for (m <- x.child) traverse(m) + result = old &+ elem(0, x.prefix, x.label, x.attributes, x.scope, x.minimizeEmpty, NodeSeq.fromSeq(result)).toList; + elemEnd(0, x.prefix, x.label) } final def validate(n: Node): Node = { diff --git a/src/library/scala/xml/parsing/ConstructingHandler.scala b/src/library/scala/xml/parsing/ConstructingHandler.scala index 60c19138c3..7e61674682 100644..100755 --- a/src/library/scala/xml/parsing/ConstructingHandler.scala +++ b/src/library/scala/xml/parsing/ConstructingHandler.scala @@ -21,8 +21,8 @@ abstract class ConstructingHandler extends MarkupHandler val preserveWS: Boolean def elem(pos: Int, pre: String, label: String, attrs: MetaData, - pscope: NamespaceBinding, nodes: NodeSeq): NodeSeq = - Elem(pre, label, attrs, pscope, nodes:_*) + pscope: NamespaceBinding, empty: Boolean, nodes: NodeSeq): NodeSeq = + Elem(pre, label, attrs, pscope, empty, nodes:_*) def procInstr(pos: Int, target: String, txt: String) = ProcInstr(target, txt) diff --git a/src/library/scala/xml/parsing/DefaultMarkupHandler.scala b/src/library/scala/xml/parsing/DefaultMarkupHandler.scala index 699c5b2b5f..e0258ba781 100644..100755 --- a/src/library/scala/xml/parsing/DefaultMarkupHandler.scala +++ b/src/library/scala/xml/parsing/DefaultMarkupHandler.scala @@ -16,7 +16,7 @@ package parsing abstract class DefaultMarkupHandler extends MarkupHandler { def elem(pos: Int, pre: String, label: String, attrs: MetaData, - scope:NamespaceBinding, args: NodeSeq) = NodeSeq.Empty + scope:NamespaceBinding, empty: Boolean, args: NodeSeq) = NodeSeq.Empty def procInstr(pos: Int, target: String, txt: String) = NodeSeq.Empty diff --git a/src/library/scala/xml/parsing/MarkupHandler.scala b/src/library/scala/xml/parsing/MarkupHandler.scala index 87e785a98f..83db2f177d 100644..100755 --- a/src/library/scala/xml/parsing/MarkupHandler.scala +++ b/src/library/scala/xml/parsing/MarkupHandler.scala @@ -75,10 +75,11 @@ abstract class MarkupHandler extends Logged * @param pre the prefix * @param label the local name * @param attrs the attributes (metadata) + * @param empty `true` if the element was previously empty; `false` otherwise. * @param args the children of this element * @return ... */ - def elem(pos: Int, pre: String, label: String, attrs: MetaData, scope: NamespaceBinding, args: NodeSeq): NodeSeq + def elem(pos: Int, pre: String, label: String, attrs: MetaData, scope: NamespaceBinding, empty: Boolean, args: NodeSeq): NodeSeq /** callback method invoked by MarkupParser after parsing PI. */ diff --git a/src/library/scala/xml/parsing/MarkupParser.scala b/src/library/scala/xml/parsing/MarkupParser.scala index 1de08b3025..32feaa2209 100644..100755 --- a/src/library/scala/xml/parsing/MarkupParser.scala +++ b/src/library/scala/xml/parsing/MarkupParser.scala @@ -569,7 +569,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests tmp } } - val res = handle.elem(pos, pre, local, aMap, scope, ts) + val res = handle.elem(pos, pre, local, aMap, scope, ts == NodeSeq.Empty, ts) handle.elemEnd(pos, pre, local) res } diff --git a/src/library/scala/xml/pull/XMLEventReader.scala b/src/library/scala/xml/pull/XMLEventReader.scala index f84d91d138..c764d042c8 100755 --- a/src/library/scala/xml/pull/XMLEventReader.scala +++ b/src/library/scala/xml/pull/XMLEventReader.scala @@ -81,7 +81,7 @@ extends collection.AbstractIterator[XMLEvent] // memory usage optimization return one <ignore/> for top level to satisfy // MarkupParser.document() otherwise NodeSeq.Empty private var ignoreWritten = false - final def elem(pos: Int, pre: String, label: String, attrs: MetaData, pscope: NamespaceBinding, nodes: NodeSeq): NodeSeq = + final def elem(pos: Int, pre: String, label: String, attrs: MetaData, pscope: NamespaceBinding, empty: Boolean, nodes: NodeSeq): NodeSeq = if (level == 1 && !ignoreWritten) {ignoreWritten = true; <ignore/> } else NodeSeq.Empty def procInstr(pos: Int, target: String, txt: String) = setEvent(EvProcInstr(target, txt)) |