/*
* Copyright: Almende B.V. (2014), Rotterdam, The Netherlands
* License: The Apache Software License, Version 2.0
*/
package com.almende.dht;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* The Class RoutingTable.
*/
public class RoutingTable {
private Key myKey;
private Bucket[] table = new Bucket[Constants.BITLENGTH];
/**
* Instantiates a new routing table.
*/
public RoutingTable() {}
/**
* Instantiates a new routing table.
*
* @param key
* the key
*/
public RoutingTable(final Key key) {
this.myKey = key;
for (int i = 0; i < Constants.BITLENGTH; i++) {
table[i] = new Bucket(i + 1);
}
}
/**
* Gets the bucket.
*
* @param key
* the key
* @param offset
* the offset
* @return the bucket
*/
public Bucket getBucket(final Key key, final int offset) {
final Key dist = myKey.dist(key);
final int rank = dist.rank()-1;
final int index = rank + offset;
if (index >= 0 && index < Constants.BITLENGTH) {
return table[index];
} else {
if (rank < -1 || rank >= Constants.BITLENGTH){
throw new IllegalArgumentException("Incorrect bucket index requested:"+rank);
}
return null;
}
}
/**
* Gets the bucket.
*
* @param key
* the key
* @return the bucket
*/
public Bucket getBucket(final Key key) {
return getBucket(key, 0);
}
/**
* Seen node.
*
* @param node
* the node
*/
public void seenNode(final Node node) {
if (node == null) {
return;
}
final Bucket bucket = getBucket(node.getKey());
bucket.seenNode(node);
}
/**
* Gets the closest nodes.
*
* @param near
* the near
* @param limit
* the limit
* @param filter
* the filter
* @return the closest nodes
*/
public List<Node> getClosestNodes(final Key near, final int limit,
final Collection<Key> filter) {
Node[] result = new Node[0];
int offset = 0;
boolean[] edges = new boolean[2];
edges[0] = false;
edges[1] = false;
Bucket bucket = null;
while (result.length < limit && !(edges[0] && edges[1])) {
bucket = getBucket(near, offset);
if (bucket != null) {
Node[] res = bucket.getClosestNodes(near,
limit - result.length, filter).toArray(new Node[0]);
if (res.length > 0) {
final Node[] oldres = result;
result = new Node[oldres.length + res.length];
for (int i = 0; i < oldres.length; i++) {
result[i] = oldres[i];
}
for (int i = 0; i < res.length; i++) {
result[i + oldres.length] = res[i];
}
}
} else {
if (offset <= 0) {
edges[0] = true;
} else {
edges[1] = true;
}
}
offset = offset <= 0 ? -offset + 1 : -offset;
}
return Arrays.asList(result);
}
/**
* Gets the closest nodes.
*
* @param near
* the near
* @param limit
* the limit
* @param filter
* the filter
* @return the closest nodes
*/
public List<Node> getClosestNodes(final Key near, final int limit,
final Key[] filter) {
final Set<Key> set = new HashSet<Key>(filter.length);
Collections.addAll(set, filter);
return getClosestNodes(near, limit, set);
}
/**
* Gets the closest nodes.
*
* @param near
* the near
* @param limit
* the limit
* @return the closest nodes
*/
public List<Node> getClosestNodes(final Key near, final int limit) {
return getClosestNodes(near, limit, Collections.<Key> emptySet());
}
/**
* Gets the closest nodes.
*
* @param near
* the near
* @return the closest nodes
*/
public List<Node> getClosestNodes(final Key near) {
return getClosestNodes(near, Integer.MAX_VALUE,
Collections.<Key> emptySet());
}
/**
* Gets the stale buckets, needed to be refreshed.
*
* @return the stale buckets
*/
public List<Bucket> getStaleBuckets() {
final ArrayList<Bucket> result = new ArrayList<Bucket>();
for (Bucket bucket : table) {
if (bucket.isStale()) {
result.add(bucket);
}
}
return result;
}
/**
* Gets the filled buckets (at least 1 node), useful for statistics and debugging.
*
* @return the filled buckets
*/
public List<Bucket> getFilledBuckets(){
final ArrayList<Bucket> result = new ArrayList<Bucket>();
for (Bucket bucket : table) {
if (bucket.size()>0) {
result.add(bucket);
}
}
return result;
}
/**
* Gets the table.
*
* @return the table
*/
public Bucket[] getTable() {
return table;
}
/**
* Sets the table.
*
* @param table
* the new table
*/
public void setTable(Bucket[] table) {
this.table = table;
}
/**
* Gets the my key.
*
* @return the my key
*/
public Key getMyKey() {
return myKey;
}
/**
* Sets the my key.
*
* @param myKey
* the new my key
*/
public void setMyKey(final Key myKey) {
this.myKey = myKey;
}
@Override
public String toString(){
return "key:"+getMyKey()+" : "+getFilledBuckets().size()+ " buckets filled.";
}
}