Exemple #1
0
        private IProtoTypeSerializer BuildSerializer(bool isRoot)
        {
            // reference tracking decorators (RootDecorator, NetObjectDecorator, NetObjectValueDecorator)
            // should always be applied only one time (otherwise will consider new objects as already written):
            // #1 For collection types references are handled either by RootDecorator or
            // by ValueMember which owns the value (so outside of this scope)
            // because the value is treated as single object
            // #2 For members: ordinal ValueMembers are used and they will handle references when appropriate

            if (Helpers.IsEnum(Type))
            {
                Debug.Assert(IsSimpleValue);
                IProtoTypeSerializer ser = new WireTypeDecorator(WireType.Variant, new EnumSerializer(Type, GetEnumMap()));
                if (isRoot && !GetRootStartsGroup())
                {
                    ser = new RootFieldNumberDecorator(ser, ListHelpers.FieldItem);
                }
                return(ser);
            }

            Type itemType = _settingsValueFinal.Member.Collection.ItemType;

            if (itemType != null)
            {
                if (_surrogate != null)
                {
                    throw new ArgumentException("Repeated data (a list, collection, etc) has inbuilt behaviour and cannot use a surrogate");
                }

                Type defaultType = null;
                ResolveListTypes(_model, Type, ref itemType, ref defaultType);

                if (_fields.Count != 0)
                {
                    throw new ArgumentException("Repeated data (an array, list, etc) has inbuilt behavior and can't have fields");
                }

                // apply default member settings to type settings too
                var s = _settingsValueFinal.Member;
                // but change this:
                s.EffectiveType           = Type;  // not merged with anything so assign
                s.Collection.ConcreteType = _settingsValueFinal.ConstructType ?? defaultType;
                s.Collection.Append       = false; // allowed only on members
                s.WriteAsDynamicType      = false; // allowed only on members
                // this should be handled as collection
                if (s.Collection.ItemType == null)
                {
                    s.Collection.ItemType = itemType;
                }

                var vs = _rootNestedVs.Clone();
                vs.SetSettings(s, 0);
                vs.DefaultLevel = new ValueSerializationSettings.LevelValue(s.MakeDefaultNestedLevel());

                WireType wt;
                var      ser = (IProtoTypeSerializer)_model.ValueSerializerBuilder.BuildValueFinalSerializer(vs, false, out wt);

                // standard root decorator won't start any field
                // in compatibility mode collections won't start subitems like normally
                // so wrap with field
                if (isRoot && !GetRootStartsGroup())
                {
                    ser = new RootFieldNumberDecorator(ser, TypeModel.EnumRootTag);
                }

                return(ser);
            }

            if (BaseType != null && !BaseType.GetFinalSettingsCopy().IgnoreListHandling&& RuntimeTypeModel.CheckTypeIsCollection(_model, BaseType.Type))
            {
                throw new ArgumentException("A subclass of a repeated data (an array, list, etc should be handled too as a collection");
            }

            // #2

            if (_surrogate != null)
            {
                MetaType mt = _model[_surrogate], mtBase;
                while ((mtBase = mt.BaseType) != null)
                {
                    mt = mtBase;
                }
                return(new SurrogateSerializer(_model, Type, _surrogate, mt.Serializer));
            }
            if (_settingsValueFinal.IsAutoTuple)
            {
                if (_tupleCtor == null)
                {
                    throw new InvalidOperationException("Can't find tuple constructor");
                }
                return(new TupleSerializer(_model, _tupleCtor, _tupleFields.ToArray(), _settingsValueFinal.PrefixLength.GetValueOrDefault(true)));
            }


            var fields = new BasicList(_fields.Cast <object>());

            fields.Trim();

            int fieldCount   = fields.Count;
            int subTypeCount = _subTypes?.Count ?? 0;

            int[] fieldNumbers = new int[fieldCount + subTypeCount];
            IProtoSerializerWithWireType[] serializers = new IProtoSerializerWithWireType[fieldCount + subTypeCount];
            int i = 0;

            if (subTypeCount != 0)
            {
                Debug.Assert(_subTypes != null, "_subTypes != null");
                foreach (SubType subType in _subTypes)
                {
#if WINRT
                    if (!subType.DerivedType.IgnoreListHandling && ienumerable.IsAssignableFrom(subType.DerivedType.Type.GetTypeInfo()))
#else
                    if (!subType.DerivedType.IgnoreListHandling && _model.MapType(ienumerable).IsAssignableFrom(subType.DerivedType.Type))
#endif
                    {
                        throw new ArgumentException("Repeated data (a list, collection, etc) has inbuilt behaviour and cannot be used as a subclass");
                    }
                    fieldNumbers[i]  = subType.FieldNumber;
                    serializers[i++] = subType.GetSerializer(_model);
                }
            }
            if (fieldCount != 0)
            {
                foreach (ValueMember member in fields)
                {
                    fieldNumbers[i]  = member.FieldNumber;
                    serializers[i++] = member.Serializer;
                }
            }

            BasicList baseCtorCallbacks = null;
            MetaType  tmp = BaseType;

            while (tmp != null)
            {
                MethodInfo method = tmp.HasCallbacks ? tmp.Callbacks.BeforeDeserialize : null;
                if (method != null)
                {
                    if (baseCtorCallbacks == null)
                    {
                        baseCtorCallbacks = new BasicList();
                    }
                    baseCtorCallbacks.Add(method);
                }
                tmp = tmp.BaseType;
            }
            MethodInfo[] arr = null;
            if (baseCtorCallbacks != null)
            {
                arr = new MethodInfo[baseCtorCallbacks.Count];
                baseCtorCallbacks.CopyTo(arr, 0);
                Array.Reverse(arr);
            }
            // root serializer should be always from base type
            Debug.Assert(!OmitTypeSearchForRootSerialization);
            if (isRoot && BaseType != null)
            {
                return(new ForbiddenRootStub(Type));
            }
            return(new TypeSerializer(_model, Type, fieldNumbers, serializers, arr, BaseType == null, !_settingsValueFinal.SkipConstructor, _callbacks, _settingsValueFinal.ConstructType, _factory, _settingsValueFinal.PrefixLength.Value));
        }
        public IProtoSerializerWithWireType TryGetCoreSerializer(BinaryDataFormat dataFormat, Type type, out WireType defaultWireType,
                                                                 ref ValueFormat format, bool dynamicType, bool appendCollection, bool isPackedCollection, bool allowComplexTypes, ref object defaultValue)
        {
            if (format == ValueFormat.NotSpecified)
            {
                throw new ArgumentException("Format should be specified for TryGetCoreSerializer", nameof(format));
            }
            if (format != ValueFormat.Compact && _model.ProtoCompatibility.SuppressValueEnhancedFormat)
            {
                throw new InvalidOperationException("TryGetCoreSerializer should receive final format with ProtoCompatibility already taken into account");
            }
            Type originalType = type;

#if !NO_GENERICS
            {
                Type tmp = Helpers.GetNullableUnderlyingType(type);
                if (tmp != null)
                {
                    type = tmp;
                }
            }
#endif
            defaultWireType = WireType.None;
            IProtoSerializerWithWireType ser = null;

            if (Helpers.IsEnum(type))
            {
                if (allowComplexTypes && _model != null)
                {
                    // need to do this before checking the typecode; an int enum will report Int32 etc
                    defaultWireType = WireType.Variant;
                    ser             = new WireTypeDecorator(defaultWireType, new EnumSerializer(type, _model.GetEnumMap(type)));
                }
                else
                { // enum is fine for adding as a meta-type
                    defaultWireType = WireType.None;
                    return(null);
                }
            }
            if (ser == null)
            {
                ser = TryGetBasicTypeSerializer(dataFormat, type, out defaultWireType, !appendCollection);
                if (ser != null && Helpers.GetTypeCode(type) == ProtoTypeCode.Uri)
                {
                    // should be after uri but uri should always be before collection
                    if (defaultValue != null)
                    {
                        ser          = new DefaultValueDecorator(_model, defaultValue, ser);
                        defaultValue = null;
                    }
                }
            }
            if (ser == null)
            {
                var parseable = _model.AllowParseableTypes ? ParseableSerializer.TryCreate(type, _model) : null;
                if (parseable != null)
                {
                    defaultWireType = WireType.String;
                    ser             = new WireTypeDecorator(defaultWireType, parseable);
                }
            }

            if (ser != null)
            {
                return((isPackedCollection || !allowComplexTypes) ? ser : DecorateValueSerializer(originalType, dynamicType ? dataFormat : (BinaryDataFormat?)null, ref format, ser));
            }

            if (allowComplexTypes)
            {
                int baseKey = _model.GetKey(type, false, true);

                defaultWireType = WireType.StartGroup;

                if (baseKey >= 0 || dynamicType)
                {
                    if (dynamicType)
                    {
                        return(new NetObjectValueDecorator(originalType, format == ValueFormat.Reference || format == ValueFormat.LateReference, dataFormat, !_model.ProtoCompatibility.SuppressNullWireType, _model));
                    }
                    else if (format == ValueFormat.LateReference && CanTypeBeAsLateReferenceOnBuildStage(baseKey, _model))
                    {
                        return(new NetObjectValueDecorator(originalType, baseKey, true, true, _model[type], !_model.ProtoCompatibility.SuppressNullWireType, _model));
                    }
                    else if (MetaType.IsNetObjectValueDecoratorNecessary(_model, format))
                    {
                        return(new NetObjectValueDecorator(originalType, baseKey, format == ValueFormat.Reference || format == ValueFormat.LateReference, false, _model[type], !_model.ProtoCompatibility.SuppressNullWireType, _model));
                    }
                    else
                    {
                        return(new ModelTypeSerializer(type, baseKey, _model[type], _model));
                    }
                }
            }
            defaultWireType = WireType.None;
            return(null);
        }