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

scalafx.collections.ObservableArray.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2011-2020, ScalaFX Project
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the ScalaFX Project nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE SCALAFX PROJECT OR ITS CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package scalafx.collections

import javafx.{collections => jfxc}
import scalafx.beans.Observable
import scalafx.delegate.SFXDelegate
import scalafx.event.subscriptions.Subscription

import scala.collection.mutable.{ArrayLike, Builder}
import scala.collection.mutable.Builder
import scala.reflect.ClassTag

/**
 * Companion Object for `[[scalafx.collections.ObservableArray]]`.
 *
 * @define OA `ObservableArray`
 */
object ObservableArray {

  // TODO: Enter link when JavaFX 8 API Docs are available on-line.
  /**
   * Indicates a change in an $OA. It is a simpler version of JavaFX's `ArrayChangeListener[T]`.
   *
   * @constructor Create new instance describing the change detected.
   *
   * @param sizeChanged `true` if the size of the $OA was changed; `false` otherwise.
   * @param start Index of first element in the array affected by the change.
   * @param end Index of first element in the array unaffected by the change.  This value is exclusive of the change
   *            and indicates the first unchanged element after the modification, or the end of the array.  Note that `end` may be
   *            less than `start`.
   */
  case class Change(sizeChanged: Boolean, start: Int, end: Int)

}

/**
 * Abstract $OA base class.
 *
 * @define OA `ObservableArray`
 * @define ARY `Array`
 *
 * @tparam V Value type to be stored in this array.
 * @tparam T Type of this ScalaFX $ARY.
 * @tparam D Type of the delegated JavaFX $ARY.
 *
 * @constructor Create new base $OA.
 * @param delegate Wrapped JavaFX $OA instance providing implementation.
 */
