LOG4PHP-141: Finished default renderer implementation.
Updated docs and tests.

git-svn-id: https://svn.apache.org/repos/asf/logging/log4php/trunk@1394956 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/php/LoggerAutoloader.php b/src/main/php/LoggerAutoloader.php
index d3b5df5..87a87c7 100644
--- a/src/main/php/LoggerAutoloader.php
+++ b/src/main/php/LoggerAutoloader.php
@@ -128,7 +128,6 @@
 		'LoggerRendererException' => '/renderers/LoggerRendererException.php',

 		'LoggerRendererMap' => '/renderers/LoggerRendererMap.php',

 		'LoggerRenderer' => '/renderers/LoggerRenderer.php',

-		'LoggerRendererObject'	=> '/renderers/LoggerRendererObject.php',

 	);

 	

 	/**

diff --git a/src/main/php/LoggerHierarchy.php b/src/main/php/LoggerHierarchy.php
index 8b71601..55fe143 100644
--- a/src/main/php/LoggerHierarchy.php
+++ b/src/main/php/LoggerHierarchy.php
@@ -204,7 +204,7 @@
 			$logger->removeAllAppenders();
 		}
 		
-		$this->rendererMap->clear();
+		$this->rendererMap->reset();
 		LoggerAppenderPool::clear();
 	}
 	
diff --git a/src/main/php/configurators/LoggerConfigurationAdapterXML.php b/src/main/php/configurators/LoggerConfigurationAdapterXML.php
index 7ee6f6d..7032da6 100644
--- a/src/main/php/configurators/LoggerConfigurationAdapterXML.php
+++ b/src/main/php/configurators/LoggerConfigurationAdapterXML.php
Binary files differ
diff --git a/src/main/php/configurators/LoggerConfiguratorDefault.php b/src/main/php/configurators/LoggerConfiguratorDefault.php
index 14f2d01..f78b797 100644
--- a/src/main/php/configurators/LoggerConfiguratorDefault.php
+++ b/src/main/php/configurators/LoggerConfiguratorDefault.php
@@ -219,61 +219,36 @@
 			foreach($config['renderers'] as $rendererConfig) {

 				$this->configureRenderer($hierarchy, $rendererConfig);

 			}

-			

-			

+		}

+		

+		if (isset($config['defaultRenderer'])) {

+			$this->configureDefaultRenderer($hierarchy, $config['defaultRenderer']);

 		}

 	}

 	

 	private function configureRenderer(LoggerHierarchy $hierarchy, $config) {

-		if (isset($config['defaultObjectRenderer'])) {

-			$objectRendererClass = $config['defaultObjectRenderer']; 

-			$this->existsRendererClass($objectRendererClass);

-			$objectRendererInstance = new $objectRendererClass;

-			$this->isRendererClassLoggerRenderer($objectRendererInstance, $objectRendererClass);

-			

-			$hierarchy->getRendererMap()->setDefaultObjectRenderer($objectRendererInstance);

+		if (empty($config['renderingClass'])) {

+			$this->warn("Rendering class not specified. Skipping renderer definition.");

 			return;

 		}

 		

-		

-		if (!isset($config['renderingClass'])) {

-			$this->warn("Rendering class not specified. Skipping renderers definition.");

-			return;			

-		}

-		

-		$renderingClass = $config['renderingClass'];

-		

-		$this->existsRendererClass($renderingClass);

-		

-		$renderingClassInstance = new $renderingClass();

-		$this->isRendererClassLoggerRenderer($renderingClassInstance, $renderingClass);

-	

-		if (!isset($config['renderedClass'])) {

-			$this->warn("Rendered class not specified for rendering Class [$renderingClass]. Skipping renderers definition.");

-			return;			

-		}

-		

-		$renderedClass = $config['renderedClass'];

-		if (!class_exists($renderedClass)) {

-			$this->warn("Nonexistant rendered class [$renderedClass] specified for renderer [$renderingClass]. Skipping renderers definition.");

+		if (empty($config['renderedClass'])) {

+			$this->warn("Rendered class not specified. Skipping renderer definition.");

 			return;

 		}

 		

-		$hierarchy->getRendererMap()->addRenderer($renderedClass, $renderingClassInstance);

+		// Error handling performed by RendererMap

+		$hierarchy->getRendererMap()->addRenderer($config['renderedClass'], $config['renderingClass']);

 	}

 	

-	private function existsRendererClass($renderingClass) {

-		if (!class_exists($renderingClass)) {

-			$this->warn("Nonexistant rendering class [$renderingClass] specified. Skipping renderers definition.");

-			return;

-		}		

-	}

-	

-	private function isRendererClassLoggerRenderer($renderingClassInstance, $renderingClass) {

-		if (!$renderingClassInstance instanceof LoggerRenderer) {

-			$this->warn("Invalid class [$renderingClass] given. Not a valid LoggerRenderer class. Skipping renderers definition.");

+	private function configureDefaultRenderer(LoggerHierarchy $hierarchy, $class) {

+		if (empty($class)) {

+			$this->warn("Rendering class not specified. Skipping default renderer definition.");

 			return;

 		}

+		

+		// Error handling performed by RendererMap

+		$hierarchy->getRendererMap()->setDefaultRenderer($class);

 	}

 	

 	/** 

diff --git a/src/main/php/renderers/LoggerRenderer.php b/src/main/php/renderers/LoggerRenderer.php
index 816d1f8..dd913fe 100644
--- a/src/main/php/renderers/LoggerRenderer.php
+++ b/src/main/php/renderers/LoggerRenderer.php
@@ -19,17 +19,7 @@
  */
 
 /**
- * Implement this interface in order to render objects as strings using {@link LoggerRendererMap}.
- *
- * Implement this interface in order to render objects as strings using {@link LoggerRendererMap}.
- *
- * Example:
- * {@example ../../examples/php/renderer_map.php 19}<br>
- * {@example ../../examples/resources/renderer_map.properties 18}<br>
- * <pre>
- * DEBUG - Now comes the current MyClass object:
- * DEBUG - Doe, John
- * </pre>
+ * Implement this interface in order to render objects to strings.
  *
  * @version $Revision$
  * @package log4php
@@ -38,9 +28,9 @@
  */
 interface LoggerRenderer {
 	/**
-	 * Render the entity passed as parameter as a String.
-	 * @param mixed $input entity to render
-	 * @return string
+	 * Renders the entity passed as <var>input</var> to a string.
+	 * @param mixed $input The entity to render.
+	 * @return string The rendered string.
 	 */
 	public function render($input);
 }
