#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; using System.Globalization; using System.IO; using System.Reflection; using System.Text; using log4net.Layout; using log4net.Core; using log4net.DateFormatter; using log4net.Layout.Pattern; using log4net.Util; namespace log4net.Util { /// /// Most of the work of the class /// is delegated to the PatternParser class. /// /// /// /// The PatternParser processes a pattern string and /// returns a chain of objects. /// /// /// Nicko Cadell /// Gert Driesen public sealed class PatternParser { #region Public Instance Constructors /// /// Constructor /// /// The pattern to parse. /// /// /// Initializes a new instance of the class /// with the specified pattern string. /// /// public PatternParser(string pattern) { m_pattern = pattern; } #endregion Public Instance Constructors #region Public Instance Methods /// /// Parses the pattern into a chain of pattern converters. /// /// The head of a chain of pattern converters. /// /// /// Parses the pattern into a chain of pattern converters. /// /// public PatternConverter Parse() { string[] converterNamesCache = BuildCache(); ParseInternal(m_pattern, converterNamesCache); return m_head; } #endregion Public Instance Methods #region Public Instance Properties /// /// Get the converter registry used by this parser /// /// /// The converter registry used by this parser /// /// /// /// Get the converter registry used by this parser /// /// public Hashtable PatternConverters { get { return m_patternConverters; } } #endregion Public Instance Properties #region Private Instance Methods /// /// Build the unified cache of converters from the static and instance maps /// /// the list of all the converter names /// /// /// Build the unified cache of converters from the static and instance maps /// /// private string[] BuildCache() { string[] converterNamesCache = new string[m_patternConverters.Keys.Count]; m_patternConverters.Keys.CopyTo(converterNamesCache, 0); // sort array so that longer strings come first Array.Sort(converterNamesCache, 0, converterNamesCache.Length, StringLengthComparer.Instance); return converterNamesCache; } #region StringLengthComparer /// /// Sort strings by length /// /// /// /// that orders strings by string length. /// The longest strings are placed first /// /// private sealed class StringLengthComparer : IComparer { public static readonly StringLengthComparer Instance = new StringLengthComparer(); private StringLengthComparer() { } #region Implementation of IComparer public int Compare(object x, object y) { string s1 = x as string; string s2 = y as string; if (s1 == null && s2 == null) { return 0; } if (s1 == null) { return 1; } if (s2 == null) { return -1; } return s2.Length.CompareTo(s1.Length); } #endregion } #endregion // StringLengthComparer /// /// Internal method to parse the specified pattern to find specified matches /// /// the pattern to parse /// the converter names to match in the pattern /// /// /// The matches param must be sorted such that longer strings come before shorter ones. /// /// private void ParseInternal(string pattern, string[] matches) { int offset = 0; while(offset < pattern.Length) { int i = pattern.IndexOf('%', offset); if (i < 0 || i == pattern.Length - 1) { ProcessLiteral(pattern.Substring(offset)); offset = pattern.Length; } else { if (pattern[i+1] == '%') { // Escaped ProcessLiteral(pattern.Substring(offset, i - offset + 1)); offset = i + 2; } else { ProcessLiteral(pattern.Substring(offset, i - offset)); offset = i + 1; FormattingInfo formattingInfo = new FormattingInfo(); // Process formatting options // Look for the align flag if (offset < pattern.Length) { if (pattern[offset] == '-') { // Seen align flag formattingInfo.LeftAlign = true; offset++; } } // Look for the minimum length while (offset < pattern.Length && char.IsDigit(pattern[offset])) { // Seen digit if (formattingInfo.Min < 0) { formattingInfo.Min = 0; } formattingInfo.Min = (formattingInfo.Min * 10) + int.Parse(pattern[offset].ToString(CultureInfo.InvariantCulture), System.Globalization.NumberFormatInfo.InvariantInfo); offset++; } // Look for the separator between min and max if (offset < pattern.Length) { if (pattern[offset] == '.') { // Seen separator offset++; } } // Look for the maximum length while (offset < pattern.Length && char.IsDigit(pattern[offset])) { // Seen digit if (formattingInfo.Max == int.MaxValue) { formattingInfo.Max = 0; } formattingInfo.Max = (formattingInfo.Max * 10) + int.Parse(pattern[offset].ToString(CultureInfo.InvariantCulture), System.Globalization.NumberFormatInfo.InvariantInfo); offset++; } int remainingStringLength = pattern.Length - offset; // Look for pattern for(int m=0; m /// Process a parsed literal /// /// the literal text private void ProcessLiteral(string text) { if (text.Length > 0) { // Convert into a pattern ProcessConverter("literal", text, new FormattingInfo()); } } /// /// Process a parsed converter pattern /// /// the name of the converter /// the optional option for the converter /// the formatting info for the converter private void ProcessConverter(string converterName, string option, FormattingInfo formattingInfo) { LogLog.Debug(declaringType, "Converter ["+converterName+"] Option ["+option+"] Format [min="+formattingInfo.Min+",max="+formattingInfo.Max+",leftAlign="+formattingInfo.LeftAlign+"]"); // Lookup the converter type ConverterInfo converterInfo = (ConverterInfo)m_patternConverters[converterName]; if (converterInfo == null) { LogLog.Error(declaringType, "Unknown converter name ["+converterName+"] in conversion pattern."); } else { // Create the pattern converter PatternConverter pc = null; try { pc = (PatternConverter)Activator.CreateInstance(converterInfo.Type); } catch(Exception createInstanceEx) { LogLog.Error(declaringType, "Failed to create instance of Type [" + converterInfo.Type.FullName + "] using default constructor. Exception: " + createInstanceEx.ToString()); } // formattingInfo variable is an instance variable, occasionally reset // and used over and over again pc.FormattingInfo = formattingInfo; pc.Option = option; pc.Properties = converterInfo.Properties; IOptionHandler optionHandler = pc as IOptionHandler; if (optionHandler != null) { optionHandler.ActivateOptions(); } AddConverter(pc); } } /// /// Resets the internal state of the parser and adds the specified pattern converter /// to the chain. /// /// The pattern converter to add. private void AddConverter(PatternConverter pc) { // Add the pattern converter to the list. if (m_head == null) { m_head = m_tail = pc; } else { // Set the next converter on the tail // Update the tail reference // note that a converter may combine the 'next' into itself // and therefore the tail would not change! m_tail = m_tail.SetNext(pc); } } #endregion Protected Instance Methods #region Private Constants private const char ESCAPE_CHAR = '%'; #endregion Private Constants #region Private Instance Fields /// /// The first pattern converter in the chain /// private PatternConverter m_head; /// /// the last pattern converter in the chain /// private PatternConverter m_tail; /// /// The pattern /// private string m_pattern; /// /// Internal map of converter identifiers to converter types /// /// /// /// This map overrides the static s_globalRulesRegistry map. /// /// private Hashtable m_patternConverters = new Hashtable(); #endregion Private Instance Fields #region Private Static Fields /// /// The fully qualified type of the PatternParser class. /// /// /// Used by the internal logger to record the Type of the /// log message. /// private readonly static Type declaringType = typeof(PatternParser); #endregion Private Static Fields } }