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:
James Strachan
2012-03-30 11:06:00 +01:00
parent d4d177c0ed
commit 8299ffa690
3 changed files with 114 additions and 76 deletions
+40 -20
View File
@@ -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) 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>() { 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()) { while (iterator.hasNext()) {
val next = iterator.next() val next = iterator.next()
if (klass.isInstance(next)) return next as R if (klass.isInstance(next)) {
setNext(next as R)
return
}
} }
done() 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) 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>() { 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()) { while (iterator.hasNext()) {
val next = iterator.next() val next = iterator.next()
if ((predicate)(next)) return next if ((predicate)(next)) {
setNext(next)
return
}
} }
done() 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) 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>() { 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) { if (iterator != null) {
while (iterator.hasNext()) { while (iterator.hasNext()) {
val next = iterator.next() val next = iterator.next()
if (next != null) return next if (next != null) {
setNext(next)
return
}
} }
} }
done() 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) 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>() { 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>() { 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 } var transformed: java.util.Iterator<R> = iterate<R> { null }
override protected fun computeNext() : R? { override protected fun computeNext() : Unit {
if (transformed.hasNext()) return transformed.next() while (true) {
if (iterator.hasNext()) { if (transformed.hasNext()) {
transformed = (transform)(iterator.next()) setNext(transformed.next())
return computeNext() 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) 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>() { 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()) { if (iterator.hasNext()) {
val item = iterator.next() val item = iterator.next()
if ((predicate)(item)) return item if ((predicate)(item)) {
setNext(item)
return
}
} }
done() 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("...") if (limit != null && count > limit) buffer.append("...")
return buffer.append(postfix).toString().sure() return buffer.append(postfix).toString().sure()
} }
+38 -42
View File
@@ -96,7 +96,7 @@ set(value) {
// Helper methods // Helper methods
/** Returns true if the element has the given CSS class style in its 'class' attribute */ /** 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 val c = this.classes
return if (c != null) return if (c != null)
c.matches("""(^|.*\s+)$cssClass($|\s+.*)""") 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 */ /** 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 classSet = this.classSet
val answer = classSet.add(cssClass) val answer = classSet.add(cssClass)
if (answer) { if (answer) {
@@ -114,7 +114,7 @@ public fun Element.addClass(cssClass: String): Boolean {
} }
/** Removes the given CSS class to this element's 'class' attribute */ /** 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 classSet = this.classSet
val answer = classSet.remove(cssClass) val answer = classSet.remove(cssClass)
if (answer) { if (answer) {
@@ -125,7 +125,7 @@ public fun Element.removeClass(cssClass: String): Boolean {
/** TODO this approach generates compiler errors... /** 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 val set = this.classSet
var answer = false var answer = false
for (cs in cssClasses) { for (cs in cssClasses) {
@@ -139,7 +139,7 @@ public fun Element.addClass(varargs cssClasses: Array<String>): Boolean {
return answer return answer
} }
public fun Element.removeClass(varargs cssClasses: Array<String>): Boolean { fun Element.removeClass(varargs cssClasses: Array<String>): Boolean {
val set = this.classSet val set = this.classSet
var answer = false var answer = false
for (cs in cssClasses) { 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 #) */ /** 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() val root = this?.getDocumentElement()
return if (root != null) { return if (root != null) {
if (selector == "*") { 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 #) */ /** 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 == "*") { return if (selector == "*") {
elements elements
} else if (selector.startsWith(".")) { } 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 */ /** 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>() { class NextSiblingIterator(var node: Node) : AbstractIterator<Node>() {
public override fun computeNext(): Node? { override fun computeNext(): Unit {
val next = node.getNextSibling() val next = node.getNextSibling()
if (next != null) { if (next != null) {
node = next setNext(next)
return next
} else { } else {
done() done()
return null
} }
} }
} }
/** Returns an [[Iterator]] over the next siblings of this node */ /** 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>() { class PreviousSiblingIterator(var node: Node) : AbstractIterator<Node>() {
public override fun computeNext(): Node? { override fun computeNext(): Unit {
val next = node.getPreviousSibling() val next = node.getPreviousSibling()
if (next != null) { if (next != null) {
node = next setNext(next)
return next
} else { } else {
done() done()
return null
} }
} }
} }
/** Returns true if this node is a Text node or a CDATA node */ /** 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() val nodeType = getNodeType()
return nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE return nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE
} }
/** Returns an [[Iterator]] of all the next [[Element]] siblings */ /** 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 */ /** 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 */ /** 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) ?: "" return this.getAttribute(name) ?: ""
} }
/** Returns the children of the element as a list */ /** 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() return this?.getChildNodes().toList()
} }
@@ -261,22 +257,22 @@ get() = this?.getElementsByTagName("*").toElementList()
/** Returns all the child elements given the local element name */ /** 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() return this?.getElementsByTagName(localName).toElementList()
} }
/** Returns all the elements given the local element name */ /** 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() return this?.getElementsByTagName(localName).toElementList()
} }
/** Returns all the child elements given the namespace URI and local element name */ /** 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() return this?.getElementsByTagNameNS(namespaceUri, localName).toElementList()
} }
/** Returns all the elements given the namespace URI and local element name */ /** 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() return this?.getElementsByTagNameNS(namespaceUri, localName).toElementList()
} }
@@ -300,7 +296,7 @@ val NodeList?.last : Node?
get() = this.tail get() = this.tail
public inline fun NodeList?.toList(): List<Node> { inline fun NodeList?.toList(): List<Node> {
return if (this == null) { return if (this == null) {
Collections.EMPTY_LIST as List<Node> 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) { return if (this == null) {
Collections.EMPTY_LIST as List<Element> 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 */ /** 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) return if (this == null)
"" else { "" else {
nodesToXmlString(this.toList(), xmlDeclaration) nodesToXmlString(this.toList(), xmlDeclaration)
@@ -327,7 +323,7 @@ public fun NodeList?.toXmlString(xmlDeclaration: Boolean = false): String {
} }
class NodeListAsList(val nodeList: NodeList): AbstractList<Node>() { 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) val node = nodeList.item(index)
if (node == null) { if (node == null) {
throw IndexOutOfBoundsException("NodeList does not contain a node at index: " + index) 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>() { 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) val node = nodeList.item(index)
if (node is Element) { if (node is Element) {
return node 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 // Syntax sugar
public inline fun Node.plus(child: Node?): Node { inline fun Node.plus(child: Node?): Node {
if (child != null) { if (child != null) {
this.appendChild(child) this.appendChild(child)
} }
return this 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 // 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 * 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() val elem = this.createElement(name).sure()
elem.init() elem.init()
return elem 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 * 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() val elem = ownerDocument(doc).createElement(name).sure()
elem.init() elem.init()
return elem return elem
} }
/** Returns the owner document of the element or uses the provided document */ /** 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 val answer = if (this is Document) this as Document
else if (doc == null) this.getOwnerDocument() else if (doc == null) this.getOwnerDocument()
else doc 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 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) val child = createElement(name, init)
this.appendChild(child) this.appendChild(child)
return 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 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) val child = createElement(name, doc, init)
this.appendChild(child) this.appendChild(child)
return 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 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) { if (text != null) {
val child = ownerDocument(doc).createTextNode(text) val child = ownerDocument(doc).createTextNode(text)
this.appendChild(child) this.appendChild(child)
@@ -2,7 +2,7 @@ package kotlin.support
import java.util.NoSuchElementException import java.util.NoSuchElementException
public enum class State { enum class State {
Ready Ready
NotReady NotReady
Done Done
@@ -14,10 +14,10 @@ public enum class State {
* to implement the iterator, calling [[done()]] when the iteration is complete. * to implement the iterator, calling [[done()]] when the iteration is complete.
*/ */
public abstract class AbstractIterator<T>: java.util.Iterator<T> { public abstract class AbstractIterator<T>: java.util.Iterator<T> {
public var state: State = State.NotReady private var state: State = State.NotReady
public var next: T? = null private var next: T? = null
public override fun hasNext(): Boolean { override fun hasNext(): Boolean {
require(state != State.Failed) require(state != State.Failed)
return when (state) { return when (state) {
State.Done -> false 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() if (!hasNext()) throw NoSuchElementException()
state = State.NotReady state = State.NotReady
return next.sure() 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 */ /** Returns the next element in the iteration without advancing the iteration */
fun peek(): T { fun peek(): T {
@@ -42,14 +42,33 @@ public abstract class AbstractIterator<T>: java.util.Iterator<T> {
private fun tryToComputeNext(): Boolean { private fun tryToComputeNext(): Boolean {
state = State.Failed state = State.Failed
next = computeNext(); computeNext();
return if (state != State.Done) { state = State.Ready; true } else false 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() { protected fun done() {
state = State.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* */ /** 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>() { class FunctionIterator<T>(val nextFunction : () -> T?) : AbstractIterator<T>() {
override protected fun computeNext(): T? { override protected fun computeNext(): Unit {
val next = (nextFunction)() val next = (nextFunction)()
if (next == null) done() if (next == null) {
return next done()
} else {
setNext(next)
}
} }
} }