1 /* 2 * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javafx.collections.transformation; 26 27 import javafx.collections.ListChangeListener.Change; 28 import java.util.List; 29 import javafx.collections.ListChangeListener; 30 import javafx.collections.ObservableList; 31 import javafx.collections.ObservableListBase; 32 import javafx.collections.WeakListChangeListener; 33 34 /** 35 * A base class for all lists that wraps other lists in a way that changes the list's 36 * elements, order, size or generally it's structure. 37 * 38 * If the source list is observable, a listener is automatically added to it 39 * and the events are delegated to {@link #onSourceChanged(javafx.collections.ListChangeListener.Change)} 40 * 41 * @param <E> the type parameter of this list 42 * @param <F> the upper bound of the type of the source list 43 * @since JavaFX 8.0 44 */ 45 public abstract class TransformationList<E, F> extends ObservableListBase<E> implements ObservableList<E> { 46 47 /** 48 * Contains the source list of this transformation list. 49 * This is never null and should be used to directly access source list content 50 */ 51 private ObservableList<? extends F> source; 52 /** 53 * This field contains the result of expression "source instanceof {@link javafx.collections.ObservableList}". 54 * If this is true, it is possible to do transforms online. 55 */ 56 private ListChangeListener<F> sourceListener; 57 58 /** 59 * Creates a new Transformation list wrapped around the source list. 60 * @param source the wrapped list 61 */ 62 @SuppressWarnings("unchecked") 63 protected TransformationList(ObservableList<? extends F> source) { 64 if (source == null) { 65 throw new NullPointerException(); 66 } 67 this.source = source; 68 source.addListener(new WeakListChangeListener<>(getListener())); 69 } 70 71 /** 72 * The source list specified in the constructor of this transformation list. 73 * @return The List that is directly wrapped by this TransformationList 74 */ 75 public final ObservableList<? extends F> getSource() { 76 return source; 77 } 78 79 /** 80 * Checks whether the provided list is in the chain under this 81 * {@code TransformationList}. 82 * 83 * This means the list is either the direct source as returned by 84 * {@link #getSource()} or the direct source is a {@code TransformationList}, 85 * and the list is in it's transformation chain. 86 * @param list the list to check 87 * @return true if the list is in the transformation chain as specified above. 88 */ 89 public final boolean isInTransformationChain(ObservableList<?> list) { 90 if (source == list) { 91 return true; 92 } 93 List<?> currentSource = source; 94 while(currentSource instanceof TransformationList) { 95 currentSource = ((TransformationList)currentSource).source; 96 if (currentSource == list) { 97 return true; 98 } 99 } 100 return false; 101 } 102 103 private ListChangeListener<F> getListener() { 104 if (sourceListener == null) { 105 sourceListener = c -> { 106 TransformationList.this.sourceChanged(c); 107 }; 108 } 109 return sourceListener; 110 } 111 112 /** 113 * Called when a change from the source is triggered. 114 * @param c the change 115 */ 116 protected abstract void sourceChanged(Change<? extends F> c); 117 118 /** 119 * Maps the index of this list's element to an index in the direct source list. 120 * @param index the index in this list 121 * @return the index of the element's origin in the source list 122 * @see #getSource() 123 */ 124 public abstract int getSourceIndex(int index); 125 126 /** 127 * Maps the index of this list's element to an index of the provided {@code list}. 128 * 129 * The {@code list} must be in the transformation chain. 130 * 131 * @param list a list from the transformation chain 132 * @param index the index of an element in this list 133 * @return the index of the element's origin in the provided list 134 * @see #isInTransformationChain(javafx.collections.ObservableList) 135 */ 136 public final int getSourceIndexFor(ObservableList<?> list, int index) { 137 if (!isInTransformationChain(list)) { 138 throw new IllegalArgumentException("Provided list is not in the transformation chain of this" 139 + "transformation list"); 140 } 141 List<?> currentSource = source; 142 int idx = getSourceIndex(index); 143 while(currentSource != list && currentSource instanceof TransformationList) { 144 final TransformationList tSource = (TransformationList)currentSource; 145 idx = tSource.getSourceIndex(idx); 146 currentSource = tSource.source; 147 } 148 return idx; 149 } 150 151 /** 152 * Maps the index of the direct source list's element to an index in this list. 153 * @param index the index in the source list 154 * @return the index of the element in this list 155 * @see #getSource(), #getSourceIndex() 156 */ 157 public abstract int getViewIndex(int index); 158 }