RendererMap.cs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. #region Apache License
  2. //
  3. // Licensed to the Apache Software Foundation (ASF) under one or more
  4. // contributor license agreements. See the NOTICE file distributed with
  5. // this work for additional information regarding copyright ownership.
  6. // The ASF licenses this file to you under the Apache License, Version 2.0
  7. // (the "License"); you may not use this file except in compliance with
  8. // the License. You may obtain a copy of the License at
  9. //
  10. // http://www.apache.org/licenses/LICENSE-2.0
  11. //
  12. // Unless required by applicable law or agreed to in writing, software
  13. // distributed under the License is distributed on an "AS IS" BASIS,
  14. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. // See the License for the specific language governing permissions and
  16. // limitations under the License.
  17. //
  18. #endregion
  19. using System;
  20. using System.IO;
  21. #if NETSTANDARD1_3
  22. using System.Reflection;
  23. #endif
  24. using log4net.Util;
  25. namespace log4net.ObjectRenderer
  26. {
  27. /// <summary>
  28. /// Map class objects to an <see cref="IObjectRenderer"/>.
  29. /// </summary>
  30. /// <remarks>
  31. /// <para>
  32. /// Maintains a mapping between types that require special
  33. /// rendering and the <see cref="IObjectRenderer"/> that
  34. /// is used to render them.
  35. /// </para>
  36. /// <para>
  37. /// The <see cref="M:FindAndRender(object)"/> method is used to render an
  38. /// <c>object</c> using the appropriate renderers defined in this map.
  39. /// </para>
  40. /// </remarks>
  41. /// <author>Nicko Cadell</author>
  42. /// <author>Gert Driesen</author>
  43. public class RendererMap
  44. {
  45. private readonly static Type declaringType = typeof(RendererMap);
  46. #region Member Variables
  47. private System.Collections.Hashtable m_map;
  48. private System.Collections.Hashtable m_cache = new System.Collections.Hashtable();
  49. private static IObjectRenderer s_defaultRenderer = new DefaultRenderer();
  50. #endregion
  51. #region Constructors
  52. /// <summary>
  53. /// Default Constructor
  54. /// </summary>
  55. /// <remarks>
  56. /// <para>
  57. /// Default constructor.
  58. /// </para>
  59. /// </remarks>
  60. public RendererMap()
  61. {
  62. m_map = System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable());
  63. }
  64. #endregion
  65. /// <summary>
  66. /// Render <paramref name="obj"/> using the appropriate renderer.
  67. /// </summary>
  68. /// <param name="obj">the object to render to a string</param>
  69. /// <returns>the object rendered as a string</returns>
  70. /// <remarks>
  71. /// <para>
  72. /// This is a convenience method used to render an object to a string.
  73. /// The alternative method <see cref="M:FindAndRender(object,TextWriter)"/>
  74. /// should be used when streaming output to a <see cref="TextWriter"/>.
  75. /// </para>
  76. /// </remarks>
  77. public string FindAndRender(object obj)
  78. {
  79. // Optimisation for strings
  80. string strData = obj as String;
  81. if (strData != null)
  82. {
  83. return strData;
  84. }
  85. StringWriter stringWriter = new StringWriter(System.Globalization.CultureInfo.InvariantCulture);
  86. FindAndRender(obj, stringWriter);
  87. return stringWriter.ToString();
  88. }
  89. /// <summary>
  90. /// Render <paramref name="obj"/> using the appropriate renderer.
  91. /// </summary>
  92. /// <param name="obj">the object to render to a string</param>
  93. /// <param name="writer">The writer to render to</param>
  94. /// <remarks>
  95. /// <para>
  96. /// Find the appropriate renderer for the type of the
  97. /// <paramref name="obj"/> parameter. This is accomplished by calling the
  98. /// <see cref="M:Get(Type)"/> method. Once a renderer is found, it is
  99. /// applied on the object <paramref name="obj"/> and the result is returned
  100. /// as a <see cref="string"/>.
  101. /// </para>
  102. /// </remarks>
  103. public void FindAndRender(object obj, TextWriter writer)
  104. {
  105. if (obj == null)
  106. {
  107. writer.Write(SystemInfo.NullText);
  108. }
  109. else
  110. {
  111. // Optimisation for strings
  112. string str = obj as string;
  113. if (str != null)
  114. {
  115. writer.Write(str);
  116. }
  117. else
  118. {
  119. // Lookup the renderer for the specific type
  120. try
  121. {
  122. Get(obj.GetType()).RenderObject(this, obj, writer);
  123. }
  124. catch(Exception ex)
  125. {
  126. // Exception rendering the object
  127. log4net.Util.LogLog.Error(declaringType, "Exception while rendering object of type ["+obj.GetType().FullName+"]", ex);
  128. // return default message
  129. string objectTypeName = "";
  130. if (obj != null && obj.GetType() != null)
  131. {
  132. objectTypeName = obj.GetType().FullName;
  133. }
  134. writer.Write("<log4net.Error>Exception rendering object type ["+objectTypeName+"]");
  135. if (ex != null)
  136. {
  137. string exceptionText = null;
  138. try
  139. {
  140. exceptionText = ex.ToString();
  141. }
  142. catch
  143. {
  144. // Ignore exception
  145. }
  146. writer.Write("<stackTrace>" + exceptionText + "</stackTrace>");
  147. }
  148. writer.Write("</log4net.Error>");
  149. }
  150. }
  151. }
  152. }
  153. /// <summary>
  154. /// Gets the renderer for the specified object type
  155. /// </summary>
  156. /// <param name="obj">the object to lookup the renderer for</param>
  157. /// <returns>the renderer for <paramref name="obj"/></returns>
  158. /// <remarks>
  159. /// <param>
  160. /// Gets the renderer for the specified object type.
  161. /// </param>
  162. /// <param>
  163. /// Syntactic sugar method that calls <see cref="M:Get(Type)"/>
  164. /// with the type of the object parameter.
  165. /// </param>
  166. /// </remarks>
  167. public IObjectRenderer Get(Object obj)
  168. {
  169. if (obj == null)
  170. {
  171. return null;
  172. }
  173. else
  174. {
  175. return Get(obj.GetType());
  176. }
  177. }
  178. /// <summary>
  179. /// Gets the renderer for the specified type
  180. /// </summary>
  181. /// <param name="type">the type to lookup the renderer for</param>
  182. /// <returns>the renderer for the specified type</returns>
  183. /// <remarks>
  184. /// <para>
  185. /// Returns the renderer for the specified type.
  186. /// If no specific renderer has been defined the
  187. /// <see cref="DefaultRenderer"/> will be returned.
  188. /// </para>
  189. /// </remarks>
  190. public IObjectRenderer Get(Type type)
  191. {
  192. if (type == null)
  193. {
  194. throw new ArgumentNullException("type");
  195. }
  196. IObjectRenderer result = null;
  197. // Check cache
  198. result = (IObjectRenderer)m_cache[type];
  199. if (result == null)
  200. {
  201. #if NETSTANDARD1_3
  202. for (Type cur = type; cur != null; cur = cur.GetTypeInfo().BaseType)
  203. #else
  204. for(Type cur = type; cur != null; cur = cur.BaseType)
  205. #endif
  206. {
  207. // Search the type's interfaces
  208. result = SearchTypeAndInterfaces(cur);
  209. if (result != null)
  210. {
  211. break;
  212. }
  213. }
  214. // if not set then use the default renderer
  215. if (result == null)
  216. {
  217. result = s_defaultRenderer;
  218. }
  219. // Add to cache
  220. m_cache[type] = result;
  221. }
  222. return result;
  223. }
  224. /// <summary>
  225. /// Internal function to recursively search interfaces
  226. /// </summary>
  227. /// <param name="type">the type to lookup the renderer for</param>
  228. /// <returns>the renderer for the specified type</returns>
  229. private IObjectRenderer SearchTypeAndInterfaces(Type type)
  230. {
  231. IObjectRenderer r = (IObjectRenderer)m_map[type];
  232. if (r != null)
  233. {
  234. return r;
  235. }
  236. else
  237. {
  238. foreach(Type t in type.GetInterfaces())
  239. {
  240. r = SearchTypeAndInterfaces(t);
  241. if (r != null)
  242. {
  243. return r;
  244. }
  245. }
  246. }
  247. return null;
  248. }
  249. /// <summary>
  250. /// Get the default renderer instance
  251. /// </summary>
  252. /// <value>the default renderer</value>
  253. /// <remarks>
  254. /// <para>
  255. /// Get the default renderer
  256. /// </para>
  257. /// </remarks>
  258. public IObjectRenderer DefaultRenderer
  259. {
  260. get { return s_defaultRenderer; }
  261. }
  262. /// <summary>
  263. /// Clear the map of renderers
  264. /// </summary>
  265. /// <remarks>
  266. /// <para>
  267. /// Clear the custom renderers defined by using
  268. /// <see cref="Put"/>. The <see cref="DefaultRenderer"/>
  269. /// cannot be removed.
  270. /// </para>
  271. /// </remarks>
  272. public void Clear()
  273. {
  274. m_map.Clear();
  275. m_cache.Clear();
  276. }
  277. /// <summary>
  278. /// Register an <see cref="IObjectRenderer"/> for <paramref name="typeToRender"/>.
  279. /// </summary>
  280. /// <param name="typeToRender">the type that will be rendered by <paramref name="renderer"/></param>
  281. /// <param name="renderer">the renderer for <paramref name="typeToRender"/></param>
  282. /// <remarks>
  283. /// <para>
  284. /// Register an object renderer for a specific source type.
  285. /// This renderer will be returned from a call to <see cref="M:Get(Type)"/>
  286. /// specifying the same <paramref name="typeToRender"/> as an argument.
  287. /// </para>
  288. /// </remarks>
  289. public void Put(Type typeToRender, IObjectRenderer renderer)
  290. {
  291. m_cache.Clear();
  292. if (typeToRender == null)
  293. {
  294. throw new ArgumentNullException("typeToRender");
  295. }
  296. if (renderer == null)
  297. {
  298. throw new ArgumentNullException("renderer");
  299. }
  300. m_map[typeToRender] = renderer;
  301. }
  302. }
  303. }