/// <summary> /// Determines what to do when an Invalid Selector is found. /// /// Returns True if we should just continue; False if we should skip this item. /// </summary> private bool OnInvalidSelector(string format, CustomFormatInfo info, PlaceholderInfo placeholder) { string invalidSelector = format.Substring(placeholder.selectorStart, placeholder.selectorLength); string message; switch (InvalidSelectorAction) { case ErrorAction.ThrowError: // Let's give a detailed description of the error: message = FormatEx( ("Invalid Format String.\\n" + ("Could not evaluate \"{0}\": \"{1}\" is not a member of {2}.\\n" + ("The error occurs at position {3} of the following format string:\\n" + "{4}"))), invalidSelector, info.Selector, info.CurrentType, placeholder.placeholderStart, format); throw new ArgumentException(message, invalidSelector); case ErrorAction.OutputErrorInResult: // Let's put the placeholder back, // along with the error. // Example: {Person.Name.ABC} becomes {Person.Name.ABC:(Error: "ABC" is not a member of String)} message = ("{" + (FormatEx("{0}:(Error: \"{1}\" is not a member of {2})", invalidSelector, info.Selector, info.CurrentType) + "}")); info.WriteError(message, placeholder); return(false); case ErrorAction.Ignore: // Allow formatting to continue! break; } return(true); }
private static void _GetDefaultOutput(object sender, ExtendFormatEventArgs e) { CustomFormatInfo info = e.FormatInfo; // Let's see if there are nested items: if (info.HasNested) { info.CustomFormatNested(); return; } // Let's do the default formatting: // We will try using IFormatProvider, IFormattable, and if all else fails, ToString. // (This code was adapted from the built-in String.Format code) if (info.Provider != null) { // Use the provider to see if a CustomFormatter is available: ICustomFormatter formatter = info.Provider.GetFormat(typeof(ICustomFormatter)) as ICustomFormatter; if (formatter != null) { info.Write(formatter.Format(info.Format, info.Current, info.Provider)); return; } } // Now try to format the object, using its own built-in formatting if possible: if (info.Current is IFormattable) { info.Write(((IFormattable)info.Current).ToString(info.Format, info.Provider)); } else { info.Write(info.Current.ToString()); } }
private void DoTimeStringFormat(object sender, ExtendFormatEventArgs e) { CustomFormatInfo info = e.FormatInfo; if (info.CurrentIsTimeSpan) { info.Write(TimestringPlugin.ToTimeString((TimeSpan)info.Current, info.Format, _formattingOptions)); } else if (info.CurrentIsDate && info.FormatStartsWith("timestring")) { info.Write(TimestringPlugin.ToTimeString(DateTime.Now.Subtract((DateTime)info.Current), info.Format.Substring(10), _formattingOptions)); } }
/// <summary> /// Determines what to do when an Invalid Selector is found. /// </summary> private void OnInvalidFormat(string format, CustomFormatInfo info, PlaceholderInfo placeholder, Exception ex) { string selector = format.Substring(placeholder.selectorStart, placeholder.selectorLength); string invalidFormat = format.Substring(placeholder.formatStart, placeholder.formatLength); string errorMessage = ex.Message; if (ex is FormatException) { errorMessage = FormatEx("\"{0}\" is not a valid format specifier for {1}", invalidFormat, info.CurrentType); } string message; switch (InvalidFormatAction) { case ErrorAction.ThrowError: // Let's give a detailed description of the error: message = FormatEx( ("Invalid Format String.\\n" + ("Could not evaluate {{0}} because {1}.\\n" + ("The error occurs at position {2} of the following format string:\\n" + "{3}"))), selector, errorMessage, placeholder.placeholderStart, format); throw new ArgumentException(message, invalidFormat, ex); case ErrorAction.OutputErrorInResult: // Let's put the placeholder back, // along with the error. // Example: {Person.Birthday:x} becomes {Person.Birthday:(Error: "x" is an invalid format specifier)} message = ("{" + (FormatEx("{0}:(Error: {1})", selector, errorMessage) + "}")); info.WriteError(message, placeholder); break; case ErrorAction.Ignore: // Allow formatting to continue! break; } }
private static void FormatConditional(object sender, ExtendFormatEventArgs e) { CustomFormatInfo info = e.FormatInfo; // See if the format string contains un-nested "|": string[] parameters = Core.ParsingServices.SplitNested(info.Format, '|'); if (parameters.Length == 1) { return; // There are no splits. } int paramCount = parameters.Length; int paramIndex = 0; // Determines which parameter to use in the result // See if there are any (optional) conditions: bool conditionResult = false; if (info.CurrentIsNumber && TryEvaluateCondition(ref parameters[0], info.Current, ref conditionResult)) { // parameters(0) contained a "conditional statement" // If the conditional statement was False, then // we will move on to the next parameters while (!conditionResult) { if (paramIndex == parameters.Length - 1) { break; } paramIndex += 1; if (!TryEvaluateCondition(ref parameters[paramIndex], info.Current, ref conditionResult)) { // (couldn't evaluate the conditional statement, which means it's an "else" statement break; } } } else { // Determine the Current item's Type: if (info.CurrentIsNumber) { // Number: Neg|Zero|One|Many or Zero|One|Many/Neg or One|Many/Neg/Zero var arg = Convert.ToDecimal(info.Current); if (arg < 0m) { paramIndex = -4; } else if (arg == 0m) { paramIndex = -3; } else if (0m < arg && arg <= 1m) { paramIndex = -2; } else { paramIndex = -1; } paramIndex = paramIndex + paramCount; if (paramIndex < 0) { paramIndex = paramCount - 1; } } else if (info.CurrentIsBoolean) { // Bool: True|False bool arg = (bool)info.Current; if (!arg) { paramIndex = 1; } } else if (info.CurrentIsDate) { // Date: Past|Present|Future or Past/Present|Future System.DateTime arg = (DateTime)info.Current; if (paramCount == 3 && arg.Date == DateTime.Today) { paramIndex = 1; } else if (arg > DateTime.Now) { paramIndex = paramCount - 1; } } else if (info.CurrentIsTimeSpan) { // TimeSpan: Negative|Zero|Positive or Negative/Zero|Positive TimeSpan arg = (TimeSpan)info.Current; if (paramCount == 3 && arg == TimeSpan.Zero) { paramIndex = 1; } else if (arg.CompareTo(TimeSpan.Zero) == 1) { paramIndex = paramCount - 1; } } else if (info.CurrentIsString) { // String: Value|NullOrEmpty var arg = (string)info.Current; if (string.IsNullOrEmpty(arg)) { paramIndex = 1; } } else { // Object: Something|Nothing object arg = info.Current; if (arg == null) { paramIndex = 1; } } } // Now, output the selected parameter: if (parameters[paramIndex].Contains("{")) { // The format has nested items, so let's evaluate those now: info.SetFormat(parameters[paramIndex], true); info.CustomFormatNested(); } else { // The format doesn't have nested items so let's just write the selected parameter: info.Write(parameters[paramIndex]); } }
/// <summary> /// Does the actual work. /// </summary> internal void FormatExInternal(CustomFormatInfo info) { if (info.Current == null && info.Arguments.Length >= 1) { info.Current = info.Arguments[0]; } // We need to store the Format and the Current items and keep them in this context string format = info.Format; object current = info.Current; // ' Here is the regular expression to use for parsing the Format string: // Static R As New Regex( _ // "{ ([0-9A-Za-z_.\[\]()]*) (?: : ( (?: (?<open>{) | (?<nest-open>}) | [^{}]+ )*? ) (?(open)(?!)) )? }" _ // , RegexOptions.IgnorePatternWhitespace Or RegexOptions.Compiled) // { ( Selectors ) (Optnl : { Nested } or Format ) } int lastAppendedIndex = 0; PlaceholderInfo placeholder = null; while (NextPlaceholder(format, lastAppendedIndex, format.Length, ref placeholder)) { // Write the text in-between placeholders: info.WriteRegularText(format, lastAppendedIndex, (placeholder.placeholderStart - lastAppendedIndex)); lastAppendedIndex = placeholder.placeholderStart + placeholder.placeholderLength; // Evaluate the source by evaluating each argSelector: info.Current = current; // Restore the current scope //bool isFirstSelector = true; // TODO: Remove this variable if it never gets used again int selectorIndex = -1; foreach (string selector in placeholder.selectors) { selectorIndex++; info.SetSelector(selector, selectorIndex); // Raise the ExtendCustomSource event to allow custom source evaluation: OnExtendSourceEvent(new ExtendSourceEventArgs(info)); // Make sure that the selector has been handled: if (!info.Handled) { break; } //isFirstSelector = false; } // Handle errors: if (!info.Handled) { // If the ExtendCustomSource event wasn't handled, // then the Selector could not be evaluated. if (!OnInvalidSelector(format, info, placeholder)) { continue; } } string argFormat = format.Substring(placeholder.formatStart, placeholder.formatLength); info.SetFormat(argFormat, placeholder.hasNested); try { // Raise the ExtendCustomFormat event to allow custom formatting: OnExtendFormatEvent(new ExtendFormatEventArgs(info)); } catch (Exception ex) { // Handle errors: OnInvalidFormat(format, info, placeholder, ex); } } // Write the substring between the last bracket and the end of the string: info.WriteRegularText(format, lastAppendedIndex, (format.Length - lastAppendedIndex)); }
private void DoArrayFormatting(object sender, ExtendFormatEventArgs e) { CustomFormatInfo info = e.FormatInfo; // This method needs the Highest priority so that it comes before Strings.Format.Conditional.vb ICollection items = info.Current as ICollection; if (items == null) { return; } // The SplitString function is in the file Strings.Format.Conditional.vb: string[] split = Core.ParsingServices.SplitNested(info.Format, '|', 3); string format = split[0]; string spacer = null, lastSpacer = null; if (split.Length >= 2) { spacer = split[1]; } else { spacer = " "; // At least put a space between items by default } if (split.Length >= 3) { lastSpacer = split[2]; } if (!info.HasNested) { format = "{:" + format + "}"; } int itemCount = -1; if (lastSpacer != null) { itemCount = items.Count; } int oldCollectionIndex = CollectionIndex; // In case we have nested arrays, we might need to restore the CollectionIndex CollectionIndex = -1; foreach (object item in items) { CollectionIndex += 1; // Keep track of the index // If it isn't the first item, then write the spacer: if (CollectionIndex > 0) { // Write either the spacer or lastSpacer: if (itemCount == -1 || CollectionIndex < itemCount - 1) { info.Write(spacer); } else { info.Write(lastSpacer); } } // Write the format for this item: info.Current = item; info.SetFormat(format, info.HasNested); info.CustomFormatNested(); } CollectionIndex = oldCollectionIndex; // Restore the CollectionIndex }