org.opendaylight.yangtools.triemap.LNodeEntries.java Source code

Java tutorial

Introduction

Here is the source code for org.opendaylight.yangtools.triemap.LNodeEntries.java

Source

/*
 * (C) Copyright 2016 Pantheon Technologies, s.r.o. and others.
 *
 * 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 org.opendaylight.yangtools.triemap;

import com.google.common.base.VerifyException;

/**
 * Similar to Scala's ListMap, this is a single-linked list of set of map entries. Aside from the java.util.Set
 * contract, this class fulfills the requirements for an immutable map entryset.
 *
 * @author Robert Varga
 *
 * @param <K> the type of keys
 * @param <V> the type of values
 */
abstract class LNodeEntries<K, V> extends LNodeEntry<K, V> {
    private static final class Single<K, V> extends LNodeEntries<K, V> {
        Single(final K key, final V value) {
            super(key, value);
        }

        @Override
        LNodeEntries<K, V> next() {
            return null;
        }
    }

    private static final class Multiple<K, V> extends LNodeEntries<K, V> {
        // Modified during remove only, otherwise final
        LNodeEntries<K, V> next;

        // Used in remove() only
        Multiple(final LNodeEntries<K, V> entry) {
            this(entry.getKey(), entry.getValue(), null);
        }

        Multiple(final K key, final V value, final LNodeEntries<K, V> next) {
            super(key, value);
            this.next = next;
        }

        @Override
        LNodeEntries<K, V> next() {
            return next;
        }
    }

    LNodeEntries(final K key, final V value) {
        super(key, value);
    }

    static <K, V> LNodeEntries<K, V> map(final K k1, final V v1, final K k2, final V v2) {
        return new Multiple<>(k1, v1, new Single<>(k2, v2));
    }

    /**
     * Return the remainder of this list. Useful for implementing Iterator-like contract. Null indicates there are no
     * more entries.
     *
     * @return Remainder of this list, or null if nothing remains
     */
    abstract LNodeEntries<K, V> next();

    final LNodeEntry<K, V> findEntry(final Equivalence<? super K> equiv, final K key) {
        // We do not perform recursion on purpose here, so we do not run out of stack if the key hashing fails.
        LNodeEntries<K, V> entry = this;
        do {
            if (equiv.equivalent(entry.getKey(), key)) {
                return entry;
            }

            entry = entry.next();
        } while (entry != null);

        return null;
    }

    final LNodeEntries<K, V> insert(final K key, final V value) {
        return new Multiple<>(key, value, this);
    }

    final LNodeEntries<K, V> replace(final LNodeEntry<K, V> entry, final V value) {
        final LNodeEntries<K, V> removed;
        return (removed = remove(entry)) == null ? new Single<>(entry.getKey(), value)
                : new Multiple<>(entry.getKey(), value, removed);
    }

    final LNodeEntries<K, V> remove(final LNodeEntry<K, V> entry) {
        if (entry == this) {
            return next();
        }

        // This will result in a list with a long tail, i.e last entry storing explicit null. Overhead is amortized
        // against the number of entries. We do not retain chains shorter than two, so the worst-case overhead is
        // half-a-reference for an entry.
        final Multiple<K, V> ret = new Multiple<>(this);

        Multiple<K, V> last = ret;
        LNodeEntries<K, V> cur = next();
        while (cur != null) {
            // We cannot use equals() here, as it is wired to key equality and we must never compare entries based on
            // that property. This method is intended to remove a known reference, so identity is what we want.
            if (entry == cur) {
                last.next = cur.next();
                return ret;
            }

            final Multiple<K, V> tmp = new Multiple<>(cur);
            last.next = tmp;
            last = tmp;
            cur = cur.next();
        }

        throw new VerifyException(String.format("Failed to find entry %s", entry));
    }
}