package com.hida.model;
import java.security.SecureRandom;
import java.util.Iterator;
import java.util.Set;
import java.util.LinkedHashSet;
import java.util.stream.LongStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An abstract Id generator used to create Pids
*
* @author Brittany Cruz
* @author lruffin
*/
public abstract class IdGenerator {
protected long maxPermutation_;
/**
* Creates and new random number generator to aid in the production of
* non-deterministic ids.
*/
private static final SecureRandom rng_ = new SecureRandom();
/**
* LOGGER; logfile to be stored in resource folder
*/
private static final Logger LOGGER = LoggerFactory.getLogger(IdGenerator.class);
/**
* The string that will be at the front of every id
*/
protected String prefix_;
public IdGenerator(String prefix) {
this.prefix_ = prefix;
}
public abstract long getMaxPermutation();
public abstract void incrementPid(Pid pid);
protected abstract String longToName(long value);
protected abstract long PidToLong(Pid pid);
/**
* Creates Pids without regard to a natural order.
*
* @param amount The number of Pids to be created
* @return A set of Pids
*/
public Set<Pid> randomMint(long amount) {
// checks to see if its possible to produce or add requested amount of
if (maxPermutation_ < amount || amount < 0) {
throw new NotEnoughPermutationsException(maxPermutation_, amount);
}
// create sets to guarantee unique membership
Set<Long> longSet = new LinkedHashSet<>();
Set<Pid> pidSet = new LinkedHashSet<>();
// create a LongStream of size amount, bound by [0, maxPermutation_)
LongStream longStream = rng_.longs(amount, 0, maxPermutation_);
// iterate through every member of the stream
Iterator<Long> longIter = longStream.iterator();
while (longIter.hasNext()) {
// try to add the value to the set, if it can't be added increment it
long value = longIter.next();
while (!longSet.add(value)) {
value = (value + 1) % maxPermutation_;
}
// if a value can be added to longSet then it can be added to PidSet
Pid pid = new Pid(longToName(value));
pidSet.add(pid);
}
return pidSet;
}
/**
* Creates Pids in ascending order
*
* @param amount The number of PIDs to be created
* @return A set of Pids
*/
public Set<Pid> sequentialMint(long amount) {
// checks to see if its possible to produce or add requested amount of
if (maxPermutation_ < amount || amount < 0) {
throw new NotEnoughPermutationsException(maxPermutation_, amount);
}
// create a set to contain Pids
Set<Pid> pidSet = new LinkedHashSet<>();
long startVal = 0;
for (int i = 0; i < amount; i++) {
Pid newPid = new Pid(longToName(startVal));
pidSet.add(newPid);
startVal++;
LOGGER.trace("Generated Custom Sequential ID: {}", newPid);
}
return pidSet;
}
/**
* Creates Pids in ascending order starting at an arbitrary value.
*
* @param amount The number of Pids to be created
* @param startingValue The value to start sequentially generating Pids
* @return A set of Pids
*/
public Set<Pid> sequentialMint(long amount, long startingValue) {
if (maxPermutation_ < amount) {
throw new NotEnoughPermutationsException(maxPermutation_, amount);
}
// create a set to contain Pids
Set<Pid> pidSet = new LinkedHashSet<>();
for (int i = 0; i < amount; i++) {
Pid newPid = new Pid(longToName(startingValue));
pidSet.add(newPid);
startingValue = (startingValue + 1) % maxPermutation_;
LOGGER.trace("Generated Custom Sequential ID: {}", newPid);
}
return pidSet;
}
/**
* Checks whether or not the prefix is valid.
*
* @param prefix The string that will be at the front of every id.
* @return true if it contains numbers and letters and does not exceed 20
* characters.
*/
public static final boolean isValidPrefix(String prefix) {
return prefix.matches("^[0-9a-zA-Z]*$") && prefix.length() <= 20;
}
/**
* Checks whether or not the requested amount is valid.
*
* @param amount The amount of ids requested.
* @return True if amount is non-negative.
*/
public static final boolean isValidAmount(long amount) {
return amount >= 0;
}
/**
* Checks whether or not the requested root length is valid
*
* @param rootLength Designates the length of the id's root.
* @return True if rootLength is non-negative and less than or equal to 10.
*/
public static final boolean isValidRootLength(long rootLength) {
return rootLength >= 0 && rootLength <= 10;
}
/**
* Checks whether or not the given charMap is valid for this minter.
*
* @param charMap The mapping used to describe range of possible characters
* at each of the id's root's digits.
* @return True if charMap only contains the characters: 'd', 'l', 'u', 'm',
* or 'e'.
*/
public static final boolean isValidCharMap(String charMap) {
return charMap.matches("^[dlume]+$");
}
/* typical getter and setter methods */
public String getPrefix() {
return prefix_;
}
public void setPrefix(String Prefix) {
this.prefix_ = Prefix;
}
}