#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.Collections.Generic;
using System.IO;

using log4net.Core;
using log4net.Layout.Pattern;
using log4net.Util;
using log4net.Util.PatternStringConverters;
using AppDomainPatternConverter = log4net.Layout.Pattern.AppDomainPatternConverter;
using DatePatternConverter = log4net.Layout.Pattern.DatePatternConverter;
using IdentityPatternConverter = log4net.Layout.Pattern.IdentityPatternConverter;
using PropertyPatternConverter = log4net.Layout.Pattern.PropertyPatternConverter;
using UserNamePatternConverter = log4net.Layout.Pattern.UserNamePatternConverter;
using UtcDatePatternConverter = log4net.Layout.Pattern.UtcDatePatternConverter;

namespace log4net.Layout;

/// <summary>
/// A flexible layout configurable with pattern string.
/// </summary>
/// <remarks>
/// <para>
/// The goal of this class is to <see cref="M:PatternLayout.Format(TextWriter,LoggingEvent)"/> a 
/// <see cref="LoggingEvent"/> as a string. The results
/// depend on the <i>conversion pattern</i>.
/// </para>
/// <para>
/// The conversion pattern is closely related to the conversion
/// pattern of the printf function in C. A conversion pattern is
/// composed of literal text and format control expressions called
/// <i>conversion specifiers</i>.
/// </para>
/// <para>
/// <i>You are free to insert any literal text within the conversion
/// pattern.</i>
/// </para>
/// <para>
/// Each conversion specifier starts with a percent sign (%) and is
/// followed by optional <i>format modifiers</i> and a <i>conversion
/// pattern name</i>. The conversion pattern name specifies the type of
/// data, e.g. logger, level, date, thread name. The format
/// modifiers control such things as field width, padding, left and
/// right justification. The following is a simple example.
/// </para>
/// <para>
/// Let the conversion pattern be <b>"%-5level [%thread]: %message%newline"</b> and assume
/// that the log4net environment was set to use a PatternLayout. Then the
/// statements
/// </para>
/// <code lang="C#">
/// ILog log = LogManager.GetLogger(typeof(TestApp));
/// log.Debug("Message 1");
/// log.Warn("Message 2");   
/// </code>
/// <para>would yield the output</para>
/// <code>
/// DEBUG [main]: Message 1
/// WARN  [main]: Message 2  
/// </code>
/// <para>
/// Note that there is no explicit separator between text and
/// conversion specifiers. The pattern parser knows when it has reached
/// the end of a conversion specifier when it reads a conversion
/// character. In the example above the conversion specifier
/// <b>%-5level</b> means the level of the logging event should be left
/// justified to a width of five characters.
/// </para>
/// <para>
/// The recognized conversion pattern names are:
/// </para>
/// <list type="table">
///     <listheader>
///         <term>Conversion Pattern Name</term>
///         <description>Effect</description>
///     </listheader>
///     <item>
///         <term>a</term>
///         <description>Equivalent to <b>appdomain</b></description>
///     </item>
///     <item>
///         <term>appdomain</term>
///         <description>
///        Used to output the friendly name of the AppDomain where the 
///        logging event was generated. 
///         </description>
///     </item>
///     <item>
///         <term>aspnet-cache</term>
///         <description>
///             <para>
///             Used to output all cache items in the case of <b>%aspnet-cache</b> or just one named item if used as <b>%aspnet-cache{key}</b>
///             </para>
///             <para>
///             This pattern is not available for Compact Framework or Client Profile assemblies.
///             </para>
///         </description>
///     </item>
///     <item>
///         <term>aspnet-context</term>
///         <description>
///             <para>
///             Used to output all context items in the case of <b>%aspnet-context</b> or just one named item if used as <b>%aspnet-context{key}</b>
///             </para>
///             <para>
///             This pattern is not available for Compact Framework or Client Profile assemblies.
///             </para>
///         </description>
///     </item>
///     <item>
///         <term>aspnet-request</term>
///         <description>
///             <para>
///             Used to output all request parameters in the case of <b>%aspnet-request</b> or just one named param if used as <b>%aspnet-request{key}</b>
///             </para>
///             <para>
///             This pattern is not available for Compact Framework or Client Profile assemblies.
///             </para>
///         </description>
///     </item>
///     <item>
///         <term>aspnet-session</term>
///         <description>
///             <para>
///             Used to output all session items in the case of <b>%aspnet-session</b> or just one named item if used as <b>%aspnet-session{key}</b>
///             </para>
///             <para>
///             This pattern is not available for Compact Framework or Client Profile assemblies.
///             </para>
///         </description>
///     </item>
///     <item>
///         <term>c</term>
///         <description>Equivalent to <b>logger</b></description>
///     </item>
///     <item>
///         <term>C</term>
///         <description>Equivalent to <b>type</b></description>
///     </item>
///     <item>
///         <term>class</term>
///         <description>Equivalent to <b>type</b></description>
///     </item>
///     <item>
///         <term>d</term>
///         <description>Equivalent to <b>date</b></description>
///     </item>
///     <item>
///      <term>date</term> 
///      <description>
///       <para>
///       Used to output the date of the logging event in the local time zone. 
///       To output the date in universal time use the <c>%utcdate</c> pattern.
///       The date conversion 
///       specifier may be followed by a <i>date format specifier</i> enclosed 
///       between braces. For example, <b>%date{HH:mm:ss,fff}</b> or
///       <b>%date{dd MMM yyyy HH:mm:ss,fff}</b>.  If no date format specifier is 
///       given then ISO8601 format is
///       assumed (<see cref="log4net.DateFormatter.Iso8601DateFormatter"/>).
///       </para>
///       <para>
///       The date format specifier admits the same syntax as the
///       time pattern string of the <see cref="M:DateTime.ToString(string)"/>.
///       </para>
///       <para>
///       For better results it is recommended to use the log4net date
///       formatters. These can be specified using one of the strings
///       "ABSOLUTE", "DATE" and "ISO8601" for specifying 
///       <see cref="log4net.DateFormatter.AbsoluteTimeDateFormatter"/>, 
///       <see cref="log4net.DateFormatter.DateTimeDateFormatter"/> and respectively 
///       <see cref="log4net.DateFormatter.Iso8601DateFormatter"/>. For example, 
///       <b>%date{ISO8601}</b> or <b>%date{ABSOLUTE}</b>.
///       </para>
///       <para>
///       These dedicated date formatters perform significantly
///       better than <see cref="M:DateTime.ToString(string)"/>.
///       </para>
///      </description>
///    </item>
///    <item>
///      <term>exception</term>
///      <description>
///        <para>
///        Used to output the exception passed in with the log message.
///        </para>
///        <para>
///       If an exception object is stored in the logging event
///       it will be rendered into the pattern output with a
///       trailing newline.
///       If there is no exception then nothing will be output
///       and no trailing newline will be appended.
///       It is typical to put a newline before the exception
///       and to have the exception as the last data in the pattern.
///        </para>
///      </description>
///    </item>
///     <item>
///         <term>F</term>
///         <description>Equivalent to <b>file</b></description>
///     </item>
///    <item>
///      <term>file</term>
///      <description>
///        <para>
///        Used to output the file name where the logging request was
///        issued.
///        </para>
///        <para>
///        <b>WARNING</b> Generating caller location information is
///        extremely slow. Its use should be avoided unless execution speed
///        is not an issue.
///        </para>
///       <para>
///       See the note below on the availability of caller location information.
///       </para>
///      </description>
///    </item>
///    <item>
///      <term>identity</term>
///      <description>
///        <para>
///        Used to output the user name for the currently active user
///        (Principal.Identity.Name).
///        </para>
///        <para>
///        <b>WARNING</b> Generating caller information is
///        extremely slow. Its use should be avoided unless execution speed
///        is not an issue.
///        </para>
///      </description>
///    </item>
///     <item>
///         <term>l</term>
///         <description>Equivalent to <b>location</b></description>
///     </item>
///     <item>
///         <term>L</term>
///         <description>Equivalent to <b>line</b></description>
///     </item>
///    <item>
///      <term>location</term>
///      <description>
///       <para>
///       Used to output location information of the caller which generated
///       the logging event.
///       </para>
///       <para>
///       The location information depends on the CLI implementation but
///       usually consists of the fully qualified name of the calling
///       method followed by the callers source the file name and line
///       number between parentheses.
///       </para>
///       <para>
///       The location information can be very useful. However, its
///       generation is <b>extremely</b> slow. Its use should be avoided
///       unless execution speed is not an issue.
///       </para>
///       <para>
///       See the note below on the availability of caller location information.
///       </para>
///      </description>
///    </item>
///    <item>
///      <term>level</term>
///      <description>
///       <para>
///       Used to output the level of the logging event.
///       </para>
///      </description>
///    </item>
///    <item>
///      <term>line</term>
///      <description>
///        <para>
///        Used to output the line number from where the logging request
///        was issued.
///        </para>
///        <para>
///        <b>WARNING</b> Generating caller location information is
///        extremely slow. Its use should be avoided unless execution speed
///        is not an issue.
///        </para>
///       <para>
///       See the note below on the availability of caller location information.
///       </para>
///      </description>
///    </item>
///     <item>
///         <term>logger</term>
///         <description>
///             <para>
///        Used to output the logger of the logging event. The
///       logger conversion specifier can be optionally followed by
///       <i>precision specifier</i>, that is a decimal constant in
///       brackets.
///             </para>
///       <para>
///       If a precision specifier is given, then only the corresponding
///       number of right most components of the logger name will be
///       printed. By default the logger name is printed in full.
///       </para>
///       <para>
///       For example, for the logger name "a.b.c" the pattern
///       <b>%logger{2}</b> will output "b.c".
///       </para>
///         </description>
///     </item>
///     <item>
///         <term>m</term>
///         <description>Equivalent to <b>message</b></description>
///     </item>
///     <item>
///         <term>M</term>
///         <description>Equivalent to <b>method</b></description>
///     </item>
///    <item>
///      <term>message</term>
///      <description>
///       <para>
///       Used to output the application supplied message associated with 
///       the logging event.
///       </para>
///      </description>
///    </item>
///    <item>
///      <term>mdc</term>
///      <description>
///       <para>
///       The MDC (old name for the ThreadContext.Properties) is now part of the
///       combined event properties. This pattern is supported for compatibility
///       but is equivalent to <b>property</b>.
///       </para>
///      </description>
///    </item>
///    <item>
///      <term>method</term>
///      <description>
///        <para>
///        Used to output the method name where the logging request was
///        issued.
///        </para>
///        <para>
///        <b>WARNING</b> Generating caller location information is
///        extremely slow. Its use should be avoided unless execution speed
///        is not an issue.
///        </para>
///       <para>
///       See the note below on the availability of caller location information.
///       </para>
///      </description>
///    </item>
///     <item>
///         <term>n</term>
///         <description>Equivalent to <b>newline</b></description>
///     </item>
///    <item>
///      <term>newline</term>
///      <description>
///       <para>
///       Outputs the platform dependent line separator character or
///       characters.
///       </para>
///       <para>
///       This conversion pattern offers the same performance as using 
///       non-portable line separator strings such as  "\n", or "\r\n". 
///       Thus, it is the preferred way of specifying a line separator.
///       </para> 
///      </description>
///    </item>
///    <item>
///      <term>ndc</term>
///      <description>
///       <para>
///       Used to output the NDC (nested diagnostic context) associated
///       with the thread that generated the logging event.
///       </para>
///      </description>
///    </item>
///     <item>
///         <term>p</term>
///         <description>Equivalent to <b>level</b></description>
///     </item>
///     <item>
///         <term>P</term>
///         <description>Equivalent to <b>property</b></description>
///     </item>
///     <item>
///         <term>properties</term>
///         <description>Equivalent to <b>property</b></description>
///     </item>
///    <item>
///      <term>property</term>
///      <description>
///       <para>
///       Used to output the an event specific property. The key to 
///       lookup must be specified within braces and directly following the
///       pattern specifier, e.g. <b>%property{user}</b> would include the value
///       from the property that is keyed by the string 'user'. Each property value
///       that is to be included in the log must be specified separately.
///       Properties are added to events by loggers or appenders. By default 
///       the <c>log4net:HostName</c> property is set to the name of machine on 
///       which the event was originally logged.
///       </para>
///       <para>
///       If no key is specified, e.g. <b>%property</b> then all the keys and their
///       values are printed in a comma separated list.
///       </para>
///       <para>
///       The properties of an event are combined from a number of different
///       contexts. These are listed below in the order in which they are searched.
///       </para>
///       <list type="definition">
///         <item>
///           <term>the event properties</term>
///           <description>
///           The event has <see cref="LoggingEvent.Properties"/> that can be set. These 
///           properties are specific to this event only.
///           </description>
///         </item>
///         <item>
///           <term>the thread properties</term>
///           <description>
///           The <see cref="ThreadContext.Properties"/> that are set on the current
///           thread. These properties are shared by all events logged on this thread.
///           </description>
///         </item>
///         <item>
///           <term>the global properties</term>
///           <description>
///           The <see cref="GlobalContext.Properties"/> that are set globally. These 
///           properties are shared by all the threads in the AppDomain.
///           </description>
///         </item>
///       </list>
///       
///      </description>
///    </item>
///     <item>
///         <term>r</term>
///         <description>Equivalent to <b>timestamp</b></description>
///     </item>
///   <item>
///      <term>stacktrace</term> 
///      <description>
///       <para>
///       Used to output the stack trace of the logging event
///       The stack trace level specifier may be enclosed 
///       between braces. For example, <b>%stacktrace{level}</b>.  
///       If no stack trace level specifier is given then 1 is assumed 
///       </para>
///       <para>
///       Output uses the format:
///       type3.MethodCall3 > type2.MethodCall2 > type1.MethodCall1
///       </para>
///             <para>
///             This pattern is not available for Compact Framework assemblies.
///             </para>
///      </description>
///    </item>
///   <item>
///      <term>stacktracedetail</term> 
///      <description>
///       <para>
///       Used to output the stack trace of the logging event
///       The stack trace level specifier may be enclosed 
///       between braces. For example, <b>%stacktracedetail{level}</b>.  
///       If no stack trace level specifier is given then 1 is assumed 
///       </para>
///       <para>
///       Output uses the format:
///             type3.MethodCall3(type param,...) > type2.MethodCall2(type param,...) > type1.MethodCall1(type param,...)
///       </para>
///             <para>
///             This pattern is not available for Compact Framework assemblies.
///             </para>
///      </description>
///    </item>
///     <item>
///         <term>t</term>
///         <description>Equivalent to <b>thread</b></description>
///     </item>
///    <item>
///      <term>timestamp</term>
///      <description>
///       <para>
///       Used to output the number of milliseconds elapsed since the start
///       of the application until the creation of the logging event.
///       </para>
///      </description>
///    </item>
///    <item>
///      <term>thread</term>
///      <description>
///       <para>
///       Used to output the name of the thread that generated the
///       logging event. Uses the thread number if no name is available.
///       </para>
///      </description>
///    </item>
///     <item>
///      <term>type</term> 
///      <description>
///       <para>
///       Used to output the fully qualified type name of the caller
///       issuing the logging request. This conversion specifier
///       can be optionally followed by <i>precision specifier</i>, that
///       is a decimal constant in brackets.
///       </para>
///       <para>
///       If a precision specifier is given, then only the corresponding
///       number of right most components of the class name will be
///       printed. By default the class name is output in fully qualified form.
///       </para>
///       <para>
///       For example, for the class name "log4net.Layout.PatternLayout", the
///       pattern <b>%type{1}</b> will output "PatternLayout".
///       </para>
///       <para>
///       <b>WARNING</b> Generating the caller class information is
///       slow. Thus, its use should be avoided unless execution speed is
///       not an issue.
///       </para>
///       <para>
///       See the note below on the availability of caller location information.
///       </para>
///      </description>
///     </item>
///     <item>
///         <term>u</term>
///         <description>Equivalent to <b>identity</b></description>
///     </item>
///    <item>
///      <term>username</term>
///      <description>
///        <para>
///        Used to output the WindowsIdentity for the currently
///        active user.
///        </para>
///        <para>
///        <b>WARNING</b> Generating caller WindowsIdentity information is
///        extremely slow. Its use should be avoided unless execution speed
///        is not an issue.
///        </para>
///      </description>
///    </item>
///     <item>
///      <term>utcdate</term> 
///      <description>
///       <para>
///       Used to output the date of the logging event in universal time. 
///       The date conversion 
///       specifier may be followed by a <i>date format specifier</i> enclosed 
///       between braces. For example, <b>%utcdate{HH:mm:ss,fff}</b> or
///       <b>%utcdate{dd MMM yyyy HH:mm:ss,fff}</b>.  If no date format specifier is 
///       given then ISO8601 format is
///       assumed (<see cref="log4net.DateFormatter.Iso8601DateFormatter"/>).
///       </para>
///       <para>
///       The date format specifier admits the same syntax as the
///       time pattern string of the <see cref="M:DateTime.ToString(string)"/>.
///       </para>
///       <para>
///       For better results it is recommended to use the log4net date
///       formatters. These can be specified using one of the strings
///       "ABSOLUTE", "DATE" and "ISO8601" for specifying 
///       <see cref="log4net.DateFormatter.AbsoluteTimeDateFormatter"/>, 
///       <see cref="log4net.DateFormatter.DateTimeDateFormatter"/> and respectively 
///       <see cref="log4net.DateFormatter.Iso8601DateFormatter"/>. For example, 
///       <b>%utcdate{ISO8601}</b> or <b>%utcdate{ABSOLUTE}</b>.
///       </para>
///       <para>
///       These dedicated date formatters perform significantly
///       better than <see cref="M:DateTime.ToString(string)"/>.
///       </para>
///      </description>
///    </item>
///     <item>
///         <term>w</term>
///         <description>Equivalent to <b>username</b></description>
///     </item>
///     <item>
///         <term>x</term>
///         <description>Equivalent to <b>ndc</b></description>
///     </item>
///     <item>
///         <term>X</term>
///         <description>Equivalent to <b>mdc</b></description>
///     </item>
///    <item>
///      <term>%</term>
///      <description>
///       <para>
///       The sequence %% outputs a single percent sign.
///       </para>
///      </description>
///    </item>
/// </list>
/// <para>
/// The single letter patterns are deprecated in favor of the 
/// longer more descriptive pattern names.
/// </para>
/// <para>
/// By default the relevant information is output as is. However,
/// with the aid of format modifiers it is possible to change the
/// minimum field width, the maximum field width and justification.
/// </para>
/// <para>
/// The optional format modifier is placed between the percent sign
/// and the conversion pattern name.
/// </para>
/// <para>
/// The first optional format modifier is the <i>left justification
/// flag</i> which is just the minus (-) character. Then comes the
/// optional <i>minimum field width</i> modifier. This is a decimal
/// constant that represents the minimum number of characters to
/// output. If the data item requires fewer characters, it is padded on
/// either the left or the right until the minimum width is
/// reached. The default is to pad on the left (right justify) but you
/// can specify right padding with the left justification flag. The
/// padding character is space. If the data item is larger than the
/// minimum field width, the field is expanded to accommodate the
/// data. The value is never truncated.
/// </para>
/// <para>
/// This behavior can be changed using the <i>maximum field
/// width</i> modifier which is designated by a period followed by a
/// decimal constant. If the data item is longer than the maximum
/// field, then the extra characters are removed from the
/// <i>beginning</i> of the data item and not from the end. For
/// example, it the maximum field width is eight and the data item is
/// ten characters long, then the first two characters of the data item
/// are dropped. This behavior deviates from the printf function in C
/// where truncation is done from the end.
/// </para>
/// <para>
/// Below are various format modifier examples for the logger
/// conversion specifier.
/// </para>
/// <div class="tablediv">
///    <table class="dtTABLE" cellspacing="0">
///      <tr>
///        <th>Format modifier</th>
///        <th>left justify</th>
///        <th>minimum width</th>
///        <th>maximum width</th>
///        <th>comment</th>
///      </tr>
///      <tr>
///        <td align="center">%20logger</td>
///        <td align="center">false</td>
///        <td align="center">20</td>
///        <td align="center">none</td>
///        <td>
///          <para>
///          Left pad with spaces if the logger name is less than 20
///          characters long.
///          </para>
///        </td>
///      </tr>
///      <tr>
///        <td align="center">%-20logger</td>
///        <td align="center">true</td>
///        <td align="center">20</td>
///        <td align="center">none</td>
///        <td>
///          <para>
///          Right pad with spaces if the logger 
///          name is less than 20 characters long.
///          </para>
///        </td>
///      </tr>
///      <tr>
///        <td align="center">%.30logger</td>
///        <td align="center">NA</td>
///        <td align="center">none</td>
///        <td align="center">30</td>
///        <td>
///          <para>
///          Truncate from the beginning if the logger 
///          name is longer than 30 characters.
///          </para>
///        </td>
///      </tr>
///      <tr>
///        <td align="center"><nobr>%20.30logger</nobr></td>
///        <td align="center">false</td>
///        <td align="center">20</td>
///        <td align="center">30</td>
///        <td>
///          <para>
///          Left pad with spaces if the logger name is shorter than 20
///          characters. However, if logger name is longer than 30 characters,
///          then truncate from the beginning.
///          </para>
///        </td>
///      </tr>
///      <tr>
///        <td align="center">%-20.30logger</td>
///        <td align="center">true</td>
///        <td align="center">20</td>
///        <td align="center">30</td>
///        <td>
///          <para>
///          Right pad with spaces if the logger name is shorter than 20
///          characters. However, if logger name is longer than 30 characters,
///          then truncate from the beginning.
///          </para>
///        </td>
///      </tr>
///    </table>
///  </div>
///  <para>
///  <b>Note about caller location information.</b><br />
///  The following patterns <c>%type %file %line %method %location %class %C %F %L %l %M</c> 
///  all generate caller location information.
/// Location information uses the <c>System.Diagnostics.StackTrace</c> class to generate
/// a call stack. The caller's information is then extracted from this stack.
/// </para>
/// <note type="caution">
/// <para>
/// The <c>System.Diagnostics.StackTrace</c> class is not supported on the 
/// .NET Compact Framework 1.0 therefore caller location information is not
/// available on that framework.
/// </para>
/// </note>
/// <note type="caution">
/// <para>
/// The <c>System.Diagnostics.StackTrace</c> class has this to say about Release builds:
/// </para>
/// <para>
/// "StackTrace information will be most informative with Debug build configurations. 
/// By default, Debug builds include debug symbols, while Release builds do not. The 
/// debug symbols contain most of the file, method name, line number, and column 
/// information used in constructing StackFrame and StackTrace objects. StackTrace 
/// might not report as many method calls as expected, due to code transformations 
/// that occur during optimization."
/// </para>
/// <para>
/// This means that in a Release build the caller information may be incomplete or may 
/// not exist at all! Therefore caller location information cannot be relied upon in a Release build.
/// </para>
/// </note>
/// <para>
/// Additional pattern converters may be registered with a specific <see cref="PatternLayout"/>
/// instance using the <see cref="M:AddConverter(string, Type)"/> method.
/// </para>
/// </remarks>
/// <example>
/// This is a more detailed pattern.
/// <code><b>%timestamp [%thread] %level %logger %ndc - %message%newline</b></code>
/// </example>
/// <example>
/// A similar pattern except that the relative time is
/// right padded if less than 6 digits, thread name is right padded if
/// less than 15 characters and truncated if longer and the logger
/// name is left padded if shorter than 30 characters and truncated if
/// longer.
/// <code><b>%-6timestamp [%15.15thread] %-5level %30.30logger %ndc - %message%newline</b></code>
/// </example>
/// <author>Nicko Cadell</author>
/// <author>Gert Driesen</author>
/// <author>Douglas de la Torre</author>
/// <author>Daniel Cazzulino</author>
public class PatternLayout : LayoutSkeleton
{
  /// <summary>
  /// Default pattern string for log output. 
  /// </summary>
  /// <remarks>
  /// <para>
  /// Default pattern string for log output. 
  /// Currently set to the string <b>"%message%newline"</b> 
  /// which just prints the application supplied message. 
  /// </para>
  /// </remarks>
  public const string DefaultConversionPattern = "%message%newline";

