Use a new kbinxml implementation that doesn't depend on xom

This commit is contained in:
skogaby 2019-09-25 20:18:43 -05:00
parent 5e5b02b3a4
commit cd502a1f6c
11 changed files with 263 additions and 405 deletions

View File

@ -1,11 +1,11 @@
package com.buttongames.butterflycore.xml.kbinxml
import com.buttongames.butterflycore.xml.kbinxml.ByteConv.E.longToBytes
import java.lang.Double
import java.lang.Float
import java.math.BigInteger
import java.nio.ByteBuffer
import java.util.*
import kotlin.experimental.and
internal class ByteConv {
@ -67,8 +67,8 @@ internal class ByteConv {
fun stringToIp(string: String) = string.split(".").map { it.toUByte().toByte() }.toByteArray()
fun floatToString(array: ByteArray) = String.format("%.6f", ByteBuffer.wrap(array).float)
fun doubleToString(array: ByteArray) = String.format("%.6f", ByteBuffer.wrap(array).double)
fun floatToString(array: ByteArray) = String.format(Locale.US, "%.6f", ByteBuffer.wrap(array).float)
fun doubleToString(array: ByteArray) = String.format(Locale.US, "%.6f", ByteBuffer.wrap(array).double)
fun stringToFloat(string: String) = Float.floatToRawIntBits(Float.parseFloat(string)).toByteArray()
fun stringToDouble(string: String) = Double.doubleToRawLongBits(Double.parseDouble(string)).toByteArray()
@ -98,4 +98,6 @@ internal fun String.toLongBytes() = BigInteger(this).toLong().toByteArray()
internal inline fun String.toUByteA() = this.toByteA()
internal inline fun String.toUShortBytes() = this.toShortBytes()
internal inline fun String.toUIntBytes() = this.toIntBytes()
internal inline fun String.toULongBytes() = this.toLongBytes()
internal inline fun String.toULongBytes() = this.toLongBytes()
internal inline fun Byte.posInt() = this.toUByte().toInt()

View File

@ -23,19 +23,29 @@ internal enum class ControlTypes {
}
internal val ControlTypeMap = mapOf(
1 to ControlTypes.NodeStart,
46 to ControlTypes.Attribute,
190 to ControlTypes.NodeEnd,
191 to ControlTypes.FileEnd
//254 to ControlTypes.NodeEnd,
//255 to ControlTypes.FileEnd
1 to ControlTypes.NodeStart,
46 to ControlTypes.Attribute,
190 to ControlTypes.NodeEnd,
191 to ControlTypes.FileEnd
//254 to ControlTypes.NodeEnd,
//255 to ControlTypes.FileEnd
)
internal class KbinConverter(val fromString: (String) -> ByteArray, val toString: (ByteArray) -> String)
internal class KbinType(var names: List<String>, val size: Int, private val handler: KbinConverter, val count: Int = 1) {
internal class KbinType(
var names: List<String>,
val size: Int,
private val handler: KbinConverter,
val count: Int = 1
) {
constructor(name: String, size: Int, handler: KbinConverter, count: Int = 1) : this(mutableListOf(name), size, handler, count)
constructor(name: String, size: Int, handler: KbinConverter, count: Int = 1) : this(
mutableListOf(name),
size,
handler,
count
)
val name: String
get() = names[0]
@ -60,10 +70,10 @@ internal operator fun Int.times(t: KbinType): KbinType {
val newNames = t.names.map { this.toString() + it }
val newSize = t.size * this
fun newToString(input: ByteArray) =
input.asIterable().chunked(t.size).joinToString(" ") { t.toString(it.toByteArray()) }
input.asIterable().chunked(t.size).joinToString(" ") { t.toString(it.toByteArray()) }
fun newFromString(input: String) =
input.split(" ").flatMap { t.fromString(it).asIterable() }.toByteArray()
input.split(" ").flatMap { t.fromString(it).asIterable() }.toByteArray()
return KbinType(newNames, newSize, KbinConverter(::newFromString, ::newToString), this * t.count)
}
@ -79,7 +89,7 @@ internal class Types {
val s64 = KbinType("s64", 8, Converters.s64)
val u64 = KbinType("u64", 8, Converters.u64)
val bin = KbinType(listOf("bin", "binary"), 1, Converters.stub)
val binStub = KbinType(listOf("bin", "binary"), 1, Converters.stub)
val strStub = KbinType(listOf("str", "string"), 1, Converters.stub)
val ip4 = KbinType("ip4", 4, Converters.ip4)
@ -92,60 +102,61 @@ internal class Types {
internal val kbinTypeMap = with(Types) {
mapOf(
2 to s8,
3 to u8,
4 to s16,
5 to u16,
6 to s32,
7 to u32,
8 to s64,
9 to u64,
10 to bin,
11 to strStub,
12 to ip4,
13 to time,
14 to float,
15 to double,
16 to 2 * s8,
17 to 2 * u8,
18 to 2 * s16,
19 to 2 * u16,
20 to 2 * s32,
21 to 2 * u32,
22 to (2 * s64).alias("vs64"),
23 to (2 * u64).alias("vu64"),
24 to (2 * float).rename("2f"),
25 to (2 * double).rename("2d").alias("vd"),
26 to 3 * s8,
27 to 3 * u8,
28 to 3 * s16,
29 to 3 * u16,
30 to 3 * s32,
31 to 3 * u32,
32 to 3 * s64,
33 to 3 * u64,
34 to (3 * float).rename("3f"),
35 to (3 * double).rename("3d"),
36 to 4 * s8,
37 to 4 * u8,
38 to 4 * s16,
39 to 4 * u16,
40 to (4 * s32).alias("vs32"),
41 to (4 * u32).alias("vu32"),
42 to 4 * s64,
43 to 4 * u64,
44 to (4 * float).rename("4f").alias("vf"),
45 to (4 * double).rename("4d"),
48 to (16 * s8).rename("vs8"),
49 to (16 * u8).rename("vu8"),
50 to (8 * s16).rename("vs16"),
51 to (8 * u16).rename("vu16"),
52 to bool,
53 to (2 * bool).rename("2b"),
54 to (3 * bool).rename("3b"),
55 to (4 * bool).rename("4b"),
56 to (16 * bool).rename("vb")
2 to s8,
3 to u8,
4 to s16,
5 to u16,
6 to s32,
7 to u32,
8 to s64,
9 to u64,
10 to binStub,
11 to strStub,
12 to ip4,
13 to time,
14 to float,
15 to double,
16 to 2 * s8,
17 to 2 * u8,
18 to 2 * s16,
19 to 2 * u16,
20 to 2 * s32,
21 to 2 * u32,
22 to (2 * s64).alias("vs64"),
23 to (2 * u64).alias("vu64"),
24 to (2 * float).rename("2f"),
25 to (2 * double).rename("2d").alias("vd"),
26 to 3 * s8,
27 to 3 * u8,
28 to 3 * s16,
29 to 3 * u16,
30 to 3 * s32,
31 to 3 * u32,
32 to 3 * s64,
33 to 3 * u64,
34 to (3 * float).rename("3f"),
35 to (3 * double).rename("3d"),
36 to 4 * s8,
37 to 4 * u8,
38 to 4 * s16,
39 to 4 * u16,
40 to (4 * s32).alias("vs32"),
41 to (4 * u32).alias("vu32"),
42 to 4 * s64,
43 to 4 * u64,
44 to (4 * float).rename("4f").alias("vf"),
45 to (4 * double).rename("4d"),
48 to (16 * s8).rename("vs8"),
49 to (16 * u8).rename("vu8"),
50 to (8 * s16).rename("vs16"),
51 to (8 * u16).rename("vu16"),
52 to bool,
53 to (2 * bool).rename("2b"),
54 to (3 * bool).rename("3b"),
55 to (4 * bool).rename("4b"),
56 to (16 * bool).rename("vb")
)
}
internal var reverseKbinTypeMap: Map<String, Int> = kbinTypeMap.entries.associateBy({ it.value.name }) { it.key }
// internal var reverseKbinTypeMap: Map<String, Int> = kbinTypeMap.entries.associateBy({ it.value.name }) { it.key }
internal var reverseKbinTypeMap = mutableMapOf<String, Int>()

View File

@ -35,8 +35,8 @@ internal class KbinDataBuffer(bytes: ByteArray, val encoding: Charset) {
}
else -> throw KbinException("Invalid read of $num bytes")
}
realign(num)
// println("Read bytes $debug")
realign(num)
return result
}
@ -67,15 +67,18 @@ internal class KbinDataBuffer(bytes: ByteArray, val encoding: Charset) {
if (pos16Follows()) {
pos16 = pos32
}
//println("Index 4: $pos32")
//println("Index 2: $pos16")
//println("Index 1: $pos8")
/*println("Index 4: $pos32")
println("Index 2: $pos16")
println("Index 1: $pos8")
println()*/
}
fun realign4Byte(num: Int) {
realign(num + (if (num % 4 != 0) {
4 - (num % 4)
} else 0))
realign(
num + (if (num % 4 != 0) {
4 - (num % 4)
} else 0)
)
}
fun reset() {
@ -100,15 +103,14 @@ internal class KbinDataBuffer(bytes: ByteArray, val encoding: Charset) {
fun readFrom4Byte(num: Int): ByteArray {
if (num == 0) return byteArrayOf()
val read = data.slice(pos32 until pos32 + num)
// println("Read bytes $pos32 - ${pos32 + num}")
realign4Byte(num)
return read.toByteArray()
}
fun readString(length: Int): String {
var readBytes = readFrom4Byte(length)
if (readBytes.last() == 0x00.toByte()) { // null bytes are scary
readBytes = readBytes.sliceArray(0 until readBytes.lastIndex)
}
readBytes = readBytes.sliceArray(0 until readBytes.lastIndex)
return readBytes.toString(encoding)
}
@ -146,7 +148,7 @@ internal class KbinDataBuffer(bytes: ByteArray, val encoding: Charset) {
}
fun writeString(string: String) {
var bytes = string.toByteArray(encoding) + 0 // null byte
val bytes = string.toByteArray(encoding) + 0 // null byte
writeU32(bytes.size.toUInt())
writeTo4Byte(bytes)
}

View File

@ -1,12 +1,16 @@
package com.buttongames.butterflycore.xml.kbinxml
import nu.xom.Document
import nu.xom.Element
import com.buttongames.butterflycore.xml.kbinxml.ControlTypes.*
import com.buttongames.butterflycore.xml.kbinxml.Types.Companion.binStub
import com.buttongames.butterflycore.xml.kbinxml.Types.Companion.strStub
import org.w3c.dom.Document
import org.w3c.dom.Element
import java.nio.charset.Charset
import java.util.*
import javax.xml.parsers.DocumentBuilderFactory
import kotlin.experimental.inv
internal class KbinReader(data: ByteArray) {
init {
@ -48,6 +52,11 @@ internal class KbinReader(data: ByteArray) {
val nodeStack = ArrayDeque<Element>()
var end = false
val dbf = DocumentBuilderFactory.newInstance()
val builder = dbf.newDocumentBuilder()
val doc = builder.newDocument()
doc.xmlStandalone = true
while (!end) {
val current = nodeBuffer.readU8().toInt()
val actual = current and (1 shl 6).inv()
@ -59,7 +68,7 @@ internal class KbinReader(data: ByteArray) {
NodeStart -> {
val name = nodeBuffer.readString()
val newNode = Element(name)
val newNode = doc.createElement(name)
if (currentNode != null) {
currentNode.appendChild(newNode)
nodeStack.push(currentNode)
@ -81,7 +90,7 @@ internal class KbinReader(data: ByteArray) {
//println("Got attribute $name with value $value")
currentNode!!.addAttribute(name, value)
currentNode!!.setAttribute(name, value)
}
FileEnd -> {
if (nodeStack.size == 0) {
@ -95,24 +104,25 @@ internal class KbinReader(data: ByteArray) {
val nodeName = nodeBuffer.readString()
val arraySize = if (isArray) dataBuffer.readU32().toInt() else valueType.size
nodeStack.push(currentNode)
val newNode = Element(nodeName)
newNode.addAttribute("__type", valueName)
currentNode!!.appendChild(newNode)
nodeStack.push(currentNode!!)
val newNode = doc.createElement(nodeName)
newNode.setAttribute("__type", valueName)
currentNode.appendChild(newNode)
currentNode = newNode
val numElements = arraySize / valueType.size
when (valueName) {
"bin" -> {
currentNode.addAttribute("__size", arraySize.toString())
when (valueType) {
binStub -> {
currentNode.setAttribute("__size", arraySize.toString())
val bytes = dataBuffer.readFrom4Byte(arraySize)
currentNode.text = ByteConv.binToString(bytes)
}
"str" -> {
strStub -> {
currentNode.text = dataBuffer.readString(arraySize)
}
else -> {
if (isArray) currentNode.addAttribute("__count", numElements.toString())
if (isArray) currentNode.setAttribute("__count", numElements.toString())
val byteList = dataBuffer.readBytes(arraySize)
val stringList = mutableListOf<String>()
@ -128,6 +138,7 @@ internal class KbinReader(data: ByteArray) {
throw KbinException("Unsupported node type with ID $actual")
}
}
return Document(currentNode)
doc.appendChild(currentNode)
return doc
}
}

View File

@ -1,26 +1,38 @@
package com.buttongames.butterflycore.xml.kbinxml
import nu.xom.Document
import nu.xom.Element
import com.buttongames.butterflycore.xml.kbinxml.Types.Companion.binStub
import com.buttongames.butterflycore.xml.kbinxml.Types.Companion.strStub
import org.w3c.dom.Attr
import org.w3c.dom.Document
import org.w3c.dom.Element
import org.w3c.dom.Node
import java.io.ByteArrayOutputStream
import java.nio.charset.Charset
import kotlin.experimental.inv
internal class KbinWriter(val xml: Document, val encoding: String = "SHIFT_JIS", val compressed: Boolean = true) {
internal class KbinWriter(val xml: Document, val encoding: String = "UTF-8", val compressed: Boolean = true) {
private val header = ByteArray(4)
val charset = Charset.forName(encoding)
fun getKbin(): ByteArray {
if (reverseKbinTypeMap.isEmpty()) {
for (entry in kbinTypeMap.entries) {
for (name in entry.value.names) {
reverseKbinTypeMap[name] = entry.key
}
}
}
val dataBuffer = KbinDataBuffer(charset)
val nodeBuffer = KbinNodeBuffer(compressed, charset)
header[0] = 0xa0u.toByte()
header[0] = 0xA0u.toByte()
header[1] = (if (compressed) 0x42u else 0x45u).toByte()
header[2] = (Constants.encodingsReverse[encoding]!! shl 5).toByte()
header[3] = header[2].inv()
nodeRecurse(xml.rootElement, dataBuffer, nodeBuffer)
nodeRecurse(xml.documentElement, dataBuffer, nodeBuffer)
nodeBuffer.writeU8(255u)
nodeBuffer.pad()
@ -36,13 +48,27 @@ internal class KbinWriter(val xml: Document, val encoding: String = "SHIFT_JIS",
}
private fun nodeRecurse(e: Element, dataBuffer: KbinDataBuffer, nodeBuffer: KbinNodeBuffer) {
val typeName = e.getAttribute("__type")?.value
var typeName = e.getAttribute("__type")
val text = e.text
if (typeName == "" && text.trim() != "") {
typeName = "str"
}
val typeId = reverseKbinTypeMap[typeName]
if (typeName != null && typeId == null) throw KbinException("Type $typeName is not supported")
if (typeName != "" && typeId == null)
throw KbinException("Type $typeName is not supported")
val type = kbinTypeMap[typeId]
val count = (e.getAttribute("__count")?.value ?: e.getAttribute("__size")?.value)?.toInt()
val isArray = (count != null) && type?.name !in listOf("bin", "str")
var count = run {
val count_attr = e.getAttribute("__count")
val size_attr = e.getAttribute("__size")
when {
count_attr != "" -> count_attr
size_attr != "" -> size_attr
else -> null
}?.toInt()
}
val isArray = (count != null) && type?.name !in strStub.names + binStub.names
if (typeId == null) {
nodeBuffer.writeU8(1u)
@ -53,45 +79,39 @@ internal class KbinWriter(val xml: Document, val encoding: String = "SHIFT_JIS",
}
nodeBuffer.writeU8(toWrite)
}
nodeBuffer.writeString(e.localName)
nodeBuffer.writeString(e.nodeName)
if (type != null) {
if (type.name == "bin") {
if (count != null) {
dataBuffer.writeU32(count.toUInt())
}
val toWrite = ByteConv.stringToBin(e.text)
if (type == binStub) {
count = text.length / 2
dataBuffer.writeU32(count.toUInt())
val toWrite = ByteConv.stringToBin(text)
dataBuffer.writeTo4Byte(toWrite)
} else if (type.name == "str") {
dataBuffer.writeString(e.text)
} else if (type == strStub) {
dataBuffer.writeString(text)
} else {
if (count != null) {
dataBuffer.writeU32((count * type.size).toUInt())
}
val split = e.text.splitAndJoin(type.count)
val split = text.splitAndJoin(type.count)
//val toWrite = split.flatMap { type.fromString(it).asIterable() }.toByteArray()
val toWrite = split.flatMap { type.fromString(it).asIterable() }.toByteArray()
dataBuffer.writeBytes(toWrite)
}
}
/*for (a in e) {
val name = a.localName
if (name in arrayOf("__count", "__size", "__type"))
continue
val value = a.value
nodeBuffer.writeU8(46u)
nodeBuffer.writeString(name)
dataBuffer.writeString(value)
}*/
val attributes = e.iterator().asSequence()
.filter { it.localName !in listOf("__type", "__count", "__size") }
.sortedBy { it.localName }
val attributes = e.attributeList.filter { it.nodeName !in listOf("__type", "__count", "__size") }
.sortedBy { it.nodeName }
for (a in attributes) {
a as Attr
nodeBuffer.writeU8(46u)
nodeBuffer.writeString(a.localName)
nodeBuffer.writeString(a.nodeName)
dataBuffer.writeString(a.value)
}
for (c in e.childElements) {
nodeRecurse(c, dataBuffer, nodeBuffer)
for (thing in e.childNodes(Node.ELEMENT_NODE)) {
nodeRecurse(thing as Element, dataBuffer, nodeBuffer)
}
nodeBuffer.writeU8(254u)
}

View File

@ -1,10 +1,9 @@
package com.buttongames.butterflycore.xml.kbinxml
import nu.xom.Builder
import nu.xom.Document
import org.w3c.dom.Document
fun kbinEncode(d: Document) = KbinWriter(d).getKbin()
fun kbinEncode(s: String) = kbinEncode(Builder().build(s, null))
fun kbinEncode(d: Document, charset: String = "UTF-8") = KbinWriter(d, charset).getKbin()
fun kbinEncode(s: String, charset: String = "UTF-8") = kbinEncode(s.toXml(), charset)
fun kbinDecode(b: ByteArray) = KbinReader(b).getXml()

View File

@ -1,7 +1,5 @@
package com.buttongames.butterflycore.xml.kbinxml
import nu.xom.Attribute
import nu.xom.Element
import java.nio.charset.Charset
fun byteArrayOfInts(vararg ints: Int) = ByteArray(ints.size) { pos -> ints[pos].toByte() }
@ -17,10 +15,6 @@ internal fun ByteArray.unsigned(): UByteArray {
class KbinException internal constructor(override var message: String) : Exception(message)
internal fun Element.addAttribute(key: String, value: String) {
this.addAttribute(Attribute(key, value))
}
internal fun MutableList<Byte>.setOrAdd(pos: Int, byte: Byte) {
if (pos == this.size) {
this.add(byte)
@ -42,14 +36,18 @@ internal fun MutableList<Byte>.setOrAddAll(pos: Int, bytes: ByteArray) {
internal fun ByteArray.toString(encoding: String) = this.toString(Charset.forName(encoding))
internal fun String.splitAndJoin(count: Int): Array<String> {
/*internal fun String.splitAndJoin(count: Int): Array<String> {
val input = this.split(" ")
val output = Array(input.size / count) { "" }
for (i in 0 until output.size) {
output[i] = input.slice(i * count until (i + 1) * count).joinToString(" ")
}
return output
}
}*/
internal fun String.splitAndJoin(count: Int) =
this.split(" ").chunked(count).map { it.joinToString(" ") }.toTypedArray()
fun measureMs(times: Int = 1, function: () -> Unit) {
for (i in 0 until times) {
@ -60,6 +58,4 @@ fun measureMs(times: Int = 1, function: () -> Unit) {
val duration = endTime - startTime //divide by 1000000 to get milliseconds.
println("Took ${duration / 1000000.0} ms")
}
}
internal inline fun Byte.posInt() = this.toUByte().toInt()
}

View File

@ -1,77 +1,92 @@
package com.buttongames.butterflycore.xml.kbinxml
import nu.xom.*
import java.io.ByteArrayOutputStream
import org.w3c.dom.Attr
import org.w3c.dom.Document
import org.w3c.dom.Element
import org.w3c.dom.Node
import org.xml.sax.InputSource
import java.io.File
fun deepCompare(a: Document, b: Document): Boolean {
return deepCompare(a.copy().rootElement, b.copy().rootElement)
}
private fun deepCompare(a: Element, b: Element): Boolean {
a.sortAttributes(); b.sortAttributes()
val nameA = a.localName
val nameB = b.localName
check(nameA == nameB) { "Element names are different: $nameA != $nameB" }
val attrItA = a.iterator()
val attrItB = b.iterator()
while (attrItA.hasNext()) {
val attrA = attrItA.next()
val attrB = attrItB.next()
check(attrA.localName == attrB.localName) { "Attribute names are different: ${attrA.localName} != ${attrB.localName}" }
if (attrB.value == "bib")
println("break")
check(attrA.value == attrB.value) { "Attribute values are different for \"${attrA.localName}\": ${attrA.value} != ${attrB.value}" }
}
if (a.text != "") {
check(a.text == b.text) { "Text inside of tags does not match: ${a.value} != ${b.value}" }
}
val elemItA = a.childElements.iterator()
val elemItB = b.childElements.iterator()
while (elemItA.hasNext()) {
deepCompare(elemItA.next(), elemItB.next())
}
return true
}
fun Document.prettyString(): String {
val o = ByteArrayOutputStream()
val serializer = Serializer(o, "UTF-8")
serializer.setIndent(4)
serializer.write(this)
return o.toString("UTF-8")
}
import java.io.StringReader
import java.io.StringWriter
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.OutputKeys
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult
var Element.text: String
get() {
for (i in 0 until childCount) {
val e = getChild(i)
if (e is Text) {
return e.value
get() = this.childNodes(Node.TEXT_NODE).firstOrNull()?.nodeValue ?: ""
set(value) {
val children = this.childNodes
for (i in 0 until children.length) {
val c = children.item(i)
if (c.nodeType == Node.TEXT_NODE) {
this.removeChild(c)
}
}
return ""
}
set(value: String) {
this.appendChild(value)
val e = this.ownerDocument.createTextNode(value)
this.appendChild(e)
}
internal fun Element.sortAttributesRec(): Element {
this.sortAttributes()
for (e in this.childElements) {
sortAttributesRecE(e)
}
return this
}
private fun sortAttributesRecE(e: Element) {
e.sortAttributes()
for (b in e.childElements) {
sortAttributesRecE(b)
fun Element.childNodes(type: Short): Sequence<Node> {
val children = this.childNodes
return sequence {
for (i in 0 until children.length) {
val c = children.item(i)
if (c.nodeType == type) {
yield(c)
}
}
}
}
fun String.toXml() = Builder().build(this, null)!!
fun File.toXml() = Builder().build(this)
fun Element.firstChild(name: String): Element? {
var child: Node? = this.firstChild
while (child != null) {
if (child is Element && name == child.nodeName) return child
child = child.nextSibling
}
return null
}
val Element.childElements: Sequence<Element>
get() {
return this.childNodes(Node.ELEMENT_NODE) as Sequence<Element>
}
val Element.attributeList: List<Attr>
get() {
val result = mutableListOf<Attr>()
val attrs = this.attributes!!
for (i in 0 until attrs.length) {
result.add(attrs.item(i) as Attr)
}
return result
}
fun Document.prettyString(indent: Int = 4): String {
this.xmlStandalone = true
val stringWriter = StringWriter()
val xmlOutput = StreamResult(stringWriter)
val transformerFactory = TransformerFactory.newInstance()
transformerFactory.setAttribute("indent-number", indent)
val transformer = transformerFactory.newTransformer()
transformer.setOutputProperty(OutputKeys.INDENT, "yes")
transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "");
transformer.transform(DOMSource(this), xmlOutput)
return xmlOutput.writer.toString()
}
fun String.toXml(): Document {
val db = DocumentBuilderFactory.newInstance().newDocumentBuilder()
val input = InputSource()
input.characterStream = (StringReader(this))
return db.parse(input)
}
fun File.toXml(): Document {
val db = DocumentBuilderFactory.newInstance().newDocumentBuilder()
return db.parse(this)
}

View File

@ -83,7 +83,7 @@ public abstract class BaseRequestHandler {
// convert them to binary XML
if (!XmlUtils.isBinaryXML(respBytes)) {
respBytes = PublicKt.kbinEncode(new String(respBytes));
respBytes = PublicKt.kbinEncode(new String(respBytes), "UTF-8");
}
response.header(COMPRESSION_HEADER, "none");

Binary file not shown.

View File

@ -1,198 +0,0 @@
diff --git a/src/nu/xom/Element.java b/src/nu/xom/Element.java
index a810cbc..50cee6a 100644
--- a/src/nu/xom/Element.java
+++ b/src/nu/xom/Element.java
@@ -21,13 +21,7 @@
package nu.xom;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Set;
-import java.util.LinkedHashSet;
+import java.util.*;
/**
* <p>
@@ -49,7 +43,7 @@ import java.util.LinkedHashSet;
* @version 1.3.0
*
*/
-public class Element extends ParentNode {
+public class Element extends ParentNode implements Iterable<Attribute> {
private String localName;
private String prefix;
@@ -1839,6 +1833,23 @@ public class Element extends ParentNode {
}
+ @Override
+ public Iterator<Attribute> iterator() {
+ return new Iterator<Attribute>() {
+ int i = 0;
+
+ @Override
+ public boolean hasNext() {
+ return (i < numAttributes && attributes[i] != null);
+ }
+
+ @Override
+ public Attribute next() {
+ return attributes[i++];
+ }
+ };
+ }
+
private class AttributeIterator implements Iterator {
@@ -1863,6 +1874,25 @@ public class Element extends ParentNode {
throw new UnsupportedOperationException();
}
}
+
+ public void sortAttributes() {
+ if (attributes == null) return;
+ Arrays.sort(attributes, new Comparator<Attribute>(){
+ @Override
+ public int compare(Attribute t1, Attribute t2) {
+ if (t1 == null && t2 == null) {
+ return 0;
+ }
+ if (t1 == null) {
+ return 1;
+ }
+ if (t2 == null) {
+ return -1;
+ }
+ return t1.getLocalName().compareTo(t2.getLocalName());
+ }
+ });
+ }
Iterator attributeIterator() {
diff --git a/src/nu/xom/Elements.java b/src/nu/xom/Elements.java
index 1fd26ea..524cbb0 100644
--- a/src/nu/xom/Elements.java
+++ b/src/nu/xom/Elements.java
@@ -22,6 +22,7 @@
package nu.xom;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
/**
@@ -38,7 +39,7 @@ import java.util.List;
*
*
*/
-public final class Elements {
+public final class Elements implements Iterable<Element> {
private List elements = new ArrayList(1);
@@ -82,4 +83,25 @@ public final class Elements {
elements.add(element);
}
+ @Override
+ public Iterator<Element> iterator() {
+ return new Iterator<Element>(){
+
+ int i = 0;
+ @Override
+ public boolean hasNext() {
+ return i < elements.size();
+ }
+
+ @Override
+ public Element next() {
+ return (Element)elements.get(i++);
+ }
+
+ @Override
+ public void remove() {
+
+ }
+ };
+ }
}
\ No newline at end of file
diff --git a/src/nu/xom/Nodes.java b/src/nu/xom/Nodes.java
index a987783..9ca938f 100644
--- a/src/nu/xom/Nodes.java
+++ b/src/nu/xom/Nodes.java
@@ -22,6 +22,7 @@
package nu.xom;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
/**
@@ -45,9 +46,9 @@ import java.util.List;
* @version 1.1b4
*
*/
-public final class Nodes {
+public final class Nodes implements Iterable<Node> {
- private final List nodes;
+ private final List<Node> nodes;
/**
@@ -56,7 +57,7 @@ public final class Nodes {
* </p>
*/
public Nodes() {
- nodes = new ArrayList();
+ nodes = new ArrayList<Node>();
}
@@ -74,7 +75,7 @@ public final class Nodes {
if (node == null) {
throw new NullPointerException("Nodes content must be non-null");
}
- nodes = new ArrayList(1);
+ nodes = new ArrayList<Node>(1);
nodes.add(node);
}
@@ -113,7 +114,7 @@ public final class Nodes {
* negative or greater than or equal to the size of the list
*/
public Node get(int index) {
- return (Node) nodes.get(index);
+ return nodes.get(index);
}
@@ -131,7 +132,7 @@ public final class Nodes {
* negative or greater than or equal to the size of the list
*/
public Node remove(int index) {
- return (Node) nodes.remove(index);
+ return nodes.remove(index);
}
@@ -187,5 +188,9 @@ public final class Nodes {
return nodes.contains(node);
}
-
+
+ @Override
+ public Iterator<Node> iterator() {
+ return nodes.iterator();
+ }
}
\ No newline at end of file