/**
*
*/
package it.univr.di.labeledvalue;
import java.util.Arrays;
import java.util.regex.Pattern;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
/**
* A customizable alphabet, where elements are strings. Each element has an index (integer) that can be used to retrieve the element.
*
* On 2017-4-13 it turns out that an A-Label introduced in the previous papers is not just a name of a node representing an upper-case letter (used in Morris's
* rules), but it must be a set of upper-case letters.
* Moreover, such upper-case letters represent node name and, therefore, they should be strings instead of letters.
*
* Class {@link ALabel} represents an A-Label. Such class needs an alphabet for building labels, {@link ALabelAlphabet}.
* A user can build a proper alphabet that can be used by {@link ALabel} for building A-Label(s).
*
* @author posenato
*/
public class ALabelAlphabet {
/**
* ALetter makes simpler to check if a node name is appropriate.
* ALabelAlphabet is built over ALetter.
*
* @author posenato
*/
public static class ALetter implements Comparable {
/**
*
*/
public final String name;
/**
* @param s
*/
public ALetter(String s) {
if (s == null || s.isEmpty())
throw new IllegalArgumentException("A ALetter cannot be null or empty");
if (!Pattern.matches(ALabelAlphabet.ALETTER_RANGE, s))
throw new IllegalArgumentException("The argument " + s + " must be in the regular-expression range: " + ALabelAlphabet.ALETTER_RANGE);
this.name = s;
}
@Override
public int compareTo(ALetter o) {
return this.name.compareTo(o.name);
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof ALetter))
return false;
return this.name.equals(((ALetter) o).name);
}
@Override
public int hashCode() {
return this.name.hashCode();
}
@Override
public String toString() {
return this.name;
}
}
/**
* A-Letter.
*/
public static final String ALETTER = "A-Za-z0-9_ωΩ? ";// α-μ_
/**
* A-Letter range.
*/
public static final String ALETTER_RANGE = "[" + ALETTER + "]+";
/**
* Default value for not found index.
*/
public static ALetter DEFAULT_ALETTER_RET_VALUE = null;
/**
* Default value for not found name.
*/
public static final byte DEFAULT_BYTE_RET_VALUE = -1;
/**
* The empty a-letter.
*/
private static ALetter[] EMPTY_ALPHABET = {};
/**
* Maximum size for the alphabet.
* Such limitation is dictated by the ALabel class implementation.
*/
public static final byte MAX_ALABELALPHABET_SIZE = ALabel.MAX_ALABELALPHABET_SIZE;
@SuppressWarnings({ "unused" })
private static final long serialVersionUID = 1L;
/**
* The number of valid entries in {@link #value}.
*/
private byte size;
/**
* The aletters of this alphabet.
*
* Such array does not contain holes.
*/
private ALetter[] value;
/**
* In order to speed up the #index method, a map int2Aletter is maintained.
*/
private Object2IntOpenHashMap value2int;
/**
* Default constructor.
*/
public ALabelAlphabet() {
this.size = 0;
this.value = EMPTY_ALPHABET;
this.value2int = new Object2IntOpenHashMap<>();
this.value2int.defaultReturnValue(DEFAULT_BYTE_RET_VALUE);
}
/**
* @param size1 initial size of alphabet
*/
public ALabelAlphabet(int size1) {
this();
if (size1 > MAX_ALABELALPHABET_SIZE)
throw new IllegalArgumentException("Dimension exceeds the maximum capacity!");
this.value = new ALetter[size1];
this.value2int = new Object2IntOpenHashMap<>(size1);
this.value2int.defaultReturnValue(DEFAULT_BYTE_RET_VALUE);
}
/**
* Cleans the map.
*/
public void clear() {
for (int i = this.size; i-- != 0;) {
this.value[i] = null;
}
this.value2int.clear();
this.size = 0;
}
/**
* @param v
* @return true if v is present, false otherwise
*/
public boolean containsValue(ALetter v) {
return index(v) >= 0;
}
/**
* @param k the index of the wanted a-letter
* @return the a-letter associated to index k, {@link #DEFAULT_ALETTER_RET_VALUE} it it does not exist
*/
public ALetter get(final byte k) {
if (k < 0 || k >= this.size)
return DEFAULT_ALETTER_RET_VALUE;
return this.value[k];
}
/**
* @param name
* @return the index associated to name if it exists, {@link #DEFAULT_BYTE_RET_VALUE} otherwise
*/
public byte index(final ALetter name) {
return (byte) this.value2int.getInt(name);
}
/**
* @return true is this does not contain a-letter
*/
public boolean isEmpty() {
return this.size == 0;
}
/**
* Puts the element v in the map if not present.
*
* @param v a non-null a-letter
* @return the index associate to the element v if {@code v} is present, {@value #DEFAULT_BYTE_RET_VALUE} if {@code v} is null
* @throws IllegalArgumentException if there are already {@link #MAX_ALABELALPHABET_SIZE} elements in the map
*/
public byte put(ALetter v) {
if (v == null)
return DEFAULT_BYTE_RET_VALUE;
byte k = this.index(v);
if (k >= 0) {
return k;
}
if (this.size == this.value.length) {
if (this.size == MAX_ALABELALPHABET_SIZE) {
throw new IllegalArgumentException("It is not possible to add new elements!");
}
this.value = Arrays.copyOf(this.value, this.size + MAX_ALABELALPHABET_SIZE / 4);
}
k = this.size;
this.value[k] = v;
this.value2int.put(v, k);
this.size++;
// Arrays.sort(this.value, 0, this.size); NON SI POSSONO ordinare perché gli indici possono essere già stati usati!
// return this.index(v);
return k;
}
/**
* @return the current size of this alphabet
*/
public int size() {
return this.size;
}
@Override
public String toString() {
return Arrays.toString(this.value);
}
}