  /// <summary>
  /// A detailed conversion pattern
  /// </summary>
  /// <remarks>
  /// <para>
  /// A conversion pattern which includes Time, Thread, Logger, and Nested Context.
  /// Current value is <b>%timestamp [%thread] %level %logger %ndc - %message%newline</b>.
  /// </para>
  /// </remarks>
  public const string DetailConversionPattern = "%timestamp [%thread] %level %logger %ndc - %message%newline";

  /// <summary>
  /// Internal map of converter identifiers to converter types.
  /// </summary>
  /// <remarks>
  /// <para>
  /// This static map is overridden by the converterRegistry instance map
  /// </para>
  /// </remarks>
  private static readonly Dictionary<string, Type> _sGlobalRulesRegistry = new(StringComparer.Ordinal)
  {
    ["literal"] = typeof(LiteralPatternConverter),
    ["newline"] = typeof(NewLinePatternConverter),
    ["n"] = typeof(NewLinePatternConverter),

    // .NET Standard has no support for ASP.NET
#if NET462_OR_GREATER
    ["aspnet-cache"] = typeof(AspNetCachePatternConverter),
    ["aspnet-context"] = typeof(AspNetContextPatternConverter),
    ["aspnet-request"] = typeof(AspNetRequestPatternConverter),
    ["aspnet-session"] = typeof(AspNetSessionPatternConverter),
#endif

    ["c"] = typeof(LoggerPatternConverter),
    ["logger"] = typeof(LoggerPatternConverter),

    ["C"] = typeof(TypeNamePatternConverter),
    ["class"] = typeof(TypeNamePatternConverter),
    ["type"] = typeof(TypeNamePatternConverter),

    ["d"] = typeof(DatePatternConverter),
    ["date"] = typeof(DatePatternConverter),

    ["exception"] = typeof(ExceptionPatternConverter),

    ["F"] = typeof(FileLocationPatternConverter),
    ["file"] = typeof(FileLocationPatternConverter),

    ["l"] = typeof(FullLocationPatternConverter),
    ["location"] = typeof(FullLocationPatternConverter),

    ["L"] = typeof(LineLocationPatternConverter),
    ["line"] = typeof(LineLocationPatternConverter),

    ["m"] = typeof(MessagePatternConverter),
    ["message"] = typeof(MessagePatternConverter),

    ["M"] = typeof(MethodLocationPatternConverter),
    ["method"] = typeof(MethodLocationPatternConverter),

    ["p"] = typeof(LevelPatternConverter),
    ["level"] = typeof(LevelPatternConverter),

    ["P"] = typeof(PropertyPatternConverter),
    ["property"] = typeof(PropertyPatternConverter),
    ["properties"] = typeof(PropertyPatternConverter),

    ["r"] = typeof(RelativeTimePatternConverter),
    ["timestamp"] = typeof(RelativeTimePatternConverter),

    ["stacktrace"] = typeof(StackTracePatternConverter),
    ["stacktracedetail"] = typeof(StackTraceDetailPatternConverter),

    ["t"] = typeof(ThreadPatternConverter),
    ["thread"] = typeof(ThreadPatternConverter),

    // For backwards compatibility the NDC patterns
    ["x"] = typeof(NdcPatternConverter),
    ["ndc"] = typeof(NdcPatternConverter),

    // For backwards compatibility the MDC patterns just do a property lookup
    ["X"] = typeof(PropertyPatternConverter),
    ["mdc"] = typeof(PropertyPatternConverter),

    ["a"] = typeof(AppDomainPatternConverter),
    ["appdomain"] = typeof(AppDomainPatternConverter),

    ["u"] = typeof(IdentityPatternConverter),
    ["identity"] = typeof(IdentityPatternConverter),

    ["utcdate"] = typeof(UtcDatePatternConverter),
    ["utcDate"] = typeof(UtcDatePatternConverter),
    ["UtcDate"] = typeof(UtcDatePatternConverter),

    ["w"] = typeof(UserNamePatternConverter),
    ["username"] = typeof(UserNamePatternConverter),
  };