diff --git a/src/main/php/renderers/LoggerRendererDefault.php b/src/main/php/renderers/LoggerRendererDefault.php
index b5f409a..2f35f97 100644
--- a/src/main/php/renderers/LoggerRendererDefault.php
+++ b/src/main/php/renderers/LoggerRendererDefault.php
@@ -19,19 +19,9 @@
  */
 
 /**
- * The default Renderer renders objects by type casting.
+ * The default renderer, which is used when no other renderer is found.
  * 
- * Example:
- * 
- * {@example ../../examples/php/renderer_default.php 19}<br>
- * {@example ../../examples/resources/renderer_default.properties 18}<br>
- * <pre>
- * DEBUG - Now comes the current MyClass object:
- * DEBUG - Person::__set_state(array(
- *  'firstName' => 'John',
- *  'lastName' => 'Doe',
- * ))
- * </pre>
+ * Renders the input using <var>print_r</var>.
  *
  * @package log4php
  * @subpackage renderers
@@ -39,13 +29,8 @@
  */
 class LoggerRendererDefault implements LoggerRenderer {
 
-	/**
-	 * Render objects by type casting
-	 *
-	 * @param mixed $input the object to render
-	 * @return string
-	 */
+	/** @inheritdoc */
 	public function render($input) {
-		return var_export($input, true);
+		return print_r($input, true);
 	}
 }
diff --git a/src/main/php/renderers/LoggerRendererException.php b/src/main/php/renderers/LoggerRendererException.php
index 4040035..d78bcb8 100644
--- a/src/main/php/renderers/LoggerRendererException.php
+++ b/src/main/php/renderers/LoggerRendererException.php
@@ -19,7 +19,7 @@
  */
 
 /**
- * Exception renderer
+ * Renderer used for Exceptions.
  *
  * @package log4php
  * @subpackage renderers
@@ -28,13 +28,9 @@
 class LoggerRendererException implements LoggerRenderer {
 
 	public function render($input) {
-		$strRep  = 'Throwable('.get_class($input).'): '.$input->getMessage().' in '.$input->getFile().' on line '.$input->getLine();
-		$strRep .= PHP_EOL.$input->getTraceAsString();
 		
-		if (method_exists($input, 'getPrevious') && $input->getPrevious() !== null) {
-			$strRep .= PHP_EOL.'Caused by: '.$this->render($input->getPrevious());
-		}
-		
-		return $strRep;		
+		// Exception class has a very decent __toString method
+		// so let's just use that instead of writing lots of code.
+		return (string) $input; 
 	}
 }
diff --git a/src/main/php/renderers/LoggerRendererMap.php b/src/main/php/renderers/LoggerRendererMap.php
index d02eed2..8e7631b 100644
--- a/src/main/php/renderers/LoggerRendererMap.php
+++ b/src/main/php/renderers/LoggerRendererMap.php
@@ -19,16 +19,9 @@
  */
 
 /**
- * Log objects using customized renderers that implement {@link LoggerRendererObject}.
+ * Manages defined renderers and determines which renderer to use for a given 
+ * input. 
  *
- * Example:
- * {@example ../../examples/php/renderer_map.php 19}<br>
- * {@example ../../examples/resources/renderer_map.properties 18}<br>
- * <pre>
- * DEBUG - Now comes the current MyClass object:
- * DEBUG - Doe, John
- * </pre>
- * 
  * @version $Revision$
  * @package log4php
  * @subpackage renderers
@@ -37,97 +30,137 @@
 class LoggerRendererMap {
 
 	/**
+	 * Maps class names to appropriate renderers.
 	 * @var array
 	 */
-	private $map;
+	private $map = array();
 
 	/**
-	 * @var LoggerDefaultRenderer
+	 * The default renderer to use if no specific renderer is found. 
+	 * @var LoggerRenderer
 	 */
 	private $defaultRenderer;
 	
-	/**
-	 * @var LoggerRendererObject
-	 */
-	private $defaultObjectRenderer;
-
-	/**
-	 * Constructor
-	 */
 	public function __construct() {
-		$this->map = array();
-		$this->defaultRenderer = new LoggerRendererDefault();
-		$this->defaultObjectRenderer = new LoggerRendererObject();
+		
+		// Set default config
+		$this->reset();
 	}
 
 	/**
-	 * Add a renderer to a hierarchy passed as parameter.
-	 * Note that hierarchy must implement getRendererMap() and setRenderer() methods.
+	 * Adds a renderer to the map.
+	 * 
+	 * If a renderer already exists for the given <var>$renderedClass</var> it 
+	 * will be overwritten without warning.
 	 *
-	 * @param LoggerHierarchy $repository a logger repository.
-	 * @param string $renderedClassName
-	 * @param string $renderingClassName
+	 * @param string $renderedClass The name of the class which will be 
+	 * 		rendered by the renderer.
+	 * @param string $renderingClass The name of the class which will 
+	 * 		perform the rendering.
 	 */
