/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.engine.value.properties;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.fudgemsg.MutableFudgeMsg;
import org.fudgemsg.types.IndicatorType;
import org.fudgemsg.wire.types.FudgeWireType;
import com.google.common.collect.Sets;
import com.opengamma.engine.fudgemsg.ValuePropertiesFudgeBuilder;
import com.opengamma.engine.value.ValueProperties;
/**
* Internal state used to implement a {@link ValueProperties} entry that has more than a small number of values.
*/
public final class SetValueProperty extends AbstractValueProperty {
private static final long serialVersionUID = 1L;
private Set<String> _values;
// construction
/**
* Creates a new instance.
*
* @param key the value key, never null
* @param optional the optional flag
* @param values the values to store, never null, containing more than {@link ArrayValueProperty#MAX_ARRAY_LENGTH} entries. The object will use this object but will not modify it.
* @param next the next property in the bucket, or null if this is the end of the chain
*/
public SetValueProperty(final String key, final boolean optional, final Set<String> values, final AbstractValueProperty next) {
super(key, optional, next);
assert values.size() > ArrayValueProperty.MAX_ARRAY_LENGTH;
_values = values;
}
@Override
public AbstractValueProperty copy(final AbstractValueProperty next) {
return new SetValueProperty(getKey(), isOptional(), _values, next);
}
@Override
public AbstractValueProperty withOptional(final boolean optional) {
if (isOptional() == optional) {
return this;
} else {
return new SetValueProperty(getKey(), optional, _values, getNext());
}
}
// query/update self
@Override
public Set<String> getValues() {
return Collections.unmodifiableSet(_values);
}
/* package */Set<String> getValuesImpl() {
return _values;
}
@Override
protected AbstractValueProperty addValueImpl(final String value) {
if (!_values.contains(value)) {
final Set<String> newValues = new HashSet<String>(_values);
newValues.add(value);
_values = newValues;
}
return this;
}
@Override
protected AbstractValueProperty addValuesImpl(final String[] values) {
int newValues = 0;
for (String value : values) {
if (!_values.contains(value)) {
newValues++;
}
}
if (newValues > 0) {
final Set<String> copy = Sets.newHashSetWithExpectedSize(_values.size() + newValues);
copy.addAll(_values);
for (String value : values) {
copy.add(value);
}
_values = copy;
}
return this;
}
@Override
protected AbstractValueProperty addValuesImpl(final Collection<String> values) {
int newValues = 0;
for (String value : values) {
if (!_values.contains(value)) {
newValues++;
}
}
if (newValues > 0) {
final Set<String> copy = Sets.newHashSetWithExpectedSize(_values.size() + newValues);
copy.addAll(_values);
copy.addAll(values);
_values = copy;
}
return this;
}
@Override
protected AbstractValueProperty addValuesToImpl(final AbstractValueProperty addTo) {
return addTo.addValuesImpl(_values);
}
@Override
protected boolean containsValue(final String value) {
return _values.contains(value);
}
@Override
protected boolean containsAllValues(final String[] values) {
for (String value : values) {
if (!_values.contains(value)) {
return false;
}
}
return true;
}
@Override
protected boolean containsAllValues(final Collection<String> values) {
return _values.containsAll(values);
}
@Override
protected boolean valuesContainedBy(final AbstractValueProperty other) {
return other.containsAllValues(_values);
}
@Override
public boolean isWildcard() {
return false;
}
@Override
public String getStrict() {
return null;
}
@Override
public String getSingle() {
return _values.iterator().next();
}
@Override
protected AbstractValueProperty setWildcardImpl() {
return new WildcardValueProperty(getKey(), isOptional(), getNext());
}
@Override
protected boolean isSatisfiedBy(final String value) {
return _values.contains(value);
}
@Override
public boolean isSatisfyValue(final AbstractValueProperty property) {
for (String myValue : _values) {
if (property.isSatisfiedBy(myValue)) {
return true;
}
}
return false;
}
@Override
protected AbstractValueProperty intersectSingletonValue(final SingletonValueProperty other) {
if (_values.contains(other.getValueImpl())) {
return other.withOptional(isOptional());
}
return null;
}
@Override
protected AbstractValueProperty intersectArrayValue(final ArrayValueProperty other) {
return other.intersectSetValue(this);
}
@Override
protected AbstractValueProperty intersectSetValue(final SetValueProperty other) {
final Set<String> otherValues = other.getValuesImpl();
final Set<String> intersect = new HashSet<String>(_values);
intersect.retainAll(otherValues);
if (intersect.isEmpty()) {
return null;
}
final int size = intersect.size();
if (size == 1) {
// Single value in intersection
return new SingletonValueProperty(getKey(), isOptional() && other.isOptional(), intersect.iterator().next(), getNext());
} else if (size <= ArrayValueProperty.MAX_ARRAY_LENGTH) {
// Small array in intersection
return new ArrayValueProperty(getKey(), isOptional() && other.isOptional(), intersect.toArray(new String[size]), getNext());
} else if (intersect.isEmpty()) {
// No intersection
return null;
} else if (size == _values.size()) {
// Intersection same as self
return withOptional(other.isOptional());
} else if (size == otherValues.size()) {
// Intersection same as other
return other.withOptional(isOptional());
} else {
// New set intersection
return new SetValueProperty(getKey(), isOptional() && other.isOptional(), intersect, null);
}
}
@Override
public AbstractValueProperty intersectValues(final AbstractValueProperty other) {
return other.intersectSetValue(this);
}
@Override
public void toFudgeMsg(final MutableFudgeMsg msg) {
final MutableFudgeMsg subMsg = msg.addSubMessage(getKey(), null);
if (isOptional()) {
subMsg.add(ValuePropertiesFudgeBuilder.OPTIONAL_FIELD, null, FudgeWireType.INDICATOR, IndicatorType.INSTANCE);
}
int ordinal = 0;
for (String value : _values) {
subMsg.add(null, ordinal++, FudgeWireType.STRING, value);
}
}
// Object
@Override
protected int valueHashCode() {
return _values.hashCode();
}
@Override
protected boolean equalsSingleton(final String value) {
return false;
}
@Override
protected boolean equalsArray(final String[] values) {
return false;
}
@Override
protected boolean equalsSet(final Set<String> values) {
return _values.equals(values);
}
@Override
protected boolean equalsValue(final AbstractValueProperty other) {
return other.equalsSet(_values);
}
}