All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.mrapp.util.IteratorUtil.kt Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2017 - 2019 Michael Rapp
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package de.mrapp.util

import de.mrapp.util.Condition.ensureNotNull


/**
 * An utility class that provides methods that allows to handle [Iterator]s and [Iterable]s.
 *
 * @author Michael Rapp
 * @since 2.2.0
 */
object IteratorUtil {

    /**
     * Creates and returns an [Iterable] that maps the items that are traversed by another
     * [iterable] using a specific [mapper].
     *
     * @param I The type of the items that are traversed by the given [Iterable]
     * @param O The type, the items are mapped to
     */
    fun  createMappedIterable(iterable: Iterable, mapper: (I) -> O): Iterable {
        ensureNotNull(iterable, "The iterable may not be null")
        ensureNotNull(mapper, "The mapper may not be null")
        return Iterable { createMappedIterator(iterable.iterator(), mapper) }
    }

    /**
     * Creates and returns an [Iterator] that maps the items that are traversed by another
     * [iterator] using a specific [mapper].
     *
     * @param I The type of the items that are traversed by the given [Iterator]
     * @param O The type, the items are mapped to
     */
    fun  createMappedIterator(iterator: Iterator, mapper: (I) -> O): Iterator {
        ensureNotNull(iterator, "The iterator may not be null")
        ensureNotNull(mapper, "The mapper may not be null")
        return object : Iterator {

            override fun hasNext() = iterator.hasNext()

            override fun next() = mapper.invoke(iterator.next())

        }
    }

    /**
     * Creates and returns an [Iterable] that allows to traverse the items that are traversed by two
     * other [Iterable]s, [first] and [second].
     *
     * @param T The type of the items that are traversed by the given [Iterable]s
     */
    fun  createConcatenatedIterable(first: Iterable, second: Iterable): Iterable {
        ensureNotNull(first, "The first iterable may not be null")
        ensureNotNull(second, "The second iterable may not be null")
        return Iterable { createConcatenatedIterator(first.iterator(), second.iterator()) }
    }

    /**
     * Creates and returns an [Iterator] that allows to traverse the items that are traversed by two
     * other [Iterator]s, [first] and [second].
     *
     * @param T The type of the items that are traversed by the given [Iterator]s
     */
    fun  createConcatenatedIterator(first: Iterator, second: Iterator): Iterator {
        ensureNotNull(first, "The first iterator may not be null")
        ensureNotNull(second, "The second iterator may not be null")
        return object : Iterator {

            override fun hasNext() = first.hasNext() || second.hasNext()

            override fun next() = try {
                first.next()
            } catch (e: NoSuchElementException) {
                second.next()
            }

        }
    }

    /**
     * Creates and returns an [Iterable] that allows to traverse the items that are traversed by
     * another [iterable] except for the items for which a [filter] returns false.
     *
     * @param T The type of the items that are traversed by the given [Iterable]
     */
    fun  createFilteredIterable(iterable: Iterable, filter: (T) -> Boolean): Iterable {
        ensureNotNull(iterable, "The iterable may not be null")
        ensureNotNull(filter, "The filter may not be null")
        return Iterable { createFilteredIterator(iterable.iterator(), filter) }
    }

    /**
     * Creates and returns an [Iterator] that allows to traverse the items that are traversed by
     * another [iterator] except for the items for which a [filter] returns false.
     *
     * @param T The type of the items that are traversed by the given [Iterator]
     */
    fun  createFilteredIterator(iterator: Iterator, filter: (T) -> Boolean): Iterator {
        ensureNotNull(iterator, "The iterator may not be null")
        ensureNotNull(filter, "The filter may not be null")
        return object : Iterator {

            private var hasNext = false

            private var next: T? = null

            init {
                computeNext()
            }

            private fun computeNext() {
                this.hasNext = false

                while (iterator.hasNext()) {
                    val next = iterator.next()

                    if (filter.invoke(next)) {
                        this.hasNext = true
                        this.next = next
                        return
                    }
                }
            }

            override fun hasNext() = hasNext

            override fun next() = if (hasNext) {
                val result = next
                computeNext()
                result
            } else throw NoSuchElementException()

        }
    }

    /**
     * Creates and returns an [Iterable] that allows to traverse the items that are traversed by
     * another [iterable] except for the items which are null.
     *
     * @param T The type of the items that are traversed by the given [Iterable]
     */
    fun  createNotNullIterable(iterable: Iterable): Iterable {
        ensureNotNull(iterable, "The iterable may not be null")
        return Iterable { createNotNullIterator(iterable.iterator()) }
    }

    /**
     * Creates and returns an [Iterator] that allows to traverse the items that are traversed by
     * another [iterator] except for the items which are null.
     *
     * @param T The type of the items that are traversed by the given [Iterator]
     */
    fun  createNotNullIterator(iterator: Iterator): Iterator {
        ensureNotNull(iterator, "The iterator may not be null")
        return object : Iterator {

            private var next = computeNext()

            private fun computeNext(): T? {
                var result: T? = null

                while (result == null && iterator.hasNext()) {
                    val next = iterator.next()

                    if (next != null) {
                        result = next
                    }
                }

                return result
            }

            override fun hasNext() = next != null

            override fun next() = next?.let {
                next = computeNext()
                it
            } ?: throw NoSuchElementException()

        }
    }

    /**
     * Creates and returns an [Iterable] that allows to traverse all items of [Iterator]s that are
     * created for each item that is traversed by an [outerIterable] using a [factory].
     *
     * @param T1 The type of the items that are traversed by the given [Iterable]
     * @param T2 The type of the items that should be traversed by the created [Iterable]
     */
    fun  createNestedIterable(outerIterable: Iterable,
                                      factory: (T1) -> Iterator): Iterable {
        ensureNotNull(outerIterable, "The iterable may not be null")
        ensureNotNull(factory, "The factory may not be null")
        return Iterable { createNestedIterator(outerIterable.iterator(), factory) }
    }

    /**
     * Creates and returns an [Iterator] that allows to traverse all items of [Iterator]s that are
     * created for each item that is traversed by an [outerIterator] using a [factory].
     *
     * @param T1 The type of the items that are traversed by the given [Iterator]
     * @param T2 The type of the items that should be traversed by the created [Iterator]
     */
    fun  createNestedIterator(outerIterator: Iterator,
                                      factory: (T1) -> Iterator): Iterator {
        ensureNotNull(outerIterator, "The iterator may not be null")
        ensureNotNull(factory, "The factory may not be null")
        return object : Iterator {

            private var innerIterator: Iterator? = null

            private var hasNext = false

            private var next: T2? = null

            init {
                computeNext()
            }

            private fun computeNext() {
                this.hasNext = false
                var iterator = innerIterator

                while ((iterator != null && iterator.hasNext()) || outerIterator.hasNext()) {
                    if (iterator != null && iterator.hasNext()) {
                        this.hasNext = true
                        this.next = iterator.next()
                        return
                    } else {
                        iterator = factory.invoke(outerIterator.next()).also {
                            innerIterator = it
                        }
                    }
                }
            }

            override fun hasNext() = hasNext

            override fun next() = if (hasNext) {
                val result = next
                computeNext()
                result
            } else throw NoSuchElementException()

        }
    }

}