modify xml decoders so additionalProperties override existing properties (allows, for example, the ability to load a set of saved events into a new tab in chainsaw, because chainsaw can modify the application/hostname properties that control event routing).

Also fixed a bug in additional properties loading.

git-svn-id: https://svn.apache.org/repos/asf/logging/log4j/companions/receivers/trunk@829655 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/log4j/xml/UtilLoggingXMLDecoder.java b/src/main/java/org/apache/log4j/xml/UtilLoggingXMLDecoder.java
index 1c2244a..b36ffb6 100644
--- a/src/main/java/org/apache/log4j/xml/UtilLoggingXMLDecoder.java
+++ b/src/main/java/org/apache/log4j/xml/UtilLoggingXMLDecoder.java
@@ -122,14 +122,14 @@
    * @param properties additional properties
    */
   public void setAdditionalProperties(final Map properties) {
-    this.additionalProperties.putAll(properties);
+    this.additionalProperties = properties;
   }
 
   /**
    * Converts the LoggingEvent data in XML string format into an actual
    * XML Document class instance.
    * @param data XML fragment
-   * @return  XML document
+   * @return  dom document
    */
   private Document parse(final String data) {
     if (docBuilder == null || data == null) {
@@ -188,15 +188,19 @@
     }
     Vector v = new Vector();
 
-    String line = null;
-    try {
-        while ((line = reader.readLine()) != null) {
-            StringBuffer buffer = new StringBuffer(line);
-            for (int i = 0; i < 100; i++) {
-                buffer.append(reader.readLine());
-            }
-            v.addAll(decodeEvents(buffer.toString()));
-        }
+      String line;
+      Vector events;
+      try {
+          while ((line = reader.readLine()) != null) {
+              StringBuffer buffer = new StringBuffer(line);
+              for (int i = 0; i < 1000; i++) {
+                  buffer.append(reader.readLine()).append("\n");
+              }
+              events = decodeEvents(buffer.toString());
+              if (events != null) {
+                  v.addAll(events);
+              }
+          }
     } finally {
       partialEvent = null;
       try {
@@ -220,14 +224,14 @@
 
       if (document != null) {
 
-          if (document.equals("")) {
+          if (document.trim().equals("")) {
               return null;
           }
 
           String newDoc;
           String newPartialEvent = null;
           //separate the string into the last portion ending with </record>
-          // (which willbe processed) and the partial event which
+          // (which will be processed) and the partial event which
           // will be combined and processed in the next section
 
           //if the document does not contain a record end,
@@ -286,7 +290,7 @@
   /**
    * Given a Document, converts the XML into a Vector of LoggingEvents.
    * @param document XML document
-   * @return vector of logging events
+   * @return Vector of LoggingEvents
    */
   private Vector decodeEvents(final Document document) {
     Vector events = new Vector();
@@ -298,17 +302,17 @@
       Node eventNode = eventList.item(eventIndex);
 
       Logger logger = null;
-      long timeStamp = 0L;
-      Level level = null;
-      String threadName = null;
-      Object message = null;
-      String ndc = null;
-      String[] exception = null;
-      String className = null;
-      String methodName = null;
-      String fileName = null;
-      String lineNumber = null;
-      Hashtable properties = new Hashtable();
+    long timeStamp = 0L;
+    Level level = null;
+    String threadName = null;
+    Object message = null;
+    String ndc = null;
+    String[] exception = null;
+    String className = null;
+    String methodName = null;
+    String fileName = null;
+    String lineNumber = null;
+    Hashtable properties = new Hashtable();
 
       //format of date: 2003-05-04T11:04:52
       //ignore date or set as a property? using millis in constructor instead
@@ -382,25 +386,22 @@
         }
       }
 
-      /**
-       * We add all the additional properties to the properties
-       * hashtable
-       */
-      if (additionalProperties.size() > 0) {
-        if (properties == null) {
-          properties = new Hashtable(additionalProperties);
-        } else {
-          Iterator i = additionalProperties.entrySet().iterator();
-          while (i.hasNext()) {
-            Map.Entry e = (Map.Entry) i.next();
-            if (!(properties.containsKey(e.getKey()))) {
+        /**
+         * We add all the additional properties to the properties
+         * hashtable. Override properties that already exist
+         */
+        if (additionalProperties.size() > 0) {
+            if (properties == null) {
+                properties = new Hashtable(additionalProperties);
+            }
+            Iterator i = additionalProperties.entrySet().iterator();
+            while (i.hasNext()) {
+                Map.Entry e = (Map.Entry) i.next();
                 properties.put(e.getKey(), e.getValue());
             }
-          }
         }
-      }
 
-      LocationInfo info = null;
+      LocationInfo info;
       if ((fileName != null)
               || (className != null)
               || (methodName != null)
diff --git a/src/main/java/org/apache/log4j/xml/XMLDecoder.java b/src/main/java/org/apache/log4j/xml/XMLDecoder.java
index 0714563..13aa495 100644
--- a/src/main/java/org/apache/log4j/xml/XMLDecoder.java
+++ b/src/main/java/org/apache/log4j/xml/XMLDecoder.java
@@ -57,7 +57,7 @@
  * even though the DTD supports multiple events nested in an eventSet.
  *
  * NOTE: This class has been created on the assumption that all XML log files
- * are encoding in UTF-8 encoding. There is no current support for any other 
+ * are encoded in UTF-8. There is no current support for any other
  * encoding format at this time.
  * 
  * @author Scott Deboy (sdeboy@apache.org)
@@ -115,7 +115,6 @@
      * Create new instance.
      */
    public XMLDecoder() {
-    super();
     DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
     dbf.setValidating(false);
 
@@ -156,7 +155,6 @@
       // complain. Indeed, "log4j.dtd" alone is not a valid URI which
       // causes Crimson to barf. The Log4jEntityResolver only cares
       // about the "log4j.dtd" ending.
-      //      buf.setLength(0);
 
       /**
        * resetting the length of the StringBuffer is dangerous, particularly
@@ -274,7 +272,7 @@
    * relevant bits to form a new LoggingEvent instance which can be used
    * by any Log4j element locally.
    * @param data XML fragment
-   * @return a single LoggingEvent
+   * @return a single LoggingEvent or null
    */
   public LoggingEvent decode(final String data) {
     Document document = parse(data);
@@ -302,7 +300,7 @@
 
     Logger logger;
     long timeStamp;
-    String level;
+    Level level;
     String threadName;
     Object message = null;
     String ndc = null;
@@ -325,19 +323,18 @@
         if (eventNode.getNodeType() != Node.ELEMENT_NODE) {
             continue;
         }
-        logger =
-            Logger.getLogger(
-               eventNode.getAttributes().getNamedItem("logger").getNodeValue());
-      timeStamp =
-        Long.parseLong(
-          eventNode.getAttributes().getNamedItem("timestamp").getNodeValue());
-      level = eventNode.getAttributes().getNamedItem("level").getNodeValue();
-      threadName =
-        eventNode.getAttributes().getNamedItem("thread").getNodeValue();
+      logger = Logger.getLogger(eventNode.getAttributes().getNamedItem("logger").getNodeValue());
+      timeStamp = Long.parseLong(eventNode.getAttributes().getNamedItem("timestamp").getNodeValue());
+      level = Level.toLevel(eventNode.getAttributes().getNamedItem("level").getNodeValue());
+      threadName = eventNode.getAttributes().getNamedItem("thread").getNodeValue();
 
       NodeList list = eventNode.getChildNodes();
       int listLength = list.getLength();
 
+      if (listLength == 0) {
+        continue;
+      }
+
       for (int y = 0; y < listLength; y++) {
         String tagName = list.item(y).getNodeName();
 
@@ -406,25 +403,21 @@
           }
         }
 
-        /**
-         * We add all the additional properties to the properties
-         * hashtable.  Don't override properties that already exist
-         */
-        if (additionalProperties.size() > 0) {
-          if (properties == null) {
-            properties = new Hashtable(additionalProperties);
-          } else {
-            Iterator i = additionalProperties.entrySet().iterator();
-            while (i.hasNext()) {
-              Map.Entry e = (Map.Entry) i.next();
-              if (!(properties.containsKey(e.getKey()))) {
-                properties.put(e.getKey(), e.getValue());
+          /**
+           * We add all the additional properties to the properties
+           * hashtable. Override properties that already exist
+           */
+          if (additionalProperties.size() > 0) {
+              if (properties == null) {
+                  properties = new Hashtable(additionalProperties);
               }
-            }
+              Iterator i = additionalProperties.entrySet().iterator();
+              while (i.hasNext()) {
+                  Map.Entry e = (Map.Entry) i.next();
+                  properties.put(e.getKey(), e.getValue());
+              }
           }
-        }
       }
-      Level levelImpl = Level.toLevel(level);
 
       LocationInfo info;
       if ((fileName != null)
@@ -440,7 +433,7 @@
       }
 
         LoggingEvent loggingEvent = new LoggingEvent(null,
-                logger, timeStamp, levelImpl, message,
+                logger, timeStamp, level, message,
                 threadName,
                 new ThrowableInformation(exception),
                 ndc,