  /// <summary>
  /// the head of the pattern converter chain
  /// </summary>
  private PatternConverter? _head;

  /// <summary>
  /// patterns defined on this PatternLayout only
  /// </summary>
  private readonly Dictionary<string, ConverterInfo> _instanceRulesRegistry = new(StringComparer.Ordinal);

  /// <summary>
  /// Constructs a PatternLayout using the DefaultConversionPattern
  /// </summary>
  /// <remarks>
  /// <para>
  /// The default pattern just produces the application supplied message.
  /// </para>
  /// <para>
  /// Note to Inheritors: This constructor calls the virtual method
  /// <see cref="CreatePatternParser"/>. If you override this method be
  /// aware that it will be called before your is called constructor.
  /// </para>
  /// <para>
  /// As per the <see cref="IOptionHandler"/> contract the <see cref="ActivateOptions"/>
  /// method must be called after the properties on this object have been
  /// configured.
  /// </para>
  /// </remarks>
  public PatternLayout() : this(DefaultConversionPattern)
  { }

  /// <summary>
  /// Constructs a PatternLayout using the supplied conversion pattern
  /// </summary>
  /// <param name="pattern">the pattern to use</param>
  /// <remarks>
  /// <para>
  /// Note to Inheritors: This constructor calls the virtual method
  /// <see cref="CreatePatternParser"/>. If you override this method be
  /// aware that it will be called before your is called constructor.
  /// </para>
  /// <para>
  /// When using this constructor the <see cref="ActivateOptions"/> method 
  /// need not be called. This may not be the case when using a subclass.
  /// </para>
  /// </remarks>
  public PatternLayout(string? pattern)
  {
    // By default we do not process the exception
    IgnoresException = true;

    ConversionPattern = pattern ?? DefaultConversionPattern;

    ActivateOptions();
  }

