/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.engine.marketdata.availability;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
import com.opengamma.engine.ComputationTargetSpecification;
import com.opengamma.engine.function.MarketDataSourcingFunction;
import com.opengamma.engine.target.ComputationTargetReferenceVisitor;
import com.opengamma.engine.target.ComputationTargetRequirement;
import com.opengamma.engine.value.ValueProperties;
import com.opengamma.engine.value.ValuePropertyNames;
import com.opengamma.engine.value.ValueRequirement;
import com.opengamma.engine.value.ValueSpecification;
import com.opengamma.id.ExternalId;
import com.opengamma.id.ExternalIdBundle;
import com.opengamma.id.UniqueId;
import com.opengamma.util.ArgumentChecker;
/**
* Implements a {@link MarketDataAvailabilityProvider} around a fixed set of available market data items.
*/
public class FixedMarketDataAvailabilityProvider extends AbstractMarketDataAvailabilityProvider {
private static final AtomicInteger s_nextIdentifier = new AtomicInteger();
private static class TargetData extends ConcurrentHashMap<String, Set<ValueSpecification>> {
private static final long serialVersionUID = 1L;
public TargetData(final ValueSpecification initialValue) {
final Set<ValueSpecification> values = new CopyOnWriteArraySet<ValueSpecification>();
values.add(initialValue);
put(initialValue.getValueName(), values);
}
public TargetData() {
}
public ValueSpecification getAvailability(final ValueRequirement desiredValue) {
final Set<ValueSpecification> specs = get(desiredValue.getValueName());
if (specs != null) {
final ValueProperties constraints = desiredValue.getConstraints().withoutAny(ValuePropertyNames.FUNCTION);
for (final ValueSpecification spec : specs) {
if (constraints.isSatisfiedBy(spec.getProperties())) {
return spec;
}
}
}
return null;
}
public void addValue(final ValueSpecification specification) {
Set<ValueSpecification> values = get(specification.getValueName());
if (values == null) {
values = new CopyOnWriteArraySet<ValueSpecification>();
values.add(specification);
final Set<ValueSpecification> existing = putIfAbsent(specification.getValueName(), values);
if (existing != null) {
existing.add(specification);
}
} else {
values.add(specification);
}
}
public void removeValue(final ValueSpecification specification) {
final Set<ValueSpecification> values = get(specification.getValueName());
if (values != null) {
values.remove(specification);
}
}
}
private final String _syntheticScheme = "InMemoryLKV" + s_nextIdentifier.getAndIncrement();
private final AtomicInteger _nextSyntheticIdentifier = new AtomicInteger();
private final ConcurrentMap<ExternalId, ComputationTargetSpecification> _weakIndex;
private final ConcurrentMap<ComputationTargetSpecification, TargetData> _strictIndex;
private final ComputationTargetReferenceVisitor<ComputationTargetSpecification> _getTargetSpecification = new ComputationTargetReferenceVisitor<ComputationTargetSpecification>() {
@Override
public ComputationTargetSpecification visitComputationTargetRequirement(final ComputationTargetRequirement requirement) {
if (requirement.getIdentifiers().isEmpty()) {
return ComputationTargetSpecification.NULL;
}
ComputationTargetSpecification found = null;
boolean update = false;
for (final ExternalId identifier : requirement.getIdentifiers()) {
final ComputationTargetSpecification weakSpec = _weakIndex.get(identifier);
if (weakSpec != null) {
if (found != null) {
if (!update) {
update = !found.equals(weakSpec);
}
} else {
found = weakSpec;
}
} else {
update = true;
}
}
if (found == null) {
found = requirement.replaceIdentifier(UniqueId.of(_syntheticScheme, Integer.toString(_nextSyntheticIdentifier.getAndIncrement())));
final TargetData data = new TargetData();
_strictIndex.put(found, data);
}
if (update) {
for (final ExternalId identifier : requirement.getIdentifiers()) {
_weakIndex.put(identifier, found);
}
}
return found;
}
@Override
public ComputationTargetSpecification visitComputationTargetSpecification(final ComputationTargetSpecification specification) {
return specification;
}
};
public FixedMarketDataAvailabilityProvider() {
this(new ConcurrentHashMap<ExternalId, ComputationTargetSpecification>(), new ConcurrentHashMap<ComputationTargetSpecification, TargetData>());
}
private FixedMarketDataAvailabilityProvider(final ConcurrentMap<ExternalId, ComputationTargetSpecification> weakIndex, final ConcurrentMap<ComputationTargetSpecification, TargetData> strictIndex) {
_weakIndex = weakIndex;
_strictIndex = strictIndex;
}
protected ValueSpecification getAvailabilityImpl(final ComputationTargetSpecification targetSpec, final ValueRequirement desiredValue) {
if (targetSpec != null) {
final TargetData values = _strictIndex.get(targetSpec);
if (values != null) {
return values.getAvailability(desiredValue);
}
}
return null;
}
protected ValueSpecification getAvailabilityImpl(final ExternalId key, final ValueRequirement desiredValue) {
final ComputationTargetSpecification target = _weakIndex.get(key);
if (target != null) {
return getAvailabilityImpl(target, desiredValue);
} else {
return null;
}
}
@Override
protected ValueSpecification getAvailability(final ComputationTargetSpecification targetSpec, final ExternalId identifier, final ValueRequirement desiredValue) {
ValueSpecification available = getAvailabilityImpl(targetSpec, desiredValue);
if (available == null) {
available = getAvailabilityImpl(identifier, desiredValue);
}
return available;
}
@Override
protected ValueSpecification getAvailability(final ComputationTargetSpecification targetSpec, final ExternalIdBundle identifiers, final ValueRequirement desiredValue) {
ValueSpecification available = getAvailabilityImpl(targetSpec, desiredValue);
if (available == null) {
for (final ExternalId identifier : identifiers) {
available = getAvailabilityImpl(identifier, desiredValue);
if (available != null) {
break;
}
}
}
return available;
}
@Override
protected ValueSpecification getAvailability(final ComputationTargetSpecification targetSpec, final UniqueId identifier, final ValueRequirement desiredValue) {
return getAvailabilityImpl(targetSpec, desiredValue);
}
@Override
protected ValueSpecification getAvailability(final ComputationTargetSpecification targetSpec, final ValueRequirement desiredValue) {
return getAvailabilityImpl(targetSpec, desiredValue);
}
/**
* Returns the {@link ValueSpecification} that is a possible resolution of the value requirement. This will be the specification used by {@link #addValue(ValueRequirement,Object)} or
* {@link #removeValue(ValueRequirement)}.
*
* @param requirement the requirement to resolve, not null
* @return the resolved {@code ValueSpecification}, not null
*/
public ValueSpecification resolveRequirement(final ValueRequirement requirement) {
final ComputationTargetSpecification targetSpec = requirement.getTargetReference().accept(_getTargetSpecification);
return new ValueSpecification(requirement.getValueName(), targetSpec, requirement.getConstraints().copy().withoutAny(ValuePropertyNames.FUNCTION)
.with(ValuePropertyNames.FUNCTION, MarketDataSourcingFunction.UNIQUE_ID).get());
}
protected void addAvailableData(final ComputationTargetSpecification target, final ValueSpecification valueSpecification) {
TargetData data = _strictIndex.get(target);
if (data == null) {
data = new TargetData(valueSpecification);
final TargetData existing = _strictIndex.putIfAbsent(target, data);
if (existing != null) {
existing.addValue(valueSpecification);
}
} else {
data.addValue(valueSpecification);
}
}
protected void removeAvailableData(final ComputationTargetSpecification target, final ValueSpecification valueSpecification) {
final TargetData data = _strictIndex.get(target);
if (data != null) {
data.removeValue(valueSpecification);
}
}
public void addAvailableData(final ExternalId identifier, final ValueSpecification valueSpecification) {
ArgumentChecker.notNull(identifier, "identifier");
ArgumentChecker.notNull(valueSpecification, "valueSpecification");
final ComputationTargetSpecification target = valueSpecification.getTargetSpecification();
addAvailableData(target, valueSpecification);
_weakIndex.put(identifier, target);
}
public void addAvailableData(final ExternalIdBundle identifiers, final ValueSpecification valueSpecification) {
ArgumentChecker.notNull(identifiers, "identifiers");
ArgumentChecker.notNull(valueSpecification, "valueSpecification");
final ComputationTargetSpecification target = valueSpecification.getTargetSpecification();
addAvailableData(target, valueSpecification);
for (final ExternalId identifier : identifiers) {
_weakIndex.put(identifier, target);
}
}
public void addAvailableData(final ValueSpecification valueSpecification) {
ArgumentChecker.notNull(valueSpecification, "valueSpecification");
addAvailableData(valueSpecification.getTargetSpecification(), valueSpecification);
}
public void removeAvailableData(final ValueSpecification valueSpecification) {
ArgumentChecker.notNull(valueSpecification, "valueSpecification");
removeAvailableData(valueSpecification.getTargetSpecification(), valueSpecification);
}
@Override
public Serializable getAvailabilityHintKey() {
final ArrayList<Serializable> key = new ArrayList<Serializable>(2);
key.add(getClass().getName());
key.add(_syntheticScheme);
return key;
}
}