#if !NO_GENERICS
using System.Collections.Generic;
#endif
using ProtoBuf.Meta;
using System.Collections;
namespace ProtoBuf
{
///
/// Simple base class for supporting unexpected fields allowing
/// for loss-less round-tips/merge, even if the data is not understod.
/// The additional fields are (by default) stored in-memory in a buffer.
///
/// As an example of an alternative implementation, you might
/// choose to use the file system (temporary files) as the back-end, tracking
/// only the paths [such an object would ideally be IDisposable and use
/// a finalizer to ensure that the files are removed].
///
public abstract class Extensible : IExtensible
{
// note: not marked ProtoContract - no local state, and can't
// predict sub-classes
private IExtension extensionObject;
IExtension IExtensible.GetExtensionObject(bool createIfMissing)
{
return GetExtensionObject(createIfMissing);
}
///
/// Retrieves the extension object for the current
/// instance, optionally creating it if it does not already exist.
///
/// Should a new extension object be
/// created if it does not already exist?
/// The extension object if it exists (or was created), or null
/// if the extension object does not exist or is not available.
/// The createIfMissing argument is false during serialization,
/// and true during deserialization upon encountering unexpected fields.
protected virtual IExtension GetExtensionObject(bool createIfMissing)
{
return GetExtensionObject(ref extensionObject, createIfMissing);
}
///
/// Provides a simple, default implementation for extension support,
/// optionally creating it if it does not already exist. Designed to be called by
/// classes implementing .
///
/// Should a new extension object be
/// created if it does not already exist?
/// The extension field to check (and possibly update).
/// The extension object if it exists (or was created), or null
/// if the extension object does not exist or is not available.
/// The createIfMissing argument is false during serialization,
/// and true during deserialization upon encountering unexpected fields.
public static IExtension GetExtensionObject(ref IExtension extensionObject, bool createIfMissing)
{
if (createIfMissing && extensionObject == null)
{
extensionObject = new BufferExtension();
}
return extensionObject;
}
#if !NO_RUNTIME && !NO_GENERICS
///
/// Appends the value as an additional (unexpected) data-field for the instance.
/// Note that for non-repeated sub-objects, this equates to a merge operation;
/// for repeated sub-objects this adds a new instance to the set; for simple
/// values the new value supercedes the old value.
///
/// Note that appending a value does not remove the old value from
/// the stream; avoid repeatedly appending values for the same field.
/// The type of the value to append.
/// The extensible object to append the value to.
/// The field identifier; the tag should not be defined as a known data-field for the instance.
/// The value to append.
public static void AppendValue(IExtensible instance, int tag, TValue value)
{
AppendValue(instance, tag, DataFormat.Default, value);
}
///
/// Appends the value as an additional (unexpected) data-field for the instance.
/// Note that for non-repeated sub-objects, this equates to a merge operation;
/// for repeated sub-objects this adds a new instance to the set; for simple
/// values the new value supercedes the old value.
///
/// Note that appending a value does not remove the old value from
/// the stream; avoid repeatedly appending values for the same field.
/// The data-type of the field.
/// The data-format to use when encoding the value.
/// The extensible object to append the value to.
/// The field identifier; the tag should not be defined as a known data-field for the instance.
/// The value to append.
public static void AppendValue(IExtensible instance, int tag, DataFormat format, TValue value)
{
ExtensibleUtil.AppendExtendValue(RuntimeTypeModel.Default, instance, tag, format, value);
}
///
/// Queries an extensible object for an additional (unexpected) data-field for the instance.
/// The value returned is the composed value after merging any duplicated content; if the
/// value is "repeated" (a list), then use GetValues instead.
///
/// The data-type of the field.
/// The extensible object to obtain the value from.
/// The field identifier; the tag should not be defined as a known data-field for the instance.
/// The effective value of the field, or the default value if not found.
public static TValue GetValue(IExtensible instance, int tag)
{
return GetValue(instance, tag, DataFormat.Default);
}
///
/// Queries an extensible object for an additional (unexpected) data-field for the instance.
/// The value returned is the composed value after merging any duplicated content; if the
/// value is "repeated" (a list), then use GetValues instead.
///
/// The data-type of the field.
/// The extensible object to obtain the value from.
/// The field identifier; the tag should not be defined as a known data-field for the instance.
/// The data-format to use when decoding the value.
/// The effective value of the field, or the default value if not found.
public static TValue GetValue(IExtensible instance, int tag, DataFormat format)
{
TValue value;
TryGetValue(instance, tag, format, out value);
return value;
}
///
/// Queries an extensible object for an additional (unexpected) data-field for the instance.
/// The value returned (in "value") is the composed value after merging any duplicated content;
/// if the value is "repeated" (a list), then use GetValues instead.
///
/// The data-type of the field.
/// The effective value of the field, or the default value if not found.
/// The extensible object to obtain the value from.
/// The field identifier; the tag should not be defined as a known data-field for the instance.
/// True if data for the field was present, false otherwise.
public static bool TryGetValue(IExtensible instance, int tag, out TValue value)
{
return TryGetValue(instance, tag, DataFormat.Default, out value);
}
///
/// Queries an extensible object for an additional (unexpected) data-field for the instance.
/// The value returned (in "value") is the composed value after merging any duplicated content;
/// if the value is "repeated" (a list), then use GetValues instead.
///
/// The data-type of the field.
/// The effective value of the field, or the default value if not found.
/// The extensible object to obtain the value from.
/// The field identifier; the tag should not be defined as a known data-field for the instance.
/// The data-format to use when decoding the value.
/// True if data for the field was present, false otherwise.
public static bool TryGetValue(IExtensible instance, int tag, DataFormat format, out TValue value)
{
return TryGetValue(instance, tag, format, false, out value);
}
///
/// Queries an extensible object for an additional (unexpected) data-field for the instance.
/// The value returned (in "value") is the composed value after merging any duplicated content;
/// if the value is "repeated" (a list), then use GetValues instead.
///
/// The data-type of the field.
/// The effective value of the field, or the default value if not found.
/// The extensible object to obtain the value from.
/// The field identifier; the tag should not be defined as a known data-field for the instance.
/// The data-format to use when decoding the value.
/// Allow tags that are present as part of the definition; for example, to query unknown enum values.
/// True if data for the field was present, false otherwise.
public static bool TryGetValue(IExtensible instance, int tag, DataFormat format, bool allowDefinedTag, out TValue value)
{
value = default(TValue);
bool set = false;
foreach (TValue val in ExtensibleUtil.GetExtendedValues(instance, tag, format, true, allowDefinedTag))
{
// expecting at most one yield...
// but don't break; need to read entire stream
value = val;
set = true;
}
return set;
}
///
/// Queries an extensible object for an additional (unexpected) data-field for the instance.
/// Each occurrence of the field is yielded separately, making this usage suitable for "repeated"
/// (list) fields.
///
/// The extended data is processed lazily as the enumerator is iterated.
/// The data-type of the field.
/// The extensible object to obtain the value from.
/// The field identifier; the tag should not be defined as a known data-field for the instance.
/// An enumerator that yields each occurrence of the field.
public static IEnumerable GetValues(IExtensible instance, int tag)
{
return ExtensibleUtil.GetExtendedValues(instance, tag, DataFormat.Default, false, false);
}
///
/// Queries an extensible object for an additional (unexpected) data-field for the instance.
/// Each occurrence of the field is yielded separately, making this usage suitable for "repeated"
/// (list) fields.
///
/// The extended data is processed lazily as the enumerator is iterated.
/// The data-type of the field.
/// The extensible object to obtain the value from.
/// The field identifier; the tag should not be defined as a known data-field for the instance.
/// The data-format to use when decoding the value.
/// An enumerator that yields each occurrence of the field.
public static IEnumerable GetValues(IExtensible instance, int tag, DataFormat format)
{
return ExtensibleUtil.GetExtendedValues(instance, tag, format, false, false);
}
#endif
///
/// Queries an extensible object for an additional (unexpected) data-field for the instance.
/// The value returned (in "value") is the composed value after merging any duplicated content;
/// if the value is "repeated" (a list), then use GetValues instead.
///
/// The data-type of the field.
/// The model to use for configuration.
/// The effective value of the field, or the default value if not found.
/// The extensible object to obtain the value from.
/// The field identifier; the tag should not be defined as a known data-field for the instance.
/// The data-format to use when decoding the value.
/// Allow tags that are present as part of the definition; for example, to query unknown enum values.
/// True if data for the field was present, false otherwise.
public static bool TryGetValue(TypeModel model, System.Type type, IExtensible instance, int tag, DataFormat format, bool allowDefinedTag, out object value)
{
value = null;
bool set = false;
foreach (object val in ExtensibleUtil.GetExtendedValues(model, type, instance, tag, format, true, allowDefinedTag))
{
// expecting at most one yield...
// but don't break; need to read entire stream
value = val;
set = true;
}
return set;
}
///
/// Queries an extensible object for an additional (unexpected) data-field for the instance.
/// Each occurrence of the field is yielded separately, making this usage suitable for "repeated"
/// (list) fields.
///
/// The extended data is processed lazily as the enumerator is iterated.
/// The model to use for configuration.
/// The data-type of the field.
/// The extensible object to obtain the value from.
/// The field identifier; the tag should not be defined as a known data-field for the instance.
/// The data-format to use when decoding the value.
/// An enumerator that yields each occurrence of the field.
public static IEnumerable GetValues(TypeModel model, System.Type type, IExtensible instance, int tag, DataFormat format)
{
return ExtensibleUtil.GetExtendedValues(model, type, instance, tag, format, false, false);
}
///
/// Appends the value as an additional (unexpected) data-field for the instance.
/// Note that for non-repeated sub-objects, this equates to a merge operation;
/// for repeated sub-objects this adds a new instance to the set; for simple
/// values the new value supercedes the old value.
///
/// Note that appending a value does not remove the old value from
/// the stream; avoid repeatedly appending values for the same field.
/// The model to use for configuration.
/// The data-format to use when encoding the value.
/// The extensible object to append the value to.
/// The field identifier; the tag should not be defined as a known data-field for the instance.
/// The value to append.
public static void AppendValue(TypeModel model, IExtensible instance, int tag, DataFormat format, object value)
{
ExtensibleUtil.AppendExtendValue(model, instance, tag, format, value);
}
}
}