/****************************************************************************** * Copyright (c) 2015, 2016 itemis AG and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Alexander Nyßen (itemis AG) - initial API and implementation * *******************************************************************************/ package org.eclipse.gef.common.beans.binding; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.eclipse.gef.common.beans.value.ObservableSetMultimapValue; import org.eclipse.gef.common.collections.CollectionUtils; import org.eclipse.gef.common.collections.ObservableSetMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Multiset; import com.google.common.collect.SetMultimap; import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; import javafx.beans.binding.ListExpression; import javafx.beans.binding.MapExpression; import javafx.beans.binding.ObjectBinding; import javafx.beans.binding.SetBinding; import javafx.beans.binding.SetExpression; import javafx.beans.binding.StringBinding; import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyIntegerProperty; import javafx.beans.value.ObservableValue; /** * A {@code SetMultimapExpression} is a {@link ObservableSetMultimapValue} plus * additional convenience methods to generate bindings. * <p> * This class provides identical functionality for {@link SetMultimap} as * {@link MapExpression} for {@link Map}, {@link SetExpression} for {@link Set}, * or {@link ListExpression} for {@link List}. * * @author anyssen * * @param <K> * The key type of the {@link ObservableSetMultimap}. * @param <V> * The value type of the {@link ObservableSetMultimap}. */ public abstract class SetMultimapExpression<K, V> implements ObservableSetMultimapValue<K, V> { private static final class SetMultimapBindingImpl<K, V> extends SetMultimapBinding<K, V> { private ObservableSetMultimapValue<K, V> setMultimapValue = null; public SetMultimapBindingImpl( ObservableSetMultimapValue<K, V> setMultimapValue) { this.setMultimapValue = setMultimapValue; bind(setMultimapValue); } @Override protected ObservableSetMultimap<K, V> computeValue() { return setMultimapValue.get(); } } /** * Returns a {@code SetMultimapExpression} that wraps an * {@link ObservableSetMultimapValue}. If the * {@code ObservableSetMultimapValue} is already a * {@code SetMultimapExpression}, it will be returned. Otherwise a new * concrete {@link SetMultimapBinding} is created that is bound to the * {@code ObservableSetMultimapValue}. * * @param <K> * The key type of the {@link SetMultimapExpression}. * @param <V> * The value type of the {@link SetMultimapExpression}. * * @param setMultimapValue * The {@code ObservableSetMultimapValue} for which to return a * {@link SetMultimapExpression}. * @return The passed in {@link ObservableSetMultimapValue} if its already a * {@link SetMultimapExpression}, or a newly created * {@link SetMultimapBinding} for it. */ public static <K, V> SetMultimapExpression<K, V> setMultimapExpression( final ObservableSetMultimapValue<K, V> setMultimapValue) { if (setMultimapValue == null) { throw new IllegalArgumentException( "setMultimapValue may not be null."); } if (setMultimapValue instanceof SetMultimapExpression) { return (SetMultimapExpression<K, V>) setMultimapValue; } return new SetMultimapBindingImpl<>(setMultimapValue); } private final ObservableSetMultimap<K, V> EMPTY_SETMULTIMAP = CollectionUtils .emptySetMultimap(); @Override public Map<K, Collection<V>> asMap() { final SetMultimap<K, V> setMultimap = get(); return (setMultimap == null) ? EMPTY_SETMULTIMAP.asMap() : setMultimap.asMap(); } /** * Creates a {@link StringBinding} that holds the value of the * {@link SetMultimapExpression} turned into a {@link String}. If the value * of this {@code SetMultimapExpression} changes, the value of the * {@link StringBinding} will be updated automatically. * * @return A new {@code StringBinding}. */ public StringBinding asString() { return (StringBinding) Bindings.convert(this); } @Override public void clear() { final SetMultimap<K, V> setMultimap = get(); if (setMultimap == null) { EMPTY_SETMULTIMAP.clear(); } else { setMultimap.clear(); } } @Override public boolean containsEntry(Object key, Object value) { final SetMultimap<K, V> setMultimap = get(); return (setMultimap == null) ? EMPTY_SETMULTIMAP.containsEntry(key, value) : setMultimap.containsEntry(key, value); } @Override public boolean containsKey(Object key) { final SetMultimap<K, V> setMultimap = get(); return (setMultimap == null) ? EMPTY_SETMULTIMAP.containsKey(key) : setMultimap.containsKey(key); } @Override public boolean containsValue(Object value) { final SetMultimap<K, V> setMultimap = get(); return (setMultimap == null) ? EMPTY_SETMULTIMAP.containsValue(value) : setMultimap.containsValue(value); } /** * A boolean property that reflects whether the {@link SetMultimap} is * empty. * * @return A read-only property. * */ public abstract ReadOnlyBooleanProperty emptyProperty(); @Override public Set<Entry<K, V>> entries() { final SetMultimap<K, V> setMultimap = get(); return (setMultimap == null) ? EMPTY_SETMULTIMAP.entries() : setMultimap.entries(); } @Override public Set<V> get(K key) { final SetMultimap<K, V> setMultimap = get(); return (setMultimap == null) ? EMPTY_SETMULTIMAP.get(key) : setMultimap.get(key); } @Override public ObservableSetMultimap<K, V> getValue() { return get(); } @Override public boolean isEmpty() { final SetMultimap<K, V> setMultimap = get(); return (setMultimap == null) ? EMPTY_SETMULTIMAP.isEmpty() : setMultimap.isEmpty(); } /** * Creates a new {@link BooleanBinding} that indicates whether this * {@link ObservableSetMultimap} is equal to the passed in * {@link ObservableSetMultimap}. * * @param other * The {@link ObservableSetMultimap} to compare this * {@link ObservableSetMultimap} to. * @return A new {@code BooleanBinding}. */ public BooleanBinding isEqualTo(final ObservableSetMultimap<?, ?> other) { return Bindings.equal(this, other); } /** * Creates a new {@link BooleanBinding} that indicates whether this * {@link ObservableSetMultimap} is not equal to the passed in * {@link ObservableSetMultimap}. * * @param other * The {@link ObservableSetMultimap} to compare this * {@link ObservableSetMultimap} to. * @return A new {@code BooleanBinding}. */ public BooleanBinding isNotEqualTo( final ObservableSetMultimap<?, ?> other) { return Bindings.notEqual(this, other); } /** * Creates a new {@link BooleanBinding} that indicates if the wrapped * {@link ObservableSetMultimap} is not <code>null</code>. * * @return A new {@code BooleanBinding}. */ public BooleanBinding isNotNull() { return Bindings.isNotNull(this); } /** * Creates a new {@link BooleanBinding} that indicates if the wrapped * {@link ObservableSetMultimap} is <code>null</code>. * * @return A new {@code BooleanBinding}. */ public BooleanBinding isNull() { return Bindings.isNull(this); } @Override public Multiset<K> keys() { final SetMultimap<K, V> setMultimap = get(); return (setMultimap == null) ? EMPTY_SETMULTIMAP.keys() : setMultimap.keys(); } @Override public Set<K> keySet() { final SetMultimap<K, V> setMultimap = get(); return (setMultimap == null) ? EMPTY_SETMULTIMAP.keySet() : setMultimap.keySet(); } @Override public boolean put(K key, V value) { final SetMultimap<K, V> setMultimap = get(); return (setMultimap == null) ? EMPTY_SETMULTIMAP.put(key, value) : setMultimap.put(key, value); } @Override public boolean putAll(K key, Iterable<? extends V> values) { final SetMultimap<K, V> setMultimap = get(); return (setMultimap == null) ? EMPTY_SETMULTIMAP.putAll(key, values) : setMultimap.putAll(key, values); } @Override public boolean putAll(Multimap<? extends K, ? extends V> multimap) { final SetMultimap<K, V> setMultimap = get(); return (setMultimap == null) ? EMPTY_SETMULTIMAP.putAll(multimap) : setMultimap.putAll(multimap); } @Override public boolean remove(Object key, Object value) { final SetMultimap<K, V> setMultimap = get(); return (setMultimap == null) ? EMPTY_SETMULTIMAP.remove(key, value) : setMultimap.remove(key, value); } @Override public Set<V> removeAll(Object key) { final SetMultimap<K, V> setMultimap = get(); return (setMultimap == null) ? EMPTY_SETMULTIMAP.removeAll(key) : setMultimap.removeAll(key); } @Override public boolean replaceAll( SetMultimap<? extends K, ? extends V> setMultimap) { final ObservableSetMultimap<K, V> delegate = get(); return (delegate == null) ? EMPTY_SETMULTIMAP.replaceAll(setMultimap) : delegate.replaceAll(setMultimap); } @Override public Set<V> replaceValues(K key, Iterable<? extends V> values) { final SetMultimap<K, V> setMultimap = get(); return (setMultimap == null) ? EMPTY_SETMULTIMAP.replaceValues(key, values) : setMultimap.replaceValues(key, values); } @Override public int size() { final SetMultimap<K, V> setMultimap = get(); return (setMultimap == null) ? EMPTY_SETMULTIMAP.size() : setMultimap.size(); } /** * An integer property that represents the size of the {@link SetMultimap}. * * @return A read-only property. */ public abstract ReadOnlyIntegerProperty sizeProperty(); @Override public Collection<V> values() { final SetMultimap<K, V> setMultimap = get(); return (setMultimap == null) ? EMPTY_SETMULTIMAP.values() : setMultimap.values(); } /** * Creates a new {@link ObjectBinding} that contains the values that are * mapped to the specified key. * * @param key * the key of the mapping * @return A new {@code SetBinding}. */ public SetBinding<V> valuesAt(final K key) { return BindingUtils.valuesAt(this, key); } /** * Creates a new {@link ObjectBinding} that contains the values that are * mapped to the specified key. * * @param key * The key of the mapping. * @return The {@code ObjectBinding}. */ public SetBinding<V> valuesAt(final ObservableValue<K> key) { return BindingUtils.valuesAt(this, key); } }