-	public function addRenderer($renderedClassName, $renderingClassName) {
-		$renderer = LoggerReflectionUtils::createObject($renderingClassName);
-		if($renderer == null) {
+	public function addRenderer($renderedClass, $renderingClass) {
+		// Check the rendering class exists
+		if (!class_exists($renderingClass)) {
+			trigger_error("log4php: Failed adding renderer. Rendering class [$renderingClass] not found.");
 			return;
-		} else {
-			$this->put($renderedClassName, $renderer);
 		}
+		
+		// Create the instance
+		$renderer = new $renderingClass();
+		
+		// Check the class implements the right interface
+		if (!($renderer instanceof LoggerRenderer)) {
+			trigger_error("log4php: Failed adding renderer. Rendering class [$renderingClass] does not implement the LoggerRenderer interface.");
+			return;
+		}
+		
+		// Convert to lowercase since class names in PHP are not case sensitive
+		$renderedClass = strtolower($renderedClass);
+		
+		$this->map[$renderedClass] = $renderer;
 	}
-
-
+	
 	/**
-	 * Find the appropriate renderer for the class type of the
-	 * <var>o</var> parameter. 
+	 * Sets a custom default renderer class.
+	 * 
+	 * TODO: there's code duplication here. This method is almost identical to 
+	 * addRenderer(). However, it has custom error messages so let it sit for 
+	 * now.
 	 *
-	 * This is accomplished by calling the {@link getByObject()} 
-	 * method if <var>o</var> is object or using {@link LoggerRendererDefault}. 
-	 * Once a renderer is found, it is applied on the object <var>o</var> and 
-	 * the result is returned as a string.
+	 * @param string $renderingClass The name of the class which will 
+	 * 		perform the rendering.
+	 */
+	public function setDefaultRenderer($renderingClass) {
+		// Check the class exists
+		if (!class_exists($renderingClass)) {
+			trigger_error("log4php: Failed setting default renderer. Rendering class [$renderingClass] not found.");
+			return;
+		}
+		
+		// Create the instance
+		$renderer = new $renderingClass();
+		
+		// Check the class implements the right interface
+		if (!($renderer instanceof LoggerRenderer)) {
+			trigger_error("log4php: Failed setting default renderer. Rendering class [$renderingClass] does not implement the LoggerRenderer interface.");
+			return;
+		}
+		
+		$this->defaultRenderer = $renderer;
+	}
+	
+	/**
+	 * Returns the default renderer.
+	 * @var LoggerRenderer
+	 */
+	public function getDefaultRenderer() {
+		return $this->defaultRenderer;
+	}
+	
+	/**
+	 * Finds the appropriate renderer for the given <var>input</var>, and 
+	 * renders it (i.e. converts it to a string). 
 	 *
-	 * @param mixed $input
-	 * @return string 
+	 * @param mixed $input Input to render.
+	 * @return string The rendered contents.
 	 */
 	public function findAndRender($input) {
-		if($input == null) {
+		if ($input === null) {
 			return null;
-		} else {
-			if(is_object($input)) {
-				$renderer = $this->getByObject($input);
-				if($renderer !== null) {
-					return $renderer->render($input);
-				}
-
-				return $this->defaultObjectRenderer->render($input);
-			} else {
-				$renderer = $this->defaultRenderer;
+		}
+		
+		// For objects, try to find a renderer in the map
+		if(is_object($input)) {
+			$renderer = $this->getByClassName(get_class($input));
+			if (isset($renderer)) {
 				return $renderer->render($input);
 			}
 		}
+		
+		// Fall back to the default renderer
+		return $this->defaultRenderer->render($input);
 	}
 
 	/**
-	 * Syntactic sugar method that calls {@link PHP_MANUAL#get_class} with the
-	 * class of the object parameter.
+	 * Returns the appropriate renderer for a given object.
 	 * 
 	 * @param mixed $object
-	 * @return string
+	 * @return LoggerRenderer Or null if none found.
 	 */
 	public function getByObject($object) {
-		return ($object == null) ? null : $this->getByClassName(get_class($object));
+		if (!is_object($object)) {
+			return null;
+		}
+		return $this->getByClassName(get_class($object));
 	}
-
-
+	
 	/**
-	 * Search the parents of <var>clazz</var> for a renderer. 
-	 *
-	 * The renderer closest in the hierarchy will be returned. If no
-	 * renderers could be found, then the default renderer is returned.
+	 * Returns the appropriate renderer for a given class name.
+	 * 
+	 * If no renderer could be found, returns NULL.
 	 *
 	 * @param string $class
-	 * @return LoggerRendererObject
+	 * @return LoggerRendererObject Or null if not found.
 	 */
 	public function getByClassName($class) {
 		for(; !empty($class); $class = get_parent_class($class)) {
@@ -139,24 +172,15 @@
 		return null;
 	}
 
+	/** Empties the renderer map. */
 	public function clear() {
 		$this->map = array();
 	}
-
-	/**
-	 * Register a {@link LoggerRendererObject}.
-	 * @param string $class Class which to render.
-	 * @param LoggerRendererObject $renderer
-	 */
-	private function put($class, $renderer) {
-		$this->map[strtolower($class)] = $renderer;
-	}
 	
-	public function setDefaultObjectRenderer($renderer) {
-		$this->defaultObjectRenderer = $renderer;
-	}
-	
-	public function getDefaultObjectRenderer() {
-		return $this->defaultObjectRenderer;
+	/** Resets the renderer map to it's default configuration. */
+	public function reset() {
+		$this->defaultRenderer = new LoggerRendererDefault();
+		$this->clear();
+		$this->addRenderer('Exception', 'LoggerRendererException');
 	}
 }
diff --git a/src/main/php/renderers/LoggerRendererObject.php b/src/main/php/renderers/LoggerRendererObject.php
deleted file mode 100644
index 0ad21d3..0000000
--- a/src/main/php/renderers/LoggerRendererObject.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php

-

-/**

- * 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.

- *

- * @package log4php

- */

-

-/**

- * Default implementation of a renreder for objects.

- *   

- * @package log4php

- * @subpackage renderers

- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0

- * @version $Revision$

- */

-class LoggerRendererObject implements LoggerRenderer {

-

-	public function render($o) {

-		return print_r($o, true);

-	}

-}

diff --git a/src/site/xdoc/changelog.xml b/src/site/xdoc/changelog.xml
index 50bc2d4..3dda45e 100644
--- a/src/site/xdoc/changelog.xml
+++ b/src/site/xdoc/changelog.xml
@@ -36,6 +36,7 @@
 				<p><strong>New features:</strong></p>

 				<ul>

 					<li>New appender: <a href="docs/appenders/firephp.html">FirePHP</a> (thanks to Bruce Ingalls)</li>

+					<li>Ability to override the <a href="docs/renderers.html#Default_renderer">default renderer</a>.</li>

 				</ul>

 				

 				<p><strong>Improvements:</strong></p>

@@ -63,6 +64,8 @@
 						access $_SERVER and $_ENV values using <code>%X{server.*}</code> and <code>%X{env.*}</code>

 						conversion words; use the new words <code>%server{*}</code> and <code>%env{*}</code> instead.

 						</li>

+					<li>Custom renderer classes should implement the <code>LoggerRenderer</code> interface instead of  

+						<code>LoggerRendererObject</code>.</li>

 				</ul>

 				<p>Please review the documentation and make any necessary changes to your configuration.</p>

 				

diff --git a/src/site/xdoc/docs/renderers.xml b/src/site/xdoc/docs/renderers.xml
index 2aab8db..6c1b921 100644
--- a/src/site/xdoc/docs/renderers.xml
+++ b/src/site/xdoc/docs/renderers.xml
@@ -35,7 +35,7 @@
 <pre class="prettyprint linenums"><![CDATA[

 class Person {

     public $firstName;

-    public $lastName;    

+    public $lastName;

     public $age;

 }

 ]]></pre>

@@ -57,24 +57,25 @@
 				

 <pre class="prettyprint linenums"><![CDATA[

 INFO - Here comes the person:

-INFO - Person::__set_state(array(

-   'firstName' => 'John',

-   'lastName' => 'Doe',

-   'age' => 37,

-))

+INFO - Person Object

+(

+    [firstName] => John

+    [lastName] => Doe

+    [age] => 37

+)

 ]]></pre>

 				

 			</subsection>

 			

 			<subsection name="Creating a custom renderer">

-				<p>In order to make log4php render our Person object in a more readable format, we need to create a 

-				custom renderer class which will convert Person objects to a string suitable for logging.</p>

+				<p>In order to make log4php log the Person object in a more readable format, a custom renderer 

+				class can be defined which will convert Person objects to a string suitable for logging.</p>

 				

-				<p>Let's call our renderer class PersonRenderer.</p>

+				<p>Let's call this renderer class PersonRenderer.</p>

 				

 <pre class="prettyprint linenums"><![CDATA[

-/** All object renderers must implement the LoggerRendererObject interface. */

-class PersonRenderer implements LoggerRendererObject {

+/** All renderers must implement the LoggerRenderer interface. */

+class PersonRenderer implements LoggerRenderer {

     public function render($person) {

         return "{$person->firstName} {$person->lastName} ({$person->age})";

     }

@@ -83,9 +84,15 @@
 

 				<p>Now log4php has to be configured to use PersonRenderer for rendering Person objects. This is done 

 				in the configuration file.</p>

-				

-				<p>XML configuration:</p>

-				

+

+				<div class="auto-tabs">

+					<ul>

+						<li>XML</li>

+						<li>PHP</li>

+					</ul>

+					

+					<div class="tab-content">

+						<div class="tab-pane">

 <pre class="prettyprint linenums"><![CDATA[

 <configuration xmlns="http://logging.apache.org/log4php/">

     <renderer renderedClass="Person" renderingClass="PersonRenderer" />

@@ -95,14 +102,15 @@
     </root>

 </configuration>

 ]]></pre>

-				

-				<p>Equivalent PHP configuration:</p>

-				

+						</div>

+						<div class="tab-pane">

 <pre class="prettyprint linenums"><![CDATA[

 array(

     'renderers' => array(

-        'renderedClass' => 'Person',

-        'renderingClass' => 'PersonRenderer'

+        array(

+            'renderedClass' => 'Person',

+            'renderingClass' => 'PersonRenderer'

+        )

     ),

     'appenders' => array(

         'default' => array(

@@ -112,8 +120,12 @@
     'rootLogger' => array(

         'appenders' => array('default'),

     ),

-);

+)

 ]]></pre>

+						</div>

+					</div>

+				</div>

+				

 			

 				<p>If the same code is run as above, the following output is produced:</p>

 			

@@ -124,6 +136,75 @@
 				<p>Which is much more readable than the default rendering.</p>

 			</subsection>

 			

+			<subsection name="Overriding the default renderer">

+				<p>It is possible to set your own custom renderer as the default renderer.</p>

+				

+				<p>For example, if you're not happy with the way in which the default renderer converts objects, 

+				you can create your own renderer:</p>

+				   

+<pre class="prettyprint linenums"><![CDATA[

+class MyRenderer implements LoggerRenderer {

+    public function render($input) {

+        return var_dump($input);

+    }

+}

+]]></pre>

+

+				<p>And set it as the default renderer in the configuration:</p>

+				

+				<div class="auto-tabs">

+					<ul>

+						<li>XML</li>

+						<li>PHP</li>

+					</ul>

+					

+					<div class="tab-content">

+						<div class="tab-pane">

+<pre class="prettyprint linenums"><![CDATA[

+<configuration xmlns="http://logging.apache.org/log4php/">

+    <defaultRenderer renderingClass="MyRenderer" />

+    <appender name="defualt" class="LoggerAppenderEcho" />

+    <root>

+        <appender_ref ref="defualt" />

+    </root>

+</configuration>

+]]></pre>

+						</div>

+						<div class="tab-pane">

+<pre class="prettyprint linenums"><![CDATA[

+array(

+    'defaultRenderer' => 'MyRenderer',

+    'appenders' => array(

+        'default' => array(

+            'class' => 'LoggerAppenderEcho',

+        ),

+    ),

+    'rootLogger' => array(

+        'appenders' => array('default'),

+    ),

+)

+]]></pre>

+						</div>

+					</div>

+				</div>

+				

+				<p>Logging a Person object using this configuration will make log4php fall back to the newly 

+				defined default renderer, which uses <code>var_dump</code> instead of <code>print_r</code>, and 

+				the result will look like:</p>

+						

+<pre class="prettyprint linenums"><![CDATA[

+INFO - Here comes the person:

+class Person#5 (3) {

+  public $firstName =>

+  string(4) "John"

+  public $lastName =>

+  string(3) "Doe"

+  public $age =>

+  int(37)

+}

+]]></pre>

+			</subsection>

+			

 			<subsection name="Class hierarchy">

 				<p>Object rendering follows the class hierarchy. </p>

 

diff --git a/src/test/php/LoggerConfiguratorTest.php b/src/test/php/LoggerConfiguratorTest.php
index d82e03c..e8ff410 100644
--- a/src/test/php/LoggerConfiguratorTest.php
+++ b/src/test/php/LoggerConfiguratorTest.php
@@ -185,7 +185,7 @@
 

     /**

  	 * @expectedException PHPUnit_Framework_Error

- 	 * @expectedExceptionMessage Invalid class [stdClass] given. Not a valid LoggerRenderer class. Skipping renderers definition.

+ 	 * @expectedExceptionMessage Failed adding renderer. Rendering class [stdClass] does not implement the LoggerRenderer interface.

  	 */

  	public function testInvalidRenderingClassSet() {

  		Logger::configure(PHPUNIT_CONFIG_DIR . '/renderers/config_invalid_rendering_class.xml');

@@ -193,7 +193,7 @@
  	

     /**

  	 * @expectedException PHPUnit_Framework_Error

- 	 * @expectedExceptionMessage Rendering class not specified. Skipping renderers definition.

+ 	 * @expectedExceptionMessage Rendering class not specified. Skipping renderer definition.

  	 */

  	public function testNoRenderingClassSet() {

  		Logger::configure(PHPUNIT_CONFIG_DIR . '/renderers/config_no_rendering_class.xml');

@@ -201,23 +201,15 @@
 

     /**

  	 * @expectedException PHPUnit_Framework_Error

- 	 * @expectedExceptionMessage Rendered class not specified for rendering Class [LoggerRendererDefault]. Skipping renderers definition.

+ 	 * @expectedExceptionMessage Rendered class not specified. Skipping renderer definition.

  	 */

  	public function testNoRenderedClassSet() {

  		Logger::configure(PHPUNIT_CONFIG_DIR . '/renderers/config_no_rendered_class.xml');

  	} 	

  	

-     /**

- 	 * @expectedException PHPUnit_Framework_Error

- 	 * @expectedExceptionMessage Nonexistant rendered class [RenderFooClass] specified for renderer [LoggerRendererDefault]. Skipping renderers definition.

- 	 */

- 	public function testNotExistingRenderedClassSet() {

- 		Logger::configure(PHPUNIT_CONFIG_DIR . '/renderers/config_not_existing_rendered_class.xml');

- 	} 	

- 	

  	/**

  	 * @expectedException PHPUnit_Framework_Error

- 	 * @expectedExceptionMessage Nonexistant rendering class [FooRenderer] specified. Skipping renderers definition.

+ 	 * @expectedExceptionMessage Failed adding renderer. Rendering class [DoesNotExistRenderer] not found.

  	 */

  	public function testNotExistingRenderingClassSet() {

  		Logger::configure(PHPUNIT_CONFIG_DIR . '/renderers/config_not_existing_rendering_class.xml');

@@ -457,33 +449,4 @@
  			) 

  		));

  	}

- 	

- 	public function testConfigureRenderer_ShouldReturnBuildInObjectRenderer() {

- 		Logger::configure(array(

- 			'rootLogger' => array(

- 				'appenders' => array('default')

- 			),

- 			'appenders' => array(

- 				'default' => array(

- 					'class' => 'LoggerAppenderEcho',

- 					'threshold' => 'INFO'

- 				),

- 			) 

- 		));

- 			

- 		$actualDefaultObjectRenderer = Logger::getHierarchy()->getRendererMap()->getDefaultObjectRenderer();

- 		

- 		$this->assertTrue($actualDefaultObjectRenderer instanceof LoggerRendererObject, 'build in object renderer');

- 	} 	

- 	

- 	public function testConfigureRenderer_SetupCostumDefaultObjectRenderer() {

- 		Logger::configure(array(

- 			'renderers' => array(

- 					array('defaultObjectRenderer' => 'CostumDefaultRenderer')

- 			)));

- 		

- 		$actualCostumObjectRenderer = Logger::getHierarchy()->getRendererMap()->getDefaultObjectRenderer();

- 		

- 		$this->assertNotNull($actualCostumObjectRenderer, 'costum default object renderer');

- 	}

  }
\ No newline at end of file
diff --git a/src/test/php/renderers/LoggerRendererDefaultTest.php b/src/test/php/renderers/LoggerRendererDefaultTest.php
deleted file mode 100644
index 66e915c..0000000
--- a/src/test/php/renderers/LoggerRendererDefaultTest.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-/**
- * 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.
- * 
- * @category   tests   
- * @package    log4php
- * @subpackage renderers
- * @license    http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
- * @version    $Revision$
- * @link       http://logging.apache.org/log4php
- */
-
-class DefaultRendererMockObject {
-	private $a;
-	protected $b;
-	public $c;
-}
-
-/**
- * @group renderers
- */
-class LoggerRendererDefaultTest extends PHPUnit_Framework_TestCase {
-
-	public function testDoRender() {
-		$class = new DefaultRendererMockObject();
-		$renderer = new LoggerRendererDefault();
-		self::assertEquals(var_export($class, true), $renderer->render($class));
-	}
-
-}
diff --git a/src/test/php/renderers/LoggerRendererExceptionTest.php b/src/test/php/renderers/LoggerRendererExceptionTest.php
deleted file mode 100644
index d612228..0000000
--- a/src/test/php/renderers/LoggerRendererExceptionTest.php
+++ /dev/null
@@ -1,74 +0,0 @@
-<?php
-/**
- * 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.
- * 
- * @category   tests   
- * @package    log4php
- * @subpackage renderers
- * @license    http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
- * @version    $Revision$
- * @link       http://logging.apache.org/log4php
- */
-
-/**
- * @group renderers
- */
-class LoggerRendererExceptionTest extends PHPUnit_Framework_TestCase {
-	
-	public function testRender() {
-		$exRenderer = new LoggerRendererException();
-		$ex1		= new LoggerRendererExceptionTestException('Message1');
-		$ex2		= new LoggerRendererExceptionTestException('Message2', 0, $ex1);
-		$ex3		= new LoggerRendererExceptionTestException('Message3', 0, $ex2);
-		
-        $rendered   = $exRenderer->render($ex3);
-        
-		$expected	= 3;        
-		$result		= substr_count($rendered, 'Throwable(LoggerRendererExceptionTestException): Message');		
-		$this->assertEquals($expected, $result);		
-        
-        $expected   = 2;        
-        $result     = substr_count($rendered, 'Caused by: Throwable(LoggerRendererExceptionTestException):');        
-        $this->assertEquals($expected, $result);
-        
-        $expected   = 1;        
-        $result     = substr_count($rendered, 'Caused by: Throwable(LoggerRendererExceptionTestException): Message2');        
-        $this->assertEquals($expected, $result);
-
-        $expected   = 1;        
-        $result     = substr_count($rendered, 'Caused by: Throwable(LoggerRendererExceptionTestException): Message1');        
-        $this->assertEquals($expected, $result);                
-	}
-}
-
-if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
-	class LoggerRendererExceptionTestException extends Exception { }
-} else {
-	class LoggerRendererExceptionTestException extends Exception {
-		
-		protected $previous;
-		
-		public function __construct($message = '', $code = 0, Exception $previous = null) {
-			parent::__construct($message, $code);
-			$this->previous = $previous;
-		}
-		
-		public function getPrevious() {
-			return $this->previous;
-		}	 
-	}
-}
-?>
\ No newline at end of file
diff --git a/src/test/php/renderers/LoggerRendererMapTest.php b/src/test/php/renderers/LoggerRendererMapTest.php
index f3f2c1c..030f4b5 100644
--- a/src/test/php/renderers/LoggerRendererMapTest.php
+++ b/src/test/php/renderers/LoggerRendererMapTest.php
@@ -23,12 +23,15 @@
  * @link       http://logging.apache.org/log4php
  */
 
-class CostumObjectRenderer implements LoggerRenderer {
-	public function render($o) {
-		return true;
+/** Renders everything as 'foo'. */
+class FooRenderer implements LoggerRenderer {
+	public function render($input) {
+		return 'foo';
 	}
 }
 
+class InvalidCostumObjectRenderer { }
+
 class Fruit3 {
     public $test1 = 'test1';
     public $test2 = 'test2';
@@ -39,84 +42,202 @@
 }
 
 class FruitRenderer3 implements LoggerRenderer {
-    public function render($o) {
-		return $o->test1.','.$o->test2.','.$o->test3;
+    public function render($fruit) {
+		return $fruit->test1 . ',' . $fruit->test2 . ',' . $fruit->test3;
 	}
 }
 
 class SampleObject {
-	
 }
 
 /**
  * @group renderers
  */
 class LoggerRendererMapTest extends PHPUnit_Framework_TestCase {
-        
-	public function testFindAndRender() {
-		$fruit = new Fruit3();
-		Logger::configure(dirname(__FILE__).'/test4.properties');
-		$hierarchy = Logger::getHierarchy();
-		
-		$map = $hierarchy->getRendererMap();
-		$e = $map->findAndRender($fruit);
-		self::assertEquals('test1,test2,test3', $e);
-	}
-        
-	public function testFindAndRenderDescendants() {
-		$fruit = new Fruit3Descendant();
-		Logger::configure(dirname(__FILE__).'/test4.properties');
-		$hierarchy = Logger::getHierarchy();
 
-		$map = $hierarchy->getRendererMap();
-		$e = $map->findAndRender($fruit);
-		self::assertEquals('test1,test2,test3', $e);
-	}
-
-	public function testGetByObject() {
-		$fruit = new Fruit3();
-		Logger::configure(dirname(__FILE__).'/test4.properties');
-		$hierarchy = Logger::getHierarchy();
+	public function testDefaults() {
 		
-		$map = $hierarchy->getRendererMap();
-		$e = $map->getByObject($fruit);
-		self::assertTrue($e instanceof FruitRenderer3);
-	}
-        
-	public function testGetByClassName() {
-		Logger::configure(dirname(__FILE__).'/test4.properties');
-		$hierarchy = Logger::getHierarchy();
+		$map = new LoggerRendererMap();
+		$actual = $map->getByClassName('Exception');
+		self::assertInstanceOf('LoggerRendererException', $actual);
 		
-		$map = $hierarchy->getRendererMap();
-		$e = $map->getByClassName('Fruit3');
-		self::assertTrue($e instanceof FruitRenderer3);
+		// Check non-configured objects return null
+		self::assertNull($map->getByObject(new stdClass()));
+		self::assertNull($map->getByClassName('stdClass'));
 	}
 	
-	public function testUsage() {
-	    Logger::resetConfiguration();
-        Logger::configure(dirname(__FILE__).'/test4.properties');
-        $logger = Logger::getRootLogger();
- 
-        ob_start();
-        $logger->warn(new Fruit3());
-        $actual = ob_get_contents();
-        ob_end_clean();
-
-        $expected = "WARN - test1,test2,test3" . PHP_EOL;
-        self::assertEquals($expected, $actual);
+	public function testClear() 
+	{
+		$map = new LoggerRendererMap();
+		$map->clear(); // This should clear the map and remove default renderers
+		self::assertNull($map->getByClassName('Exception'));
 	}
 	
-	public function testGetByObject_CostumRendererShouldRenderObject() {
-		$sampleObject = new SampleObject();
+	public function testFindAndRender() 
+	{
+		$map = new LoggerRendererMap();
+		$map->addRenderer('Fruit3', 'FruitRenderer3');
+
+		$fruit = new Fruit3();
+		$descendant = new Fruit3Descendant();
 		
-		Logger::configure(dirname(__FILE__).'/test4.properties');
-		$hierarchy = Logger::getHierarchy();
+		// Check rendering of fruit
+		$actual = $map->findAndRender($fruit);
+		$expected = 'test1,test2,test3';
+		self::assertSame($expected, $actual);
 		
-		$map = $hierarchy->getRendererMap();
-		$map->setDefaultObjectRenderer(new CostumObjectRenderer());
+		$actual = $map->getByObject($fruit);
+		self::assertInstanceOf('FruitRenderer3', $actual);
 		
-		$actual = $map->findAndRender($sampleObject);
+		// Check rendering of fruit's descendant
+		$actual = $map->findAndRender($descendant);
+		$expected = 'test1,test2,test3';
+		self::assertSame($expected, $actual);
 		
-		$this->assertTrue($actual, 'costumobjectrenderer was rendered object');
+		$actual = $map->getByObject($descendant);
+		self::assertInstanceOf('FruitRenderer3', $actual);
+		
+		// Test rendering null returns null
+		self::assertNull($map->findAndRender(null));
 	}
+	
+	/**
+	 * Try adding a non-existant class as renderer.
+	 * @expectedException PHPUnit_Framework_Error
+ 	 * @expectedExceptionMessage Failed adding renderer. Rendering class [DoesNotExist] not found.
+	 */
+	public function testAddRendererError1() 
+	{
+		$map = new LoggerRendererMap();
+		$map->addRenderer('Fruit3', 'DoesNotExist');
+	}
+	
+	/**
+	 * Try adding a class which does not implement LoggerRenderer as renderer.
+	 * @expectedException PHPUnit_Framework_Error
+ 	 * @expectedExceptionMessage Failed adding renderer. Rendering class [stdClass] does not implement the LoggerRenderer interface.
+	 */
+	public function testAddRendererError2() 
+	{
+		$map = new LoggerRendererMap();
+		$map->addRenderer('Fruit3', 'stdClass');
+	}
+	
+	public function testAddRendererError3() 
+	{
+		$map = new LoggerRendererMap();
+		@$map->addRenderer('Fruit3', 'stdClass');
+		self::assertNull($map->getByClassName('Fruit3'));
+		
+		@$map->addRenderer('Fruit3', 'DoesNotExist');
+		self::assertNull($map->getByClassName('Fruit3'));
+	}
+	
+	/**
+	 * Try setting a non-existant class as default renderer.
+	 * @expectedException PHPUnit_Framework_Error
+	 * @expectedExceptionMessage Failed setting default renderer. Rendering class [DoesNotExist] not found.
+	 */
+	public function testSetDefaultRendererError1() 
+	{
+		$map = new LoggerRendererMap();
+		$map->setDefaultRenderer('DoesNotExist');
+	}
+	
+	/**
+	 * Try setting a class which does not implement LoggerRenderer as default renderer.
+	 * @expectedException PHPUnit_Framework_Error
+	 * @expectedExceptionMessage Failed setting default renderer. Rendering class [stdClass] does not implement the LoggerRenderer interface.
+	 */
+	public function testSetDefaultRendererError2()
+	{
+		$map = new LoggerRendererMap();
+		$map->setDefaultRenderer('stdClass');
+	}
+	
+	public function testSetDefaultRendererError3()
+	{
+		$map = new LoggerRendererMap();
+		$expected =  $map->getDefaultRenderer();
+		
+		@$map->setDefaultRenderer('stdClass');
+		$actual = $map->getDefaultRenderer();
+		self::assertSame($expected, $actual);
+	
+		@$map->setDefaultRenderer('DoesNotExist');
+		$actual = $map->getDefaultRenderer();
+		self::assertSame($expected, $actual);
+	}
+	
+	public function testFetchingRenderer() 
+	{
+		$map = new LoggerRendererMap();
+		$map->addRenderer('Fruit3', 'FruitRenderer3');
+	}
+	
+	public function testDefaultRenderer() 
+	{
+		$fruit = new Fruit3();
+		
+		$map = new LoggerRendererMap();
+		$actual = $map->findAndRender($fruit);
+		
+		$defaultRenderer = new LoggerRendererDefault();
+		$expected = $defaultRenderer->render($fruit);
+		self::assertSame($expected, $actual);
+	}
+	
+	public function testOverrideDefaultRenderer() 
+	{
+		$map = new LoggerRendererMap();
+		$default = $map->getDefaultRenderer();
+		
+		$array = array(1, 2, 3);
+		
+		$actual = $map->findAndRender($array);
+		$expected = print_r($array, true);
+		self::assertSame($actual, $expected);
+		
+		// Now switch the default renderer
+		$map->setDefaultRenderer('FooRenderer');
+		$actual = $map->findAndRender($array);
+		$expected = 'foo';
+		self::assertSame($actual, $expected);
+	}
+
+	public function testGetByObjectCrap()
+	{
+		$map = new LoggerRendererMap();
+		
+		// Non object input should always return null
+		self::assertNull($map->getByObject(null));
+		self::assertNull($map->getByObject(array()));
+		self::assertNull($map->getByObject('sdasda'));
+	}
+	
+	public function testXMLConfig() 
+	{
+		$map = Logger::getHierarchy()->getRendererMap();
+		Logger::resetConfiguration();
+		self::assertInstanceOf('LoggerRendererDefault', $map->getDefaultRenderer());
+		
+		Logger::configure(PHPUNIT_CONFIG_DIR . '/renderers/config_default_renderer.xml');
+		self::assertInstanceOf('FruitRenderer3', $map->getDefaultRenderer());
+		
+		Logger::resetConfiguration();
+		self::assertInstanceOf('LoggerRendererDefault', $map->getDefaultRenderer());
+	}
+	
+	public function testExceptionRenderer() 
+	{
+		$ex = new LoggerException("This is a test");
+		
+		$map = new LoggerRendererMap();
+		$actual = $map->findAndRender($ex);
+		$expected = (string) $ex;
+
+		self::assertSame($expected, $actual);
+	}
+	
+	
 }
diff --git a/src/test/php/renderers/LoggerRendererObjectTest.php b/src/test/php/renderers/LoggerRendererObjectTest.php
deleted file mode 100644
index 090c148..0000000
--- a/src/test/php/renderers/LoggerRendererObjectTest.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php

-/**

- * 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.

- * 

- * @category   tests   

- * @package    log4php

- * @subpackage renderers

- * @license    http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0

- * @version    $Revision$

- * @link       http://logging.apache.org/log4php

- */

-

-class DefaultObject {

-	private $a;

-	protected $b;

-	public $c;

-}

-

-/**

- * @group renderers

- */

-class LoggerRendererObjectTest extends PHPUnit_Framework_TestCase {

-

-	public function testDoRender() {

-		$class = new DefaultObject();

-		$renderer = new LoggerRendererObject();

-		self::assertEquals(print_r($class, true), $renderer->render($class));

-	}

-

-}

diff --git a/src/test/php/renderers/test4.properties b/src/test/php/renderers/test4.properties
deleted file mode 100644
index 16acb2a..0000000
--- a/src/test/php/renderers/test4.properties
+++ /dev/null
@@ -1,27 +0,0 @@
-; 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.
-;
-log4php.renderer.Fruit3=FruitRenderer3
-
-log4php.appender.default = LoggerAppenderEcho
-log4php.appender.default.layout = LoggerLayoutSimple
-log4php.appender.default.threshold = WARN
-
-log4php.appender.rendr2 = LoggerAppenderEcho
-log4php.appender.rendr2.layout = LoggerLayoutSimple
-log4php.appender.rendr2.threshold = ERROR
-
-log4php.threshold = WARN
-log4php.rootLogger = WARN, default, rendr2
diff --git a/src/test/resources/configs/renderers/config_not_existing_rendered_class.xml b/src/test/resources/configs/renderers/config_default_renderer.xml
similarity index 82%
rename from src/test/resources/configs/renderers/config_not_existing_rendered_class.xml
rename to src/test/resources/configs/renderers/config_default_renderer.xml
index d54f39f..69f7dff 100644
--- a/src/test/resources/configs/renderers/config_not_existing_rendered_class.xml
+++ b/src/test/resources/configs/renderers/config_default_renderer.xml
@@ -16,11 +16,9 @@
  limitations under the License.

 -->

 <configuration xmlns="http://logging.apache.org/log4php">

-    <appender name="foo" class="LoggerAppenderConsole">

-        <layout class="LoggerLayoutSimple"/>

-    </appender>

+    <appender name="foo" class="LoggerAppenderConsole" />

     

-    <renderer renderedClass="RenderFooClass" renderingClass="LoggerRendererDefault" />

+    <defaultRenderer renderingClass="FruitRenderer3"  />

     

     <root>

         <level value="DEBUG" />

diff --git a/src/test/resources/configs/renderers/config_not_existing_rendering_class.xml b/src/test/resources/configs/renderers/config_not_existing_rendering_class.xml
index fa73181..8a32d3e 100644
--- a/src/test/resources/configs/renderers/config_not_existing_rendering_class.xml
+++ b/src/test/resources/configs/renderers/config_not_existing_rendering_class.xml
@@ -20,7 +20,7 @@
         <layout class="LoggerLayoutSimple"/>

     </appender>

     

-    <renderer renderedClass="stdClass" renderingClass="FooRenderer" />

+    <renderer renderedClass="stdClass" renderingClass="DoesNotExistRenderer" />

     

     <root>

         <level value="DEBUG" />