package org.simpleflatmapper.map;
import org.simpleflatmapper.jdbc.JdbcColumnKey;
import org.simpleflatmapper.map.mapper.MapperKey;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
public final class SortedMapperCache<K extends FieldKey<K>, M> implements IMapperCache<K, M> {
private static final int BSEARCH_THRESHOLD = 0;
private final Comparator<CacheEntry<K, M>> comparator;
@SuppressWarnings("unchecked")
private final AtomicReference<CacheEntry<K, M>[]> mapperCache = new AtomicReference<CacheEntry<K, M>[]>(new CacheEntry[0]);
public SortedMapperCache(Comparator<MapperKey<K>> comparator) {
this.comparator = comparator == null ? null : new CacheEntryComparator<>(comparator);
}
private static final class CacheEntry<K extends FieldKey<K>, M> {
final MapperKey<K> key;
final M mapper;
CacheEntry(final MapperKey<K> key, final M mapper) {
this.key = key;
this.mapper = mapper;
}
@Override
public String toString() {
return "{" + key +
"," + mapper +
'}';
}
}
@SuppressWarnings("unchecked")
public void add(final MapperKey<K> key, final M mapper) {
CacheEntry<K, M>[] entries;
CacheEntry<K, M>[] newEntries;
do {
entries = mapperCache.get();
for (CacheEntry<K, M> entry : entries) {
if (entry.key.equals(key)) {
// already added
return;
}
}
newEntries = new CacheEntry[entries.length + 1];
System.arraycopy(entries, 0, newEntries, 0, entries.length);
newEntries[entries.length] = new CacheEntry<K, M>(key, mapper);
Arrays.sort(newEntries, comparator);
} while(!mapperCache.compareAndSet(entries, newEntries));
}
@SuppressWarnings("unchecked")
private CacheEntry<K, M>[] insertEntry(CacheEntry<K, M> entry, CacheEntry<K, M>[] entries, int insertionPoint) {
CacheEntry<K, M>[] newEntries = new CacheEntry[entries.length + 1];
System.arraycopy(entries, 0, newEntries, 0, insertionPoint);
newEntries[insertionPoint] = entry;
System.arraycopy(entries, insertionPoint, newEntries, insertionPoint + 1, entries.length - insertionPoint);
return newEntries;
}
public M get(MapperKey<K> key) {
final CacheEntry<K, M>[] entries = mapperCache.get();
return bSearch(key, entries);
}
@Override
public int size() {
return mapperCache.get().length;
}
private M bSearch(MapperKey<K> key, CacheEntry<K, M>[] entries) {
final int i = Arrays.binarySearch(entries, new CacheEntry<K, M>(key, null), comparator);
if (i >= 0) {
return entries[i].mapper;
}
return null;
}
private int findKey(MapperKey<K> key, CacheEntry<K, M>[] entries) {
if (comparator == null || entries.length < BSEARCH_THRESHOLD) {
return findKeyIterativeSearch(key, entries);
} else {
return findKeyBSearch(key, entries);
}
}
private int findKeyIterativeSearch(MapperKey<K> key, CacheEntry<K, M>[] entries) {
return - entries.length - 1;
}
private int findKeyBSearch(MapperKey<K> key, CacheEntry<K, M>[] entries) {
return Arrays.binarySearch(entries, new CacheEntry<K, M>(key, null), comparator);
}
@Override
public String toString() {
return "SortedMapperCache{" + Arrays.toString(mapperCache.get()) +
'}';
}
private static class CacheEntryComparator<K extends FieldKey<K>, M> implements Comparator<CacheEntry<K, M>> {
private final Comparator<MapperKey<K>> comparator;
public CacheEntryComparator(Comparator<MapperKey<K>> comparator) {
this.comparator = comparator;
}
@Override
public int compare(CacheEntry<K, M> o1, CacheEntry<K, M> o2) {
return comparator.compare(o1.key, o2.key);
}
}
public static void main(String[] args) {
final IMapperCache<JdbcColumnKey, Object> mapperCache = CacheType.SARRAY.newCache();
final IMapperCache<JdbcColumnKey, Object> mapperCache2 = CacheType.TS2ARRAY.newCache();
final List<MapperKey<JdbcColumnKey>> mapperKeys = Utils.generateKeys(30, 10);
final Map<MapperKey<JdbcColumnKey>, Object> objects = new HashMap<>();
for(MapperKey<JdbcColumnKey> key : mapperKeys) {
Object o = objects.getOrDefault(key, new Object());
mapperCache.add(key, o);
mapperCache2.add(key, o);
objects.put(key, o);
}
System.out.println("objects = " + objects.size());
System.out.println("mapperCache = " + mapperCache.size());
System.out.println("mapperCache2 = " + mapperCache2.size());
for(MapperKey<JdbcColumnKey> key : mapperKeys) {
Object o = objects.get(key);
if (o != mapperCache.get(key)) {
throw new IllegalArgumentException();
}
if (o != mapperCache2.get(key)) {
throw new IllegalArgumentException();
}
}
}
}