// Copyright 2013 Thomas Müller
// This file is part of MarMoT, which is licensed under GPLv3.
package marmot.util;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Map.Entry;
public class SymbolTable<T> implements Serializable {
private static final long serialVersionUID = 1L;
private Map<T, Integer> toIndex;
private Map<Integer, T> fromIndex;
private boolean bidirectional_;
public SymbolTable(boolean bidirectional, int capacity) {
toIndex = new HashMap<T, Integer>(capacity);
if (bidirectional) {
fromIndex = new HashMap<Integer, T>(capacity);
}
bidirectional_ = bidirectional;
}
public SymbolTable(int capacity) {
this(false, capacity);
}
public SymbolTable() {
this(false, 10);
}
public SymbolTable(SymbolTable<T> symbol_table) {
toIndex = new HashMap<T, Integer>(
(HashMap<T, Integer>) symbol_table.toIndex);
bidirectional_ = symbol_table.bidirectional_;
if (bidirectional_) {
fromIndex = new HashMap<Integer, T>(
(HashMap<Integer, T>) symbol_table.fromIndex);
}
}
public SymbolTable(boolean bidirectional) {
this(bidirectional, 10);
}
public List<Integer> toIndexes(Collection<T> symbols) {
List<Integer> indexes = new ArrayList<Integer>(symbols.size());
for (T symbol : symbols) {
indexes.add(toIndex(symbol));
}
return indexes;
}
public List<T> toSymbols(Collection<Integer> indexes) {
List<T> symbols = new ArrayList<T>(indexes.size());
for (Integer i : indexes) {
symbols.add(toSymbol(i));
}
return symbols;
}
public int toIndex(T symbol) {
return toIndex(symbol, false);
}
public int toIndex(T symbol, int default_index) {
return toIndex(symbol, default_index, false);
}
public int toIndex(T symbol, int default_index, boolean insert) {
if (symbol == null)
throw new NullPointerException();
Integer index = toIndex.get(symbol);
if (index == null) {
if (insert) {
index = toIndex.size();
toIndex.put(symbol, index);
if (bidirectional_) {
fromIndex.put(index, symbol);
}
} else {
return default_index;
}
}
return index;
}
public int toIndex(T symbol, boolean insert) {
int index = toIndex(symbol, -1, insert);
if (index == -1) {
throw new NoSuchElementException(symbol.toString());
}
return index;
}
public T toSymbol(Integer index) {
if (!bidirectional_) {
throw new UnsupportedOperationException("Table is unidirectional!");
}
T t = fromIndex.get(index);
if (t == null) {
throw new NoSuchElementException();
}
return t;
}
public int size() {
assert (!bidirectional_) || (toIndex.size() == fromIndex.size());
return toIndex.size();
}
public String toString() {
return toIndex.toString();
}
public boolean hasSymbol(T object) {
return toIndex.containsKey(object);
}
public Set<Entry<T, Integer>> entrySet() {
return toIndex.entrySet();
}
public Collection<T> getSymbols() {
return toIndex.keySet();
}
public void setBidirectional(boolean bidirectional) {
if (bidirectional_ != bidirectional) {
if (!bidirectional_) {
fromIndex = new HashMap<Integer, T>((int)(toIndex.size() * 1.25));
for (Map.Entry<T, Integer> entry : toIndex.entrySet()) {
fromIndex.put(entry.getValue(), entry.getKey());
}
bidirectional_ = true;
} else {
bidirectional_ = false;
fromIndex = null;
}
}
}
public boolean isBidirectional() {
return bidirectional_;
}
public void insert(T symbol) {
toIndex(symbol, true);
}
public Set<T> keySet() {
return toIndex.keySet();
}
}