/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.engine.cache;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.longs.LongCollection;
import it.unimi.dsi.fastutil.longs.LongList;
import it.unimi.dsi.fastutil.objects.Object2LongMap;
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentMap;
import com.google.common.collect.MapMaker;
import com.opengamma.engine.MemoryUtils;
import com.opengamma.engine.value.ValueSpecification;
import com.opengamma.util.ArgumentChecker;
/**
* Caches value identifiers on top of another identifier source. This class is internally synchronized.
*/
public class CachingIdentifierMap implements IdentifierMap {
private final IdentifierMap _underlying;
// NOTE kirk 2010-08-06 -- This INTENTIONALLY is not an EHCache instance.
// Since getting a remote value specification identifier has to be a super-fast operation
// (probably faster than disk anyway), and the only reason we'd ever want to flush is
// based on low GCs, and the elements are so small, EHCache doesn't actually work here.
// NOTE andrew 2010-09-06 -- Don't use the Google map with weakKeys; it will do comparison by identity which isn't right!
private final Map<ValueSpecification, Long> _specificationToIdentifier = Collections.synchronizedMap(new WeakHashMap<ValueSpecification, Long>());
private final ConcurrentMap<Long, ValueSpecification> _identifierToSpecification = new MapMaker().softValues().makeMap();
public CachingIdentifierMap(IdentifierMap underlying) {
ArgumentChecker.notNull(underlying, "Underlying source");
_underlying = underlying;
}
/**
* Gets the underlying source.
*
* @return the underlying
*/
public IdentifierMap getUnderlying() {
return _underlying;
}
@Override
public long getIdentifier(final ValueSpecification spec) {
Long value = _specificationToIdentifier.get(spec);
if (value != null) {
return value.longValue();
}
long longValue = getUnderlying().getIdentifier(spec);
value = longValue;
_specificationToIdentifier.put(spec, value);
_identifierToSpecification.put(value, spec);
return longValue;
}
@Override
public Object2LongMap<ValueSpecification> getIdentifiers(Collection<ValueSpecification> specs) {
final Object2LongMap<ValueSpecification> identifiers = new Object2LongOpenHashMap<ValueSpecification>();
List<ValueSpecification> cacheMisses = null;
for (ValueSpecification spec : specs) {
Long value = _specificationToIdentifier.get(spec);
if (value != null) {
identifiers.put(spec, value.longValue());
} else {
if (cacheMisses == null) {
cacheMisses = new LinkedList<ValueSpecification>();
}
cacheMisses.add(MemoryUtils.instance(spec));
}
}
if (cacheMisses != null) {
if (cacheMisses.size() == 1) {
final ValueSpecification spec = cacheMisses.get(0);
final long value = getUnderlying().getIdentifier(spec);
final Long keyValue = value;
_specificationToIdentifier.put(spec, keyValue);
_identifierToSpecification.put(keyValue, spec);
identifiers.put(spec, value);
} else {
final Object2LongMap<ValueSpecification> values = getUnderlying().getIdentifiers(cacheMisses);
for (Object2LongMap.Entry<ValueSpecification> entry : values.object2LongEntrySet()) {
final Long value = entry.getValue();
_specificationToIdentifier.put(entry.getKey(), value);
_identifierToSpecification.put(value, entry.getKey());
}
identifiers.putAll(values);
}
}
return identifiers;
}
@Override
public ValueSpecification getValueSpecification(final long identifier) {
final Long key = identifier;
ValueSpecification spec = _identifierToSpecification.get(key);
if (spec != null) {
return spec;
}
spec = getUnderlying().getValueSpecification(identifier);
_specificationToIdentifier.put(spec, key);
_identifierToSpecification.put(key, spec);
return spec;
}
@Override
public Long2ObjectMap<ValueSpecification> getValueSpecifications(LongCollection identifiers) {
final Long2ObjectMap<ValueSpecification> specifications = new Long2ObjectOpenHashMap<ValueSpecification>();
LongList cacheMisses = null;
for (long identifier : identifiers) {
final Long key = identifier;
final ValueSpecification specification = _identifierToSpecification.get(key);
if (specification != null) {
specifications.put(identifier, specification);
} else {
if (cacheMisses == null) {
cacheMisses = new LongArrayList(identifiers.size());
}
cacheMisses.add(identifier);
}
}
if (cacheMisses != null) {
if (cacheMisses.size() == 1) {
final long identifier = cacheMisses.getLong(0);
final ValueSpecification specification = getUnderlying().getValueSpecification(identifier);
final Long key = identifier;
_specificationToIdentifier.put(specification, key);
_identifierToSpecification.put(key, specification);
specifications.put(identifier, specification);
} else {
final Long2ObjectMap<ValueSpecification> values = getUnderlying().getValueSpecifications(cacheMisses);
for (Long2ObjectMap.Entry<ValueSpecification> entry : values.long2ObjectEntrySet()) {
final Long value = entry.getKey();
_specificationToIdentifier.put(entry.getValue(), value);
_identifierToSpecification.put(value, entry.getValue());
}
specifications.putAll(values);
}
}
return specifications;
}
}