1 /*
   2  * Copyright (c) 2011, 2016, 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 
  26 package javafx.beans.binding;
  27 
  28 import com.sun.javafx.binding.StringFormatter;
  29 import javafx.beans.property.ReadOnlyBooleanProperty;
  30 import javafx.beans.property.ReadOnlyIntegerProperty;
  31 import javafx.beans.value.ObservableIntegerValue;
  32 import javafx.beans.value.ObservableListValue;
  33 import javafx.collections.FXCollections;
  34 import javafx.collections.ObservableList;
  35 
  36 import java.util.Collection;
  37 import java.util.Iterator;
  38 import java.util.List;
  39 import java.util.ListIterator;
  40 
  41 /**
  42  * {@code ListExpression} is an
  43  * {@link javafx.beans.value.ObservableListValue} plus additional convenience
  44  * methods to generate bindings in a fluent style.
  45  * <p>
  46  * A concrete sub-class of {@code ListExpression} has to implement the method
  47  * {@link javafx.beans.value.ObservableListValue#get()}, which provides the
  48  * actual value of this expression.
  49  * <p>
  50  * If the wrapped list of a {@code ListExpression} is {@code null}, all methods implementing the {@code List}
  51  * interface will behave as if they were applied to an immutable empty list.
  52  *
  53  * @param <E> the type of the {@code List} elements.
  54  * @since JavaFX 2.1
  55  */
  56 public abstract class ListExpression<E> implements ObservableListValue<E> {
  57 
  58     private static final ObservableList EMPTY_LIST = FXCollections.emptyObservableList();
  59 
  60     @Override
  61     public ObservableList<E> getValue() {
  62         return get();
  63     }
  64 
  65     /**
  66      * Returns a {@code ListExpression} that wraps a
  67      * {@link javafx.beans.value.ObservableListValue}. If the
  68      * {@code ObservableListValue} is already a {@code ListExpression}, it
  69      * will be returned. Otherwise a new
  70      * {@link javafx.beans.binding.ListBinding} is created that is bound to
  71      * the {@code ObservableListValue}.
  72      *
  73      * @param <E> the type of the wrapped {@code List}
  74      * @param value
  75      *            The source {@code ObservableListValue}
  76      * @return A {@code ListExpression} that wraps the
  77      *         {@code ObservableListValue} if necessary
  78      * @throws NullPointerException
  79      *             if {@code value} is {@code null}
  80      */
  81     public static <E> ListExpression<E> listExpression(final ObservableListValue<E> value) {
  82         if (value == null) {
  83             throw new NullPointerException("List must be specified.");
  84         }
  85         return value instanceof ListExpression ? (ListExpression<E>) value
  86                 : new ListBinding<E>() {
  87             {
  88                 super.bind(value);
  89             }
  90 
  91             @Override
  92             public void dispose() {
  93                 super.unbind(value);
  94             }
  95 
  96             @Override
  97             protected ObservableList<E> computeValue() {
  98                 return value.get();
  99             }
 100 
 101             @Override
 102             public ObservableList<ObservableListValue<E>> getDependencies() {
 103                 return FXCollections.singletonObservableList(value);
 104             }
 105         };
 106     }
 107 
 108     /**
 109      * The size of the list
 110      * @return the size
 111      */
 112     public int getSize() {
 113         return size();
 114     }
 115 
 116     /**
 117      * An integer property that represents the size of the list.
 118      * @return the property
 119      */
 120     public abstract ReadOnlyIntegerProperty sizeProperty();
 121 
 122     /**
 123      * A boolean property that is {@code true}, if the list is empty.
 124      * @return the {@code ReadOnlyBooleanProperty}
 125      *
 126      */
 127     public abstract ReadOnlyBooleanProperty emptyProperty();
 128 
 129     /**
 130      * Creates a new {@link ObjectBinding} that contains the element at the specified position.
 131      * If {@code index} points behind the list, the {@code ObjectBinding} contains {@code null}.
 132      *
 133      * @param index the index of the element
 134      * @return the {@code ObjectBinding}
 135      * @throws IllegalArgumentException if {@code index < 0}
 136      */
 137     public ObjectBinding<E> valueAt(int index) {
 138         return Bindings.valueAt(this, index);
 139     }
 140 
 141     /**
 142      * Creates a new {@link ObjectBinding} that contains the element at the specified position.
 143      * If {@code index} points outside of the list, the {@code ObjectBinding} contains {@code null}.
 144      *
 145      * @param index the index of the element
 146      * @return the {@code ObjectBinding}
 147      * @throws NullPointerException if {@code index} is {@code null}
 148      */
 149     public ObjectBinding<E> valueAt(ObservableIntegerValue index) {
 150         return Bindings.valueAt(this, index);
 151     }
 152 
 153     /**
 154      * Creates a new {@link BooleanBinding} that holds {@code true} if this list is equal to
 155      * another {@link javafx.collections.ObservableList}.
 156      *
 157      * @param other
 158      *            the other {@code ObservableList}
 159      * @return the new {@code BooleanBinding}
 160      * @throws NullPointerException
 161      *             if {@code other} is {@code null}
 162      */
 163     public BooleanBinding isEqualTo(final ObservableList<?> other) {
 164         return Bindings.equal(this, other);
 165     }
 166 
 167     /**
 168      * Creates a new {@link BooleanBinding} that holds {@code true} if this list is not equal to
 169      * another {@link javafx.collections.ObservableList}.
 170      *
 171      * @param other
 172      *            the other {@code ObservableList}
 173      * @return the new {@code BooleanBinding}
 174      * @throws NullPointerException
 175      *             if {@code other} is {@code null}
 176      */
 177     public BooleanBinding isNotEqualTo(final ObservableList<?> other) {
 178         return Bindings.notEqual(this, other);
 179     }
 180 
 181     /**
 182      * Creates a new {@link BooleanBinding} that holds {@code true} if the wrapped list is {@code null}.
 183      *
 184      * @return the new {@code BooleanBinding}
 185      */
 186     public BooleanBinding isNull() {
 187         return Bindings.isNull(this);
 188     }
 189 
 190     /**
 191      * Creates a new {@link BooleanBinding} that holds {@code true} if the wrapped list is not {@code null}.
 192      *
 193      * @return the new {@code BooleanBinding}
 194      */
 195     public BooleanBinding isNotNull() {
 196         return Bindings.isNotNull(this);
 197     }
 198 
 199     /**
 200      * Creates a {@link javafx.beans.binding.StringBinding} that holds the value
 201      * of the {@code ListExpression} turned into a {@code String}. If the
 202      * value of this {@code ListExpression} changes, the value of the
 203      * {@code StringBinding} will be updated automatically.
 204      *
 205      * @return the new {@code StringBinding}
 206      */
 207     public StringBinding asString() {
 208         return (StringBinding) StringFormatter.convert(this);
 209     }
 210 
 211     @Override
 212     public int size() {
 213         final ObservableList<E> list = get();
 214         return (list == null)? EMPTY_LIST.size() : list.size();
 215     }
 216 
 217     @Override
 218     public boolean isEmpty() {
 219         final ObservableList<E> list = get();
 220         return (list == null)? EMPTY_LIST.isEmpty() : list.isEmpty();
 221     }
 222 
 223     @Override
 224     public boolean contains(Object obj) {
 225         final ObservableList<E> list = get();
 226         return (list == null)? EMPTY_LIST.contains(obj) : list.contains(obj);
 227     }
 228 
 229     @Override
 230     public Iterator<E> iterator() {
 231         final ObservableList<E> list = get();
 232         return (list == null)? EMPTY_LIST.iterator() : list.iterator();
 233     }
 234 
 235     @Override
 236     public Object[] toArray() {
 237         final ObservableList<E> list = get();
 238         return (list == null)? EMPTY_LIST.toArray() : list.toArray();
 239     }
 240 
 241     @Override
 242     public <T> T[] toArray(T[] array) {
 243         final ObservableList<E> list = get();
 244         return (list == null)? (T[]) EMPTY_LIST.toArray(array) : list.toArray(array);
 245      }
 246 
 247     @Override
 248     public boolean add(E element) {
 249         final ObservableList<E> list = get();
 250         return (list == null)? EMPTY_LIST.add(element) : list.add(element);
 251     }
 252 
 253     @Override
 254     public boolean remove(Object obj) {
 255         final ObservableList<E> list = get();
 256         return (list == null)? EMPTY_LIST.remove(obj) : list.remove(obj);
 257     }
 258 
 259     @Override
 260     public boolean containsAll(Collection<?> objects) {
 261         final ObservableList<E> list = get();
 262         return (list == null)? EMPTY_LIST.contains(objects) : list.containsAll(objects);
 263     }
 264 
 265     @Override
 266     public boolean addAll(Collection<? extends E> elements) {
 267         final ObservableList<E> list = get();
 268         return (list == null)? EMPTY_LIST.addAll(elements) : list.addAll(elements);
 269     }
 270 
 271     @Override
 272     public boolean addAll(int i, Collection<? extends E> elements) {
 273         final ObservableList<E> list = get();
 274         return (list == null)? EMPTY_LIST.addAll(i, elements) : list.addAll(i, elements);
 275     }
 276 
 277     @Override
 278     public boolean removeAll(Collection<?> objects) {
 279         final ObservableList<E> list = get();
 280         return (list == null)? EMPTY_LIST.removeAll(objects) : list.removeAll(objects);
 281     }
 282 
 283     @Override
 284     public boolean retainAll(Collection<?> objects) {
 285         final ObservableList<E> list = get();
 286         return (list == null)? EMPTY_LIST.retainAll(objects) : list.retainAll(objects);
 287     }
 288 
 289     @Override
 290     public void clear() {
 291         final ObservableList<E> list = get();
 292         if (list == null) {
 293             EMPTY_LIST.clear();
 294         } else {
 295             list.clear();
 296         }
 297     }
 298 
 299     @Override
 300     public E get(int i) {
 301         final ObservableList<E> list = get();
 302         return (list == null)? (E) EMPTY_LIST.get(i) : list.get(i);
 303     }
 304 
 305     @Override
 306     public E set(int i, E element) {
 307         final ObservableList<E> list = get();
 308         return (list == null)? (E) EMPTY_LIST.set(i, element) : list.set(i, element);
 309     }
 310 
 311     @Override
 312     public void add(int i, E element) {
 313         final ObservableList<E> list = get();
 314         if (list == null) {
 315             EMPTY_LIST.add(i, element);
 316         } else {
 317             list.add(i, element);
 318         }
 319     }
 320 
 321     @Override
 322     public E remove(int i) {
 323         final ObservableList<E> list = get();
 324         return (list == null)? (E) EMPTY_LIST.remove(i) : list.remove(i);
 325     }
 326 
 327     @Override
 328     public int indexOf(Object obj) {
 329         final ObservableList<E> list = get();
 330         return (list == null)? EMPTY_LIST.indexOf(obj) : list.indexOf(obj);
 331     }
 332 
 333     @Override
 334     public int lastIndexOf(Object obj) {
 335         final ObservableList<E> list = get();
 336         return (list == null)? EMPTY_LIST.lastIndexOf(obj) : list.lastIndexOf(obj);
 337     }
 338 
 339     @Override
 340     public ListIterator<E> listIterator() {
 341         final ObservableList<E> list = get();
 342         return (list == null)? EMPTY_LIST.listIterator() : list.listIterator();
 343     }
 344 
 345     @Override
 346     public ListIterator<E> listIterator(int i) {
 347         final ObservableList<E> list = get();
 348         return (list == null)? EMPTY_LIST.listIterator(i) : list.listIterator(i);
 349     }
 350 
 351     @Override
 352     public List<E> subList(int from, int to) {
 353         final ObservableList<E> list = get();
 354         return (list == null)? EMPTY_LIST.subList(from, to) : list.subList(from, to);
 355     }
 356 
 357     @Override
 358     public boolean addAll(E... elements) {
 359         final ObservableList<E> list = get();
 360         return (list == null)? EMPTY_LIST.addAll(elements) : list.addAll(elements);
 361     }
 362 
 363     @Override
 364     public boolean setAll(E... elements) {
 365         final ObservableList<E> list = get();
 366         return (list == null)? EMPTY_LIST.setAll(elements) : list.setAll(elements);
 367     }
 368 
 369     @Override
 370     public boolean setAll(Collection<? extends E> elements) {
 371         final ObservableList<E> list = get();
 372         return (list == null)? EMPTY_LIST.setAll(elements) : list.setAll(elements);
 373     }
 374 
 375     @Override
 376     public boolean removeAll(E... elements) {
 377         final ObservableList<E> list = get();
 378         return (list == null)? EMPTY_LIST.removeAll(elements) : list.removeAll(elements);
 379     }
 380 
 381     @Override
 382     public boolean retainAll(E... elements) {
 383         final ObservableList<E> list = get();
 384         return (list == null)? EMPTY_LIST.retainAll(elements) : list.retainAll(elements);
 385     }
 386 
 387     @Override
 388     public void remove(int from, int to) {
 389         final ObservableList<E> list = get();
 390         if (list == null) {
 391             EMPTY_LIST.remove(from, to);
 392         } else {
 393             list.remove(from, to);
 394         }
 395     }
 396 
 397 }