/// <summary> /// Returns the detected kind of a serialized sentinel value, or None if it's not a sentinel value. /// </summary> /// <param name="value">The serialized value to inspect.</param> /// <returns>The kind of sentinel serialized in <paramref name="value"/> or None if it's not a sentinel value.</returns> internal static SentinelKind GetKind(Value value) { // If it's not a null value to start with, fetching NullValue returns NullValue.None // so we'll drop out to the right place. SentinelKind sentinelKind = (SentinelKind)value.NullValue; return(sentinelKind > SentinelKind.None && sentinelKind <= s_maxSentinelKind ? sentinelKind : SentinelKind.None); }
internal static SentinelValue ForArrayValue(SentinelKind sentinelKind, object[] values) { GaxPreconditions.CheckNotNull(values, nameof(values)); ArrayValue array = ValueSerializer.Serialize(values).ArrayValue; // This is just checking that the simple approach we've taken in the previous line // really did what we expect. GaxPreconditions.CheckState(array != null, "Input wasn't serialized as an array"); AugmentedValue augmented = new AugmentedValue { Kind = sentinelKind, Array = array }; return(new SentinelValue(augmented)); }
/// <summary> /// Finds all the sentinel values in a field map. /// Additionally, this validates that no sentinels exist in arrays (even nested). /// </summary> /// <param name="fields">The field map to find sentinels within.</param> /// <returns>The sentinel fields in the field map: both the value and the corresponding field path.</returns> private static List <SentinelField> FindSentinels(IDictionary <string, Value> fields) { List <SentinelField> result = new List <SentinelField>(); FindSentinelsRecursively(fields, FieldPath.Empty); return(result); void FindSentinelsRecursively(IDictionary <string, Value> currentFields, FieldPath currentParentPath) { foreach (var pair in currentFields) { Value value = pair.Value; string key = pair.Key; SentinelKind sentinelKind = SentinelValue.GetKind(value); if (sentinelKind != SentinelKind.None) { result.Add(new SentinelField(currentParentPath.Append(key), value)); } else if (value.MapValue != null) { FindSentinelsRecursively(value.MapValue.Fields, currentParentPath.Append(pair.Key)); } else if (value.ArrayValue != null) { ValidateNoSentinelValues(value.ArrayValue.Values); } } } void ValidateNoSentinelValues(IEnumerable <Value> values) { foreach (var value in values) { if (SentinelValue.GetKind(value) != SentinelKind.None) { // We don't know what parameter name to use here throw new ArgumentException("Sentinel values must not appear directly or indirectly within array values"); } else if (value.MapValue != null) { ValidateNoSentinelValues(value.MapValue.Fields.Values); } else if (value.ArrayValue != null) { ValidateNoSentinelValues(value.ArrayValue.Values); } } } }
internal static SentinelValue ForArrayValue(SerializationContext context, SentinelKind sentinelKind, object[] values) { GaxPreconditions.CheckNotNull(values, nameof(values)); ArrayValue array = ValueSerializer.Serialize(context, values).ArrayValue; // This is just checking that the simple approach we've taken in the previous line // really did what we expect. GaxPreconditions.CheckState(array != null, "Input wasn't serialized as an array"); GaxPreconditions.CheckState(!array.Values.Any(FindNestedSentinels), "Sentinel values cannot be nested in array union/remove values."); AugmentedValue augmented = new AugmentedValue { Kind = sentinelKind, Array = array }; return(new SentinelValue(augmented)); }
// There will be other sentinels with more parameters. private SentinelValue(SentinelKind kind, Func <Value> protoFactory) { Kind = kind; _protoFactory = protoFactory; }