Skip to content
This repository has been archived by the owner on Jul 13, 2020. It is now read-only.

Commit

Permalink
childrenRecursiveSequence: fix NoSuchElementException being thrown wh…
Browse files Browse the repository at this point in the history
…en hasNext() returned true (#388)
  • Loading branch information
DarkEspresso authored and yanex committed May 29, 2017
1 parent 61f3055 commit d2e3ff2
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package test

import android.app.Activity
import android.os.Bundle
import android.widget.LinearLayout
import android.widget.RelativeLayout
import android.widget.TextView
import org.jetbrains.anko.*
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.Robolectric
import org.robolectric.RobolectricGradleTestRunner
import org.robolectric.annotation.Config

open class ChildrenSequenceTestActivity: Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

verticalLayout {
id = 1

relativeLayout {
id = 2
textView {
id = 3
}
textView {
id = 4
}
relativeLayout {
id = 5
textView {
id = 6
}
}
}
textView {
id = 7
}
}
}
}

@RunWith(RobolectricGradleTestRunner::class)
@Config(constants = BuildConfig::class) class ChildrenSequenceTest {
@Test fun testChildrenSequence() {
val activity = Robolectric.buildActivity(ChildrenSequenceTestActivity::class.java).create().get()

val verticalLayout = activity.findViewById(1) as LinearLayout
val relativeLayout = activity.findViewById(2) as RelativeLayout
val textView = activity.findViewById(3) as TextView

assertEquals(listOf(2, 7), verticalLayout.childrenSequence().map { it.id }.toList())
assertEquals(listOf(3, 4, 5), relativeLayout.childrenSequence().map { it.id }.toList())
assert(textView.childrenSequence().toList().isEmpty())
}

@Test fun testChildrenRecursiveSequence() {
val activity = Robolectric.buildActivity(ChildrenSequenceTestActivity::class.java).create().get()

val verticalLayout = activity.findViewById(1) as LinearLayout
val relativeLayout = activity.findViewById(2) as RelativeLayout
val textView = activity.findViewById(3) as TextView

assertEquals(listOf(2, 7, 3, 4, 5, 6), verticalLayout.childrenRecursiveSequence().map { it.id }.toList())
assertEquals(listOf(3, 4, 5, 6), relativeLayout.childrenRecursiveSequence().map { it.id }.toList())
assert(textView.childrenRecursiveSequence().toList().isEmpty())
}
}
35 changes: 13 additions & 22 deletions anko/library/static/commons/src/viewChildrenSequences.kt
Original file line number Diff line number Diff line change
Expand Up @@ -118,43 +118,34 @@ private class ViewChildrenSequence(private val view: View) : Sequence<View> {
}

private class ViewChildrenRecursiveSequence(private val view: View) : Sequence<View> {
override fun iterator() = RecursiveViewIterator(view)
override fun iterator(): Iterator<View> {
if (view !is ViewGroup) return emptyList<View>().iterator()
return RecursiveViewIterator(view)
}

private class RecursiveViewIterator(view: View) : Iterator<View> {
private val sequences = arrayListOf(sequenceOf(view))
private var itemIterator: Iterator<View>? = null
private val sequences = arrayListOf(view.childrenSequence())
private var current = sequences.removeLast().iterator()

override fun next(): View {
initItemIterator()
val iterator = itemIterator ?: throw NoSuchElementException()

val view = iterator.next()
if (!hasNext()) throw NoSuchElementException()
val view = current.next()
if (view is ViewGroup && view.childCount > 0) {
sequences.add(view.childrenSequence())
}
return view
}

override fun hasNext(): Boolean {
initItemIterator()
val iterator = itemIterator ?: return false
return iterator.hasNext()
}

private fun initItemIterator() {
val seqs = sequences
val iterator = itemIterator

if (iterator == null || (!iterator.hasNext() && seqs.isNotEmpty())) {
itemIterator = seqs.removeLast()?.iterator()
} else {
itemIterator = null
if (!current.hasNext() && sequences.isNotEmpty()) {
current = sequences.removeLast().iterator()
}
return current.hasNext()
}

@Suppress("NOTHING_TO_INLINE")
private inline fun <T: Any> MutableList<T>.removeLast(): T? {
if (isEmpty()) return null
private inline fun <T : Any> MutableList<T>.removeLast(): T {
if (isEmpty()) throw NoSuchElementException()
return removeAt(size - 1)
}
}
Expand Down

0 comments on commit d2e3ff2

Please sign in to comment.