#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.IO;
#if NETSTANDARD1_3
using System.Reflection;
#endif
using log4net.Util;
namespace log4net.ObjectRenderer
{
///
/// Map class objects to an .
///
///
///
/// Maintains a mapping between types that require special
/// rendering and the that
/// is used to render them.
///
///
/// The method is used to render an
/// object using the appropriate renderers defined in this map.
///
///
/// Nicko Cadell
/// Gert Driesen
public class RendererMap
{
private static readonly Type declaringType = typeof(RendererMap);
#region Member Variables
private System.Collections.Hashtable m_map;
private System.Collections.Hashtable m_cache = new System.Collections.Hashtable();
private static IObjectRenderer s_defaultRenderer = new DefaultRenderer();
#endregion
#region Constructors
///
/// Default Constructor
///
///
///
/// Default constructor.
///
///
public RendererMap()
{
m_map = System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable());
}
#endregion
///
/// Render using the appropriate renderer.
///
/// the object to render to a string
/// the object rendered as a string
///
///
/// This is a convenience method used to render an object to a string.
/// The alternative method
/// should be used when streaming output to a .
///
///
public string FindAndRender(object obj)
{
// Optimisation for strings
string strData = obj as String;
if (strData != null)
{
return strData;
}
using StringWriter stringWriter = new StringWriter(System.Globalization.CultureInfo.InvariantCulture);
FindAndRender(obj, stringWriter);
return stringWriter.ToString();
}
///
/// Render using the appropriate renderer.
///
/// the object to render to a string
/// The writer to render to
///
///
/// Find the appropriate renderer for the type of the
/// parameter. This is accomplished by calling the
/// method. Once a renderer is found, it is
/// applied on the object and the result is returned
/// as a .
///
///
public void FindAndRender(object obj, TextWriter writer)
{
if (obj == null)
{
writer.Write(SystemInfo.NullText);
}
else
{
// Optimisation for strings
string str = obj as string;
if (str != null)
{
writer.Write(str);
}
else
{
// Lookup the renderer for the specific type
try
{
Get(obj.GetType()).RenderObject(this, obj, writer);
}
catch(Exception ex)
{
// Exception rendering the object
log4net.Util.LogLog.Error(declaringType, "Exception while rendering object of type ["+obj.GetType().FullName+"]", ex);
// return default message
string objectTypeName = "";
if (obj != null && obj.GetType() != null)
{
objectTypeName = obj.GetType().FullName;
}
writer.Write("Exception rendering object type ["+objectTypeName+"]");
if (ex != null)
{
string exceptionText = null;
try
{
exceptionText = ex.ToString();
}
catch
{
// Ignore exception
}
writer.Write("" + exceptionText + "");
}
writer.Write("");
}
}
}
}
///
/// Gets the renderer for the specified object type
///
/// the object to lookup the renderer for
/// the renderer for
///
///
/// Gets the renderer for the specified object type.
///
///
/// Syntactic sugar method that calls
/// with the type of the object parameter.
///
///
public IObjectRenderer Get(Object obj)
{
if (obj == null)
{
return null;
}
else
{
return Get(obj.GetType());
}
}
///
/// Gets the renderer for the specified type
///
/// the type to lookup the renderer for
/// the renderer for the specified type
///
///
/// Returns the renderer for the specified type.
/// If no specific renderer has been defined the
/// will be returned.
///
///
public IObjectRenderer Get(Type type)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
IObjectRenderer result = null;
// Check cache
result = (IObjectRenderer)m_cache[type];
if (result == null)
{
#if NETSTANDARD1_3
for (Type cur = type; cur != null; cur = cur.GetTypeInfo().BaseType)
#else
for(Type cur = type; cur != null; cur = cur.BaseType)
#endif
{
// Search the type's interfaces
result = SearchTypeAndInterfaces(cur);
if (result != null)
{
break;
}
}
// if not set then use the default renderer
if (result == null)
{
result = s_defaultRenderer;
}
// Add to cache
m_cache[type] = result;
}
return result;
}
///
/// Internal function to recursively search interfaces
///
/// the type to lookup the renderer for
/// the renderer for the specified type
private IObjectRenderer SearchTypeAndInterfaces(Type type)
{
IObjectRenderer r = (IObjectRenderer)m_map[type];
if (r != null)
{
return r;
}
else
{
foreach(Type t in type.GetInterfaces())
{
r = SearchTypeAndInterfaces(t);
if (r != null)
{
return r;
}
}
}
return null;
}
///
/// Get the default renderer instance
///
/// the default renderer
///
///
/// Get the default renderer
///
///
public IObjectRenderer DefaultRenderer
{
get { return s_defaultRenderer; }
}
///
/// Clear the map of renderers
///
///
///
/// Clear the custom renderers defined by using
/// . The
/// cannot be removed.
///
///
public void Clear()
{
m_map.Clear();
m_cache.Clear();
}
///
/// Register an for .
///
/// the type that will be rendered by
/// the renderer for
///
///
/// Register an object renderer for a specific source type.
/// This renderer will be returned from a call to
/// specifying the same as an argument.
///
///
public void Put(Type typeToRender, IObjectRenderer renderer)
{
m_cache.Clear();
if (typeToRender == null)
{
throw new ArgumentNullException("typeToRender");
}
if (renderer == null)
{
throw new ArgumentNullException("renderer");
}
m_map[typeToRender] = renderer;
}
}
}