/****************************************************************************** * 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.property; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.gef.common.beans.binding.BindingUtils; import org.eclipse.gef.common.beans.binding.MultisetExpression; import org.eclipse.gef.common.collections.ObservableMultiset; import org.eclipse.gef.common.collections.ObservableSetMultimap; import com.google.common.collect.Multiset; import com.google.common.collect.SetMultimap; import javafx.beans.property.Property; import javafx.beans.property.ReadOnlyListProperty; import javafx.beans.property.ReadOnlyMapProperty; import javafx.beans.property.ReadOnlyProperty; import javafx.beans.property.ReadOnlySetProperty; /** * Abstract base class defining contract for a read-only {@link Property} * wrapping an {@link ObservableMultiset}. * <p> * This class provides identical functionality for {@link SetMultimap} as * {@link ReadOnlyMapProperty} for {@link Map}, {@link ReadOnlySetProperty} for * {@link Set}, or {@link ReadOnlyListProperty} for {@link List}. * * @param <E> * The element type of the wrapped {@link ObservableMultiset}. * * @author anyssen */ public abstract class ReadOnlyMultisetProperty<E> extends MultisetExpression<E> implements ReadOnlyProperty<ObservableMultiset<E>> { /** * Appends a representation of this {@link SetMultimapProperty}'s value to * the given {@link StringBuilder}. Gets called from {@link #toString()} to * allow subclasses to provide a changed value representation. * * @param result * A {@link StringBuilder} to append the value representation to. */ protected void appendValueToString(final StringBuilder result) { result.append("value: " + get()); } /** * Creates a unidirectional content binding between the * {@link ObservableMultiset}, that is wrapped in this * {@link ReadOnlyMultisetProperty}, and the given * {@link ObservableMultiset}. * <p> * A content binding ensures that the content of the wrapped * {@link ObservableMultiset} is the same as that of the other * {@link ObservableMultiset}. If the content of the other * {@link ObservableMultiset} changes, the wrapped * {@link ObservableMultiset} will be updated automatically. * * @param target * The {@link ObservableSetMultimap} this property should be * unidirectionally bound to. */ public void bindContent(ObservableMultiset<E> target) { BindingUtils.bindContent(this, target); } /** * Creates a bidirectional content binding of the {@link ObservableMultiset} * , that is wrapped in this {@link ReadOnlyMultisetProperty}, and the given * {@link ObservableMultiset} . * <p> * A bidirectional content binding ensures that the content of the two * {@link ObservableMultiset ObservableMultisets} are the same. If the * content of one of the {@link ObservableMultiset ObservableMultiset} * changes, the other one will be updated automatically. * * @param other * The {@link ObservableSetMultimap} this property should be * bidirectionally bound to. */ public void bindContentBidirectional(ObservableMultiset<E> other) { BindingUtils.bindContentBidirectional(this, other); } @Override public boolean equals(Object other) { if (other == this) { return true; } if (other == null || !(other instanceof Multiset)) { return false; } if (get() == null) { return false; } return get().equals(other); } @Override public int hashCode() { // XXX: As we rely on equality to remove a binding again, we have to // ensure the hash code is the same for a pair of given properties. // We fall back to the very easiest case here (and use a constant). return 0; } @Override public String toString() { final StringBuilder sb = new StringBuilder( getClass().getSimpleName() + " ["); final Object bean = getBean(); if (bean != null) { sb.append("bean: " + bean + ", "); } final String name = getName(); if ((name != null) && !name.equals("")) { sb.append("name: " + name + ", "); } appendValueToString(sb); sb.append("]"); return sb.toString(); } /** * Deletes a content binding between the {@link ObservableSetMultimap}, that * is wrapped in this {@link ReadOnlyMultisetProperty}, and another * {@link Object}. * * @param target * The {@link Object} to which the binding should be removed. */ @SuppressWarnings("unchecked") public void unbindContent(Object target) { try { BindingUtils.unbindContent(this, (ObservableMultiset<? extends E>) target); } catch (ClassCastException e) { // nothing to do in case the types don't match } } /** * Deletes a bidirectional content binding between the * {@link ObservableSetMultimap}, that is wrapped in this * {@link ReadOnlyMultisetProperty}, and another {@link Object}. * * @param other * The {@link Object} to which the bidirectional binding should * be removed. */ @SuppressWarnings("unchecked") public void unbindContentBidirectional(Object other) { try { BindingUtils.unbindContentBidirectional(this, (ObservableMultiset<E>) other); } catch (ClassCastException e) { // nothing to do in case the types don't match } } }