/** * 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.Arrays; import java.util.Collection; 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 a small number of values. */ public final class ArrayValueProperty extends AbstractValueProperty { /** * The maximum length this should be used for before {@link SetValueProperty} is used instead. */ public static final int MAX_ARRAY_LENGTH = 5; private static final long serialVersionUID = 1L; private 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 at least one entry and not more than {@link #MAX_ARRAY_LENGTH}. The object will use this object but won't modify it. * @param next the next property in the bucket, or null if this is the end of the chain */ public ArrayValueProperty(final String key, final boolean optional, final String[] values, final AbstractValueProperty next) { super(key, optional, next); assert (values.length > 1) && (values.length <= MAX_ARRAY_LENGTH); _values = values; } @Override public AbstractValueProperty copy(final AbstractValueProperty next) { return new ArrayValueProperty(getKey(), isOptional(), _values, next); } @Override protected AbstractValueProperty withOptional(final boolean optional) { if (isOptional() == optional) { return this; } else { return new ArrayValueProperty(getKey(), optional, _values, getNext()); } } // query/update self @Override public Set<String> getValues() { return new StringArraySet(_values); } /* package */String[] getValuesImpl() { return _values; } @Override protected AbstractValueProperty addValueImpl(final String value) { for (String existing : _values) { if (value.equals(existing)) { return this; } } if (_values.length < MAX_ARRAY_LENGTH) { final String[] newValues = Arrays.copyOf(_values, _values.length + 1); newValues[_values.length] = value; _values = newValues; return this; } else { final Set<String> newValues = Sets.newHashSet(_values); newValues.add(value); return new SetValueProperty(getKey(), isOptional(), newValues, getNext()); } } @Override protected AbstractValueProperty addValuesImpl(final String[] values) { int newValues = 0; newValueLoop: for (String value : values) { //CSIGNORE for (String existing : _values) { if (value.equals(existing)) { continue newValueLoop; } } newValues++; } if (newValues == 0) { return this; } newValues += _values.length; if (newValues <= MAX_ARRAY_LENGTH) { final String[] copy = Arrays.copyOf(_values, newValues); newValueLoop: for (String value : values) { //CSIGNORE for (String existing : _values) { if (value.equals(existing)) { continue newValueLoop; } } copy[--newValues] = value; } _values = copy; return this; } else { final Set<String> copy = Sets.newHashSetWithExpectedSize(newValues); for (String value : _values) { copy.add(value); } for (String value : values) { copy.add(value); } return new SetValueProperty(getKey(), isOptional(), copy, getNext()); } } @Override protected AbstractValueProperty addValuesImpl(final Collection<String> values) { int newValues = 0; newValueLoop: for (String value : values) { //CSIGNORE for (String existing : _values) { if (value.equals(existing)) { continue newValueLoop; } } newValues++; } if (newValues == 0) { return this; } if (newValues <= MAX_ARRAY_LENGTH) { newValues += _values.length; final String[] copy = Arrays.copyOf(_values, newValues); newValueLoop: for (String value : values) { //CSIGNORE for (String existing : _values) { if (value.equals(existing)) { continue newValueLoop; } } copy[--newValues] = value; } _values = copy; return this; } else { final Set<String> copy = Sets.newHashSetWithExpectedSize(newValues); for (String value : _values) { copy.add(value); } copy.addAll(values); return new SetValueProperty(getKey(), isOptional(), copy, getNext()); } } @Override protected AbstractValueProperty addValuesToImpl(final AbstractValueProperty addTo) { return addTo.addValuesImpl(_values); } @Override protected boolean containsValue(final String value) { for (String myValue : _values) { if (value.equals(myValue)) { return true; } } return false; } @Override protected boolean containsAllValues(final String[] values) { int lo = 0; int hi = _values.length - 1; for (String value : values) { boolean match = false; for (int i = lo; i <= hi; i++) { if (value.equals(_values[i])) { match = true; if (i == lo) { lo++; } else if (i == hi) { hi--; } break; } } if (!match) { return false; } } return true; } @Override protected boolean containsAllValues(final Collection<String> values) { int lo = 0; int hi = _values.length - 1; for (String value : values) { boolean match = false; for (int i = lo; i <= hi; i++) { if (value.equals(_values[i])) { match = true; if (i == lo) { lo++; } else if (i == hi) { hi--; } break; } } if (!match) { return false; } } return true; } @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[0]; } @Override protected AbstractValueProperty setWildcardImpl() { return new WildcardValueProperty(getKey(), isOptional(), getNext()); } @Override protected boolean isSatisfiedBy(final String value) { for (String myValue : _values) { if (value.equals(myValue)) { return true; } } return false; } @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) { final String value = other.getValueImpl(); for (String myValue : _values) { if (value.equals(myValue)) { return other.withOptional(isOptional()); } } return null; } @Override protected AbstractValueProperty intersectArrayValue(final ArrayValueProperty other) { if (other._values.length < _values.length) { return other.intersectArrayValue(this); } outerLoop: for (int i = 0; i < _values.length; i++) { //CSIGNORE String si = _values[i]; for (int j = 0; j < other._values.length; j++) { final String sj = other._values[j]; if (si.equals(sj)) { continue outerLoop; } } final String[] result = new String[_values.length - 1]; int x = i; if (i > 0) { System.arraycopy(_values, 0, result, 0, i); } i++; while (i < _values.length) { si = _values[i]; for (int j = 0; j < other._values.length; j++) { final String sj = other._values[j]; if (si.equals(sj)) { result[x++] = si; break; } } i++; } if (x > 1) { if (result.length != x) { return new ArrayValueProperty(getKey(), isOptional() && other.isOptional(), Arrays.copyOf(result, x), getNext()); } else { return new ArrayValueProperty(getKey(), isOptional() && other.isOptional(), result, getNext()); } } else if (x == 1) { // Single value in intersection return new SingletonValueProperty(getKey(), isOptional() && other.isOptional(), result[0], getNext()); } else { // Nothing in intersection return null; } } // Everything in this array was present in the other return withOptional(other.isOptional()); } @Override protected AbstractValueProperty intersectSetValue(final SetValueProperty other) { final Set<String> otherValues = other.getValuesImpl(); for (int i = 0; i < _values.length; i++) { // CSIGNORE if (otherValues.contains(_values[i])) { continue; } final String[] result = new String[_values.length - 1]; int x = i; if (i > 0) { System.arraycopy(_values, 0, result, 0, i); } i++; while (i < _values.length) { final String si = _values[i++]; if (otherValues.contains(si)) { result[x++] = si; } } if (x > 1) { if (result.length != x) { return new ArrayValueProperty(getKey(), isOptional() && other.isOptional(), Arrays.copyOf(result, x), getNext()); } else { return new ArrayValueProperty(getKey(), isOptional() && other.isOptional(), result, getNext()); } } else if (x == 1) { // Single value in intersection return new SingletonValueProperty(getKey(), isOptional() && other.isOptional(), result[0], getNext()); } else { // Nothing in intersection return null; } } // Everything in this array was present in the set return withOptional(other.isOptional()); } @Override public AbstractValueProperty intersectValues(final AbstractValueProperty other) { return other.intersectArrayValue(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); } for (int i = 0; i < _values.length; i++) { subMsg.add(null, i, FudgeWireType.STRING, _values[i]); } } // Object @Override protected int valueHashCode() { // Hash code of a set of these strings int hc = 0; for (String value : _values) { hc += value.hashCode(); } return hc; } @Override protected boolean equalsSingleton(final String value) { return false; } @Override protected boolean equalsArray(final String[] values) { if (_values.length != values.length) { return false; } return containsAllValues(values); } @Override protected boolean equalsSet(final Set<String> values) { return false; } @Override protected boolean equalsValue(final AbstractValueProperty other) { return other.equalsArray(_values); } }