diff --git a/stdlib/ktSrc/dom/Dom.kt b/stdlib/ktSrc/dom/Dom.kt index 268d58d4a2a..669678cf825 100644 --- a/stdlib/ktSrc/dom/Dom.kt +++ b/stdlib/ktSrc/dom/Dom.kt @@ -1,11 +1,57 @@ package std.dom import std.* +import std.util.* +import java.util.* import org.w3c.dom.* // Properties +val Document?.rootElement : Element? +get() = if (this != null) this.getDocumentElement() else null + + +var Node.text : String +get() { + if (this is Element) { + return this.text + } else { + return this.getNodeValue() ?: "" + } +} +set(value) { + if (this is Element) { + this.text = value + } else { + this.setNodeValue(value) + } +} + +var Element.text : String +get() { + val buffer = StringBuilder() + val nodeList = this.getChildNodes() + if (nodeList != null) { + var i = 0 + val size = nodeList.getLength() + while (i < size) { + val node = nodeList.item(i) + if (node != null) { + if (node.getNodeType() == Node.TEXT_NODE || node.getNodeType() == Node.CDATA_SECTION_NODE) { + buffer.append(node.getNodeValue()) + } + } + i++ + } + } + return buffer.toString().sure() +} +set(value) { + // lets remove all the previous text nodes first + this.setAttribute("id", value) +} + var Element.id : String get() = this.getAttribute("id")?: "" set(value) { @@ -25,6 +71,104 @@ set(value) { this.setAttribute("class", value) } + +// Helper methods + +inline fun Element?.children(): List { + return this?.getChildNodes().toList() +} + +inline fun Element?.elementsByTagName(name: String?): List { + return this?.getElementsByTagName(name).toElementList() +} + +inline fun Element?.elementsByTagNameNS(namespaceUri: String?, localName: String?): List { + return this?.getElementsByTagNameNS(namespaceUri, localName).toElementList() +} + +inline fun Document?.elementsByTagName(name: String?): List { + return this?.getElementsByTagName(name).toElementList() +} + +inline fun Document?.elementsByTagNameNS(namespaceUri: String?, localName: String?): List { + return this?.getElementsByTagNameNS(namespaceUri, localName).toElementList() +} + +val NodeList?.head : Node? +get() = if (this != null && this.getLength() > 0) this.item(0) else null + +val NodeList?.first : Node? +get() = this.head + +val NodeList?.tail : Node? +get() { + if (this == null) { + return null + } else { + val s = this.getLength() + return if (s > 0) this.item(s - 1) else null + } +} + +val NodeList?.last : Node? +get() = this.tail + + +inline fun NodeList?.toList(): List { + return if (this == null) { + Collections.EMPTY_LIST as List + } + else { + NodeListAsList(this) + } +} + +inline fun NodeList?.toElementList(): List { + return if (this == null) { + Collections.EMPTY_LIST as List + } + else { + ElementListAsList(this) + } +} + +fun NodeList?.toXmlString(xmlDeclaration: Boolean = false): String { + return if (this == null) + "" else { + this.toList().toXmlString(xmlDeclaration) + } +} + +class NodeListAsList(val nodeList: NodeList): AbstractList() { + override fun get(index: Int): Node { + val node = nodeList.item(index) + if (node == null) { + throw IndexOutOfBoundsException("NodeList does not contain a node at index: " + index) + } else { + return node + } + } + + override fun size(): Int = nodeList.getLength() +} + +class ElementListAsList(val nodeList: NodeList): AbstractList() { + override fun get(index: Int): Element { + val node = nodeList.item(index) + if (node is Element) { + return node + } else { + if (node == null) { + throw IndexOutOfBoundsException("NodeList does not contain a node at index: " + index) + } else { + throw IllegalArgumentException("Node is not an Element as expected but is $node") + } + } + } + + override fun size(): Int = nodeList.getLength() + +} // Syntax sugar inline fun Node.plus(child: Node?): Node { diff --git a/stdlib/ktSrc/dom/DomJVM.kt b/stdlib/ktSrc/dom/DomJVM.kt index f281b56f358..93d01af2bfe 100644 --- a/stdlib/ktSrc/dom/DomJVM.kt +++ b/stdlib/ktSrc/dom/DomJVM.kt @@ -14,6 +14,9 @@ import javax.xml.transform.stream.StreamResult import java.io.StringWriter import javax.xml.transform.OutputKeys +import java.lang.Iterable +import java.util.List +import java.util.Collection fun createDocument(builder: DocumentBuilder): Document { return builder.newDocument().sure() @@ -36,6 +39,17 @@ fun Node.toXmlString(xmlDeclaration: Boolean = this is Document): String { return nodeToXmlString(this, xmlDeclaration) } +fun java.lang.Iterable.toXmlString(xmlDeclaration: Boolean = false): String { + // TODO this should work... + // return this.map{it.toXmlString()}.join("") + val builder = StringBuilder() + for (n in this) { + builder.append(n.toXmlString(xmlDeclaration)) + } + return builder.toString().sure() +} + + /* fun Document.toXmlString(xmlDeclaration: Boolean = true): String { return nodeToXmlString(this, xmlDeclaration) diff --git a/testlib/test/dom/DomBuilderTest.kt b/testlib/test/dom/DomBuilderTest.kt index fb4cd426671..dea799d0dae 100644 --- a/testlib/test/dom/DomBuilderTest.kt +++ b/testlib/test/dom/DomBuilderTest.kt @@ -2,12 +2,13 @@ package test.dom import std.* import std.dom.* +import std.util.* import stdhack.test.* import org.w3c.dom.* class DomBuilderTest() : TestSupport() { - fun testBuildDocumnet() { + fun testBuildDocument() { var doc = createDocument() doc.addElement("foo") { @@ -19,11 +20,24 @@ class DomBuilderTest() : TestSupport() { addElement("grandChild") { cssClass = "tiny" addText("Hello World!") - // TODO support neater syntax sugar for adding text? - // += "Hello World!" + // TODO support neater syntax sugar for adding text? + // += "Hello World!" } } } println("builder document: ${doc.toXmlString()}") + + + val grandChild = doc.elementsByTagName("grandChild").first + if (grandChild != null) { + println("got element ${grandChild.toXmlString()} with text '${grandChild.text}`") + assertEquals("Hello World!", grandChild.text) + } else { + fail("Not an Element $grandChild") + } + val children = doc.rootElement.children() + val xml = children.toXmlString() + println("root element has children: ${xml}") + assertEquals(1, children.size()) } }