Exemple #1
0
 /// <summary>
 /// These settings are only applicable for collection types when they are serialized not as a member (root or LateReference)
 /// </summary>
 public void SetNestedSettingsWhenRoot(MemberLevelSettingsValue value, int level)
 {
     if (level == 0)
     {
         throw new ArgumentOutOfRangeException(nameof(level), "Zero level settings should be specified through DefaultX properties on MetaType");
     }
     ThrowIfFrozen();
     Helpers.MemoryBarrier();
     _rootNestedVs.SetSettings(value, level);
 }
Exemple #2
0
        internal MetaType(RuntimeTypeModel model, Type type, MethodInfo factory)
        {
            this._factory = factory;
            if (model == null)
            {
                throw new ArgumentNullException(nameof(model));
            }
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            this._model = model;

            IProtoSerializer coreSerializer = model.TryGetBasicTypeSerializer(type);

            if (coreSerializer != null)
            {
                throw InbuiltType(type);
            }

            this.Type = type;

#if WINRT
            this._typeInfo = type.GetTypeInfo();
#endif

            MemberInfo[] members;

            _tupleCtor = ResolveTupleConstructor(Type, out members);
            if (_tupleCtor != null)
            {
                foreach (MemberInfo memberInfo in members)
                {
                    var level = new MemberLevelSettingsValue();
                    var vs    = new ValueSerializationSettings();
                    vs.SetSettings(new ValueSerializationSettings.LevelValue(level)
                    {
                        IsNotAssignable = true
                    }, 0);
                    vs.DefaultLevel = new ValueSerializationSettings.LevelValue(level.MakeDefaultNestedLevel());
                    var main = new MemberMainSettingsValue()
                    {
                        Name = memberInfo.Name
                    };
                    var vm = new ValueMember(main, vs, memberInfo, Type, model, canHaveDefaultValue: false, isAccessHandledOutside: true);
                    AddTupleField(vm);
                }
            }
        }
