package org.fenixedu.bennu.core.json;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
class TableRegistry<T> {
private static final Logger LOGGER = LoggerFactory.getLogger(TableRegistry.class);
private final Table<Class<?>, Class<? extends T>, T> wrapped;
private final Map<Class<?>, Class<? extends T>> defaultValues;
private final Map<Class<?>, Object> adapters;
public TableRegistry(Map<Class<?>, Object> adapters) {
wrapped = HashBasedTable.create();
defaultValues = new ConcurrentHashMap<>();
this.adapters = adapters;
}
@SuppressWarnings("unchecked")
private T getOrCreateRecord(Class<? extends T> recordClass) {
if (adapters.containsKey(recordClass)) {
return (T) adapters.get(recordClass);
}
try {
final T record = recordClass.newInstance();
adapters.put(recordClass, record);
return record;
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException("Can't create instance for type " + recordClass.getName(), e);
}
}
private synchronized T register(Class<?> objectClass, Class<? extends T> recordClass) {
//if (!defaultValues.containsKey(objectClass)) {
// defaultValues.put(objectClass, recordClass);
//}
if (wrapped.contains(objectClass, recordClass)) {
return wrapped.get(objectClass, recordClass);
}
return register(objectClass, recordClass, getOrCreateRecord(recordClass));
}
private synchronized T register(Class<?> objectClass, Class<? extends T> recordClass, T record) {
LOGGER.trace("record : objectClass {} recordClass {} record {}", objectClass, recordClass, record);
wrapped.put(objectClass, recordClass, record);
return record;
}
public T setDefault(Class<?> objectClass, Class<? extends T> recordClass) {
LOGGER.trace("setDefault : objectClass {} recordClass {}", objectClass, recordClass);
defaultValues.put(objectClass, recordClass);
if (!wrapped.contains(objectClass, recordClass)) {
register(objectClass, recordClass);
}
return wrapped.get(objectClass, recordClass);
}
private Class<? extends T> internalGet(Class<?> objectClass) {
if (objectClass == null) {
return null;
}
if (defaultValues.containsKey(objectClass)) {
return defaultValues.get(objectClass);
}
for (Class<?> objectInterface : objectClass.getInterfaces()) {
if (defaultValues.containsKey(objectInterface)) {
return defaultValues.get(objectInterface);
}
}
return internalGet(objectClass.getSuperclass());
}
private T get(Class<?> objectClass) {
final Class<? extends T> defaultValue = internalGet(objectClass);
if (defaultValue == null) {
throw new DefaultNotAvailableException(objectClass);
}
return get(objectClass, defaultValue);
}
public T get(Class<?> objectClass, Class<? extends T> recordClass) {
if (recordClass == null) {
return get(objectClass);
}
return register(objectClass, recordClass);
}
public T lookup(Type objectClass, Type recordClass) {
return wrapped.get(objectClass, recordClass);
}
}