com.bitvantage.bitvantagetypes.collections.TreeBidirectionalMap.java Source code

Java tutorial

Introduction

Here is the source code for com.bitvantage.bitvantagetypes.collections.TreeBidirectionalMap.java

Source

/*
 * Copyright 2017 Matt Laquidara.
 *
 * 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 com.bitvantage.bitvantagetypes.collections;

import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.Collection;
import java.util.Comparator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.bidimap.TreeBidiMap;

/**
 * Wrapper on the ApacheCommons BidiMap that allows non-comparables and does
 * not allow re-insertion.
 * 
 * @author Matt Laquidara
 */
public class TreeBidirectionalMap<K, V> implements Map<K, V> {

    private final TreeBidiMap<ComparableWrapper<K>, ComparableWrapper<V>> map;
    private final Comparator<K> keyComparator;
    private final Comparator<V> valueComparator;

    public TreeBidirectionalMap(final Comparator<K> keyComparator, final Comparator<V> valueComparator) {
        map = new TreeBidiMap();
        this.keyComparator = keyComparator;
        this.valueComparator = valueComparator;
    }

    @Override
    public int size() {
        return map.size();
    }

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

    @Override
    public boolean containsKey(Object key) {
        return map.containsKey(new ComparableWrapper<>((K) key, keyComparator));
    }

    @Override
    public boolean containsValue(Object value) {
        return map.containsValue(new ComparableWrapper<>((V) value, valueComparator));
    }

    @Override
    public V get(Object key) {
        final ComparableWrapper<V> wrappedValue = map.get(new ComparableWrapper<>((K) key, keyComparator));
        return (wrappedValue == null) ? null : wrappedValue.getWrapped();
    }

    @Override
    public V put(K key, V value) {
        final ComparableWrapper<K> wrappedKey = new ComparableWrapper<>(key, keyComparator);
        final ComparableWrapper<V> wrappedValue = new ComparableWrapper<>(value, valueComparator);

        if (map.containsKey(wrappedKey)) {
            final V preexistingValue = map.get(wrappedKey).getWrapped();
            if (!preexistingValue.equals(value)) {
                throw new IllegalArgumentException(
                        String.format("%s is aready bound to %s.", key, preexistingValue));
            }
            return preexistingValue;
        }
        final ComparableWrapper<V> previous = map.put(wrappedKey, wrappedValue);
        return (previous == null) ? null : previous.getWrapped();
    }

    @Override
    public V remove(Object key) {
        return map.remove(new ComparableWrapper<>((K) key, keyComparator)).getWrapped();
    }

    @Override
    public void putAll(Map<? extends K, ? extends V> m) {
        for (final Entry<? extends K, ? extends V> entry : m.entrySet()) {
            put(entry.getKey(), entry.getValue());
        }
    }

    @Override
    public void clear() {
        map.clear();
    }

    @Override
    public Set<K> keySet() {
        return map.keySet().stream().map((ComparableWrapper<K> k) -> k.getWrapped()).collect(Collectors.toSet());
    }

    @Override
    public Collection<V> values() {
        return map.values().stream().map((ComparableWrapper<V> v) -> v.getWrapped()).collect(Collectors.toSet());
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
        return map.entrySet().stream()
                .map((Entry<ComparableWrapper<K>, ComparableWrapper<V>> e) -> new SimpleImmutableEntry<K, V>(
                        e.getKey().getWrapped(), e.getValue().getWrapped()))
                .collect(Collectors.toSet());
    }

    public K nextKey(final K key) {
        final ComparableWrapper<K> wrappedKey = map.nextKey(new ComparableWrapper<>(key, keyComparator));
        return (wrappedKey == null) ? null : wrappedKey.getWrapped();
    }

    public K previousKey(final K key) {
        final ComparableWrapper<K> wrappedKey = map.previousKey(new ComparableWrapper<>(key, keyComparator));
        return (wrappedKey == null) ? null : wrappedKey.getWrapped();
    }

    public V nextValue(final V value) {
        final ComparableWrapper<V> wrappedValue = map.inverseBidiMap()
                .nextKey(new ComparableWrapper<>(value, valueComparator));
        return (wrappedValue == null) ? null : wrappedValue.getWrapped();
    }

    public V previousValue(final V value) {
        final ComparableWrapper<V> wrappedValue = map.inverseBidiMap()
                .previousKey(new ComparableWrapper<>(value, valueComparator));
        return (wrappedValue == null) ? null : wrappedValue.getWrapped();
    }

    public K getKey(final V value) {
        return map.getKey(new ComparableWrapper<>(value, valueComparator)).getWrapped();
    }

    @RequiredArgsConstructor
    private static class ComparableWrapper<T> implements Comparable<ComparableWrapper<T>> {

        @Getter
        private final T wrapped;
        private final Comparator<T> comparator;

        @Override
        public int compareTo(final ComparableWrapper<T> o) {
            return comparator.compare(wrapped, o.wrapped);
        }

    }

}