Exemple #3
0
        IProtoSerializerWithWireType BuildSerializer()
        {
            if (_serializer != null)
            {
                return(_serializer);
            }

            int opaqueToken = 0;

            try
            {
                // this lock is for whole model, not just this value member
                // so it should be taken anyway, right?
                _model.TakeLock(ref opaqueToken); // check nobody is still adding this type

                if (_serializer != null)
                {
                    return(_serializer);                     // double check
                }
                FinalizingSettings?.Invoke(this, new FinalizingSettingsArgs(this));

                if (FieldNumber < 1 && !Helpers.IsEnum(ParentType) && !_isAccessHandledOutside)
                {
                    throw new ProtoException("FieldNumber < 1 for member " + Member);
                }

                if (Helpers.IsNullOrEmpty(_main.Name))
                {
                    _main.Name = Member.Name;
                }

                _vsFinal = _vsByClient.Clone();

                var level0 = _vsFinal.GetSettingsCopy(0);
                if (level0.Basic.EffectiveType == null)
                {
                    level0.Basic.EffectiveType = MemberType;
                }
                if (!level0.IsNotAssignable)
                {
                    level0.IsNotAssignable = !Helpers.CanWrite(_model, Member);
                }
                _vsFinal.SetSettings(level0, 0);

                if (_vsFinal.DefaultValue != null && _getSpecified != null)
                {
                    throw new ProtoException("Can't use default value \"" + _vsFinal.DefaultValue + "\" on member with *Specified or ShouldSerialize check " + Member);
                }

                if (_vsFinal.DefaultValue != null && IsRequired)
                {
                    throw new ProtoException("Can't use default value \"" + _vsFinal.DefaultValue + "\" on Required member " + Member);
                }

                if (_getSpecified == null && !IsRequired && _vsFinal.DefaultValue == null)
                {
                    if (_model.UseImplicitZeroDefaults)
                    {
                        switch (Helpers.GetTypeCode(MemberType))
                        {
                        case ProtoTypeCode.Boolean:
                            _vsFinal.DefaultValue = false;
                            break;

                        case ProtoTypeCode.Decimal:
                            _vsFinal.DefaultValue = (decimal)0;
                            break;

                        case ProtoTypeCode.Single:
                            _vsFinal.DefaultValue = (float)0;
                            break;

                        case ProtoTypeCode.Double:
                            _vsFinal.DefaultValue = (double)0;
                            break;

                        case ProtoTypeCode.Byte:
                            _vsFinal.DefaultValue = (byte)0;
                            break;

                        case ProtoTypeCode.Char:
                            _vsFinal.DefaultValue = (char)0;
                            break;

                        case ProtoTypeCode.Int16:
                            _vsFinal.DefaultValue = (short)0;
                            break;

                        case ProtoTypeCode.Int32:
                            _vsFinal.DefaultValue = (int)0;
                            break;

                        case ProtoTypeCode.Int64:
                            _vsFinal.DefaultValue = (long)0;
                            break;

                        case ProtoTypeCode.SByte:
                            _vsFinal.DefaultValue = (sbyte)0;
                            break;

                        case ProtoTypeCode.UInt16:
                            _vsFinal.DefaultValue = (ushort)0;
                            break;

                        case ProtoTypeCode.UInt32:
                            _vsFinal.DefaultValue = (uint)0;
                            break;

                        case ProtoTypeCode.UInt64:
                            _vsFinal.DefaultValue = (ulong)0;
                            break;

                        case ProtoTypeCode.TimeSpan:
                            _vsFinal.DefaultValue = TimeSpan.Zero;
                            break;

                        case ProtoTypeCode.Guid:
                            _vsFinal.DefaultValue = Guid.Empty;
                            break;
                        }
                    }
                }

#if !DEBUG
                try
#endif
                {
                    WireType wt;
                    var      ser = _model.ValueSerializerBuilder.BuildValueFinalSerializer(
                        _vsFinal,
                        true,
                        out wt);

                    if (!_isAccessHandledOutside)
                    {
                        PropertyInfo prop = Member as PropertyInfo;
                        if (prop != null)
                        {
                            ser = new PropertyDecorator(_model, ParentType, (PropertyInfo)Member, ser);
                        }
                        else
                        {
                            FieldInfo fld = Member as FieldInfo;
                            if (fld != null)
                            {
                                ser = new FieldDecorator(ParentType, (FieldInfo)Member, ser);
                            }
                            else
                            {
                                throw new InvalidOperationException();
                            }
                        }

                        if (_getSpecified != null || _setSpecified != null)
                        {
                            ser = new MemberSpecifiedDecorator(_getSpecified, _setSpecified, ser);
                        }
                    }
                    _serializer = ser;

                    return(ser);
                }
#if !DEBUG
                catch (ProtoException ex)
                {
                    throw new ProtoException(GetRethrowExceptionText(ex), ex);
                }
                catch (InvalidOperationException ex)
                {
                    throw new InvalidOperationException(GetRethrowExceptionText(ex), ex);
                }
                catch (NotSupportedException ex)
                {
                    throw new InvalidOperationException(GetRethrowExceptionText(ex), ex);
                }
                catch (NotImplementedException ex)
                {
                    throw new NotImplementedException(GetRethrowExceptionText(ex), ex);
                }
                catch (ArgumentNullException ex)
                {
                    throw new ArgumentNullException(GetRethrowExceptionText(ex), ex);
                }
                catch (ArgumentOutOfRangeException ex)
                {
                    throw new ArgumentOutOfRangeException(GetRethrowExceptionText(ex), ex);
                }
                catch (ArgumentException ex)
                {
#if SILVERLIGHT || PORTABLE
                    throw new ArgumentException(GetRethrowExceptionText(ex), ex);
#else
                    throw new ArgumentException(GetRethrowExceptionText(ex), ex.ParamName, ex);
#endif
                }
                catch (System.MissingMemberException ex)
                {
                    throw new System.MissingMemberException(GetRethrowExceptionText(ex), ex);
                }
                catch (MemberAccessException ex)
                {
                    throw new MemberAccessException(GetRethrowExceptionText(ex), ex);
                }
                catch (Exception ex)
                {
                    throw new ProtoException(GetRethrowExceptionText(ex), ex);
                }
#endif
            }
            finally
            {
                _model.ReleaseLock(opaqueToken);
            }
        }
        ValueSerializationSettings.LevelValue CompleteLevel(ValueSerializationSettings vs, int levelNr, out object defaultValue)
        {
            var lv    = vs.GetSettingsCopy(levelNr);
            var level = lv.Basic; // do not use lv.Basic, it's overwritten at the end of this method

            var originalLevel = level;

            if (levelNr == 0)
            {
                //#if WINRT
                if (vs.DefaultValue != null && _model.MapType(vs.DefaultValue.GetType()) != level.EffectiveType)
                //#else
                //            if (defaultValue != null && !memberType.IsInstanceOfType(defaultValue))
                //#endif
                {
                    vs.DefaultValue = ParseDefaultValue(level.EffectiveType, vs.DefaultValue);
                }
                defaultValue = vs.DefaultValue;
            }
            else
            {
                defaultValue = null;
            }

            int      idx = _model.FindOrAddAuto(level.EffectiveType, false, true, false);
            MetaType effectiveMetaType = null;

            if (idx >= 0)
            {
                effectiveMetaType = _model[idx];
                var typeSettings = effectiveMetaType.GetFinalSettingsCopy();
                level = MemberLevelSettingsValue.Merge(typeSettings.Member, level);
            }

            MemberDefaultsMode defaultsMode = level.DefaultsMode.GetValueOrDefault();

            if (level.Format == ValueFormat.NotSpecified && defaultsMode == MemberDefaultsMode.Legacy)
            {
                level.Format = GetDefaultLegacyFormat(level.EffectiveType, _model);
            }

            if (level.Format == ValueFormat.LateReference)
            {
                if (vs.MaxSpecifiedNestedLevel > levelNr)
                {
                    throw new ProtoException("LateReference member levels can't have nested levels");
                }
                var defaultSettings = effectiveMetaType?.GetFinalSettingsCopy().Member ?? new MemberLevelSettingsValue().GetInitializedToValueOrDefault();
                if (level.ContentBinaryFormatHint.GetValueOrDefault() != defaultSettings.ContentBinaryFormatHint.GetValueOrDefault() ||
                    level.WriteAsDynamicType.GetValueOrDefault() != defaultSettings.WriteAsDynamicType.GetValueOrDefault() ||
                    level.Collection.Append.GetValueOrDefault() != defaultSettings.Collection.Append.GetValueOrDefault() ||
                    level.Collection.PackedWireTypeForRead != defaultSettings.Collection.PackedWireTypeForRead)
                {
                    throw new ProtoException("LateReference member levels can't override default member settings specified on MetaType");
                }
            }

            Type newCollectionConcreteType = null;
            Type newItemType = null;

            if (!effectiveMetaType?.GetFinalSettingsCopy().IgnoreListHandling ?? true)
            {
                MetaType.ResolveListTypes(_model, level.EffectiveType, ref newItemType, ref newCollectionConcreteType);
            }

            // tuples depend on collection
            if (defaultsMode == MemberDefaultsMode.LegacyTuple && level.Format == ValueFormat.NotSpecified && newItemType != null)
            {
                level.Format = ValueFormat.Compact;
            }

            EnsureCorrectFormatSpecified(_model, ref level.Format, level.EffectiveType, ref level.WriteAsDynamicType, true);

            #region Collections

            {
                if (effectiveMetaType?.GetFinalSettingsCopy().IgnoreListHandling ?? false)
                {
                    ResetCollectionSettings(ref level);
                }
                else
                {
                    // defaults for ItemType and others were already merged from type settings
                    if (level.Collection.ItemType != null)
                    {
                        if (level.Format == ValueFormat.LateReference)
                        {
                            Type defaultItemType = (effectiveMetaType?.GetFinalSettingsCopy().Member.Collection.ItemType ?? newItemType);
                            if (level.Collection.ItemType != defaultItemType)
                            {
                                throw new ProtoException("LateReference member settings level should have default collection item type (" + defaultItemType + ")");
                            }
                        }
                    }
                    else
                    {
                        level.Collection.ItemType = newItemType;
                    }

                    if (level.Collection.ItemType == null)
                    {
                        ResetCollectionSettings(ref level);
                    }
                    else
                    {
                        // should not override with default because: what if specified something like List<string> for IList?
                        if (level.Collection.ConcreteType != null)
                        {
                            if (!Helpers.IsAssignableFrom(level.EffectiveType, level.Collection.ConcreteType))
                            {
                                throw new ProtoException(
                                          "Specified CollectionConcreteType " + level.Collection.ConcreteType.Name + " is not assignable to " + level.EffectiveType);
                            }

                            if (level.Format == ValueFormat.LateReference)
                            {
                                Type defaultConcreteType = (effectiveMetaType?.GetFinalSettingsCopy().Member.Collection.ConcreteType ?? newCollectionConcreteType);
                                if (level.Collection.ConcreteType != defaultConcreteType)
                                {
                                    throw new ProtoException("LateReference member settings level should have default collection concrete type (" + defaultConcreteType + ")");
                                }
                            }
                        }
                        else
                        {
                            level.Collection.ConcreteType = newCollectionConcreteType;
                        }

                        if (!level.Collection.Append.GetValueOrDefault() && lv.IsNotAssignable)
                        {
                            if (level.Collection.Append == null)
                            {
                                level.Collection.Append = true;
                            }
                            else
                            {
                                throw new ProtoException("The property is not writable but AppendCollection was set to false");
                            }
                        }

                        if (!CanPack(level.Collection.ItemType, level.ContentBinaryFormatHint))
                        {
                            if (level.Collection.PackedWireTypeForRead != null && level.Collection.PackedWireTypeForRead != WireType.None)
                            {
                                throw new ProtoException("PackedWireTypeForRead " + level.Collection.PackedWireTypeForRead + " specified but type can't be packed");
                            }

                            level.Collection.PackedWireTypeForRead = WireType.None;
                        }

                        if (level.Collection.Format == CollectionFormat.NotSpecified)
                        {
                            level.Collection.Format = !_model.ProtoCompatibility.SuppressCollectionEnhancedFormat
                                                          ? CollectionFormat.Enhanced
                                                          : CollectionFormat.Protobuf;
                        }
                        else if (level.Collection.Format == CollectionFormat.Enhanced && _model.ProtoCompatibility.SuppressCollectionEnhancedFormat)
                        {
                            level.Collection.Format = CollectionFormat.Protobuf;
                        }
                    }
                }

                if (!level.EffectiveType.IsArray)
                {
                    if (level.Collection.ArrayLengthReadLimit != null)
                    {
                        throw new ProtoException("ArrayLengthReadLimit specified for non-array");
                    }
                }
                else if (level.Collection.ArrayLengthReadLimit == null)
                {
                    level.Collection.ArrayLengthReadLimit = TypeModel.DefaultArrayLengthReadLimit;
                }
                else if (level.Collection.ArrayLengthReadLimit <= 0)
                {
                    throw new ProtoException("ArrayLengthReadLimit should be greater than zero or not specified");
                }
            }

            #endregion

            lv.Basic = level.GetInitializedToValueOrDefault();

            vs.SetSettings(lv, levelNr);

            originalLevel.GetHashCode();

            return(lv);
        }
        IProtoSerializerWithWireType BuildValueFinalSerializer(ValueSerializationSettings settings, bool isMemberOrNested, out WireType wireType, int levelNumber)
        {
            object defaultValue;
            var    l = CompleteLevel(settings, levelNumber, out defaultValue).Basic;

            // to ensure that model can be copied and used again
            for (int i = 1; i <= 3; i++)
            {
                var l2 = CompleteLevel(settings, levelNumber, out defaultValue);
                Debug.Assert(l.Equals(l2.Basic));
            }

            Debug.Assert(l.ContentBinaryFormatHint != null, "l.ContentBinaryFormatHint != null");
            Debug.Assert(l.WriteAsDynamicType != null, "l.WriteAsDynamicType != null");
            Debug.Assert(l.Collection.Append != null, "l.Collection.Append != null");

            // postpone all checks for types when adding member till BuildSerializer, resolve everything only on buildserializer! till that have only local not inherited settings.
            // do not allow EnumPassthru and other settings to affect anything until buildling serializer
            wireType = 0;
            Type itemType = l.Collection.ItemType ?? l.EffectiveType;

            bool itemTypeCanBeNull = CanTypeBeNull(itemType);

            bool isPacked = CanBePackedCollection(l);

            IProtoSerializerWithWireType ser = null;

            if (l.Collection.IsCollection)
            {
                Type     nestedItemType    = null;
                Type     nestedDefaultType = null;
                int      idx  = _model.FindOrAddAuto(itemType, false, true, false);
                MetaType type = idx < 0 ? null : _model[itemType];
                if (!type?.GetFinalSettingsCopy().IgnoreListHandling ?? true)
                {
                    MetaType.ResolveListTypes(_model, itemType, ref nestedItemType, ref nestedDefaultType);
                }

                bool itemIsNestedCollection = nestedItemType != null;

                // primitive types except System.Object may be handled as nested through recursion
                bool tryHandleAsRegistered = !isMemberOrNested || itemType == _model.MapType(typeof(object));

                if (tryHandleAsRegistered)
                {
                    var nestedLevel = settings.GetSettingsCopy(levelNumber + 1);
                    nestedLevel = PrepareNestedLevelForBuild(nestedLevel, itemType);
                    settings.SetSettings(nestedLevel, levelNumber + 1);
                    // should use its level settings and merge from type, ...
                    ser = BuildValueFinalSerializer(settings, true, out wireType, levelNumber + 1);

                    //object dummy = null;

                    //ser = TryGetCoreSerializer(l.ContentBinaryFormatHint.Value, nestedLevel.Basic.EffectiveType, out wireType, ref nestedLevel.Basic.Format, nestedLevel.Basic.WriteAsDynamicType.GetValueOrDefault(), l.Collection.Append.Value, isPacked, true, ref dummy);
                    //if (ser != null)
                    //    ThrowIfHasMoreLevels(settings, levelNumber + 1, l, ", no more nested type detected");
                }
                else if (!itemIsNestedCollection)
                {
                    var nestedLevel = settings.GetSettingsCopy(levelNumber + 1);
                    nestedLevel = PrepareNestedLevelForBuild(nestedLevel, itemType);
                    nestedLevel.Basic.Collection.ItemType = null; // IgnoreListHandling or not a collection
                    settings.SetSettings(nestedLevel, levelNumber + 1);

                    ser = BuildValueFinalSerializer(settings, true, out wireType, levelNumber + 1);
                }

                if (ser == null && itemIsNestedCollection)
                {
                    // if we already tried to lookup registered type no need to do it again

                    MetaType metaType;
                    if (_model.FindOrAddAuto(itemType, false, true, false, out metaType) >= 0)
                    {
                        nestedDefaultType = metaType.GetFinalSettingsCopy().ConstructType ?? nestedDefaultType ?? metaType.Type;
                    }

                    var nestedLevel = settings.GetSettingsCopy(levelNumber + 1);

                    if (nestedLevel.Basic.Collection.ConcreteType == null)
                    {
                        nestedLevel.Basic.Collection.ConcreteType = nestedDefaultType;
                    }

                    if (nestedLevel.IsNotAssignable)
                    {
                        throw new ProtoException("Nested collection item should be assignable");
                    }

                    nestedLevel.Basic.Collection.Append = false;
                    // TODO throw if set to true: throw new ProtoException("AppendCollection is not supported for nested types: " + objectType.Name);

                    if (nestedLevel.Basic.Collection.ItemType == null)
                    {
                        nestedLevel.Basic.Collection.ItemType = nestedItemType;
                    }
                    else if (!Helpers.IsAssignableFrom(nestedItemType, nestedLevel.Basic.Collection.ItemType))
                    {
                        throw new ProtoException(
                                  "Nested collection item type " + nestedLevel.Basic.Collection.ItemType + " is not assignable to " + nestedItemType + " for declared collection type " +
                                  l.EffectiveType);
                    }

                    nestedLevel = PrepareNestedLevelForBuild(nestedLevel, itemType);

                    settings.SetSettings(nestedLevel, levelNumber + 1);

                    WireType wt;
                    ser = BuildValueFinalSerializer(
                        settings,
                        true,
                        out wt,
                        levelNumber + 1);

                    isPacked = false;
                }
            }
            else
            {
                // handled outside and not wrapped with collection
                if (!isMemberOrNested)
                {
                    l.Format = ValueFormat.Compact;
                }

                isPacked = false; // it's not even a collection
                ser      = TryGetCoreSerializer(l.ContentBinaryFormatHint.Value, itemType, out wireType, ref l.Format, l.WriteAsDynamicType.Value, l.Collection.Append.Value, isPacked, true, ref defaultValue);
                if (ser != null)
                {
                    ThrowIfHasMoreLevels(settings, levelNumber, l, ", no more nested type detected");
                }
            }

            if (ser == null)
            {
                throw new InvalidOperationException("No serializer defined for type: " + itemType.FullName);
            }

            if (itemTypeCanBeNull &&
                (
                    (Helpers.GetNullableUnderlyingType(itemType) != null && Helpers.GetNullableUnderlyingType(ser.ExpectedType) == null)
                    // TODO get rid of ugly casting, maybe use builder pattern
                    || (!Helpers.IsValueType(itemType) && !(ser is NetObjectValueDecorator))
                ))
            {
                // nested level may be not collection and already wrapped with nonull, may later add check whether handled as registered vs as nested
                if (!(ser is NoNullDecorator))
                {
                    // if not wrapped with net obj - wrap with write-null check
                    ser = new NoNullDecorator(_model, ser, l.Collection.ItemType != null); // throw only for collection elements, otherwise don't write
                }
            }

            if (itemType != ser.ExpectedType && (!l.WriteAsDynamicType.Value || !Helpers.IsAssignableFrom(ser.ExpectedType, itemType)))
            {
                throw new ProtoException(string.Format("Wrong type in the tail; expected {0}, received {1}", ser.ExpectedType, itemType));
            }

            // apply lists if appropriate
            if (l.Collection.IsCollection)
            {
                bool protoCompatibility = l.Collection.Format == CollectionFormat.Protobuf || l.Collection.Format == CollectionFormat.ProtobufNotPacked;

                WireType packedReadWt;
                if (!protoCompatibility)
                {
                    packedReadWt = WireType.None;
                    Debug.Assert(!isPacked); // should be determinated before passing to TryGetCoreSerializer
                    isPacked = false;
                }
                else
                {
                    packedReadWt = CanPack(l.Collection.ItemType, l.ContentBinaryFormatHint)
                                       ? l.Collection.PackedWireTypeForRead.GetValueOrDefault(wireType)
                                       : WireType.None;
                }

                if (l.EffectiveType.IsArray)
                {
                    if (l.EffectiveType.GetArrayRank() == 1)
                    {
                        ser = new ArrayDecorator(
                            _model,
                            ser,
                            isPacked,
                            packedReadWt,
                            l.EffectiveType,
                            !l.Collection.Append.Value,
                            l.Collection.ArrayLengthReadLimit.Value,
                            protoCompatibility);
                    }
                    else
                    {
                        if (protoCompatibility)
                        {
                            throw new NotSupportedException("Multi-dimensional arrays are supported only in Enhanced collection format");
                        }

                        ser = new MultiDimensionalArrayDecorator(
                            _model,
                            ser,
                            l.EffectiveType,
                            !l.Collection.Append.Value,
                            l.Collection.ArrayLengthReadLimit.Value);
                    }
                }
                else
                {
                    ser = ListDecorator.Create(
                        _model,
                        l.EffectiveType,
                        l.Collection.ConcreteType,
                        ser,
                        isPacked,
                        packedReadWt,
                        !l.Collection.Append.Value,
                        protoCompatibility,
                        true);
                }

                if (isMemberOrNested)
                {
                    if (MetaType.IsNetObjectValueDecoratorNecessary(_model, l.Format))
                    {
                        ser = new NetObjectValueDecorator(
                            ser,
                            Helpers.GetNullableUnderlyingType(l.EffectiveType) != null,
                            l.Format == ValueFormat.Reference || l.Format == ValueFormat.LateReference,
                            l.Format == ValueFormat.LateReference && CanTypeBeAsLateReferenceOnBuildStage(_model.GetKey(l.EffectiveType, false, true), _model),
                            !_model.ProtoCompatibility.SuppressNullWireType,
                            _model);
                    }
                    else if (!Helpers.IsValueType(l.EffectiveType) || Helpers.GetNullableUnderlyingType(l.EffectiveType) != null)
                    {
                        ser = new NoNullDecorator(_model, ser, false);
                    }
                }


                if (l.EffectiveType != ser.ExpectedType && (!l.WriteAsDynamicType.Value || !Helpers.IsAssignableFrom(ser.ExpectedType, itemType)))
                {
                    throw new ProtoException(string.Format("Wrong type in the tail; expected {0}, received {1}", ser.ExpectedType, itemType));
                }
            }

            if (levelNumber == 0 && defaultValue != null)
            {
                ser = new DefaultValueDecorator(_model, defaultValue, ser);
            }

            return(ser);
        }
