/*
* This software copyright by various authors including the RPTools.net
* development team, and licensed under the LGPL Version 3 or, at your option,
* any later version.
*
* Portions of this software were originally covered under the Apache Software
* License, Version 1.1 or Version 2.0.
*
* See the file LICENSE elsewhere in this distribution for license details.
*/
package net.rptools.maptool.client.utilities;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import javax.management.RuntimeErrorException;
import net.rptools.maptool.client.MapTool;
/**
* This class produces a numeric suffix in the range of 1 to 256 intended to
* uniquely identify a token on the map. The class remembers previous token
* names and will never return the same value for any given name. If all
* possible 256 suffixes have been used then the class simply starts over
* but using an offset of 256.
* <p>
* Internally the class uses bytes (almost) exclusively in order to reduce the
* memory footprint. In addition, the class has been designed in a manner to
* reduce running time.
*
* @author Alexander "d4rkAlf" Johansson Werne
*/
public class RandomSuffixFactory {
private static final Random RAND = new Random();
private static final byte[] ALL_BYTE_VALUES;
private final Map<String, SuffixGenerator> tokenSuffixMap;
static {
ALL_BYTE_VALUES = new byte[256];
int suffixNbr = Byte.MIN_VALUE;
do {
ALL_BYTE_VALUES[suffixNbr & 0xFF] = (byte) suffixNbr;
suffixNbr++;
} while (suffixNbr <= Byte.MAX_VALUE);
}
/**
*/
public RandomSuffixFactory() {
this.tokenSuffixMap = new HashMap<String, SuffixGenerator>();
}
/**
* Returns a unique suffix for any given token name. The first batch will
* generate values in the range of 1 to 256. When this first batch has been
* used up it will return values in the range of 257 to 512 and so on and so
* forth.
*
* @param tokenName is used to check if a suffix has already been generated for
* that name
* @return a unique suffix for the given token name
*/
public int nextSuffixForToken(String tokenName) {
SuffixGenerator rs;
if (tokenSuffixMap.containsKey(tokenName)) {
rs = tokenSuffixMap.get(tokenName);
} else {
rs = new SuffixGenerator(tokenName);
}
return rs.nextSuffix();
}
/**
* This inner class is used to encapsulate an array containing all possible
* suffix values in random order, an index and an offset.
*/
private class SuffixGenerator {
private byte[] suffixes;
private short index;
private int offset;
private SuffixGenerator(String tokenName) {
index = 0;
offset = 0;
initializeAndShuffle();
tokenSuffixMap.put(tokenName, this);
}
/**
* Simultaneously initialize and shuffle the suffixes by using the
* inside-out Fisher-Yates shuffle.
*/
private void initializeAndShuffle() {
suffixes = new byte[256];
for (int i = 0; i < ALL_BYTE_VALUES.length; i++) {
int j = RAND.nextInt(i + 1);
if (j != i) {
suffixes[i] = suffixes[j];
}
suffixes[j] = ALL_BYTE_VALUES[i];
}
}
/**
* Gives out the next random suffix in order and increments the index.
*
* @return a value between 1 and 256
*/
private int nextSuffix() {
int unsignedByte = suffixes[index] & 0xFF;
int suffix = unsignedByte + 1;
suffix += (256 * (offset));
if (index < 255) {
index++;
} else {
index = 0;
offset++;
}
return suffix;
}
}
}