using System; using System.Collections.Generic; using System.Diagnostics; using System.Text; using System.Xml; namespace Wayne.Lib.Log { /// /// This class manages the path and file name to a log file, /// composing the correct name depending on datetime, id-entity properties etc. /// internal class LogConfigTextFilePath { #region Inner classes #region Param abstract class Param { public abstract string ToString(EntityCategory entityCategory); } #endregion #region ConstantTextParam class ConstantTextParam : Param { private string text; public ConstantTextParam(string text) { this.text = text; } // MLA COMMENTED OUT THIS UNUSED CODE. //public string Text //{ // get { return text; } //} public override string ToString(EntityCategory entityCategory) { return text; } public override string ToString() { return text; } } class PIDParam : Param { private string text; public PIDParam() { text = Process.GetCurrentProcess().Id.ToString(); } public override string ToString(EntityCategory entityCategory) { return text; } public override string ToString() { return text; } } #endregion #region EntityParamType enum enum EntityParamType { EntityType, EntitySubType } #endregion #region EntityParam class EntityParam : Param { private EntityParamType paramType; public EntityParam(EntityParamType entityParamType) { this.paramType = entityParamType; } public override string ToString(EntityCategory entityCategory) { switch (paramType) { case EntityParamType.EntityType: return entityCategory.Entity.EntityType; case EntityParamType.EntitySubType: return entityCategory.Entity.EntitySubType; default: return ""; } } public override string ToString() { return "<" + paramType.ToString() + "/>"; } } #endregion #region DateParam class DateParam : Param { private string dateTimeFormat; public DateParam(string dateTimeFormat) { this.dateTimeFormat = dateTimeFormat; } public override string ToString(EntityCategory entityCategory) { return DateTime.Now.ToString(dateTimeFormat, System.Globalization.CultureInfo.InvariantCulture); } public override string ToString() { return ""; } } #endregion #region IdParam class IdParam : Param { private string fromEntityType; private string fromEntitySubType; public IdParam(string fromEntityType, string fromEntitySubType) { this.fromEntityType = fromEntityType; this.fromEntitySubType = fromEntitySubType; } public override string ToString(EntityCategory entityCategory) { if ((fromEntityType != null) || (fromEntitySubType != null)) { IIdentifiableEntity[] ancestors = IdentifiableEntity.GetAncestorArray(entityCategory.Entity); if ((fromEntityType != null) && (fromEntitySubType != null)) { foreach (IIdentifiableEntity ancestor in ancestors) { if (System.Text.RegularExpressions.Regex.IsMatch(ancestor.EntityType, fromEntityType, System.Text.RegularExpressions.RegexOptions.IgnoreCase) && System.Text.RegularExpressions.Regex.IsMatch(ancestor.EntitySubType, fromEntitySubType, System.Text.RegularExpressions.RegexOptions.IgnoreCase)) { if (ancestor.Id != IdentifiableEntity.NoId) return ancestor.Id.ToString(System.Globalization.CultureInfo.InvariantCulture); return string.Empty; } } } else if (fromEntityType != null) { foreach (IIdentifiableEntity ancestor in ancestors) { if (System.Text.RegularExpressions.Regex.IsMatch(ancestor.EntityType, fromEntityType, System.Text.RegularExpressions.RegexOptions.IgnoreCase)) { if (ancestor.Id != IdentifiableEntity.NoId) return ancestor.Id.ToString(System.Globalization.CultureInfo.InvariantCulture); return string.Empty; } } } else { foreach (IIdentifiableEntity ancestor in ancestors) { if (System.Text.RegularExpressions.Regex.IsMatch(ancestor.EntitySubType, fromEntitySubType, System.Text.RegularExpressions.RegexOptions.IgnoreCase)) { if (ancestor.Id != IdentifiableEntity.NoId) return ancestor.Id.ToString(System.Globalization.CultureInfo.InvariantCulture); return string.Empty; } } } } else if (entityCategory.Entity.Id != IdentifiableEntity.NoId) return entityCategory.Entity.Id.ToString(System.Globalization.CultureInfo.InvariantCulture); return string.Empty; } public override string ToString() { return string.Format("", fromEntityType); } } #endregion #region FileNameInvalidTimeResolution enum /// /// The resolution of the time when the log file gets a new name. /// enum FileNameInvalidTimeResolution { // The log file gets a new name every... Second, Minute, Hour, Day, Month, Year, Never } #endregion #endregion #region Fields private List paramList = new List(); private FileNameInvalidTimeResolution fileNameInvalidTimeResolution; // The log file gets a new name every... private DateTime fileNameInvalidTime = DateTime.MinValue; // At this time the log file should get a new name. private Dictionary cachedFileNames = new Dictionary(); private object cachedFileNamesLock = new object(); private string cachedToString; private string cachedBaseName; #endregion #region Construction /// /// Construct a LogPath object from an configuration XML element. /// /// internal LogConfigTextFilePath(XmlElement logPathElement) { fileNameInvalidTimeResolution = FileNameInvalidTimeResolution.Never; for (int i = 0; i < logPathElement.ChildNodes.Count; i++) { XmlNode node = logPathElement.ChildNodes[i]; if (node.NodeType == XmlNodeType.Text) { string text = node.Value.Replace("\r", "").Replace("\n", ""); if (i == 0) text = text.TrimStart(' '); if (i == logPathElement.ChildNodes.Count - 1) text = text.TrimEnd(' '); paramList.Add(new ConstantTextParam(text)); } else if (node.NodeType == XmlNodeType.Element) { switch (node.Name) { case "Id": XmlAttribute fromEntityTypeAttribute = node.Attributes["FromEntityType"]; string fromEntityType; if (fromEntityTypeAttribute != null) fromEntityType = fromEntityTypeAttribute.Value; else fromEntityType = null; XmlAttribute fromEntitySubTypeAttribute = node.Attributes["FromEntitySubType"]; string fromEntitySubType; if (fromEntitySubTypeAttribute != null) fromEntitySubType = fromEntitySubTypeAttribute.Value; else fromEntitySubType = null; paramList.Add(new IdParam(fromEntityType, fromEntitySubType)); break; case "EntityType": paramList.Add(new EntityParam(EntityParamType.EntityType)); break; case "EntitySubType": paramList.Add(new EntityParam(EntityParamType.EntitySubType)); break; case "PID": paramList.Add(new PIDParam()); break; case "Date": string dateFormat; XmlAttribute formatAttribute = node.Attributes["Format"]; if (formatAttribute != null) dateFormat = formatAttribute.Value; else dateFormat = "yyyyMMdd"; paramList.Add(new DateParam(dateFormat)); // Find the smallest time resolution in the date format. This often the file name will be refreshed. if ((dateFormat.IndexOf("s", StringComparison.Ordinal) > -1) || (dateFormat.IndexOf("f", StringComparison.OrdinalIgnoreCase) > -1)) fileNameInvalidTimeResolution = FileNameInvalidTimeResolution.Second; else if ((dateFormat.IndexOf("m", StringComparison.Ordinal) > -1) && (fileNameInvalidTimeResolution > FileNameInvalidTimeResolution.Minute)) fileNameInvalidTimeResolution = FileNameInvalidTimeResolution.Minute; else if ((dateFormat.IndexOf("h", StringComparison.OrdinalIgnoreCase) > -1) && (fileNameInvalidTimeResolution > FileNameInvalidTimeResolution.Hour)) fileNameInvalidTimeResolution = FileNameInvalidTimeResolution.Hour; else if ((dateFormat.IndexOf("d", StringComparison.Ordinal) > -1) && (fileNameInvalidTimeResolution > FileNameInvalidTimeResolution.Day)) fileNameInvalidTimeResolution = FileNameInvalidTimeResolution.Day; else if ((dateFormat.IndexOf("M", StringComparison.Ordinal) > -1) && (fileNameInvalidTimeResolution > FileNameInvalidTimeResolution.Month)) fileNameInvalidTimeResolution = FileNameInvalidTimeResolution.Month; else if ((dateFormat.IndexOf("y", StringComparison.Ordinal) > -1) && (fileNameInvalidTimeResolution > FileNameInvalidTimeResolution.Year)) fileNameInvalidTimeResolution = FileNameInvalidTimeResolution.Year; break; } } } } #endregion #region Methods /// /// Checks whether it's time for a new file name. /// If it is, the datetime for the next name change is calculated and all cached file names /// are deleted. /// public bool IsTimeForNewFileName() { if (DateTime.Now > fileNameInvalidTime) { lock (cachedFileNamesLock) { // All the cached file names are now invalid. cachedFileNames.Clear(); } // Calculate the next time for file name change. DateTime now = DateTime.Now; switch (fileNameInvalidTimeResolution) { case FileNameInvalidTimeResolution.Second: fileNameInvalidTime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, DateTimeKind.Local).AddSeconds(1); break; case FileNameInvalidTimeResolution.Minute: fileNameInvalidTime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, 0, DateTimeKind.Local).AddMinutes(1); break; case FileNameInvalidTimeResolution.Hour: fileNameInvalidTime = new DateTime(now.Year, now.Month, now.Day, now.Hour, 0, 0, DateTimeKind.Local).AddHours(1); break; case FileNameInvalidTimeResolution.Day: fileNameInvalidTime = new DateTime(now.Year, now.Month, now.Day, 0, 0, 0, DateTimeKind.Local).AddDays(1); break; case FileNameInvalidTimeResolution.Month: fileNameInvalidTime = new DateTime(now.Year, now.Month, 1, 0, 0, 0, DateTimeKind.Local).AddMonths(1); break; case FileNameInvalidTimeResolution.Year: fileNameInvalidTime = new DateTime(now.Year, 1, 1, 0, 0, 0, DateTimeKind.Local).AddYears(1); break; case FileNameInvalidTimeResolution.Never: fileNameInvalidTime = DateTime.MaxValue; break; } return true; } return false; } /// /// Equality operator /// /// /// public override bool Equals(object obj) { return obj.ToString().Equals(this.ToString()); } /// /// Hash code generator. Not correctly implemented I think.... /// /// public override int GetHashCode() { return this.ToString().GetHashCode(); } /// /// Returns the log path using the specified identifiable entity. /// /// public string ToString(EntityCategory entityCategory) { lock (cachedFileNamesLock) { string text; if (cachedFileNames.TryGetValue(entityCategory, out text)) return text; StringBuilder sb = new StringBuilder(); foreach (Param param in paramList) sb.Append(param.ToString(entityCategory)); string fileName = sb.ToString(); cachedFileNames.Add(entityCategory, fileName); return fileName; } } /// /// ToString method. /// /// public override string ToString() { if (cachedToString == null) { StringBuilder sb = new StringBuilder(); foreach (Param param in paramList) sb.Append(param.ToString()); cachedToString = sb.ToString(); } return cachedToString; } /// /// Clean the internal lists. /// /// internal void PerformListCleaning(DateTime oldestAllowedTouch) { lock (cachedFileNamesLock) { List entityCategoriesToRemove = new List(); foreach (EntityCategory entityCategory in cachedFileNames.Keys) { if (entityCategory.LastTouched < oldestAllowedTouch) entityCategoriesToRemove.Add(entityCategory); } //Remove from the list. foreach (EntityCategory entityCategoryToRemove in entityCategoriesToRemove) { cachedFileNames.Remove(entityCategoryToRemove); } } } #endregion #region Properties /// /// Returns constant prefix base name - adds parameters until it finds a PID or Date parameter /// /// public string BaseName { get { if (cachedBaseName == null) { StringBuilder sb = new StringBuilder(); PIDParam pp = null; DateParam dp = null; IdParam ip = null; foreach (Param param in paramList) { pp = param as PIDParam; if (pp != null) { break; } dp = param as DateParam; if (dp != null) { break; } ip = param as IdParam; if (ip != null) { break; } if (param.ToString().IndexOf('<') >= 0) break; if (param.ToString().IndexOf('/') >= 0) break; sb.Append(param.ToString()); } cachedBaseName = sb.ToString(); } return cachedBaseName; } } #endregion // Properties } }