com.sun.javafx.collections.ObservableMapWrapper Maven / Gradle / Ivy
/*
* Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.javafx.collections;
import javafx.beans.InvalidationListener;
import javafx.collections.MapChangeListener;
import javafx.collections.ObservableMap;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* A Map wrapper class that implements observability.
*
*/
public class ObservableMapWrapper implements ObservableMap{
private ObservableEntrySet entrySet;
private ObservableKeySet keySet;
private ObservableValues values;
private MapListenerHelper listenerHelper;
private final Map backingMap;
public ObservableMapWrapper(Map map) {
this.backingMap = map;
}
private class SimpleChange extends MapChangeListener.Change {
private final K key;
private final V old;
private final V added;
private final boolean wasAdded;
private final boolean wasRemoved;
public SimpleChange(K key, V old, V added, boolean wasAdded, boolean wasRemoved) {
super(ObservableMapWrapper.this);
assert(wasAdded || wasRemoved);
this.key = key;
this.old = old;
this.added = added;
this.wasAdded = wasAdded;
this.wasRemoved = wasRemoved;
}
@Override
public boolean wasAdded() {
return wasAdded;
}
@Override
public boolean wasRemoved() {
return wasRemoved;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValueAdded() {
return added;
}
@Override
public V getValueRemoved() {
return old;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (wasAdded) {
if (wasRemoved) {
builder.append(old).append(" replaced by ").append(added);
} else {
builder.append(added).append(" added");
}
} else {
builder.append(old).append(" removed");
}
builder.append(" at key ").append(key);
return builder.toString();
}
}
protected void callObservers(MapChangeListener.Change change) {
MapListenerHelper.fireValueChangedEvent(listenerHelper, change);
}
@Override
public void addListener(InvalidationListener listener) {
listenerHelper = MapListenerHelper.addListener(listenerHelper, listener);
}
@Override
public void removeListener(InvalidationListener listener) {
listenerHelper = MapListenerHelper.removeListener(listenerHelper, listener);
}
@Override
public void addListener(MapChangeListener super K, ? super V> observer) {
listenerHelper = MapListenerHelper.addListener(listenerHelper, observer);
}
@Override
public void removeListener(MapChangeListener super K, ? super V> observer) {
listenerHelper = MapListenerHelper.removeListener(listenerHelper, observer);
}
@Override
public int size() {
return backingMap.size();
}
@Override
public boolean isEmpty() {
return backingMap.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return backingMap.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return backingMap.containsValue(value);
}
@Override
public V get(Object key) {
return backingMap.get(key);
}
@Override
public V put(K key, V value) {
V ret;
if (backingMap.containsKey(key)) {
ret = backingMap.put(key, value);
if (ret == null && value != null || ret != null && !ret.equals(value)) {
callObservers(new SimpleChange(key, ret, value, true, true));
}
} else {
ret = backingMap.put(key, value);
callObservers(new SimpleChange(key, ret, value, true, false));
}
return ret;
}
@Override
@SuppressWarnings("unchecked")
public V remove(Object key) {
if (!backingMap.containsKey(key)) {
return null;
}
V ret = backingMap.remove(key);
callObservers(new SimpleChange((K)key, ret, null, false, true));
return ret;
}
@Override
public void putAll(Map extends K, ? extends V> m) {
for (Map.Entry extends K, ? extends V> e : m.entrySet()) {
put(e.getKey(), e.getValue());
}
}
@Override
public void clear() {
for (Iterator> i = backingMap.entrySet().iterator(); i.hasNext(); ) {
Entry e = i.next();
K key = e.getKey();
V val = e.getValue();
i.remove();
callObservers(new SimpleChange(key, val, null, false, true));
}
}
@Override
public Set keySet() {
if (keySet == null) {
keySet = new ObservableKeySet();
}
return keySet;
}
@Override
public Collection values() {
if (values == null) {
values = new ObservableValues();
}
return values;
}
@Override
public Set> entrySet() {
if (entrySet == null) {
entrySet = new ObservableEntrySet();
}
return entrySet;
}
@Override
public String toString() {
return backingMap.toString();
}
@Override
public boolean equals(Object obj) {
return backingMap.equals(obj);
}
@Override
public int hashCode() {
return backingMap.hashCode();
}
private class ObservableKeySet implements Set{
@Override
public int size() {
return backingMap.size();
}
@Override
public boolean isEmpty() {
return backingMap.isEmpty();
}
@Override
public boolean contains(Object o) {
return backingMap.keySet().contains(o);
}
@Override
public Iterator iterator() {
return new Iterator<>() {
private Iterator> entryIt = backingMap.entrySet().iterator();
private K lastKey;
private V lastValue;
@Override
public boolean hasNext() {
return entryIt.hasNext();
}
@Override
public K next() {
Entry last = entryIt.next();
lastKey = last.getKey();
lastValue = last.getValue();
return last.getKey();
}
@Override
public void remove() {
entryIt.remove();
callObservers(new SimpleChange(lastKey, lastValue, null, false, true));
}
};
}
@Override
public Object[] toArray() {
return backingMap.keySet().toArray();
}
@Override
public T[] toArray(T[] a) {
return backingMap.keySet().toArray(a);
}
@Override
public boolean add(K e) {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public boolean remove(Object o) {
return ObservableMapWrapper.this.remove(o) != null;
}
@Override
public boolean containsAll(Collection> c) {
return backingMap.keySet().containsAll(c);
}
@Override
public boolean addAll(Collection extends K> c) {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public boolean retainAll(Collection> c) {
// implicit check to ensure c != null
if (c.isEmpty() && !backingMap.isEmpty()) {
clear();
return true;
}
if (backingMap.isEmpty()) {
return false;
}
return removeRetain(c, false);
}
private boolean removeRetain(Collection> c, boolean remove) {
boolean removed = false;
for (Iterator> i = backingMap.entrySet().iterator(); i.hasNext();) {
Entry e = i.next();
if (remove == c.contains(e.getKey())) {
removed = true;
K key = e.getKey();
V value = e.getValue();
i.remove();
callObservers(new SimpleChange(key, value, null, false, true));
}
}
return removed;
}
@Override
public boolean removeAll(Collection> c) {
// implicit check to ensure c != null
if (c.isEmpty() || backingMap.isEmpty()) {
return false;
}
return removeRetain(c, true);
}
@Override
public void clear() {
ObservableMapWrapper.this.clear();
}
@Override
public String toString() {
return backingMap.keySet().toString();
}
@Override
public boolean equals(Object obj) {
return backingMap.keySet().equals(obj);
}
@Override
public int hashCode() {
return backingMap.keySet().hashCode();
}
}
private class ObservableValues implements Collection {
@Override
public int size() {
return backingMap.size();
}
@Override
public boolean isEmpty() {
return backingMap.isEmpty();
}
@Override
public boolean contains(Object o) {
return backingMap.values().contains(o);
}
@Override
public Iterator iterator() {
return new Iterator<>() {
private Iterator> entryIt = backingMap.entrySet().iterator();
private K lastKey;
private V lastValue;
@Override
public boolean hasNext() {
return entryIt.hasNext();
}
@Override
public V next() {
Entry last = entryIt.next();
lastKey = last.getKey();
lastValue = last.getValue();
return lastValue;
}
@Override
public void remove() {
entryIt.remove();
callObservers(new SimpleChange(lastKey, lastValue, null, false, true));
}
};
}
@Override
public Object[] toArray() {
return backingMap.values().toArray();
}
@Override
public T[] toArray(T[] a) {
return backingMap.values().toArray(a);
}
@Override
public boolean add(V e) {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public boolean remove(Object o) {
for(Iterator i = iterator(); i.hasNext();) {
if (i.next().equals(o)) {
i.remove();
return true;
}
}
return false;
}
@Override
public boolean containsAll(Collection> c) {
return backingMap.values().containsAll(c);
}
@Override
public boolean addAll(Collection extends V> c) {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public boolean removeAll(Collection> c) {
// implicit check to ensure c != null
if (c.isEmpty() || backingMap.isEmpty()) {
return false;
}
return removeRetain(c, true);
}
private boolean removeRetain(Collection> c, boolean remove) {
boolean removed = false;
for (Iterator> i = backingMap.entrySet().iterator(); i.hasNext();) {
Entry e = i.next();
if (remove == c.contains(e.getValue())) {
removed = true;
K key = e.getKey();
V value = e.getValue();
i.remove();
callObservers(new SimpleChange(key, value, null, false, true));
}
}
return removed;
}
@Override
public boolean retainAll(Collection> c) {
// implicit check to ensure c != null
if (c.isEmpty() && !backingMap.isEmpty()) {
clear();
return true;
}
if (backingMap.isEmpty()) {
return false;
}
return removeRetain(c, false);
}
@Override
public void clear() {
ObservableMapWrapper.this.clear();
}
@Override
public String toString() {
return backingMap.values().toString();
}
@Override
public boolean equals(Object obj) {
return backingMap.values().equals(obj);
}
@Override
public int hashCode() {
return backingMap.values().hashCode();
}
}
private class ObservableEntry implements Entry {
private final Entry backingEntry;
public ObservableEntry(Entry backingEntry) {
this.backingEntry = backingEntry;
}
@Override
public K getKey() {
return backingEntry.getKey();
}
@Override
public V getValue() {
return backingEntry.getValue();
}
@Override
public V setValue(V value) {
V oldValue = backingEntry.setValue(value);
callObservers(new SimpleChange(getKey(), oldValue, value, true, true));
return oldValue;
}
@Override
public final boolean equals(Object o) {
if (!(o instanceof Map.Entry, ?> e)) {
return false;
}
Object k1 = getKey();
Object k2 = e.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2))) {
return true;
}
}
return false;
}
@Override
public final int hashCode() {
return (getKey() == null ? 0 : getKey().hashCode())
^ (getValue() == null ? 0 : getValue().hashCode());
}
@Override
public final String toString() {
return getKey() + "=" + getValue();
}
}
private class ObservableEntrySet implements Set>{
@Override
public int size() {
return backingMap.size();
}
@Override
public boolean isEmpty() {
return backingMap.isEmpty();
}
@Override
public boolean contains(Object o) {
return backingMap.entrySet().contains(o);
}
@Override
public Iterator> iterator() {
return new Iterator<>() {
private Iterator> backingIt = backingMap.entrySet().iterator();
private K lastKey;
private V lastValue;
@Override
public boolean hasNext() {
return backingIt.hasNext();
}
@Override
public Entry next() {
Entry last = backingIt.next();
lastKey = last.getKey();
lastValue = last.getValue();
return new ObservableEntry(last);
}
@Override
public void remove() {
backingIt.remove();
callObservers(new SimpleChange(lastKey, lastValue, null, false, true));
}
};
}
@Override
@SuppressWarnings("unchecked")
public Object[] toArray() {
Object[] array = backingMap.entrySet().toArray();
for (int i = 0; i < array.length; ++i) {
array[i] = new ObservableEntry((Entry)array[i]);
}
return array;
}
@Override
@SuppressWarnings("unchecked")
public T[] toArray(T[] a) {
T[] array = backingMap.entrySet().toArray(a);
for (int i = 0; i < array.length; ++i) {
array[i] = (T) new ObservableEntry((Entry)array[i]);
}
return array;
}
@Override
public boolean add(Entry e) {
throw new UnsupportedOperationException("Not supported.");
}
@Override
@SuppressWarnings("unchecked")
public boolean remove(Object o) {
boolean ret = backingMap.entrySet().remove(o);
if (ret) {
Entry entry = (Entry) o;
callObservers(new SimpleChange(entry.getKey(), entry.getValue(), null, false, true));
}
return ret;
}
@Override
public boolean containsAll(Collection> c) {
return backingMap.entrySet().containsAll(c);
}
@Override
public boolean addAll(Collection extends Entry> c) {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public boolean retainAll(Collection> c) {
// implicit check to ensure c != null
if (c.isEmpty() && !backingMap.isEmpty()) {
clear();
return true;
}
if (backingMap.isEmpty()) {
return false;
}
return removeRetain(c, false);
}
private boolean removeRetain(Collection> c, boolean remove) {
boolean removed = false;
for (Iterator> i = backingMap.entrySet().iterator(); i.hasNext();) {
Entry e = i.next();
if (remove == c.contains(e)) {
removed = true;
K key = e.getKey();
V value = e.getValue();
i.remove();
callObservers(new SimpleChange(key, value, null, false, true));
}
}
return removed;
}
@Override
public boolean removeAll(Collection> c) {
// implicit check to ensure c != null
if (c.isEmpty() || backingMap.isEmpty()) {
return false;
}
return removeRetain(c, true);
}
@Override
public void clear() {
ObservableMapWrapper.this.clear();
}
@Override
public String toString() {
return backingMap.entrySet().toString();
}
@Override
public boolean equals(Object obj) {
return backingMap.entrySet().equals(obj);
}
@Override
public int hashCode() {
return backingMap.entrySet().hashCode();
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy