Spec-Zone .ru
спецификации, руководства, описания, API
001/*
002 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package javafx.collections;
027
028import com.sun.javafx.collections.ListListenerHelper;
029import java.util.AbstractList;
030import java.util.ArrayList;
031import java.util.Arrays;
032import java.util.Collection;
033import java.util.List;
034import javafx.beans.InvalidationListener;
035
036/**
037 * Abstract class that serves as a base class for {@link ObservableList} implementations.
038 * The base class provides two functionalities for the implementing classes.
039 * <ul>
040 * <li> Listener handling by implementing {@code addListener} and {@code removeListener} methods.
041 *      {@link #fireChange(javafx.collections.ListChangeListener.Change)  } method is provided
042 *      for notifying the listeners with a {@code Change} object.
043 * <li> Methods for building up a {@link ListChangeListener.Change} object. There are various methods called
044 *      {@code next*}, like {@link #nextAdd(int, int) } for new items in the lists or {@link #nextRemove(int, java.lang.Object) } for
045 *      an item being removed from the list.
046 *      <p><strong>These methods must be always enclosed in {@link #beginChange() } and {@link #endChange() } block.</strong>
047 *      <p>See the example below.
048 * </ul>
049 *
050 * The following example shows how the Change build-up works:
051 * <pre>
052 *  <strong>public void</strong> removeOddIndexes() {
053 *      beginChange();
054 *      try {
055 *          for (<strong>int</strong> i = 1; i &lt; size(); ++i) {
056 *              remove(i);
057 *          }
058 *      } finally {
059 *          endChange();
060 *      }
061 *  }
062 *
063 *  <strong>public void</strong> remove(<strong>int</strong> i) {
064 *      beginChange();
065 *      try {
066 *          <strong>E</strong> removed = ... //do some stuff that will actually remove the element at index i
067 *          nextRemove(i, removed);
068 *      } finally {
069 *          endChange();
070 *      }
071 *  }
072 *
073 * </pre>
074 *
075 * The {@code try}/{@code finally} blocks in the example are needed only if there's a possibility for an exception to occur
076 * inside a {@code beginChange()} / {@code endChange()} block
077 *
078 * <p>
079 * Note: If you want to create modifiable {@link ObservableList} implementation, consider
080 * using {@link ModifiableObservableListBase} as a superclass.
081 * <p>
082 * Note: In order to create list with sequential access, you should override {@link #listIterator()},
083 * {@link #iterator() } methods and use them in {@link #get}, {@link #size()} and other methods accordingly.
084 *
085 * @param <E> the type of the elements contained in the List
086 * @see ObservableList
087 * @see ListChangeListener.Change
088 * @see ModifiableObservableListBase
089 * @since 8.0
090 */
091public abstract class ObservableListBase<E> extends AbstractList<E>  implements ObservableList<E> {
092
093    private ListListenerHelper<E> listenerHelper;
094    private final ListChangeBuilder<E> changeBuilder = new ListChangeBuilder<E>(this);
095
096    /**
097     * Adds a new update operation to the change.
098     * <p><strong>Note</strong>: needs to be called inside {@code beginChange()} / {@code endChange()} block.
099     * <p><strong>Note</strong>: needs to reflect the <em>current</em> state of the list.
100     * @param pos the position in the list where the updated element resides.
101     */
102    protected final void nextUpdate(int pos) {
103        changeBuilder.nextUpdate(pos);
104    }
105
106    /**
107     * Adds a new set operation to the change.
108     * Equivalent to {@code nextRemove(idx); nextAdd(idx, idx + 1); }.
109     * <p><strong>Note</strong>: needs to be called inside {@code beginChange()} / {@code endChange()} block.
110     * <p><strong>Note</strong>: needs to reflect the <em>current</em> state of the list.
111     * @param idx the index of the item that was set
112     * @param old the old value at the {@code idx} position.
113     */
114    protected final void nextSet(int idx, E old) {
115        changeBuilder.nextSet(idx, old);
116    }
117
118    /**
119     * Adds a new replace operation to the change.
120     * Equivalent to {@code nextRemove(from, removed); nextAdd(from, to); }
121     * <p><strong>Note</strong>: needs to be called inside {@code beginChange()} / {@code endChange()} block.
122     * <p><strong>Note</strong>: needs to reflect the <em>current</em> state of the list.
123     * @param from the index where the items were replaced
124     * @param to the end index (exclusive) of the range where the new items reside
125     * @param removed the list of items that were removed
126     */
127    protected final void nextReplace(int from, int to, ArrayList<? extends E> removed) {
128        changeBuilder.nextReplace(from, to, removed);
129    }
130
131    /**
132     * Adds a new remove operation to the change with multiple items removed.
133     * <p><strong>Note</strong>: needs to be called inside {@code beginChange()} / {@code endChange()} block.
134     * <p><strong>Note</strong>: needs to reflect the <em>current</em> state of the list.
135     * @param idx the index where the items were removed
136     * @param removed the list of items that were removed
137     */
138    protected final void nextRemove(int idx, List<? extends E> removed) {
139        changeBuilder.nextRemove(idx, removed);
140    }
141
142    /**
143     * Adds a new remove operation to the change with single item removed.
144     * <p><strong>Note</strong>: needs to be called inside {@code beginChange()} / {@code endChange()} block.
145     * <p><strong>Note</strong>: needs to reflect the <em>current</em> state of the list.
146     * @param idx the index where the item was removed
147     * @param removed the item that was removed
148     */
149    protected final void nextRemove(int idx, E removed) {
150        changeBuilder.nextRemove(idx, removed);
151    }
152
153    /**
154     * Adds a new permutation operation to the change.
155     * The permutation on index {@code "i"} contains the index, where the item from the index {@code "i"} was moved.
156     * <p>It's not necessary to provide the smallest permutation possible. It's correct to always call this method
157     * with {@code nextPermutation(0, size(), permutation); }
158     * <p><strong>Note</strong>: needs to be called inside {@code beginChange()} / {@code endChange()} block.
159     * <p><strong>Note</strong>: needs to reflect the <em>current</em> state of the list.
160     * @param from marks the beginning (inclusive) of the range that was permutated
161     * @param to marks the end (exclusive) of the range that was permutated
162     * @param perm the permutation in that range. Even if {@code from != 0}, the array should
163     * contain the indexes of the list. Therefore, such permutation would not contain indexes of range {@code (0, from)}
164     */
165    protected final void nextPermutation(int from, int to, int[] perm) {
166        changeBuilder.nextPermutation(from, to, perm);
167    }
168
169    /**
170     * Adds a new add operation to the change.
171     * There's no need to provide the list of added items as they can be found directly in the list
172     * under the specified indexes.
173     * <p><strong>Note</strong>: needs to be called inside {@code beginChange()} / {@code endChange()} block.
174     * <p><strong>Note</strong>: needs to reflect the <em>current</em> state of the list.
175     * @param from marks the beginning (inclusive) of the range that was added
176     * @param to marks the end (exclusive) of the range that was added
177     */
178    protected final void nextAdd(int from, int to) {
179        changeBuilder.nextAdd(from, to);
180    }
181
182    /**
183     * Begins a change block.
184     *
185     * Must be called before any of the {@code next*} methods is called.
186     * For every {@code beginChange()}, there must be a corresponding {@link #endChange() } call.
187     * <p>{@code beginChange()} calls can be nested in a {@code beginChange()}/{@code endChange()} block.
188     * 
189     * @see #endChange() 
190     */
191    protected final void beginChange() {
192        changeBuilder.beginChange();
193    }
194
195    /**
196     * Ends the change block.
197     *
198     * If the block is the outer-most block for the {@code ObservableList}, the
199     * {@code Change} is constructed and all listeners are notified.
200     * <p> Ending a nested block doesn't fire a notification.
201     *
202     * @see #beginChange()
203     */
204    protected final void endChange() {
205        changeBuilder.endChange();
206    }
207
208    @Override
209    public final void addListener(InvalidationListener listener) {
210        listenerHelper = ListListenerHelper.addListener(listenerHelper, listener);
211    }
212
213    @Override
214    public final void removeListener(InvalidationListener listener) {
215        listenerHelper = ListListenerHelper.removeListener(listenerHelper, listener);
216    }
217
218    @Override
219    public final void addListener(ListChangeListener<? super E> listener) {
220        listenerHelper = ListListenerHelper.addListener(listenerHelper, listener);
221    }
222
223    @Override
224    public final void removeListener(ListChangeListener<? super E> listener) {
225        listenerHelper = ListListenerHelper.removeListener(listenerHelper, listener);
226    }
227
228    /**
229     * Notifies all listeners of a change
230     * @param change
231     */
232    protected final void fireChange(ListChangeListener.Change<? extends E> change) {
233        ListListenerHelper.fireValueChangedEvent(listenerHelper, change);
234    }
235
236    /**
237     * Returns true if there are some listeners registered for this list.
238     */
239    protected final boolean hasListeners() {
240        return ListListenerHelper.hasListeners(listenerHelper);
241    }
242
243    @Override
244    public boolean addAll(E... elements) {
245        return addAll(Arrays.asList(elements));
246    }
247
248    @Override
249    public boolean setAll(E... elements) {
250        return setAll(Arrays.asList(elements));
251    }
252
253    @Override
254    public boolean setAll(Collection<? extends E> col) {
255        throw new UnsupportedOperationException();
256    }
257
258    @Override
259    public boolean removeAll(E... elements) {
260        return removeAll(Arrays.asList(elements));
261    }
262
263    @Override
264    public boolean retainAll(E... elements) {
265        return retainAll(Arrays.asList(elements));
266    }
267
268    @Override
269    public void remove(int from, int to) {
270        removeRange(from, to);
271    }
272}