  /// <summary>
  /// Gets or sets the pattern formatting string.
  /// </summary>
  /// <remarks>
  /// <para>
  /// The <b>ConversionPattern</b> option. This is the string which
  /// controls formatting and consists of a mix of literal content and
  /// conversion specifiers.
  /// </para>
  /// </remarks>
  public string ConversionPattern { get; set; }

  /// <summary>
  /// Create the pattern parser instance
  /// </summary>
  /// <param name="pattern">the pattern to parse</param>
  /// <returns>The <see cref="PatternParser"/> that will format the event</returns>
  /// <remarks>
  /// <para>
  /// Creates the <see cref="PatternParser"/> used to parse the conversion string. Sets the
  /// global and instance rules on the <see cref="PatternParser"/>.
  /// </para>
  /// </remarks>
  protected virtual PatternParser CreatePatternParser(string pattern)
  {
    PatternParser patternParser = new(pattern);

    // Add all the builtin patterns
    foreach (KeyValuePair<string, Type> entry in _sGlobalRulesRegistry)
    {
      ConverterInfo converterInfo = new()
      {
        Name = entry.Key,
        Type = entry.Value
      };
      patternParser.PatternConverters[entry.Key] = converterInfo;
    }
    // Add the instance patterns
    foreach (KeyValuePair<string, ConverterInfo> entry in _instanceRulesRegistry)
    {
      patternParser.PatternConverters[entry.Key] = entry.Value;
    }

    return patternParser;
  }

