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

org.opalj.collection.mutable.RefArrayStack.scala Maven / Gradle / Ivy

The newest version!
/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj.collection
package mutable

import java.util.{Arrays ⇒ JArrays}

import scala.collection.mutable
import scala.collection.generic

/**
 * An array based implementation of a mutable stack of `ref` values which has a
 * given initial size. If the stack is non-empty, the index of the top value is `0` and the
 * index of the bottom value is (`length-1`).
 *
 * @note __This stack generally keeps all references and is only intended to be used to
 *       store elements that outlive the stack. Otherwise, garbage collection may be prevented.__
 *
 * @param data The array containing the values.
 * @param size0 The number of stored values.
 * @author Michael Eichberg
 */
final class RefArrayStack[N >: Null <: AnyRef] private (
        private var data:  Array[AnyRef],
        private var size0: Int
) extends mutable.IndexedSeq[N]
    with mutable.IndexedSeqLike[N, RefArrayStack[N]]
    with mutable.Cloneable[RefArrayStack[N]]
    with Serializable { stack ⇒

    def this(initialSize: Int = 4) { this(new Array[AnyRef](initialSize), 0) }

    def this(e: N, initialSize: Int) {
        this(new Array[AnyRef](Math.max(initialSize, 1)), 1)
        data(0) = e
    }

    /**
     * Resets the size of the stack, but does not clear the underlying array; hence,
     * the stack may prevent the garbage collection of the still referenced values.
     * This is generally not a problem, if the stack is only used locally and the
     * referenced objects outlive the lifetime of the stack!
     */
    def resetSize(): Unit = size0 = 0

    def clear(): Unit = { JArrays.fill(data, null); size0 = 0 }

    override def size: Int = size0
    override def length: Int = size0
    override def isEmpty: Boolean = size0 == 0
    override def nonEmpty: Boolean = size0 > 0

    /**
     * Returns the n-th value of the stack; where n = 0 identifies the top level value!
     */
    override def apply(index: Int): N = {
        val size0 = this.size0
        val valueIndex = size0 - 1 - index
        if (valueIndex < 0 || valueIndex >= size0)
            throw new IndexOutOfBoundsException(s"$index (size: $size0)");

        data(valueIndex).asInstanceOf[N]
    }

    /**
     * Returns the n-th value of the stack; where n = 0 identifies the bottom value!
     */
    def fromBottom(index: Int): N = {
        if (index < 0 || index >= this.size0)
            throw new IndexOutOfBoundsException(s"$index (from bottom) (size: $size0)");
        data(index).asInstanceOf[N]
    }

    override def update(index: Int, v: N): Unit = data(size0 - 1 - index) = v

    override def newBuilder: mutable.Builder[N, RefArrayStack[N]] = RefArrayStack.newBuilder[N]

    /** The same as push but additionally returns `this`. */
    final def +=(i: N): this.type = {
        push(i)
        this
    }

    final def ++=(is: TraversableOnce[N]): this.type = {
        is foreach { push }
        this
    }

    def push(i: N): Unit = {
        val size0 = this.size0
        var data = this.data
        if (data.length == size0) {
            val newData = new Array[AnyRef]((size0 + 1) * 2)
            System.arraycopy(data, 0, newData, 0, size0)
            data = newData
            this.data = newData
        }

        data(size0) = i
        this.size0 = size0 + 1
    }

    /**
     * Pushes the value of the given stack on this stack while maintaining the order
     * in which the values were pushed on the given stack. I.e.,
     * if this contains the values `[1|2->` and the given one the values `[3,4->`
     * then the resulting stack will contain the values `[1|2|3|4...`.
     *
     * @note In case of `++` the order of the values is reversed.
     */
    def push(that: RefArrayStack[N]): Unit = {
        val thatSize = that.size0

        if (thatSize == 0) {
            return ;
        }

        val thisSize = this.size0
        var thisData = this.data

        val newSize = thisSize + thatSize
        if (newSize > thisData.length) {
            val newData = new Array[AnyRef](newSize + 10)
            System.arraycopy(thisData, 0, newData, 0, thisSize)
            thisData = newData
            this.data = thisData
        }

        System.arraycopy(that.data, 0, thisData, thisSize, thatSize)

        this.size0 = newSize
    }

    /**
     * Returns and virtually removes the top most value from the stack.
     *
     * @note If the stack is empty a `NoSuchElementException` will be thrown.
     */
    def pop(): N = {
        val index = this.size0 - 1
        if (index < 0)
            throw new NoSuchElementException("the stack is empty");

        val i = this.data(index)
        this.size0 = index
        i.asInstanceOf[N]
    }

    /**
     * Returns the stack's top-most value.
     *
     * @note If the stack is empty a `NoSuchElementException` will be thrown.
     */
    def top: N = {
        val index = this.size0 - 1
        if (index < 0)
            throw new NoSuchElementException("the stack is empty");

        this.data(index).asInstanceOf[N]
    }

    /** @see `top` */
    final def peek: N = top

    /**
     * Same as `top`.
     */
    override /*TraversableLike*/ def head: N = top

    override /*TraversableLike*/ def last: N = {
        if (this.size0 == 0)
            throw new NoSuchElementException("the stack is empty");

        this.data(0).asInstanceOf[N]
    }

    override def foreach[U](f: N ⇒ U): Unit = {
        val data = this.data
        var i = this.size0 - 1
        while (i >= 0) {
            f(data(i).asInstanceOf[N])
            i -= 1
        }
    }

    override def foldLeft[B](z: B)(f: (B, N) ⇒ B): B = {
        val data = this.data
        var v = z
        var i = this.size0 - 1
        while (i >= 0) {
            v = f(v, data(i).asInstanceOf[N])
            i -= 1
        }
        v
    }

    /**
     * Returns an iterator which produces the values in LIFO order.
     *
     * @note    The `next` method will throw an `IndexOutOfBoundsException`
     *          when all elements are already returned.
     */
    override def iterator: RefIterator[N] = new RefIterator[N] {
        private[this] var currentIndex = stack.size0 - 1
        def hasNext: Boolean = currentIndex >= 0

        def next(): N = {
            val currentIndex = this.currentIndex
            val r = stack.data(currentIndex)
            this.currentIndex = currentIndex - 1
            r.asInstanceOf[N]
        }
    }

    override def clone(): RefArrayStack[N] = new RefArrayStack(data.clone(), size0)

    override def toString: String = {
        s"RefArrayStack(/*size=$size0;*/data=${data.take(size0).mkString("[", ",", "→")})"
    }
}

/**
 * Factory to create [[RefArrayStack]]s.
 */
object RefArrayStack {

    implicit def canBuildFrom[N >: Null <: AnyRef]: generic.CanBuildFrom[RefArrayStack[N], N, RefArrayStack[N]] = {
        new generic.CanBuildFrom[RefArrayStack[N], N, RefArrayStack[N]] {
            def apply(): mutable.Builder[N, RefArrayStack[N]] = newBuilder
            def apply(from: RefArrayStack[N]): mutable.Builder[N, RefArrayStack[N]] = newBuilder
        }
    }

    def newBuilder[N >: Null <: AnyRef]: mutable.Builder[N, RefArrayStack[N]] = {
        new mutable.ArrayBuffer[N] mapResult fromSeq
    }

    /**
     * Creates a new stack based on a given sequence. The last value of the sequence will
     * be the top value of the stack.
     */
    def fromSeq[N >: Null <: AnyRef](seq: TraversableOnce[N]): RefArrayStack[N] = {
        seq.foldLeft(new RefArrayStack[N](8))(_ += _)
    }

    def empty[N >: Null <: AnyRef]: RefArrayStack[N] = new RefArrayStack
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy