org.fxmisc.wellbehaved.event.internal.PrefixTree 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.fxmisc.wellbehaved.event.internal;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Stream;
/**
* Prefix tree (Trie) with an additional property that no data is stored in
* internal nodes.
*
* @param type of "strings" used to index values
* @param type of values (data) indexed by this trie
*/
public abstract class PrefixTree {
public static interface Ops {
boolean isPrefixOf(K k1, K k2);
K commonPrefix(K k1, K k2);
V promote(V v, K oldKey, K newKey);
V squash(V v1, V v2);
}
private static class Empty extends PrefixTree {
public Empty(Ops ops) {
super(ops);
}
@Override
public Stream> entries() {
return Stream.empty();
}
@Override
public PrefixTree insert(K key, V value, BiFunction super V, ? super V, ? extends V> combine) {
return insertInside(key, value, combine);
}
@Override
PrefixTree insertInside(K key, V value, BiFunction super V, ? super V, ? extends V> combine) {
return new Data<>(ops, key, value);
}
@Override
public PrefixTree map(
Function super V, ? extends W> f,
Ops ops) {
return new Empty<>(ops);
}
}
private static abstract class NonEmpty extends PrefixTree {
public NonEmpty(Ops ops) {
super(ops);
}
abstract K getPrefix();
abstract Data collapse();
@Override
public abstract NonEmpty map(Function super V, ? extends W> f, Ops ops);
@Override
abstract NonEmpty insertInside(K key, V value, BiFunction super V, ? super V, ? extends V> combine);
@Override
public PrefixTree insert(K key, V value, BiFunction super V, ? super V, ? extends V> combine) {
if(ops.isPrefixOf(key, getPrefix())) { // key is a prefix of this tree
return new Data<>(ops, key, value).insertInside(collapse(), flip(combine));
} else if(ops.isPrefixOf(getPrefix(), key)) { // key is inside this tree
return insertInside(key, value, combine);
} else {
return new Branch<>(ops, this, new Data<>(ops, key, value));
}
}
}
private static class Branch extends NonEmpty {
private final K prefix;
private final List> subTrees;
Branch(Ops ops, K prefix, List> subTrees) {
super(ops);
assert Objects.equals(prefix, subTrees.stream().map(NonEmpty::getPrefix).reduce(ops::commonPrefix).get());
assert subTrees.stream().noneMatch(tree -> Objects.equals(tree.getPrefix(), prefix));
this.prefix = prefix;
this.subTrees = subTrees;
}
private Branch(Ops ops, NonEmpty t1, NonEmpty t2) {
this(ops, ops.commonPrefix(t1.getPrefix(), t2.getPrefix()), Arrays.asList(t1, t2));
}
@Override
K getPrefix() {
return prefix;
}
@Override
public Stream> entries() {
return subTrees.stream().flatMap(tree -> tree.entries());
}
@Override
Data collapse() {
return subTrees.stream()
.map(tree -> tree.collapse().promote(prefix))
.reduce(Data::squash).get();
}
@Override
NonEmpty insertInside(K key, V value, BiFunction super V, ? super V, ? extends V> combine) {
assert ops.isPrefixOf(prefix, key);
if(Objects.equals(key, prefix)) {
return new Data<>(ops, key, value).insertInside(collapse(), flip(combine));
}
// try to find a sub-tree that has common prefix with key longer than this branch's prefix
for(int i = 0; i < subTrees.size(); ++i) {
NonEmpty st = subTrees.get(i);
K commonPrefix = ops.commonPrefix(key, st.getPrefix());
if(!Objects.equals(commonPrefix, prefix)) {
if(Objects.equals(commonPrefix, st.getPrefix())) {
// st contains key, insert inside st
return replaceBranch(i, st.insertInside(key, value, combine));
} else if(Objects.equals(commonPrefix, key)) {
// st is under key, insert st inside Data(key, value)
return replaceBranch(i, new Data<>(ops, key, value).insertInside(st.collapse(), flip(combine)));
} else {
return replaceBranch(i, new Branch<>(ops, st, new Data<>(ops, key, value)));
}
}
}
// no branch intersects key, adjoin Data(key, value) to this branch
List> branches = new ArrayList<>(subTrees.size() + 1);
branches.addAll(subTrees);
branches.add(new Data<>(ops, key, value));
return new Branch<>(ops, prefix, branches);
}
private Branch replaceBranch(int i, NonEmpty replacement) {
assert ops.isPrefixOf(prefix, replacement.getPrefix());
assert ops.isPrefixOf(replacement.getPrefix(), subTrees.get(i).getPrefix());
ArrayList> branches = new ArrayList<>(subTrees);
branches.set(i, replacement);
return new Branch<>(ops, prefix, branches);
}
@Override
public NonEmpty map(
Function super V, ? extends W> f,
Ops ops) {
List> mapped = new ArrayList<>(subTrees.size());
for(NonEmpty tree: subTrees) {
mapped.add(tree.map(f, ops));
}
return new Branch<>(ops, prefix, mapped);
}
}
private static class Data extends NonEmpty {
private final K key;
private final V value;
Data(Ops ops, K key, V value) {
super(ops);
this.key = key;
this.value = value;
}
@Override
K getPrefix() {
return key;
}
@Override
public Stream> entries() {
return Stream.of(new SimpleEntry<>(key, value));
}
@Override
Data collapse() {
return this;
}
@Override
NonEmpty insertInside(K key, V value, BiFunction super V, ? super V, ? extends V> combine) {
assert ops.isPrefixOf(this.key, key);
return new Data<>(
this.ops,
this.key,
combine.apply(this.value, ops.promote(value, key, this.key)));
}
NonEmpty insertInside(NonEmpty tree, BiFunction super V, ? super V, ? extends V> combine) {
Data d = tree.collapse();
return insertInside(d.key, d.value, combine);
}
Data promote(K key) {
assert ops.isPrefixOf(key, this.key);
return new Data<>(ops, key, ops.promote(value, this.key, key));
}
Data squash(Data that) {
assert Objects.equals(this.key, that.key);
return new Data<>(ops, key, ops.squash(this.value, that.value));
}
@Override
public Data map(
Function super V, ? extends W> f,
Ops ops) {
return new Data<>(ops, key, f.apply(value));
}
}
public static PrefixTree empty(Ops ops) {
return new Empty<>(ops);
}
private static BiFunction flip(BiFunction f) {
return (a, b) -> f.apply(b, a);
}
final Ops ops;
private PrefixTree(Ops ops) {
this.ops = ops;
}
public abstract Stream> entries();
public abstract PrefixTree insert(K key, V value, BiFunction super V, ? super V, ? extends V> combine);
public abstract PrefixTree map(Function super V, ? extends W> f, Ops ops);
public final PrefixTree map(Function super V, ? extends V> f) {
return map(f, ops);
}
abstract PrefixTree insertInside(K key, V value, BiFunction super V, ? super V, ? extends V> combine);
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy