/*
* Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package javafx.collections.transformation;
import javafx.collections.
ListChangeListener.
Change;
import java.util.
List;
import javafx.collections.
ListChangeListener;
import javafx.collections.
ObservableList;
import javafx.collections.
ObservableListBase;
import javafx.collections.
WeakListChangeListener;
/**
* A base class for all lists that wraps other lists in a way that changes the list's
* elements, order, size or generally it's structure.
*
* If the source list is observable, a listener is automatically added to it
* and the events are delegated to {@link #onSourceChanged(javafx.collections.ListChangeListener.Change)}
*
* @param <E> the type parameter of this list
* @param <F> the upper bound of the type of the source list
* @since JavaFX 8.0
*/
public abstract class
TransformationList<E, F> extends
ObservableListBase<E> implements
ObservableList<E> {
/**
* Contains the source list of this transformation list.
* This is never null and should be used to directly access source list content
*/
private
ObservableList<? extends F>
source;
/**
* This field contains the result of expression "source instanceof {@link javafx.collections.ObservableList}".
* If this is true, it is possible to do transforms online.
*/
private
ListChangeListener<F>
sourceListener;
/**
* Creates a new Transformation list wrapped around the source list.
* @param source the wrapped list
*/
@
SuppressWarnings("unchecked")
protected
TransformationList(
ObservableList<? extends F>
source) {
if (
source == null) {
throw new
NullPointerException();
}
this.
source =
source;
source.
addListener(new
WeakListChangeListener<>(
getListener()));
}
/**
* The source list specified in the constructor of this transformation list.
* @return The List that is directly wrapped by this TransformationList
*/
public final
ObservableList<? extends F>
getSource() {
return
source;
}
/**
* Checks whether the provided list is in the chain under this
* {@code TransformationList}.
*
* This means the list is either the direct source as returned by
* {@link #getSource()} or the direct source is a {@code TransformationList},
* and the list is in it's transformation chain.
* @param list the list to check
* @return true if the list is in the transformation chain as specified above.
*/
public final boolean
isInTransformationChain(
ObservableList<?>
list) {
if (
source ==
list) {
return true;
}
List<?>
currentSource =
source;
while(
currentSource instanceof
TransformationList) {
currentSource = ((
TransformationList)
currentSource).
source;
if (
currentSource ==
list) {
return true;
}
}
return false;
}
private
ListChangeListener<F>
getListener() {
if (
sourceListener == null) {
sourceListener =
c -> {
TransformationList.this.
sourceChanged(
c);
};
}
return
sourceListener;
}
/**
* Called when a change from the source is triggered.
* @param c the change
*/
protected abstract void
sourceChanged(
Change<? extends F>
c);
/**
* Maps the index of this list's element to an index in the direct source list.
* @param index the index in this list
* @return the index of the element's origin in the source list
* @see #getSource()
*/
public abstract int
getSourceIndex(int
index);
/**
* Maps the index of this list's element to an index of the provided {@code list}.
*
* The {@code list} must be in the transformation chain.
*
* @param list a list from the transformation chain
* @param index the index of an element in this list
* @return the index of the element's origin in the provided list
* @see #isInTransformationChain(javafx.collections.ObservableList)
*/
public final int
getSourceIndexFor(
ObservableList<?>
list, int
index) {
if (!
isInTransformationChain(
list)) {
throw new
IllegalArgumentException("Provided list is not in the transformation chain of this"
+ "transformation list");
}
List<?>
currentSource =
source;
int
idx =
getSourceIndex(
index);
while(
currentSource !=
list &&
currentSource instanceof
TransformationList) {
final
TransformationList tSource = (
TransformationList)
currentSource;
idx =
tSource.
getSourceIndex(
idx);
currentSource =
tSource.
source;
}
return
idx;
}
}