  /// <summary>
  /// Initializes layout options.
  /// </summary>
  /// <remarks>
  /// <para>
  /// This is part of the <see cref="IOptionHandler"/> delayed object
  /// activation scheme. The <see cref="ActivateOptions"/> method must 
  /// be called on this object after the configuration properties have
  /// been set. Until <see cref="ActivateOptions"/> is called this
  /// object is in an undefined state and must not be used. 
  /// </para>
  /// <para>
  /// If any of the configuration properties are modified then 
  /// <see cref="ActivateOptions"/> must be called again.
  /// </para>
  /// </remarks>
  public override void ActivateOptions()
  {
    _head = CreatePatternParser(ConversionPattern).Parse();

    PatternConverter? curConverter = _head;
    while (curConverter is not null)
    {
      if (curConverter is PatternLayoutConverter layoutConverter)
      {
        if (!layoutConverter.IgnoresException)
        {
          // Found converter that handles the exception
          IgnoresException = false;

          break;
        }
      }
      curConverter = curConverter.Next;
    }
  }

  /// <summary>
  /// Produces a formatted string as specified by the conversion pattern.
  /// </summary>
  /// <param name="loggingEvent">The event being logged.</param>
  /// <param name="writer">The TextWriter to write the formatted event to.</param>
  /// <remarks>
  /// <para>
  /// Parses the <see cref="LoggingEvent"/> using the patter format
  /// specified in the <see cref="ConversionPattern"/> property.
  /// </para>
  /// </remarks>
  public override void Format(TextWriter writer, LoggingEvent loggingEvent)
  {
    writer.EnsureNotNull();
    loggingEvent.EnsureNotNull();

    PatternConverter? c = _head;
    // loop through the chain of pattern converters
    while (c is not null)
    {
      c.Format(writer, loggingEvent);
      c = c.Next;
    }
  }

