com.bol.ipresource.etree.NestedIntervalMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ip-resource Show documentation
Show all versions of ip-resource Show documentation
High performance, low memory IP library (originally developed for RIPE NCC Whois server)
package com.bol.ipresource.etree;
import com.bol.ipresource.ip.Interval;
import com.bol.ipresource.util.CollectionHelper;
import com.bol.ipresource.util.Validate;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* A map with intervals as keys. Intervals are only allowed to intersect if they
* are fully contained in the other interval (in other words, siblings are not
* allowed to intersect, but nesting is ok).
*
* Note that this implementation is not synchronized. If
* multiple threads access a map concurrently, and at least one of the threads
* modifies the map structurally, it must be synchronized externally. (A
* structural modification is any operation that adds or deletes one or more
* mappings; merely changing the value associated with an existing key is not a
* structural modification.) This is typically accomplished by synchronizing on
* some object that naturally encapsulates the map.
*
* @param the type of the interval (must implement {@link Interval}).
* @param the type of the values to store.
*/
public final class NestedIntervalMap, V> implements IntervalMap {
private final ChildNodeMap children;
/**
* Construct an empty {@link NestedIntervalMap}.
*/
public NestedIntervalMap() {
this.children = new ChildNodeTreeMap<>();
}
/**
* Construct a new {@link NestedIntervalMap} with (key, values) of
* source
copied.
*
* @param source the source to copy.
*/
public NestedIntervalMap(NestedIntervalMap source) {
this.children = new ChildNodeTreeMap<>(source.children);
}
@Override
public void put(K key, V value) {
Validate.notNull(key);
Validate.notNull(value);
children.addChild(new InternalNode<>(key, value));
}
@Override
public void remove(K key) {
Validate.notNull(key);
children.removeChild(key);
}
@Override
public void remove(K key, V value) {
Validate.notNull(key);
Validate.notNull(value);
if (value.equals(CollectionHelper.uniqueResult(findExact(key)))) {
remove(key);
}
}
@Override
public List findFirstLessSpecific(K key) {
Validate.notNull(key);
InternalNode node = internalFindFirstLessSpecific(key);
return mapToValues(node);
}
@Override
public List findAllLessSpecific(K key) {
Validate.notNull(key);
return mapToValues(internalFindAllLessSpecific(key));
}
@Override
public List findExactAndAllLessSpecific(K key) {
Validate.notNull(key);
return mapToValues(internalFindExactAndAllLessSpecific(key));
}
@Override
public List findExact(K key) {
Validate.notNull(key);
InternalNode node = internalFindExact(key);
return mapToValues(node);
}
@Override
public List findExactOrFirstLessSpecific(K key) {
Validate.notNull(key);
return mapToValues(internalFindExactOrFirstLessSpecific(key));
}
@Override
public List findFirstMoreSpecific(K key) {
Validate.notNull(key);
return mapToValues(internalFindFirstMoreSpecific(key));
}
@Override
public List findAllMoreSpecific(K key) {
Validate.notNull(key);
return mapToValues(internalFindAllMoreSpecific(key));
}
@Override
public List findExactAndAllMoreSpecific(K key) {
Validate.notNull(key);
return mapToValues(internalFindExactAndAllMoreSpecific(key));
}
/**
* Clears all values from the map.
*/
@Override
public void clear() {
children.clear();
}
@Override
public boolean equals(Object obj) {
return this == obj || obj != null && getClass() == obj.getClass() && this.children.equals(((NestedIntervalMap, ?>) obj).children);
}
@Override
public int hashCode() {
return children.hashCode();
}
@Override
public String toString() {
return children.toString();
}
private List mapToValues(InternalNode node) {
if (node == null) {
return Collections.emptyList();
}
return Collections.singletonList(node.getValue());
}
private List mapToValues(Collection> nodes) {
List result = Lists.newArrayListWithExpectedSize(nodes.size());
for (InternalNode node : nodes) {
result.add(node.getValue());
}
return result;
}
private InternalNode internalFindExactOrFirstLessSpecific(K range) {
List> list = internalFindExactAndAllLessSpecific(range);
return list.isEmpty() ? null : list.get(list.size() - 1);
}
private InternalNode internalFindFirstLessSpecific(K range) {
List> list = internalFindAllLessSpecific(range);
if (list.isEmpty()) {
return null;
} else {
return list.get(list.size() - 1);
}
}
private List> internalFindAllLessSpecific(K range) {
List> result = internalFindExactAndAllLessSpecific(range);
if (result.isEmpty()) {
return result;
}
InternalNode last = result.get(result.size() - 1);
if (last.getInterval().equals(range)) {
return result.subList(0, result.size() - 1);
} else {
return result;
}
}
private List> internalFindExactAndAllLessSpecific(K range) {
List> result = new ArrayList<>();
children.findExactAndAllLessSpecific(result, range);
return result;
}
private InternalNode internalFindExact(K range) {
List> exactAndAllLessSpecific = internalFindExactAndAllLessSpecific(range);
if (exactAndAllLessSpecific.isEmpty()) {
return null;
}
InternalNode last = exactAndAllLessSpecific.get(exactAndAllLessSpecific.size() - 1);
if (last.getInterval().equals(range)) {
return last;
}
return null;
}
private List> internalFindFirstMoreSpecific(K range) {
List> result = new ArrayList<>();
InternalNode container = internalFindExactOrFirstLessSpecific(range);
if (container == null) {
children.findFirstMoreSpecific(result, range);
} else {
container.getChildren().findFirstMoreSpecific(result, range);
}
return result;
}
private List> internalFindAllMoreSpecific(K range) {
List> result = internalFindExactAndAllMoreSpecific(range);
if (!result.isEmpty() && result.get(0).getInterval().equals(range)) {
return result.subList(1, result.size());
} else {
return result;
}
}
private List> internalFindExactAndAllMoreSpecific(K range) {
List> result = new ArrayList<>();
InternalNode containing = internalFindExactOrFirstLessSpecific(range);
if (containing == null) {
children.findExactAndAllMoreSpecific(result, range);
} else {
if (containing.getInterval().equals(range)) {
result.add(containing);
}
containing.getChildren().findExactAndAllMoreSpecific(result, range);
}
return result;
}
public abstract static class Key> {
private final K key;
public Key(K key) {
Validate.notNull(key);
this.key = key;
}
public K getKey() {
return key;
}
@Override
public int hashCode() {
return key.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Key> that = (Key>) obj;
return this.key.equals(that.key);
}
@Override
public String toString() {
return "IpResource(" + key + ")";
}
}
}