
io.github.palexdev.materialfx.bindings.BindingsMap Maven / Gradle / Ivy
/*
* Copyright (C) 2022 Parisi Alessandro
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
*
* MaterialFX is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MaterialFX 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with MaterialFX. If not, see .
*/
package io.github.palexdev.materialfx.bindings;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* A special {@link WeakHashMap} that allows to retrieve the keys ordered by insertion.
*
* Writing a {@code LinkedWeakHashMap} would have been too much work and a very hard task.
* For this reason this Map simply uses a {@link LinkedList} to store the keys (wrapped in {@link WeakReference}s) by
* their insertion order.
*
* Just like the {@link WeakHashMap} this list is cleared (all null references are removed) when
* major operations occur, such as: put, putAll, combine.
*
* Allows to retrieve the first and the last keys, and also a copy of the actual {@link LinkedList}.
*/
public class BindingsMap extends WeakHashMap {
//================================================================================
// Properties
//================================================================================
private LinkedList> orderedKeys = new LinkedList<>();
//================================================================================
// Constructors
//================================================================================
public BindingsMap() {
}
public BindingsMap(Map extends K, ? extends V> m) {
super(m);
}
public BindingsMap(int initialCapacity) {
super(initialCapacity);
}
public BindingsMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
}
//================================================================================
// Methods
//================================================================================
/**
* Scans the {@link LinkedList} contains the keys references and removes the null ones.
* Also calls {@link #size()} to also trigger the {@link WeakHashMap} cleaning too.
*/
private void clearReferences() {
orderedKeys.removeIf(reference -> reference != null && reference.get() == null);
size();
}
@SafeVarargs
private void updateKeysList(K... keys) {
LinkedHashSet uniqueKeys = orderedKeys.stream().map(WeakReference::get).collect(Collectors.toCollection(LinkedHashSet::new));
uniqueKeys.addAll(Arrays.asList(keys));
orderedKeys = uniqueKeys.stream().map(WeakReference::new).collect(Collectors.toCollection(LinkedList::new));
clearReferences();
}
@SafeVarargs
private void updateKeysList(Map.Entry... entries) {
LinkedHashSet uniqueKeys = orderedKeys.stream().map(WeakReference::get).collect(Collectors.toCollection(LinkedHashSet::new));
List keys = Stream.of(entries).map(Map.Entry::getKey).collect(Collectors.toList());
uniqueKeys.addAll(keys);
orderedKeys = uniqueKeys.stream().map(WeakReference::new).collect(Collectors.toCollection(LinkedList::new));
clearReferences();
}
/**
* Allows to combine the given {@code BindingsMap} to this one.
*
* This method exists to ensure that insertion order is kept with the {@link LinkedList} but most
* importantly ensures that there are no duplicates in the list by using a {@link LinkedHashSet}.
*/
public void combine(BindingsMap source) {
LinkedHashSet uniqueKeys = Stream.concat(orderedKeys.stream(), source.orderedKeys.stream())
.map(WeakReference::get)
.collect(Collectors.toCollection(LinkedHashSet::new));
orderedKeys = uniqueKeys.stream().map(WeakReference::new).collect(LinkedList::new, LinkedList::add, LinkedList::addAll);
clearReferences();
for (Map.Entry entry : source.entrySet()) {
super.put(entry.getKey(), entry.getValue());
}
}
/**
* Adds the given key to the keys {@link LinkedList}, performs {@link #clearReferences()}
* and then calls the super method.
*/
@Override
public V put(K key, V value) {
updateKeysList(key);
return super.put(key, value);
}
/**
* Overridden to call {@link #putAll(Map.Entry[])}.
*/
@SuppressWarnings("unchecked")
@Override
public void putAll(Map extends K, ? extends V> m) {
putAll(m.entrySet().toArray(Map.Entry[]::new));
}
/**
* For each entry adds the key to the keys {@link LinkedList},
* then calls the super method.
* At the end performs {@link #clearReferences()}.
*/
@SafeVarargs
public final void putAll(Map.Entry... entries) {
updateKeysList(entries);
for (Map.Entry entry : entries) {
super.put(entry.getKey(), entry.getValue());
}
}
/**
* Removes the given key from the keys {@link LinkedList}
* and then calls the super method.
*/
@Override
public V remove(Object key) {
orderedKeys.removeIf(reference -> reference != null && reference.get() == key);
return super.remove(key);
}
/**
* Clears the keys {@link LinkedList} and then calls the super method.
*/
@Override
public void clear() {
orderedKeys.clear();
super.clear();
}
/**
* UNSUPPORTED
*/
@Override
public void replaceAll(BiFunction super K, ? super V, ? extends V> function) {
throw new UnsupportedOperationException();
}
/**
* @return a copy of the {@link LinkedList} containing the Map's keys ordered by insertion
*/
public LinkedList> unmodifiableKeysList() {
return new LinkedList<>(orderedKeys);
}
/**
* @return the first inserted key
*/
public K getFirstKey() {
if (isEmpty()) return null;
return orderedKeys.getFirst().get();
}
/**
* @return the last inserted key
*/
public K getLastKey() {
if (isEmpty()) return null;
return orderedKeys.getLast().get();
}
}