/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.convention;
import java.util.Collection;
import java.util.HashSet;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimap;
import com.opengamma.id.ExternalId;
import com.opengamma.id.ExternalIdBundle;
import com.opengamma.id.UniqueId;
import com.opengamma.id.UniqueIdSupplier;
/**
* A class to manage {@code ExternalId}, {@code ExternalIdBundle} and {@code UniqueId}
* references to generic objects.
* <p>
* This is an optimized data structure for storing multiple different references to the same object.
* The mapper also creates the unique identifier using the specified scheme.
* <p>
* This class is mutable and thread-safe via synchronization.
*
* @param <T> the type of the object being referred to by the identifiers
*/
/* package */ class ExternalIdBundleMapper<T> {
private final ReentrantReadWriteLock _lock = new ReentrantReadWriteLock();
private final WriteLock _writeLock = _lock.writeLock();
private final ReadLock _readLock = _lock.readLock();
private final Multimap<ExternalId, T> _toMap = HashMultimap.create();
private final Multimap<T, ExternalId> _fromMap = HashMultimap.create();
private final BiMap<UniqueId, T> _uniqueIdMap = HashBiMap.create();
private final UniqueIdSupplier _idSupplier;
/**
* Constructor taking the name of the scheme to use for the unique identifiers that this class generates.
*
* @param uniqueIdScheme the scheme to use for the automatically allocated unique identifiers
*/
ExternalIdBundleMapper(String uniqueIdScheme) {
_idSupplier = new UniqueIdSupplier(uniqueIdScheme);
}
//-------------------------------------------------------------------------
/**
* Adds a mapping from a bundle to an object.
*
* @param bundle the bundle that the object is referred to as, not null
* @param obj the object being referred to, not null
* @return the created unique identifier, not null
*/
UniqueId add(ExternalIdBundle bundle, T obj) {
_writeLock.lock();
try {
_fromMap.putAll(obj, bundle.getExternalIds());
for (ExternalId identifier : bundle.getExternalIds()) {
_toMap.put(identifier, obj);
}
if (_uniqueIdMap.inverse().containsKey(obj)) {
return _uniqueIdMap.inverse().get(obj);
} else {
UniqueId uniqueId = _idSupplier.get();
_uniqueIdMap.put(uniqueId, obj);
return uniqueId;
}
} finally {
_writeLock.unlock();
}
}
//-------------------------------------------------------------------------
/**
* Gets those objects which match the specified bundle.
* <p>
* Note that this method erroneously treats a bundle as a collection of individual identifiers.
*
* @param bundle the bundle to search for, not null
* @return the matching objects, not null
*/
Collection<T> get(ExternalIdBundle bundle) {
_readLock.lock();
try {
// TODO: semantics are wrong
Collection<T> results = new HashSet<T>();
for (ExternalId identifier : bundle) {
if (_toMap.containsKey(identifier)) {
results.addAll(_toMap.get(identifier));
}
}
return results;
} finally {
_readLock.unlock();
}
}
/**
* Gets those objects which match the specified bundle.
*
* @param externalId the external identifier to search for, not null
* @return the matching objects, not null
*/
Collection<T> get(ExternalId externalId) {
_readLock.lock();
try {
return _toMap.get(externalId);
} finally {
_readLock.unlock();
}
}
/**
* Gets those objects which match the specified unique identifier.
*
* @param uniqueId the unique identifier to search for, not null
* @return the matching objects, not null
*/
T get(UniqueId uniqueId) {
_readLock.lock();
try {
return _uniqueIdMap.get(uniqueId);
} finally {
_readLock.unlock();
}
}
/**
* Gets all objects in the mapper.
*
* @return an unmodifiable collection of all the objects in the mapper, not null
*/
Collection<T> getAll() {
_readLock.lock();
try {
return ImmutableList.copyOf(_toMap.values());
} finally {
_readLock.unlock();
}
}
}