IProtoSerializerWithWireType DecorateValueSerializer(Type type, BinaryDataFormat?dynamicTypeDataFormat, ref ValueFormat format, IProtoSerializerWithWireType ser) { // Uri decorator is applied after default value // because default value for Uri is treated as string if (ser.ExpectedType == _model.MapType(typeof(string)) && type == _model.MapType(typeof(Uri))) { ser = new UriDecorator(_model, ser); } #if PORTABLE else if (ser.ExpectedType == _model.MapType(typeof(string)) && type.FullName == typeof(Uri).FullName) { // In PCLs, the Uri type may not match (WinRT uses Internal/Uri, .Net uses System/Uri) ser = new ReflectedUriDecorator(type, _model, ser); } #endif if (dynamicTypeDataFormat != null) { ser = new NetObjectValueDecorator(type, format == ValueFormat.Reference || format == ValueFormat.LateReference, dynamicTypeDataFormat.Value, !_model.ProtoCompatibility.SuppressNullWireType, _model); } else if (MetaType.IsNetObjectValueDecoratorNecessary(_model, format)) { ser = new NetObjectValueDecorator( ser, Helpers.GetNullableUnderlyingType(type) != null, format == ValueFormat.Reference || format == ValueFormat.LateReference, format == ValueFormat.LateReference && CanTypeBeAsLateReferenceOnBuildStage(_model.GetKey(type, false, true), _model), !_model.ProtoCompatibility.SuppressNullWireType, _model); } else { format = ValueFormat.Compact; } return(ser); }
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); }