org.opendaylight.jsonrpc.hmap.HierarchicalEnumHashMap Maven / Gradle / Ivy
/*
* 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);
}
}
}