io.datakernel.aggregation.RangeTree Maven / Gradle / Ivy
/*
* Copyright (C) 2015 SoftIndex LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.datakernel.aggregation;
import java.util.*;
import static java.util.Collections.*;
public final class RangeTree {
public static final class Segment {
private final LinkedHashSet set = new LinkedHashSet<>();
private final LinkedHashSet closing = new LinkedHashSet<>();
public Set getSet() {
return unmodifiableSet(set);
}
public Set getClosingSet() {
return unmodifiableSet(closing);
}
private static Segment cloneOf(Segment segment) {
Segment result = new Segment<>();
result.set.addAll(segment.set);
result.closing.addAll(segment.closing);
return result;
}
@Override
public String toString() {
return set + "; " + closing;
}
}
private final TreeMap> segments;
public SortedMap> getSegments() {
return unmodifiableSortedMap(segments);
}
private RangeTree(TreeMap> segments) {
this.segments = segments;
}
private RangeTree() {
this(new TreeMap>());
}
public static RangeTree create() {return new RangeTree();}
public static RangeTree cloneOf(RangeTree source) {
RangeTree result = create();
result.segments.putAll(source.segments);
for (Map.Entry> entry : result.segments.entrySet()) {
entry.setValue(Segment.cloneOf(entry.getValue()));
}
return result;
}
private Segment ensureSegment(K key) {
Segment segment = segments.get(key);
if (segment == null) {
Map.Entry> prevEntry = segments.lowerEntry(key);
segment = new Segment<>();
if (prevEntry != null)
segment.set.addAll(prevEntry.getValue().set);
segments.put(key, segment);
}
return segment;
}
public void put(K lower, K upper, V value) {
ensureSegment(lower);
ensureSegment(upper).closing.add(value);
SortedMap> subMap = segments.subMap(lower, upper);
for (Map.Entry> entry : subMap.entrySet()) {
entry.getValue().set.add(value);
}
}
@SuppressWarnings({"ConstantConditions", "EqualsBetweenInconvertibleTypes"})
public boolean remove(K lower, K upper, V value) {
boolean removed = false;
ensureSegment(lower);
removed |= ensureSegment(upper).closing.remove(value);
Map.Entry> prevEntry = segments.lowerEntry(lower);
Segment prev = prevEntry != null ? prevEntry.getValue() : null;
Iterator> it = segments.subMap(lower, true, upper, true).values().iterator();
while (it.hasNext()) {
Segment segment = it.next();
removed |= segment.set.remove(value);
if (segment.closing.isEmpty() && segment.set.equals(prev != null ? prev.set : emptySet())) {
it.remove();
} else {
prev = segment;
}
}
return removed;
}
public Set get(K key) {
LinkedHashSet result = new LinkedHashSet<>();
for (Map.Entry> entry : segments.headMap(key, true).descendingMap().entrySet()) {
result.addAll(entry.getValue().set);
if (!entry.getKey().equals(key)) {
break;
}
result.addAll(entry.getValue().closing);
}
return result;
}
public Set getRange(K lower, K upper) {
LinkedHashSet result = new LinkedHashSet<>();
Map.Entry> floorEntry = segments.floorEntry(lower);
if (floorEntry != null) {
result.addAll(floorEntry.getValue().set);
}
SortedMap> subMap = segments.subMap(lower, true, upper, true);
for (Map.Entry> entry : subMap.entrySet()) {
result.addAll(entry.getValue().set);
result.addAll(entry.getValue().closing);
}
return result;
}
public Set getAll() {
LinkedHashSet result = new LinkedHashSet<>();
for (Segment segment : segments.values()) {
result.addAll(segment.closing);
}
return result;
}
}