All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opendaylight.jsonrpc.hmap.HierarchicalEnumHashMap Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2017 Brocade Communications Systems, Inc. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.jsonrpc.hmap;

import com.google.common.base.Strings;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Implementation of a HierarchicalMap which uses {@link HashMap}
 * internally to perform child lookups and {@link EnumMap} for key-value
 * mapping.
 *
 * @author Richard Kosegi
 *
 */
public final class HierarchicalEnumHashMap, D, I> implements HierarchicalEnumMap {
    private final EnumTreeNode root;
    private final PathCodec pathCodec;

    private HierarchicalEnumHashMap(Class keyType, PathCodec pathSupplier) {
        this.pathCodec = Objects.requireNonNull(pathSupplier);
        this.root = newRootNode(Objects.requireNonNull(keyType));
    }

    public static , D, I> HierarchicalEnumMap create(Class keyType,
            PathCodec pathSupplier) {
        return new HierarchicalEnumHashMap<>(keyType, pathSupplier);
    }

    @Override
    public Optional lookup(P path, K key) {
        EnumTreeNode current = root;
        D value = null;
        final Iterator iterator = pathCodec.serialize(path).iterator();
        if (!iterator.hasNext()) {
            return Optional.ofNullable(root.value(key));
        }
        while (iterator.hasNext()) {
            final I id = iterator.next();
            if (current.value(key) != null) {
                value = current.value(key);
            }
            final Optional> candidate = current.lookupChild(id);
            if (candidate.isPresent()) {
                current = candidate.orElseThrow();
                if (current.value(key) != null) {
                    value = current.value(key);
                }
            } else {
                current = current.appendChild(id);
            }
        }
        return Optional.ofNullable(value);
    }

    @Override
    public D put(P path, K key, D data) {
        EnumTreeNode current = root;
        final Iterator iterator = pathCodec.serialize(path).iterator();
        while (iterator.hasNext()) {
            final I id = iterator.next();
            final Optional> candidate = current.lookupChild(id);
            current = candidate.isPresent() ? candidate.orElseThrow() : current.appendChild(id);
        }
        final D previousValue = current.value(key);
        current.setValue(key, data);
        return previousValue;
    }

    @Override
    public Map toMap(K key) {
        final Map map = new HashMap<>();
        final LinkedList currentPath = new LinkedList<>();
        collectChildren(root, key, currentPath, map);
        return Collections.unmodifiableMap(map);
    }

    private void collectChildren(EnumTreeNode current, K key, LinkedList currentPath, Map map) {
        final LinkedList newPath = new LinkedList<>(currentPath);
        newPath.addLast(current.id());
        final D value = current.value(key);
        if (value != null) {
            final P path = pathCodec.deserialize(newPath);
            map.put(path, value);
        }
        for (final EnumTreeNode child : current.children()) {
            collectChildren(child, key, newPath, map);
        }
    }

    @Override
    public String dump() {
        final StringBuilder sb = new StringBuilder();
        sb.append("\n");
        append(sb, root, 1);
        return sb.toString();
    }

    private void append(StringBuilder sb, EnumTreeNode node, int level) {
        sb.append(Strings.repeat(" ", level * 2));
        sb.append(node.id()).append("[").append(node.allValues()).append("]");
        sb.append("\n");
        for (final EnumTreeNode child : node.children()) {
            append(sb, child, level + 1);
        }
    }

    private static , D> RootTreeNode newRootNode(Class keyType) {
        return new RootTreeNode<>(keyType);
    }

    private static class ChildTreeNode, D> extends AbstractTreeNode {
        ChildTreeNode(I id, Class keyType, EnumTreeNode parent) {
            super(id, keyType);
        }
    }

    private static class RootTreeNode, D> extends AbstractTreeNode {
        RootTreeNode(Class keyType) {
            super(null, keyType);
        }
    }

    private abstract static class AbstractTreeNode, D> implements EnumTreeNode {
        private final I id;
        private final Map> children;
        private final EnumMap value;
        private final Class keyType;

        private AbstractTreeNode(I id, Class keyType) {
            this.id = id;
            this.children = new ConcurrentHashMap<>();
            this.value = new EnumMap<>(keyType);
            this.keyType = keyType;
        }

        @Override
        public I id() {
            return id;
        }

        @Override
        public Collection> children() {
            return Collections.unmodifiableCollection(children.values());
        }

        @Override
        public Optional> lookupChild(I childId) {
            return Optional.ofNullable(children.get(childId));
        }

        @Override
        public void setValue(K key, D data) {
            this.value.put(key, data);
        }

        @Override
        public D value(K key) {
            return value.get(key);
        }

        @Override
        public Map allValues() {
            return Collections.unmodifiableMap(value);
        }

        @Override
        public EnumTreeNode appendChild(I childId) {
            final EnumTreeNode child = newNode(childId, keyType, this);
            children.put(childId, child);
            return child;
        }

        private static , D> EnumTreeNode newNode(I id, Class keyType,
                EnumTreeNode parent) {
            return new ChildTreeNode<>(id, keyType, parent);
        }
    }
}