Exemple #6
0
        private ValueMember AddField(int fieldNumber, string memberName, Type itemType, Type defaultType, object defaultValue)
        {
            if (Type.IsArray)
            {
                throw new InvalidOperationException("Can't add fields to array type");
            }
            MemberInfo mi = null;

#if WINRT
            mi = Helpers.IsEnum(Type) ? Type.GetTypeInfo().GetDeclaredField(memberName) : Helpers.GetInstanceMember(Type.GetTypeInfo(), memberName);
#else
            MemberInfo[] members = Type.GetMember(memberName, Helpers.IsEnum(Type) ? BindingFlags.Static | BindingFlags.Public : BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            if (members != null && members.Length == 1)
            {
                mi = members[0];
            }
#endif
            if (mi == null)
            {
                throw new ArgumentException("Unable to determine member: " + memberName, nameof(memberName));
            }

            Type miType;
#if WINRT || PORTABLE
            PropertyInfo pi = mi as PropertyInfo;
            if (pi == null)
            {
                FieldInfo fi = mi as FieldInfo;
                if (fi == null)
                {
                    throw new NotSupportedException(mi.GetType().Name);
                }
                else
                {
                    miType = fi.FieldType;
                }
            }
            else
            {
                miType = pi.PropertyType;
            }
#else
            switch (mi.MemberType)
            {
            case MemberTypes.Field:
                miType = ((FieldInfo)mi).FieldType; break;

            case MemberTypes.Property:
                miType = ((PropertyInfo)mi).PropertyType; break;

            default:
                throw new NotSupportedException(mi.MemberType.ToString());
            }
#endif
            // we can't check IgnoreListHandling (because of recursion when adding type) but we don't need to
            // it will be checked in ValueSerializedBuilder.CompleteLevel stage
            ResolveListTypes(_model, miType, ref itemType, ref defaultType);

            var serializationSettings = new ValueSerializationSettings();
            var memberSettings        = new MemberMainSettingsValue {
                Tag = fieldNumber
            };

            var level0 = serializationSettings.GetSettingsCopy(0).Basic;
            level0.Collection.ConcreteType = defaultType;
            level0.Collection.ItemType     = itemType;

            serializationSettings.SetSettings(level0, 0);

            serializationSettings.DefaultValue = defaultValue;

            var def = serializationSettings.DefaultLevel.GetValueOrDefault();
            def.Basic = level0.MakeDefaultNestedLevel();
            serializationSettings.DefaultLevel = def;

            ValueMember newField = new ValueMember(memberSettings, serializationSettings, mi, Type, _model);
            Add(newField);
            return(newField);
        }