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)
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()
}
+38 -42
View File
@@ -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)
}
}
}