jt
2021-06-10 5d0d028456874576560552f5a5c4e8b801786f11
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
#region Apache License
//
// Licensed to the Apache Software Foundation (ASF) under one or more 
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership. 
// The ASF licenses this file to you under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with 
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#endregion
 
using System;
using System.Text;
using System.IO;
using System.Collections;
 
using log4net.Util;
 
namespace log4net.ObjectRenderer
{
    /// <summary>
    /// The default object Renderer.
    /// </summary>
    /// <remarks>
    /// <para>
    /// The default renderer supports rendering objects and collections to strings.
    /// </para>
    /// <para>
    /// See the <see cref="RenderObject"/> method for details of the output.
    /// </para>
    /// </remarks>
    /// <author>Nicko Cadell</author>
    /// <author>Gert Driesen</author>
    public sealed class DefaultRenderer : IObjectRenderer
    {
        #region Constructors
 
        /// <summary>
        /// Default constructor
        /// </summary>
        /// <remarks>
        /// <para>
        /// Default constructor
        /// </para>
        /// </remarks>
        public DefaultRenderer()
        {
        }
 
        #endregion
 
        #region Implementation of IObjectRenderer
 
        /// <summary>
        /// Render the object <paramref name="obj"/> to a string
        /// </summary>
        /// <param name="rendererMap">The map used to lookup renderers</param>
        /// <param name="obj">The object to render</param>
        /// <param name="writer">The writer to render to</param>
        /// <remarks>
        /// <para>
        /// Render the object <paramref name="obj"/> to a string.
        /// </para>
        /// <para>
        /// The <paramref name="rendererMap"/> parameter is
        /// provided to lookup and render other objects. This is
        /// very useful where <paramref name="obj"/> contains
        /// nested objects of unknown type. The <see cref="RendererMap.FindAndRender(object)"/>
        /// method can be used to render these objects.
        /// </para>
        /// <para>
        /// The default renderer supports rendering objects to strings as follows:
        /// </para>
        /// <list type="table">
        ///        <listheader>
        ///            <term>Value</term>
        ///            <description>Rendered String</description>
        ///        </listheader>
        ///        <item>
        ///            <term><c>null</c></term>
        ///            <description>
        ///            <para>"(null)"</para>
        ///            </description>
        ///        </item>
        ///        <item>
        ///            <term><see cref="Array"/></term>
        ///            <description>
        ///            <para>
        ///            For a one dimensional array this is the
        ///            array type name, an open brace, followed by a comma
        ///            separated list of the elements (using the appropriate
        ///            renderer), followed by a close brace. 
        ///            </para>
        ///            <para>
        ///            For example: <c>int[] {1, 2, 3}</c>.
        ///            </para>
        ///            <para>
        ///            If the array is not one dimensional the 
        ///            <c>Array.ToString()</c> is returned.
        ///            </para>
        ///            </description>
        ///        </item>
        ///        <item>
        ///            <term><see cref="IEnumerable"/>, <see cref="ICollection"/> &amp; <see cref="IEnumerator"/></term>
        ///            <description>
        ///            <para>
        ///            Rendered as an open brace, followed by a comma
        ///            separated list of the elements (using the appropriate
        ///            renderer), followed by a close brace.
        ///            </para>
        ///            <para>
        ///            For example: <c>{a, b, c}</c>.
        ///            </para>
        ///            <para>
        ///            All collection classes that implement <see cref="ICollection"/> its subclasses, 
        ///            or generic equivalents all implement the <see cref="IEnumerable"/> interface.
        ///            </para>
        ///            </description>
        ///        </item>        
        ///        <item>
        ///            <term><see cref="DictionaryEntry"/></term>
        ///            <description>
        ///            <para>
        ///            Rendered as the key, an equals sign ('='), and the value (using the appropriate
        ///            renderer). 
        ///            </para>
        ///            <para>
        ///            For example: <c>key=value</c>.
        ///            </para>
        ///            </description>
        ///        </item>        
        ///        <item>
        ///            <term>other</term>
        ///            <description>
        ///            <para><c>Object.ToString()</c></para>
        ///            </description>
        ///        </item>
        /// </list>
        /// </remarks>
        public void RenderObject(RendererMap rendererMap, object obj, TextWriter writer)
        {
            if (rendererMap == null)
            {
                throw new ArgumentNullException("rendererMap");
            }
 
            if (obj == null)
            {
                writer.Write(SystemInfo.NullText);
                return;
            }
            
            Array objArray = obj as Array;
            if (objArray != null)
            {
                RenderArray(rendererMap, objArray, writer);
                return;
            }
 
            // Test if we are dealing with some form of collection object
            IEnumerable objEnumerable = obj as IEnumerable;
            if (objEnumerable != null)
            {
                // Get a collection interface if we can as its .Count property may be more
                // performant than getting the IEnumerator object and trying to advance it.
                ICollection objCollection = obj as ICollection;
                if (objCollection != null && objCollection.Count == 0)
                {
                    writer.Write("{}");
                    return;
                }
                
                // This is a special check to allow us to get the enumerator from the IDictionary
                // interface as this guarantees us DictionaryEntry objects. Note that in .NET 2.0
                // the generic IDictionary<> interface enumerates KeyValuePair objects rather than
                // DictionaryEntry ones. However the implementation of the plain IDictionary 
                // interface on the generic Dictionary<> still returns DictionaryEntry objects.
                IDictionary objDictionary = obj as IDictionary;
                if (objDictionary != null)
                {
                    RenderEnumerator(rendererMap, objDictionary.GetEnumerator(), writer);
                    return;
                }
 
                RenderEnumerator(rendererMap, objEnumerable.GetEnumerator(), writer);
                return;
            }
 
            IEnumerator objEnumerator = obj as IEnumerator;
            if (objEnumerator != null)
            {
                RenderEnumerator(rendererMap, objEnumerator, writer);
                return;
            }
            
            if (obj is DictionaryEntry)
            {
                RenderDictionaryEntry(rendererMap, (DictionaryEntry)obj, writer);
                return;
            }
 
            string str = obj.ToString();
            writer.Write( (str==null) ? SystemInfo.NullText : str );
        }
 
