blob: adfec6ce85f23efca779a63827c7d356f072846f [file] [log] [blame]
/*
* Copyright (c) 2017 CEA.
* 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
*
* Contributors:
* CEA - initial API and implementation
*/
package org.eclipse.sensinact.gateway.util.tree;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* A list of {PathNode}s
*
* @author <a href="mailto:christophe.munilla@cea.fr">Christophe Munilla</a>
*/
public class PathNodeList<P extends PathNode<P>> implements Iterable<P> {
private final Object lock = new Object();
private PathNodeBucket<?>[] table;
private int threshold = 2;
private int size = 0;
private int length = 5;
/**
* Constructor
*/
protected PathNodeList() {
clear();
}
/**
*
*/
public void clear() {
this.length = 5;
this.size = 0;
this.table = new PathNodeBucket<?>[length];
}
// /**
// */
// public P get(String nodeName)
// {
// if (nodeName == null)
// {
// return null;
// }
// int hash = nodeName.hashCode();
// hash ^= (hash >>> 20) ^ (hash >>> 12);
// hash ^= (hash >>> 7) ^ (hash >>> 4);
//
// synchronized(lock)
// {
// //search for exact match
// for (PathNodeBucket<P> b = (PathNodeBucket<P>)
// table[(hash & (length - 2)) + 1];
// b != null; b = b.next)
// {
// if (b.node.nodeName.equals(nodeName))
// {
// return b.node;
// }
// }
// //search for pattern match
// for (PathNodeBucket<P> b = (PathNodeBucket<P>)table[0];
// b != null; b = b.next)
// {
// if (b.node.equals(nodeName))
// {
// return b.node;
// }
// }
// }
// return null;
// }
/**
* @param nodeName
* @return
*/
public P getStrictNode(String nodeName) {
if (nodeName == null) {
return null;
}
int hash = nodeName.hashCode();
hash ^= (hash >>> 20) ^ (hash >>> 12);
hash ^= (hash >>> 7) ^ (hash >>> 4);
synchronized (lock) {
//search for exact match
for (PathNodeBucket<P> b = (PathNodeBucket<P>) table[(hash & (length - 2)) + 1]; b != null; b = b.next) {
if (b.node.nodeName.equals(nodeName)) {
return b.node;
}
}
//search for strict match into pattern nodes
for (PathNodeBucket<P> b = (PathNodeBucket<P>) table[0]; b != null; b = b.next) {
if (b.node.nodeName.equals(nodeName)) {
return b.node;
}
}
}
return null;
}
/**
* @param nodeName
* @return
*/
public List<P> getPatternNodes(String nodeName) {
if (nodeName == null) {
return Collections.<P>emptyList();
}
List<P> list = new ArrayList<P>();
synchronized (lock) {
//search for pattern match
for (PathNodeBucket<P> b = (PathNodeBucket<P>) table[0]; b != null; b = b.next) {
if (b.node.equals(nodeName)) {
list.add(b.node);
}
}
}
return list;
}
/**
* Maps the specified key to the specified value.
*
* @param node the PathNode to add.
* @return the added PathNode or null if an error
* occurred.
*/
public P add(P node) {
if (node == null) {
return null;
}
synchronized (lock) {
if (++size > threshold) {
PathNodeBucket<?>[] oldTable = table;
int oldCapacity = length;
length = oldCapacity * 2;
table = new PathNodeBucket<?>[length];
threshold = (length >> 1) + (length >> 2);
table[0] = oldTable[0];
for (int j = 1; j < oldCapacity; j++) {
PathNodeBucket<P> b = (PathNodeBucket<P>) oldTable[j];
while (b != null) {
PathNodeBucket<P> p = b;
b = b.next;
p.next = null;
add(p);
}
}
}
add(new PathNodeBucket<P>(node));
}
return node;
}
/**
* Adds the PathNodeBucket passed as parameter
*
* @param b the PathNodeBucket to be added
*/
private void add(PathNodeBucket<P> b) {
int index = 0;
if (!b.node.isPattern) {
int hash = b.hash;
hash ^= (hash >>> 20) ^ (hash >>> 12);
hash ^= (hash >>> 7) ^ (hash >>> 4);
index = (hash & (length - 2)) + 1;
}
PathNodeBucket<P> bu = null;
if ((bu = (PathNodeBucket<P>) table[index]) == null) {
table[index] = b;
} else {
for (; bu.next != null; bu = bu.next) ;
bu.next = b;
}
}
/**
* Removes the PathNode whose name is passed as
* parameter.
*
* @param nodeName the name of the PathNode to be
* removed
* @return the remove PathNode
*/
public P remove(String nodeName) {
if (nodeName == null) {
return null;
}
int hash = nodeName.hashCode();
hash ^= (hash >>> 20) ^ (hash >>> 12);
hash ^= (hash >>> 7) ^ (hash >>> 4);
synchronized (lock) {
int index = (hash & (table.length - 2)) + 1;
//search for name
for (PathNodeBucket<P> b = (PathNodeBucket<P>) table[index], prev = null; b != null; prev = b, b = b.next) {
if (nodeName.equals(b.node.nodeName)) {
if (prev == null) {
table[index] = b.next;
} else {
prev.next = b.next;
}
size--;
return b.node;
}
}
//search for patterns
for (PathNodeBucket<P> b = (PathNodeBucket<P>) table[0], prev = null; b != null; prev = b, b = b.next) {
if (nodeName.equals(b.node.nodeName)) {
if (prev == null) {
table[index] = b.next;
} else {
prev.next = b.next;
}
size--;
return b.node;
}
}
}
return null;
}
/**
* Returns this PathNodeList's size
*
* @return the size of this PathNodeList
*/
public int size() {
return size;
}
/**
* @param parent
* @return
*/
public <N extends ImmutablePathNode<N>> ImmutablePathNodeList<N> immutable(final N parent) {
return new ImmutablePathNodeList<N>(new ArrayList<ImmutablePathNodeBucket<N>>() {
private static final long serialVersionUID = 1L;
public List<ImmutablePathNodeBucket<N>> addAll() {
int index = 0;
int length = PathNodeList.this.table.length;
for (; index < length; index++) {
if (PathNodeList.this.table[index] == null) {
super.add(null);
continue;
}
super.add(index, PathNodeList.this.table[index].<N>immutable((Class<N>) parent.getClass(), parent));
}
return this;
}
}.addAll());
}
/**
* @inheritDoc
* @see java.lang.Iterable#iterator()
*/
public Iterator<P> iterator() {
return new Iterator<P>() {
int position = -1;
PathNodeBucket<P> bucket = null;
P node = null;
/**
* @inheritDoc
*
* @see java.util.Iterator#hasNext()
*/
@Override
public boolean hasNext() {
if (position == -1) {
next();
}
return node != null;
}
/**
* @inheritDoc
*
* @see java.util.Iterator#next()
*/
@Override
public P next() {
P current = node;
if (bucket != null) {
bucket = bucket.next;
}
if (bucket == null) {
while (++position < length && (bucket = (PathNodeBucket<P>) table[position]) == null) ;
}
node = bucket == null ? null : bucket.node;
return current;
}
/**
* @inheritDoc
*
* @see java.util.Iterator#remove()
*/
@Override
public void remove() {
//unimplemented
}
};
}
}