context: #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.Xml;
  using System.IO;
  
  using log4net.Core;
  using log4net.Util;
  
  namespace log4net.Layout
  {
  	/// <summary>
  	/// Layout that formats the log events as XML elements compatible with the log4j schema
  	/// </summary>
  	/// <remarks>
  	/// <para>
  	/// Formats the log events according to the http://logging.apache.org/log4j schema.
  	/// </para>
  	/// </remarks>
  	/// <author>Nicko Cadell</author>
  	public class XmlLayoutSchemaLog4j : XmlLayoutBase
  	{
  		#region Static Members
  
  		/// <summary>
  		/// The 1st of January 1970 in UTC
  		/// </summary>
  		private static readonly DateTime s_date1970 = new DateTime(1970, 1, 1);
  
  		#endregion
  
  		#region Constructors
  
  		/// <summary>
  		/// Constructs an XMLLayoutSchemaLog4j
  		/// </summary>
  		public XmlLayoutSchemaLog4j() : base()
  		{
  		}
  
  		/// <summary>
  		/// Constructs an XMLLayoutSchemaLog4j.
  		/// </summary>
  		/// <remarks>
  		/// <para>
  		/// The <b>LocationInfo</b> option takes a boolean value. By
  		/// default, it is set to false which means there will be no location
  		/// information output by this layout. If the the option is set to
  		/// true, then the file name and line number of the statement
  		/// at the origin of the log statement will be output. 
  		/// </para>
  		/// <para>
  		/// If you are embedding this layout within an SMTPAppender
  		/// then make sure to set the <b>LocationInfo</b> option of that 
  		/// appender as well.
  		/// </para>
  		/// </remarks>
  		public XmlLayoutSchemaLog4j(bool locationInfo) :  base(locationInfo)
  		{
  		}
  
  		#endregion
  
  		#region Public Properties
  
  		/// <summary>
  		/// The version of the log4j schema to use.
  		/// </summary>
  		/// <remarks>
  		/// <para>
  		/// Only version 1.2 of the log4j schema is supported.
  		/// </para>
  		/// </remarks>
  		public string Version
  		{
  			get { return "1.2"; }
  			set 
  			{ 
  				if (value != "1.2")
  				{
  					throw new ArgumentException("Only version 1.2 of the log4j schema is currently supported");
  				}
  			}
  		}
  
  		#endregion
  
  		/* Example log4j schema event
  
  <log4j:event logger="first logger" level="ERROR" thread="Thread-3" timestamp="1051494121460">
    <log4j:message><![CDATA[errormsg 3]]></log4j:message>
    <log4j:NDC><![CDATA[third]]></log4j:NDC>
    <log4j:MDC>
      <log4j:data name="some string" value="some valuethird"/>
    </log4j:MDC>
    <log4j:throwable><![CDATA[java.lang.Exception: someexception-third
   	at org.apache.log4j.chainsaw.Generator.run(Generator.java:94)
  ]]></log4j:throwable>
    <log4j:locationInfo class="org.apache.log4j.chainsaw.Generator"
  method="run" file="Generator.java" line="94"/>
    <log4j:properties>
      <log4j:data name="log4jmachinename" value="windows"/>
      <log4j:data name="log4japp" value="udp-generator"/>
    </log4j:properties>
  </log4j:event>
  
  		*/
  
  		/* Since log4j 1.3 the log4j:MDC has been combined into the log4j:properties element */
  
  		/// <summary>
  		/// Actually do the writing of the xml
  		/// </summary>
  		/// <param name="writer">the writer to use</param>
  		/// <param name="loggingEvent">the event to write</param>
  		/// <remarks>
  		/// <para>
  		/// Generate XML that is compatible with the log4j schema.
  		/// </para>
  		/// </remarks>
  		override protected void FormatXml(XmlWriter writer, LoggingEvent loggingEvent)
  		{
  			// Translate logging events for log4j
  
  			// Translate hostname property
  			if (loggingEvent.LookupProperty(LoggingEvent.HostNameProperty) != null && 
  				loggingEvent.LookupProperty("log4jmachinename") == null)
  			{
  				loggingEvent.GetProperties()["log4jmachinename"] = loggingEvent.LookupProperty(LoggingEvent.HostNameProperty);
  			}
  
  			// translate appdomain name
  			if (loggingEvent.LookupProperty("log4japp") == null && 
  				loggingEvent.Domain != null && 
  				loggingEvent.Domain.Length > 0)
  			{
  				loggingEvent.GetProperties()["log4japp"] = loggingEvent.Domain;
  			}
  
  			// translate identity name
  			if (loggingEvent.Identity != null && 
  				loggingEvent.Identity.Length > 0 && 
  				loggingEvent.LookupProperty(LoggingEvent.IdentityProperty) == null)
  			{
  				loggingEvent.GetProperties()[LoggingEvent.IdentityProperty] = loggingEvent.Identity;
  			}
  
  			// translate user name
  			if (loggingEvent.UserName != null && 
  				loggingEvent.UserName.Length > 0 && 
  				loggingEvent.LookupProperty(LoggingEvent.UserNameProperty) == null)
  			{
  				loggingEvent.GetProperties()[LoggingEvent.UserNameProperty] = loggingEvent.UserName;
  			}
  
  			// Write the start element
  			writer.WriteStartElement("log4j:event");
  			writer.WriteAttributeString("logger", loggingEvent.LoggerName);
  
  			// Calculate the timestamp as the number of milliseconds since january 1970
  			// 
  			// We must convert the TimeStamp to UTC before performing any mathematical
  			// operations. This allows use to take into account discontinuities
  			// caused by daylight savings time transitions.
  			TimeSpan timeSince1970 = loggingEvent.TimeStamp.ToUniversalTime() - s_date1970;
  
  			writer.WriteAttributeString("timestamp", XmlConvert.ToString((long)timeSince1970.TotalMilliseconds));
  			writer.WriteAttributeString("level", loggingEvent.Level.DisplayName);
  			writer.WriteAttributeString("thread", loggingEvent.ThreadName);
      
  			// Append the message text
  			writer.WriteStartElement("log4j:message");
  #if UNITY_4_3 || UNITY_5
              TransformLog4Net.WriteEscapedXmlString(writer, loggingEvent.RenderedMessage,this.InvalidCharReplacement);
  #else
  			Transform.WriteEscapedXmlString(writer, loggingEvent.RenderedMessage,this.InvalidCharReplacement);
  #endif
  			writer.WriteEndElement();
  
  			object ndcObj = loggingEvent.LookupProperty("NDC");
  			if (ndcObj != null)
  			{
  				string valueStr = loggingEvent.Repository.RendererMap.FindAndRender(ndcObj);
  
  
gt: 				if (valueStr != null && valueStr.Length > 0)