abstract class ObservableArray[V: ClassTag, T <: ObservableArray[V, T, D], D <: jfxc.ObservableArray[D]]
(override val delegate: D)
  extends ArrayLike[V, T]
  with Builder[V, T]
  with Observable
  with SFXDelegate[D] {

  // ObservableArray[D] interface functions, allow class to act like it implements the JavaFX ObservableArray
  // interface, without actually being interchangeable with one.
  /**
   * Shrinks capacity to current length of data in this array.
   */
  def trimToSize(): Unit = {
    delegate.trimToSize()
  }

  /**
   * Grow array capacity if currently smaller than given `capacity`; do nothing otherwise.
   *
   * @param capacity Required capacity.
   */
  def ensureCapacity(capacity: Int): Unit = {
    delegate.ensureCapacity(capacity)
  }

  // ObservableArray interface functions.  Note: These functions are present in ObservableFloatArray and
  // ObservableIntegerArray, but they are not in ObservableArray[T].
  /**
   * Copy specified portion of this observable array to `dest` regular array.
   *
   * @param srcIdx Start position in this array.
   * @param dest Array into which the portion of this array is to be copied.
   * @param destIdx Start position in the `dest` array.
   * @param length Number of data elements to be copied.
   * @throws java.lang.ArrayIndexOutOfBoundsException if copying would cause access out of either array bounds.
   * @throws java.lang.ArrayStoreException if element in this array could not be stored in `dest` array due to a type
   *                                       mismatch.
   * @throws java.lang.NullPointerException if `dest` is `null`.
   */
  def copyTo(srcIdx: Int, dest: Array[V], destIdx: Int, length: Int): Unit

  /**
   * Copy specified portion of this observable array to `dest` observable array.
   *
   * @param srcIdx Start position in this array.
   * @param dest Array into which the portion of this array is to be copied.
   * @param destIdx Start position in the `dest` array.
   * @param length Number of data elements to be copied.
   * @throws java.lang.ArrayIndexOutOfBoundsException if copying would cause access out of either array bounds.
   * @throws java.lang.ArrayStoreException if element in this array could not be stored in `dest` array due to a type
   *                                       mismatch.
   * @throws java.lang.NullPointerException if `dest` is `null`.
   */
  def copyTo(srcIdx: Int, dest: T, destIdx: Int, length: Int): Unit

  /**
   * Select the element at `idx` in the array.
   *
   * @param idx Index of selected element.
   * @return Element at given `idx`.
   * @throws java.lang.ArrayIndexOutOfBoundsException if `idx` does not satisfy `0 <= idx < length`.
   */
  def get(idx: Int): V

  /**
   * Append given observable array to the end of this array.
   *
   * Capacity is increased, if necessary, to match the new size of the data.
   *
   * @param src Elements to be appended.
   * @throws java.lang.ArrayStoreException if element in `src` array could not be stored in this array due to a type
   *                                       mismatch.
   * @throws java.lang.NullPointerException if `src` is `null`.
   */
  def addAll(src: T): Unit

  /**
   * Append given `elements` to the end of this array.
   *
   * Capacity is increased, if necessary, to match the new size of the data.
   *
   * @param elems Elements to be appended.
   * @throws java.lang.ArrayStoreException if element in `elements` could not be stored in this array due to a type
   *                                       mismatch.
   * @throws java.lang.NullPointerException if `elements` is `null`.
   */
  def addAll(elems: V*): Unit

  /**
   * Append portion of given regular array to the end of this array.
   *
   * Capacity is increased, if necessary, to match the new size of the data.
   *
   * @param src Elements to be appended.
   * @param srcIdx Start position in the `src` array.
   * @param length Number of data elements to be appended.
   * @throws java.lang.ArrayIndexOutOfBoundsException if copying would cause access out of `src` array bounds.
   * @throws java.lang.ArrayStoreException if element in `src` array could not be stored in this array due to a type
   *                                       mismatch.
   * @throws java.lang.NullPointerException if `src` is `null`.
   */
  def addAll(src: Array[V], srcIdx: Int, length: Int): Unit

  /**
   * Append portion of given regular array to the end of this array.
   *
   * Capacity is increased, if necessary, to match the new size of the data.
   *
   * @param src Elements to be appended.
   * @param srcIdx Start position in the `src` array.
   * @param length Number of data elements to be appended.
   * @throws java.lang.ArrayIndexOutOfBoundsException if copying would cause access out of `src` array bounds.
   * @throws java.lang.ArrayStoreException if element in `src` array could not be stored in this array due to a type
   *                                       mismatch.
   * @throws java.lang.NullPointerException if `src` is `null`.
   */
  def addAll(src: T, srcIdx: Int, length: Int): Unit

  /**
   * Replace the contents of this array with the given `elements`.
   *
   * Capacity is increased, if necessary, to match the new size of the data.
   *
   * @param elems New contents of this array.
   * @throws java.lang.ArrayStoreException if element in `elements` could not be stored in this array due to a type
   *                                       mismatch.
   * @throws java.lang.NullPointerException if `elements` is `null`.
   */
  def setAll(elems: V*): Unit

  /**
   * Replace the contents of this array with the given observable array.
   *
   * Capacity is increased, if necessary, to match the new size of the data.
   *
   * @param src Array to replace contents this array.
   * @throws java.lang.ArrayStoreException if element in `src` could not be stored in this array due to a type
   *                                       mismatch.
   * @throws java.lang.NullPointerException if `src` is `null`.
   */
  def setAll(src: T): Unit

  /**
   * Replace the contents of this array with portion of the given regular array.
   *
   * Capacity is increased, if necessary, to match the new size of the data.
   *
   * @param src Array to replace contents this array.
   * @param srcIdx Start position in the `src` array.
   * @param length Number of data elements to be copied.
   * @throws java.lang.ArrayIndexOutOfBoundsException if copying would cause access out of array bounds.
   * @throws java.lang.ArrayStoreException if element in `src` could not be stored in this array due to a type
   *                                       mismatch.
   * @throws java.lang.NullPointerException if `src` is `null`.
   */
  def setAll(src: Array[V], srcIdx: Int, length: Int): Unit

  /**
   * Replace the contents of this array with portion of the given observable array.
   *
   * Capacity is increased, if necessary, to match the new size of the data.
   *
   * @param src Array to replace contents this array.
   * @param srcIdx Start position in the `src` array.
   * @param length Number of data elements to be copied.
   * @throws java.lang.ArrayIndexOutOfBoundsException if copying would cause access out of array bounds.
   * @throws java.lang.ArrayStoreException if element in `src` could not be stored in this array due to a type
   *                                       mismatch.
   * @throws java.lang.NullPointerException if `src` is `null`.
   */
  def setAll(src: T, srcIdx: Int, length: Int): Unit

  /**
   * Set the element at `idx` in the array to `value`.
   *
   * @param idx Index of element to be changed.
   * @param value New value for element at `idx`.
   * @throws java.lang.ArrayIndexOutOfBoundsException if `idx` does not satisfy `0 <= idx < length`.
   */
  def set(idx: Int, value: V): Unit

  /**
   * Copy a portion of given regular array into this array, replacing affected contents.
   *
   * @param destIdx Start position in this array.
   * @param src Array containing data to be copied.
   * @param srcIdx Start position in `src` array.
   * @param length Number of data elements to be copied.
   * @throws java.lang.ArrayIndexOutOfBoundsException if copying would cause access out of array bounds.
   * @throws java.lang.ArrayStoreException if element in `src` could not be stored in this array due to a type
   *                                       mismatch.
   * @throws java.lang.NullPointerException if `src` is `null`.
   */
  def set(destIdx: Int, src: Array[V], srcIdx: Int, length: Int): Unit

  /**
   * Copy a portion of given observable array into this array, replacing affected contents.
   *
   * @param destIdx Start position in this array.
   * @param src Array containing data to be copied.
   * @param srcIdx Start position in `src` array.
   * @param length Number of data elements to be copied.
   * @throws java.lang.ArrayIndexOutOfBoundsException if copying would cause access out of array bounds.
   * @throws java.lang.ArrayStoreException if element in `src` could not be stored in this array due to a type
   *                                       mismatch.
   * @throws java.lang.NullPointerException if `src` is `null`.
   */
  def set(destIdx: Int, src: T, srcIdx: Int, length: Int): Unit

  /**
   * Translate this observable array to a regular array.
   *
   * @return Regular array containing this array's contents.
   */
  def toArray: Array[V] = toArray(null.asInstanceOf[Array[V]])

  /**
   * Write the contents of this array into the specified array, if it is large enough, or a new array if it is not.
   *
   * @param dest Array into which this array will be written, if large enough to hold this array's contents.  If
   *             `null`, this argument is ignored.
   * @return The `dest` array if it is large enough to hold this array's data, or a new array, containing this array's
   *         copied contents.
   * @throws java.lang.ArrayStoreException if element in `src` could not be stored in this array due to a type
   *                                       mismatch.
   */
  def toArray(dest: Array[V]): Array[V]

  /**
   * Write a portion of this array's contents into the specified array, if it is large enough, or a new array if it is
   * not.
   *
   * @param srcIdx Start position in this array.
   * @param dest Array into which this array will be written, if large enough to hold this array's contents.  If
   *             `null`, this argument is ignored.
   * @param length Number of data elements to be copied.
   * @return The `dest` array if it is large enough to hold this array's data, or a new array, containing this array's
   *         copied contents.
   * @throws java.lang.ArrayStoreException if element in `src` could not be stored in this array due to a type
   *                                       mismatch.
   */
  def toArray(srcIdx: Int, dest: Array[V], length: Int): Array[V]

  // ArrayLike[V, T] abstract member function implementations.
  /**
   * Select an element by its index in the array.
   *
   * @param idx Index of selected element.
   * @return Element at given `idx`.
   * @throws java.lang.ArrayIndexOutOfBoundsException if `idx` does not satisfy `0 <= idx < length`.
   */
  def apply(idx: Int): V = get(idx)

  /**
   * Set the element at `idx` in the array to `value`.
   *
   * @param idx   Index of element to be changed.
   * @param value New value for element at `idx`.
   * @throws java.lang.ArrayIndexOutOfBoundsException if `idx` does not satisfy `0 <= idx < length`.
   */
  def update(idx: Int, value: V): Unit = {
    set(idx, value)
  }

  /**
   * Retrieve length of data in this array.
   *
   * @return Length of data in this array.
   */
  override def size = delegate.size

  /**
   * Retrieve length of data in this array.
   *
   * @return Length of data in this array.
   */
  override def length = size

  /**
   * Convert to a sequence in which all elements are implemented sequentially.
   *
   * @return Sequence with contents of this array.
   */
  override def seq = toArray.seq

  /**
   * Append another array to this array.
   *
   * @param src Array to be appended to this array.
   * @return This array, expanded to contain the indicated array.
   */
  def ++(src: Array[V]): T = {
    addAll(src: _*)
    this.asInstanceOf[T]
  }

  /**
   * Append another observable array to this array.
   *
   * @param src Array to be appended to this array.
   * @return This array, expanded to contain the indicated array.
   */
  def ++(src: T): T = {
    addAll(src)
    this.asInstanceOf[T]
  }

  // Builder[V, T] abstract member function implementations.
  /**
   * Empty array, clearing builder contents, resizing it to zero.
   *
   * Capacity is unchanged.
   */
  override def clear(): Unit = {
    delegate.clear()
  }

  /**
   * Produces collection from builder.
   *
   * @return This $OA.
   */

  override def result(): T = this.asInstanceOf[T]

  /**
   * Append new element to this $OA.
   *
   * @param elem Element to be added to end of this array.
   * @return This $OA.
   */
  override def +=(elem: V) = {
    addAll(elem)
    this
  }

  /**
   * Add a listener function to $ARY's changes.
   *
   * @note This function '''will handle''' this array's modifications data.  That is, it will be notified which array
   *       has been modified and which array elements have been changed.
   *
   * @param op Function that will handle this $OA's modifications data, to be activated when some change is made.
   */

  def onChange(op: (T, ObservableArray.Change) => Unit): Subscription = {
    val listener = new jfxc.ArrayChangeListener[D] {
      override def onChanged(array: D, sizeChanged: Boolean, start: Int, end: Int): Unit = {
        op(ObservableArray.this.asInstanceOf[T], ObservableArray.Change(sizeChanged, start, end))
      }
    }

    delegate.addListener(listener)

    new Subscription {
      def cancel(): Unit = {
        delegate.removeListener(listener)
      }
    }
  }

  /**
   * Add a listener function to $ARY's changes.
   *
   * @note This function '''will not handle''' this array's modifications data.  That is, it will be notified that an
   *       array it is associated with has changed, but not which array the which data within it was changed.
   *
   * @param op Function that will handle this $OA's modifications data, to be activated when some change is made.
   */

  def onChange(op: => Unit): Subscription = {
    val listener = new jfxc.ArrayChangeListener[D] {
      override def onChanged(array: D, sizeChanged: Boolean, start: Int, end: Int): Unit = {
        op
      }
    }

    delegate.addListener(listener)

    new Subscription {
      def cancel(): Unit = {
        delegate.removeListener(listener)
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy