#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); } } }