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 }