summaryrefslogtreecommitdiff
path: root/external/aspnetwebstack/src/System.Net.Http.Formatting/ObjectContent.cs
blob: df781ad3cadb8750ffb6e834d6fea211471ff3a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.

using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.IO;
using System.Net.Http.Formatting;
using System.Threading.Tasks;

namespace System.Net.Http
{
    /// <summary>
    /// Contains a value as well as an associated <see cref="MediaTypeFormatter"/> that will be
    /// used to serialize the value when writing this content.
    /// </summary>
    public class ObjectContent : HttpContent
    {
        private object _value;
        private readonly MediaTypeFormatter _formatter;

        /// <summary>
        /// Initializes a new instance of the <see cref="ObjectContent"/> class.
        /// </summary>
        /// <param name="type">The type of object this instance will contain.</param>
        /// <param name="value">The value of the object this instance will contain.</param>
        /// <param name="formatter">The formatter to use when serializing the value.</param>
        public ObjectContent(Type type, object value, MediaTypeFormatter formatter)
            : this(type, value, formatter, null)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ObjectContent"/> class.
        /// </summary>
        /// <param name="type">The type of object this instance will contain.</param>
        /// <param name="value">The value of the object this instance will contain.</param>
        /// <param name="formatter">The formatter to use when serializing the value.</param>
        /// <param name="mediaType">The media type to associate with this object.</param>
        public ObjectContent(Type type, object value, MediaTypeFormatter formatter, string mediaType)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }
            if (formatter == null)
            {
                throw new ArgumentNullException("formatter");
            }

            _formatter = formatter;
            ObjectType = type;

            VerifyAndSetObject(value);
            _formatter.SetDefaultContentHeaders(type, Headers, mediaType);
        }

        /// <summary>
        /// Gets the type of object managed by this <see cref="ObjectContent"/> instance.
        /// </summary>
        public Type ObjectType { get; private set; }

        /// <summary>
        /// The <see cref="MediaTypeFormatter">formatter</see> associated with this content instance.
        /// </summary>
        public MediaTypeFormatter Formatter
        {
            get { return _formatter; }
        }

        /// <summary>
        /// Gets or sets the value of the current <see cref="ObjectContent"/>.
        /// </summary>
        public object Value
        {
            get { return _value; }
            set { VerifyAndSetObject(value); }
        }

        /// <summary>
        /// Asynchronously serializes the object's content to the given <paramref name="stream"/>.
        /// </summary>
        /// <param name="stream">The <see cref="Stream"/> to which to write.</param>
        /// <param name="context">The associated <see cref="TransportContext"/>.</param>
        /// <returns>A <see cref="Task"/> instance that is asynchronously serializing the object's content.</returns>
        protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
        {
            return _formatter.WriteToStreamAsync(ObjectType, Value, stream, Headers, context);
        }

        /// <summary>
        /// Computes the length of the stream if possible.
        /// </summary>
        /// <param name="length">The computed length of the stream.</param>
        /// <returns><c>true</c> if the length has been computed; otherwise <c>false</c>.</returns>
        protected override bool TryComputeLength(out long length)
        {
            length = -1;
            return false;
        }

        private static bool IsTypeNullable(Type type)
        {
            return !type.IsValueType ||
                    (type.IsGenericType &&
                    type.GetGenericTypeDefinition() == typeof(Nullable<>));
        }

        private void VerifyAndSetObject(object value)
        {
            Contract.Assert(ObjectType != null, "Type cannot be null");

            if (value == null)
            {
                // Null may not be assigned to value types (unless Nullable<T>)
                if (!IsTypeNullable(ObjectType))
                {
                    throw new InvalidOperationException(RS.Format(Properties.Resources.CannotUseNullValueType, typeof(ObjectContent).Name, ObjectType.Name));
                }
            }
            else
            {
                // Non-null objects must be a type assignable to Type
                Type objectType = value.GetType();
                if (!ObjectType.IsAssignableFrom(objectType))
                {
                    throw new ArgumentException(RS.Format(Properties.Resources.ObjectAndTypeDisagree, objectType.Name, ObjectType.Name), "value");
                }

                if (!_formatter.CanWriteType(objectType))
                {
                    throw new InvalidOperationException(RS.Format(Properties.Resources.ObjectContent_FormatterCannotWriteType, _formatter.GetType().FullName, objectType.Name));
                }
            }

            _value = value;
        }
    }

    /// <summary>
    /// Generic form of <see cref="ObjectContent"/>.
    /// </summary>
    /// <typeparam name="T">The type of object this <see cref="ObjectContent"/> class will contain.</typeparam>
    [SuppressMessage("Microsoft.StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Class contains generic forms")]
    public class ObjectContent<T> : ObjectContent
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ObjectContent{T}"/> class.
        /// </summary>
        /// <param name="value">The value of the object this instance will contain.</param>
        /// <param name="formatter">The formatter to use when serializing the value.</param>
        public ObjectContent(T value, MediaTypeFormatter formatter)
            : this(value, formatter, null)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ObjectContent{T}"/> class.
        /// </summary>
        /// <param name="value">The value of the object this instance will contain.</param>
        /// <param name="formatter">The formatter to use when serializing the value.</param>
        /// <param name="mediaType">The media type to associate with this object.</param>
        public ObjectContent(T value, MediaTypeFormatter formatter, string mediaType)
            : base(typeof(T), value, formatter, mediaType)
        {
        }
    }
}