implemented Stepan's excellent suggestion of a cleaner computeNext() method on AbstractIterator to make a little easier to implement clean iterators - and avoid pesky nulls in the process
This commit is contained in:
@@ -14,13 +14,15 @@ public inline fun <T> iterate(nextFunction: () -> T?) : java.util.Iterator<T> =
|
||||
public inline fun <T, R: T> java.util.Iterator<T>.filterIsInstance(klass: Class<R>): java.util.Iterator<R> = FilterIsIterator<T,R>(this, klass)
|
||||
|
||||
private class FilterIsIterator<T, R :T>(val iterator : java.util.Iterator<T>, val klass: Class<R>) : AbstractIterator<R>() {
|
||||
override protected fun computeNext(): R? {
|
||||
override protected fun computeNext(): Unit {
|
||||
while (iterator.hasNext()) {
|
||||
val next = iterator.next()
|
||||
if (klass.isInstance(next)) return next as R
|
||||
if (klass.isInstance(next)) {
|
||||
setNext(next as R)
|
||||
return
|
||||
}
|
||||
}
|
||||
done()
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,13 +34,15 @@ private class FilterIsIterator<T, R :T>(val iterator : java.util.Iterator<T>, va
|
||||
public inline fun <T> java.util.Iterator<T>.filter(predicate: (T) -> Boolean) : java.util.Iterator<T> = FilterIterator<T>(this, predicate)
|
||||
|
||||
private class FilterIterator<T>(val iterator : java.util.Iterator<T>, val predicate: (T)-> Boolean) : AbstractIterator<T>() {
|
||||
override protected fun computeNext(): T? {
|
||||
override protected fun computeNext(): Unit {
|
||||
while (iterator.hasNext()) {
|
||||
val next = iterator.next()
|
||||
if ((predicate)(next)) return next
|
||||
if ((predicate)(next)) {
|
||||
setNext(next)
|
||||
return
|
||||
}
|
||||
}
|
||||
done()
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,15 +53,17 @@ public inline fun <T> java.util.Iterator<T>.filterNot(predicate: (T) -> Boolean)
|
||||
public inline fun <T> java.util.Iterator<T?>?.filterNotNull() : java.util.Iterator<T> = FilterNotNullIterator(this)
|
||||
|
||||
private class FilterNotNullIterator<T>(val iterator : java.util.Iterator<T?>?) : AbstractIterator<T>() {
|
||||
override protected fun computeNext(): T? {
|
||||
override protected fun computeNext(): Unit {
|
||||
if (iterator != null) {
|
||||
while (iterator.hasNext()) {
|
||||
val next = iterator.next()
|
||||
if (next != null) return next
|
||||
if (next != null) {
|
||||
setNext(next)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
done()
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +75,13 @@ private class FilterNotNullIterator<T>(val iterator : java.util.Iterator<T?>?) :
|
||||
public inline fun <T, R> java.util.Iterator<T>.map(transform: (T) -> R): java.util.Iterator<R> = MapIterator<T, R>(this, transform)
|
||||
|
||||
private class MapIterator<T, R>(val iterator : java.util.Iterator<T>, val transform: (T) -> R) : AbstractIterator<R>() {
|
||||
override protected fun computeNext() : R? = if (iterator.hasNext()) (transform)(iterator.next()) else { done(); null }
|
||||
override protected fun computeNext() : Unit {
|
||||
if (iterator.hasNext()) {
|
||||
setNext((transform)(iterator.next()))
|
||||
} else {
|
||||
done()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,14 +94,19 @@ public inline fun <T, R> java.util.Iterator<T>.flatMap(transform: (T) -> java.ut
|
||||
private class FlatMapIterator<T, R>(val iterator : java.util.Iterator<T>, val transform: (T) -> java.util.Iterator<R>) : AbstractIterator<R>() {
|
||||
var transformed: java.util.Iterator<R> = iterate<R> { null }
|
||||
|
||||
override protected fun computeNext() : R? {
|
||||
if (transformed.hasNext()) return transformed.next()
|
||||
if (iterator.hasNext()) {
|
||||
transformed = (transform)(iterator.next())
|
||||
return computeNext()
|
||||
override protected fun computeNext() : Unit {
|
||||
while (true) {
|
||||
if (transformed.hasNext()) {
|
||||
setNext(transformed.next())
|
||||
return
|
||||
}
|
||||
if (iterator.hasNext()) {
|
||||
transformed = (transform)(iterator.next())
|
||||
} else {
|
||||
done()
|
||||
return
|
||||
}
|
||||
}
|
||||
done()
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,13 +131,15 @@ public inline fun <T> java.util.Iterator<T>.take(n: Int): java.util.Iterator<T>
|
||||
public inline fun <T> java.util.Iterator<T>.takeWhile(predicate: (T) -> Boolean): java.util.Iterator<T> = TakeWhileIterator<T>(this, predicate)
|
||||
|
||||
private class TakeWhileIterator<T>(val iterator: java.util.Iterator<T>, val predicate: (T) -> Boolean) : AbstractIterator<T>() {
|
||||
override protected fun computeNext() : T? {
|
||||
override protected fun computeNext() : Unit {
|
||||
if (iterator.hasNext()) {
|
||||
val item = iterator.next()
|
||||
if ((predicate)(item)) return item
|
||||
if ((predicate)(item)) {
|
||||
setNext(item)
|
||||
return
|
||||
}
|
||||
}
|
||||
done()
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,3 +158,4 @@ public inline fun <T> java.util.Iterator<T>.join(separator: String = ", ", prefi
|
||||
if (limit != null && count > limit) buffer.append("...")
|
||||
return buffer.append(postfix).toString().sure()
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ set(value) {
|
||||
// Helper methods
|
||||
|
||||
/** Returns true if the element has the given CSS class style in its 'class' attribute */
|
||||
public fun Element.hasClass(cssClass: String): Boolean {
|
||||
fun Element.hasClass(cssClass: String): Boolean {
|
||||
val c = this.classes
|
||||
return if (c != null)
|
||||
c.matches("""(^|.*\s+)$cssClass($|\s+.*)""")
|
||||
@@ -104,7 +104,7 @@ public fun Element.hasClass(cssClass: String): Boolean {
|
||||
}
|
||||
|
||||
/** Adds the given CSS class to this element's 'class' attribute */
|
||||
public fun Element.addClass(cssClass: String): Boolean {
|
||||
fun Element.addClass(cssClass: String): Boolean {
|
||||
val classSet = this.classSet
|
||||
val answer = classSet.add(cssClass)
|
||||
if (answer) {
|
||||
@@ -114,7 +114,7 @@ public fun Element.addClass(cssClass: String): Boolean {
|
||||
}
|
||||
|
||||
/** Removes the given CSS class to this element's 'class' attribute */
|
||||
public fun Element.removeClass(cssClass: String): Boolean {
|
||||
fun Element.removeClass(cssClass: String): Boolean {
|
||||
val classSet = this.classSet
|
||||
val answer = classSet.remove(cssClass)
|
||||
if (answer) {
|
||||
@@ -125,7 +125,7 @@ public fun Element.removeClass(cssClass: String): Boolean {
|
||||
|
||||
/** TODO this approach generates compiler errors...
|
||||
|
||||
public fun Element.addClass(varargs cssClasses: Array<String>): Boolean {
|
||||
fun Element.addClass(varargs cssClasses: Array<String>): Boolean {
|
||||
val set = this.classSet
|
||||
var answer = false
|
||||
for (cs in cssClasses) {
|
||||
@@ -139,7 +139,7 @@ public fun Element.addClass(varargs cssClasses: Array<String>): Boolean {
|
||||
return answer
|
||||
}
|
||||
|
||||
public fun Element.removeClass(varargs cssClasses: Array<String>): Boolean {
|
||||
fun Element.removeClass(varargs cssClasses: Array<String>): Boolean {
|
||||
val set = this.classSet
|
||||
var answer = false
|
||||
for (cs in cssClasses) {
|
||||
@@ -155,7 +155,7 @@ public fun Element.removeClass(varargs cssClasses: Array<String>): Boolean {
|
||||
*/
|
||||
|
||||
/** Searches for elements using the element name, an element ID (if prefixed with dot) or element class (if prefixed with #) */
|
||||
public fun Document?.get(selector: String): List<Element> {
|
||||
fun Document?.get(selector: String): List<Element> {
|
||||
val root = this?.getDocumentElement()
|
||||
return if (root != null) {
|
||||
if (selector == "*") {
|
||||
@@ -179,7 +179,7 @@ public fun Document?.get(selector: String): List<Element> {
|
||||
}
|
||||
|
||||
/** Searches for elements using the element name, an element ID (if prefixed with dot) or element class (if prefixed with #) */
|
||||
public fun Element.get(selector: String): List<Element> {
|
||||
fun Element.get(selector: String): List<Element> {
|
||||
return if (selector == "*") {
|
||||
elements
|
||||
} else if (selector.startsWith(".")) {
|
||||
@@ -197,57 +197,53 @@ public fun Element.get(selector: String): List<Element> {
|
||||
}
|
||||
|
||||
/** Returns an [[Iterator]] over the next siblings of this node */
|
||||
public fun Node.nextSiblings() : Iterator<Node> = NextSiblingIterator(this)
|
||||
fun Node.nextSiblings() : Iterator<Node> = NextSiblingIterator(this)
|
||||
|
||||
class NextSiblingIterator(var node: Node) : AbstractIterator<Node>() {
|
||||
|
||||
public override fun computeNext(): Node? {
|
||||
override fun computeNext(): Unit {
|
||||
val next = node.getNextSibling()
|
||||
if (next != null) {
|
||||
node = next
|
||||
return next
|
||||
setNext(next)
|
||||
} else {
|
||||
done()
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Returns an [[Iterator]] over the next siblings of this node */
|
||||
public fun Node.previousSiblings() : Iterator<Node> = PreviousSiblingIterator(this)
|
||||
fun Node.previousSiblings() : Iterator<Node> = PreviousSiblingIterator(this)
|
||||
|
||||
class PreviousSiblingIterator(var node: Node) : AbstractIterator<Node>() {
|
||||
|
||||
public override fun computeNext(): Node? {
|
||||
override fun computeNext(): Unit {
|
||||
val next = node.getPreviousSibling()
|
||||
if (next != null) {
|
||||
node = next
|
||||
return next
|
||||
setNext(next)
|
||||
} else {
|
||||
done()
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true if this node is a Text node or a CDATA node */
|
||||
public fun Node.isText(): Boolean {
|
||||
fun Node.isText(): Boolean {
|
||||
val nodeType = getNodeType()
|
||||
return nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE
|
||||
}
|
||||
|
||||
/** Returns an [[Iterator]] of all the next [[Element]] siblings */
|
||||
public fun Node.nextElements(): Iterator<Element> = nextSiblings().filterIsInstance<Node, Element>(javaClass<Element>())
|
||||
fun Node.nextElements(): Iterator<Element> = nextSiblings().filterIsInstance<Node, Element>(javaClass<Element>())
|
||||
|
||||
/** Returns an [[Iterator]] of all the previous [[Element]] siblings */
|
||||
public fun Node.previousElements(): Iterator<Element> = previousSiblings().filterIsInstance<Node, Element>(javaClass<Element>())
|
||||
fun Node.previousElements(): Iterator<Element> = previousSiblings().filterIsInstance<Node, Element>(javaClass<Element>())
|
||||
|
||||
/** Returns the attribute value or empty string if its not present */
|
||||
public inline fun Element.attribute(name: String): String {
|
||||
inline fun Element.attribute(name: String): String {
|
||||
return this.getAttribute(name) ?: ""
|
||||
}
|
||||
|
||||
/** Returns the children of the element as a list */
|
||||
public inline fun Element?.children(): List<Node> {
|
||||
inline fun Element?.children(): List<Node> {
|
||||
return this?.getChildNodes().toList()
|
||||
}
|
||||
|
||||
@@ -261,22 +257,22 @@ get() = this?.getElementsByTagName("*").toElementList()
|
||||
|
||||
|
||||
/** Returns all the child elements given the local element name */
|
||||
public inline fun Element?.elements(localName: String?): List<Element> {
|
||||
inline fun Element?.elements(localName: String?): List<Element> {
|
||||
return this?.getElementsByTagName(localName).toElementList()
|
||||
}
|
||||
|
||||
/** Returns all the elements given the local element name */
|
||||
public inline fun Document?.elements(localName: String?): List<Element> {
|
||||
inline fun Document?.elements(localName: String?): List<Element> {
|
||||
return this?.getElementsByTagName(localName).toElementList()
|
||||
}
|
||||
|
||||
/** Returns all the child elements given the namespace URI and local element name */
|
||||
public inline fun Element?.elements(namespaceUri: String?, localName: String?): List<Element> {
|
||||
inline fun Element?.elements(namespaceUri: String?, localName: String?): List<Element> {
|
||||
return this?.getElementsByTagNameNS(namespaceUri, localName).toElementList()
|
||||
}
|
||||
|
||||
/** Returns all the elements given the namespace URI and local element name */
|
||||
public inline fun Document?.elements(namespaceUri: String?, localName: String?): List<Element> {
|
||||
inline fun Document?.elements(namespaceUri: String?, localName: String?): List<Element> {
|
||||
return this?.getElementsByTagNameNS(namespaceUri, localName).toElementList()
|
||||
}
|
||||
|
||||
@@ -300,7 +296,7 @@ val NodeList?.last : Node?
|
||||
get() = this.tail
|
||||
|
||||
|
||||
public inline fun NodeList?.toList(): List<Node> {
|
||||
inline fun NodeList?.toList(): List<Node> {
|
||||
return if (this == null) {
|
||||
Collections.EMPTY_LIST as List<Node>
|
||||
}
|
||||
@@ -309,7 +305,7 @@ public inline fun NodeList?.toList(): List<Node> {
|
||||
}
|
||||
}
|
||||
|
||||
public inline fun NodeList?.toElementList(): List<Element> {
|
||||
inline fun NodeList?.toElementList(): List<Element> {
|
||||
return if (this == null) {
|
||||
Collections.EMPTY_LIST as List<Element>
|
||||
}
|
||||
@@ -319,7 +315,7 @@ public inline fun NodeList?.toElementList(): List<Element> {
|
||||
}
|
||||
|
||||
/** Converts the node list to an XML String */
|
||||
public fun NodeList?.toXmlString(xmlDeclaration: Boolean = false): String {
|
||||
fun NodeList?.toXmlString(xmlDeclaration: Boolean = false): String {
|
||||
return if (this == null)
|
||||
"" else {
|
||||
nodesToXmlString(this.toList(), xmlDeclaration)
|
||||
@@ -327,7 +323,7 @@ public fun NodeList?.toXmlString(xmlDeclaration: Boolean = false): String {
|
||||
}
|
||||
|
||||
class NodeListAsList(val nodeList: NodeList): AbstractList<Node>() {
|
||||
public override fun get(index: Int): Node {
|
||||
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)
|
||||
@@ -336,11 +332,11 @@ class NodeListAsList(val nodeList: NodeList): AbstractList<Node>() {
|
||||
}
|
||||
}
|
||||
|
||||
public override fun size(): Int = nodeList.getLength()
|
||||
override fun size(): Int = nodeList.getLength()
|
||||
}
|
||||
|
||||
class ElementListAsList(val nodeList: NodeList): AbstractList<Element>() {
|
||||
public override fun get(index: Int): Element {
|
||||
override fun get(index: Int): Element {
|
||||
val node = nodeList.item(index)
|
||||
if (node is Element) {
|
||||
return node
|
||||
@@ -353,23 +349,23 @@ class ElementListAsList(val nodeList: NodeList): AbstractList<Element>() {
|
||||
}
|
||||
}
|
||||
|
||||
public override fun size(): Int = nodeList.getLength()
|
||||
override fun size(): Int = nodeList.getLength()
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Syntax sugar
|
||||
|
||||
public inline fun Node.plus(child: Node?): Node {
|
||||
inline fun Node.plus(child: Node?): Node {
|
||||
if (child != null) {
|
||||
this.appendChild(child)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
public inline fun Element.plus(text: String?): Element = this.addText(text)
|
||||
inline fun Element.plus(text: String?): Element = this.addText(text)
|
||||
|
||||
public inline fun Element.plusAssign(text: String?): Element = this.addText(text)
|
||||
inline fun Element.plusAssign(text: String?): Element = this.addText(text)
|
||||
|
||||
|
||||
// Builder
|
||||
@@ -377,7 +373,7 @@ public inline fun Element.plusAssign(text: String?): Element = this.addText(text
|
||||
/**
|
||||
* Creates a new element which can be configured via a function
|
||||
*/
|
||||
public fun Document.createElement(name: String, init: Element.()-> Unit): Element {
|
||||
fun Document.createElement(name: String, init: Element.()-> Unit): Element {
|
||||
val elem = this.createElement(name).sure()
|
||||
elem.init()
|
||||
return elem
|
||||
@@ -386,14 +382,14 @@ public fun Document.createElement(name: String, init: Element.()-> Unit): Elemen
|
||||
/**
|
||||
* Creates a new element to an element which has an owner Document which can be configured via a function
|
||||
*/
|
||||
public fun Element.createElement(name: String, doc: Document? = null, init: Element.()-> Unit): Element {
|
||||
fun Element.createElement(name: String, doc: Document? = null, init: Element.()-> Unit): Element {
|
||||
val elem = ownerDocument(doc).createElement(name).sure()
|
||||
elem.init()
|
||||
return elem
|
||||
}
|
||||
|
||||
/** Returns the owner document of the element or uses the provided document */
|
||||
public fun Node.ownerDocument(doc: Document? = null): Document {
|
||||
fun Node.ownerDocument(doc: Document? = null): Document {
|
||||
val answer = if (this is Document) this as Document
|
||||
else if (doc == null) this.getOwnerDocument()
|
||||
else doc
|
||||
@@ -408,7 +404,7 @@ public fun Node.ownerDocument(doc: Document? = null): Document {
|
||||
/**
|
||||
Adds a newly created element which can be configured via a function
|
||||
*/
|
||||
public fun Document.addElement(name: String, init: Element.()-> Unit): Element {
|
||||
fun Document.addElement(name: String, init: Element.()-> Unit): Element {
|
||||
val child = createElement(name, init)
|
||||
this.appendChild(child)
|
||||
return child
|
||||
@@ -417,7 +413,7 @@ public fun Document.addElement(name: String, init: Element.()-> Unit): Element {
|
||||
/**
|
||||
Adds a newly created element to an element which has an owner Document which can be configured via a function
|
||||
*/
|
||||
public fun Element.addElement(name: String, doc: Document? = null, init: Element.()-> Unit): Element {
|
||||
fun Element.addElement(name: String, doc: Document? = null, init: Element.()-> Unit): Element {
|
||||
val child = createElement(name, doc, init)
|
||||
this.appendChild(child)
|
||||
return child
|
||||
@@ -426,7 +422,7 @@ public fun Element.addElement(name: String, doc: Document? = null, init: Element
|
||||
/**
|
||||
Adds a newly created text node to an element which either already has an owner Document or one must be provided as a parameter
|
||||
*/
|
||||
public fun Element.addText(text: String?, doc: Document? = null): Element {
|
||||
fun Element.addText(text: String?, doc: Document? = null): Element {
|
||||
if (text != null) {
|
||||
val child = ownerDocument(doc).createTextNode(text)
|
||||
this.appendChild(child)
|
||||
|
||||
@@ -2,7 +2,7 @@ package kotlin.support
|
||||
|
||||
import java.util.NoSuchElementException
|
||||
|
||||
public enum class State {
|
||||
enum class State {
|
||||
Ready
|
||||
NotReady
|
||||
Done
|
||||
@@ -14,10 +14,10 @@ public enum class State {
|
||||
* to implement the iterator, calling [[done()]] when the iteration is complete.
|
||||
*/
|
||||
public abstract class AbstractIterator<T>: java.util.Iterator<T> {
|
||||
public var state: State = State.NotReady
|
||||
public var next: T? = null
|
||||
private var state: State = State.NotReady
|
||||
private var next: T? = null
|
||||
|
||||
public override fun hasNext(): Boolean {
|
||||
override fun hasNext(): Boolean {
|
||||
require(state != State.Failed)
|
||||
return when (state) {
|
||||
State.Done -> false
|
||||
@@ -26,13 +26,13 @@ public abstract class AbstractIterator<T>: java.util.Iterator<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public override fun next(): T {
|
||||
override fun next(): T {
|
||||
if (!hasNext()) throw NoSuchElementException()
|
||||
state = State.NotReady
|
||||
return next.sure()
|
||||
}
|
||||
|
||||
public override fun remove() { throw UnsupportedOperationException() }
|
||||
override fun remove() { throw UnsupportedOperationException() }
|
||||
|
||||
/** Returns the next element in the iteration without advancing the iteration */
|
||||
fun peek(): T {
|
||||
@@ -42,14 +42,33 @@ public abstract class AbstractIterator<T>: java.util.Iterator<T> {
|
||||
|
||||
private fun tryToComputeNext(): Boolean {
|
||||
state = State.Failed
|
||||
next = computeNext();
|
||||
return if (state != State.Done) { state = State.Ready; true } else false
|
||||
computeNext();
|
||||
return state == State.Ready
|
||||
}
|
||||
|
||||
/** Computes the next element in the iterator, calling [[done()]] when there are no more elements */
|
||||
abstract protected fun computeNext(): T?
|
||||
/**
|
||||
* Computes the next item in the iterator.
|
||||
*
|
||||
* This callback method should call one of these two methods
|
||||
*
|
||||
* * [[setNext(T)]] with the next value of the iteration
|
||||
* * [[done()]] to indicate there are no more elements
|
||||
*
|
||||
* Failure to call either method will result in the iteration terminating with a failed state
|
||||
*/
|
||||
abstract protected fun computeNext(): Unit
|
||||
|
||||
/** Sets the state to done so that the iteration terminates */
|
||||
/**
|
||||
* Sets the next value in the iteration, called from the [[computeNext()]] function
|
||||
*/
|
||||
protected fun setNext(value: T): Unit {
|
||||
next = value
|
||||
state = State.Ready
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state to done so that the iteration terminates
|
||||
*/
|
||||
protected fun done() {
|
||||
state = State.Done
|
||||
}
|
||||
@@ -58,9 +77,12 @@ public abstract class AbstractIterator<T>: java.util.Iterator<T> {
|
||||
/** An [[Iterator]] which invokes a function to calculate the next value in the iteration until the function returns *null* */
|
||||
class FunctionIterator<T>(val nextFunction : () -> T?) : AbstractIterator<T>() {
|
||||
|
||||
override protected fun computeNext(): T? {
|
||||
override protected fun computeNext(): Unit {
|
||||
val next = (nextFunction)()
|
||||
if (next == null) done()
|
||||
return next
|
||||
if (next == null) {
|
||||
done()
|
||||
} else {
|
||||
setNext(next)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user