org.reactfx.util.AccuMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of richtextfx Show documentation
Show all versions of richtextfx Show documentation
FX-Text-Area for formatted text and other special effects.
package org.reactfx.util;
import static org.reactfx.util.Tuples.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* Accumulation map.
*
* @param key type
* @param type of individual (non-accumulated) values
* @param type of accumulated values
*/
public interface AccuMap {
static AccuMap empty() {
return EmptyAccuMap.instance();
}
boolean isEmpty();
Tuple2 peek(AccumulationFacility af);
AccuMap dropPeeked();
AccuMap updatePeeked(A newAccumulatedValue);
AccuMap addAll(Iterator keys, V value, AccumulationFacility af);
}
class EmptyAccuMap implements AccuMap {
private static final AccuMap, ?, ?> INSTANCE = new EmptyAccuMap<>();
@SuppressWarnings("unchecked")
static AccuMap instance() {
return (AccuMap) INSTANCE;
}
// private constructor to prevent instantiation
private EmptyAccuMap() {}
@Override
public boolean isEmpty() {
return true;
}
@Override
public Tuple2 peek(AccumulationFacility af) {
throw new NoSuchElementException();
}
@Override
public AccuMap dropPeeked() {
throw new NoSuchElementException();
}
@Override
public AccuMap updatePeeked(A newAccumulatedValue) {
throw new NoSuchElementException();
}
@Override
public AccuMap addAll(
Iterator keys, V value,
AccumulationFacility af) {
return new IteratorBasedAccuMap<>(keys, value);
}
}
class IteratorBasedAccuMap
implements AccuMap {
private K currentKey = null;
private A currentAccumulatedValue = null;
private Iterator it;
private V value;
IteratorBasedAccuMap(Iterator keys, V value) {
this.it = keys;
this.value = value;
}
@Override
public boolean isEmpty() {
return currentKey == null && !it.hasNext();
}
@Override
public Tuple2 peek(AccumulationFacility af) {
if(currentKey == null) {
currentKey = it.next();
currentAccumulatedValue = af.initialAccumulator(value);
}
return t(currentKey, currentAccumulatedValue);
}
@Override
public AccuMap dropPeeked() {
checkPeeked();
currentKey = null;
currentAccumulatedValue = null;
return this;
}
@Override
public AccuMap updatePeeked(A newAccumulatedValue) {
checkPeeked();
currentAccumulatedValue = newAccumulatedValue;
return this;
}
@Override
public AccuMap addAll(Iterator keys, V value, AccumulationFacility af) {
if(isEmpty()) {
this.it = keys;
this.value = value;
return this;
} else if(!keys.hasNext()) {
return this;
} else {
HashAccuMap res = new HashAccuMap<>();
if(currentKey != null) {
res.put(currentKey, currentAccumulatedValue);
}
return res
.addAll(it, this.value, af)
.addAll(keys, value, af);
}
}
private final void checkPeeked() {
if(currentKey == null) {
throw new NoSuchElementException("No peeked value present. Use peek() first.");
}
}
}
@SuppressWarnings("serial")
class HashAccuMap extends HashMap implements AccuMap {
@Override
public Tuple2 peek(AccumulationFacility af) {
K key = pickKey();
A acc = this.get(key);
return t(key, acc);
}
@Override
public AccuMap dropPeeked() {
K key = pickKey();
this.remove(key);
return this;
}
@Override
public AccuMap updatePeeked(A newAccumulatedValue) {
K key = pickKey();
this.put(key, newAccumulatedValue);
return this;
}
@Override
public AccuMap addAll(Iterator keys, V value, AccumulationFacility af) {
while(keys.hasNext()) {
K key = keys.next();
if(this.containsKey(key)) {
A accum = this.get(key);
accum = af.reduce(accum, value);
this.put(key, accum);
} else {
A accum = af.initialAccumulator(value);
this.put(key, accum);
}
}
return this;
}
private K pickKey() {
return this.keySet().iterator().next();
}
}