  /// <summary>
  /// Add a converter to this PatternLayout
  /// </summary>
  /// <param name="converterInfo">the converter info</param>
  /// <remarks>
  /// <para>
  /// This version of the method is used by the configurator.
  /// Programmatic users should use the alternative <see cref="M:AddConverter(string,Type)"/> method.
  /// </para>
  /// </remarks>
  public void AddConverter(ConverterInfo converterInfo)
  {
    if (!typeof(PatternConverter).IsAssignableFrom(converterInfo.EnsureNotNull().Type))
    {
      throw new ArgumentException($"The converter type specified [{converterInfo.Type}] must be a subclass of log4net.Util.PatternConverter", nameof(converterInfo));
    }
    _instanceRulesRegistry[converterInfo.Name] = converterInfo;
  }

  /// <summary>
  /// Adds a named pattern converter to this PatternLayout.
  /// </summary>
  /// <param name="name">the name of the conversion pattern for this converter</param>
  /// <param name="type">the type of the converter</param>
  /// <remarks>
  /// <para>
  /// This converter will be used in the formatting of the event.
  /// This method must be called before <see cref="ActivateOptions"/>.
  /// </para>
  /// <para>
  /// The <paramref name="type"/> specified must extend the 
  /// <see cref="PatternConverter"/> type.
  /// </para>
  /// </remarks>
  public void AddConverter(string name, Type type)
  {
    AddConverter(new()
    {
      Name = name.EnsureNotNull(),
      Type = type.EnsureNotNull()
    });
  }
}