net.ripe.ipresource.etree.NestedIntervalMap.java Source code

Java tutorial

Introduction

Here is the source code for net.ripe.ipresource.etree.NestedIntervalMap.java

Source

/**
 * The BSD License
 *
 * Copyright (c) 2010-2012 RIPE NCC
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *   - Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *   - Neither the name of the RIPE NCC nor the names of its contributors may be
 *     used to endorse or promote products derived from this software without
 *     specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package net.ripe.ipresource.etree;

import org.apache.commons.lang3.Validate;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * A map with intervals as keys. Intervals are only allowed to overlap if they
 * are fully contained in the other interval (in other words, siblings are not
 * allowed to overlap, but nesting is ok).
 * <p/>
 * <strong>Note that this implementation is not synchronized.</strong> If
 * multiple threads access a map concurrently, and at least one of the threads
 * modifies the map structurally, it <i>must</i> 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 <K> the type of the interval (must implement {@link IntervalStrategy}).
 * @param <V> the type of the values to store.
 */
public final class NestedIntervalMap<K, V> implements IntervalMap<K, V> {
    private final ChildNodeMap<K, V> children;

    private final IntervalStrategy<K> strategy;

    /**
     * Construct an empty {@link NestedIntervalMap}.
     */
    public NestedIntervalMap(IntervalStrategy<K> strategy) {
        this.strategy = strategy;
        this.children = new ChildNodeTreeMap<K, V>(strategy);
    }

    /**
     * Construct a new {@link NestedIntervalMap} with (key, values) of
     * <code>source</code> copied.
     *
     * @param source the source to copy.
     */
    public NestedIntervalMap(NestedIntervalMap<K, V> source, IntervalStrategy<K> strategy) {
        this.strategy = strategy;
        this.children = new ChildNodeTreeMap<K, V>(source.children, strategy);
    }

    @Override
    public boolean isEmpty() {
        return children.isEmpty();
    }

    @Override
    public void put(K key, V value) {
        Validate.notNull(key);
        Validate.notNull(value);
        children.addChild(new InternalNode<K, V>(key, value), strategy);
    }

    @Override
    public void remove(K key) {
        Validate.notNull(key);
        children.removeChild(key, strategy);
    }

    @Override
    public void remove(K key, V value) {
        Validate.notNull(key);
        Validate.notNull(value);

        if (value.equals(findExact(key))) {
            remove(key);
        }
    }

    @Override
    public V findFirstLessSpecific(K key) {
        Validate.notNull(key);
        InternalNode<K, V> node = internalFindFirstLessSpecific(key);
        return mapToValue(node);
    }

    @Override
    public List<V> findAllLessSpecific(K key) {
        Validate.notNull(key);
        return mapToValues(internalFindAllLessSpecific(key));
    }

    @Override
    public List<V> findExactAndAllLessSpecific(K key) {
        Validate.notNull(key);
        return mapToValues(internalFindExactAndAllLessSpecific(key));
    }

    @Override
    public V findExact(K key) {
        Validate.notNull(key);
        InternalNode<K, V> node = internalFindExact(key);
        return node == null ? null : node.getValue();
    }

    @Override
    public V findExactOrFirstLessSpecific(K key) {
        Validate.notNull(key);
        return mapToValue(internalFindExactOrFirstLessSpecific(key));
    }

    @Override
    public List<V> findFirstMoreSpecific(K key) {
        Validate.notNull(key);
        return mapToValues(internalFindFirstMoreSpecific(key));
    }

    @Override
    public List<V> findAllMoreSpecific(K key) {
        Validate.notNull(key);
        return mapToValues(internalFindAllMoreSpecific(key));
    }

    @Override
    public List<V> findExactAndAllMoreSpecific(K key) {
        Validate.notNull(key);
        return mapToValues(internalFindExactAndAllMoreSpecific(key));
    }

    /**
     * Clears all values from the map.
     */
    @Override
    public void clear() {
        children.clear();
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + children.hashCode();
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        NestedIntervalMap<?, ?> that = (NestedIntervalMap<?, ?>) obj;
        return this.children.equals(that.children);
    }

    @Override
    public String toString() {
        return children.toString();
    }

    private V mapToValue(InternalNode<K, V> node) {
        return node == null ? null : node.getValue();
    }

    private List<V> mapToValues(Collection<InternalNode<K, V>> nodes) {
        List<V> result = new ArrayList<V>(nodes.size());
        for (InternalNode<K, V> node : nodes) {
            result.add(node.getValue());
        }
        return result;
    }

    private InternalNode<K, V> internalFindExactOrFirstLessSpecific(K range) {
        List<InternalNode<K, V>> list = internalFindExactAndAllLessSpecific(range);
        return list.isEmpty() ? null : list.get(list.size() - 1);
    }

    private InternalNode<K, V> internalFindFirstLessSpecific(K range) {
        List<InternalNode<K, V>> list = internalFindAllLessSpecific(range);
        if (list.isEmpty()) {
            return null;
        } else {
            return list.get(list.size() - 1);
        }
    }

    private List<InternalNode<K, V>> internalFindAllLessSpecific(K range) {
        List<InternalNode<K, V>> result = internalFindExactAndAllLessSpecific(range);
        if (result.isEmpty()) {
            return result;
        }
        InternalNode<K, V> last = result.get(result.size() - 1);
        if (last.getKey().equals(range)) {
            return result.subList(0, result.size() - 1);
        } else {
            return result;
        }
    }

    private List<InternalNode<K, V>> internalFindExactAndAllLessSpecific(K range) {
        List<InternalNode<K, V>> result = new ArrayList<InternalNode<K, V>>();
        children.findExactAndAllLessSpecific(result, range, strategy);
        return result;
    }

    private InternalNode<K, V> internalFindExact(K range) {
        List<InternalNode<K, V>> exactAndAllLessSpecific = internalFindExactAndAllLessSpecific(range);
        if (exactAndAllLessSpecific.isEmpty()) {
            return null;
        }
        InternalNode<K, V> last = exactAndAllLessSpecific.get(exactAndAllLessSpecific.size() - 1);
        if (last.getKey().equals(range)) {
            return last;
        }
        return null;
    }

    private List<InternalNode<K, V>> internalFindFirstMoreSpecific(K range) {
        List<InternalNode<K, V>> result = new ArrayList<InternalNode<K, V>>();
        InternalNode<K, V> container = internalFindExactOrFirstLessSpecific(range);
        if (container == null) {
            children.findFirstMoreSpecific(result, range, strategy);
        } else {
            container.getChildren().findFirstMoreSpecific(result, range, strategy);
        }
        return result;
    }

    private List<InternalNode<K, V>> internalFindAllMoreSpecific(K range) {
        List<InternalNode<K, V>> result = internalFindExactAndAllMoreSpecific(range);
        if (!result.isEmpty() && result.get(0).getKey().equals(range)) {
            return result.subList(1, result.size());
        } else {
            return result;
        }
    }

    private List<InternalNode<K, V>> internalFindExactAndAllMoreSpecific(K range) {
        List<InternalNode<K, V>> result = new ArrayList<InternalNode<K, V>>();
        InternalNode<K, V> containing = internalFindExactOrFirstLessSpecific(range);
        if (containing == null) {
            children.findExactAndAllMoreSpecific(result, range, strategy);
        } else {
            if (containing.getKey().equals(range)) {
                result.add(containing);
            }
            containing.getChildren().findExactAndAllMoreSpecific(result, range, strategy);
        }
        return result;
    }

    public abstract static class Key<K extends IntervalStrategy<K>> {
        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 + ")";
        }
    }
}