        #endregion
 
        /// <summary>
        /// Render the array argument into a string
        /// </summary>
        /// <param name="rendererMap">The map used to lookup renderers</param>
        /// <param name="array">the array to render</param>
        /// <param name="writer">The writer to render to</param>
        /// <remarks>
        /// <para>
        /// For a one dimensional array this is the
        ///    array type name, an open brace, followed by a comma
        ///    separated list of the elements (using the appropriate
        ///    renderer), followed by a close brace. For example:
        ///    <c>int[] {1, 2, 3}</c>.
        ///    </para>
        ///    <para>
        ///    If the array is not one dimensional the 
        ///    <c>Array.ToString()</c> is returned.
        ///    </para>
        /// </remarks>
        private void RenderArray(RendererMap rendererMap, Array array, TextWriter writer)
        {
            if (array.Rank != 1)
            {
                writer.Write(array.ToString());
            }
            else
            {
                writer.Write(array.GetType().Name + " {");
                int len = array.Length;
 
                if (len > 0)
                {
                    rendererMap.FindAndRender(array.GetValue(0), writer);
                    for(int i=1; i<len; i++)
                    {
                        writer.Write(", ");
                        rendererMap.FindAndRender(array.GetValue(i), writer);
                    }
                }
                writer.Write("}");
            }
        }
 
        /// <summary>
        /// Render the enumerator argument into a string
        /// </summary>
        /// <param name="rendererMap">The map used to lookup renderers</param>
        /// <param name="enumerator">the enumerator to render</param>
        /// <param name="writer">The writer to render to</param>
        /// <remarks>
        /// <para>
        /// Rendered as an open brace, followed by a comma
        ///    separated list of the elements (using the appropriate
        ///    renderer), followed by a close brace. For example:
        ///    <c>{a, b, c}</c>.
        ///    </para>
        /// </remarks>
        private void RenderEnumerator(RendererMap rendererMap, IEnumerator enumerator, TextWriter writer)
        {
            writer.Write("{");
 
            if (enumerator != null && enumerator.MoveNext())
            {
                rendererMap.FindAndRender(enumerator.Current, writer);
 
                while (enumerator.MoveNext())
                {
                    writer.Write(", ");
                    rendererMap.FindAndRender(enumerator.Current, writer);
                }
            }
 
            writer.Write("}");
        }
 
        /// <summary>
        /// Render the DictionaryEntry argument into a string
        /// </summary>
        /// <param name="rendererMap">The map used to lookup renderers</param>
        /// <param name="entry">the DictionaryEntry to render</param>
        /// <param name="writer">The writer to render to</param>
        /// <remarks>
        /// <para>
        /// Render the key, an equals sign ('='), and the value (using the appropriate
        ///    renderer). For example: <c>key=value</c>.
        ///    </para>
        /// </remarks>
        private void RenderDictionaryEntry(RendererMap rendererMap, DictionaryEntry entry, TextWriter writer)
        {
            rendererMap.FindAndRender(entry.Key, writer);
            writer.Write("=");
            rendererMap.FindAndRender(entry.Value, writer);
        }    
    }
}