/// <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); }
/// <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; } }
/// <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; }
/// <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; } }
/// <summary> /// Returns True if the placeholder was formatted correctly; False if a placeholder couldn't be found. /// Outputs all relevant placeholder information. /// /// This function takes the place of the Regular Expression. /// It is faster and more direct, and does not suffer from Regex endless loops. /// In tests, this nearly doubles the speed vs Regex. /// </summary> private bool NextPlaceholder(string format, int startIndex, int endIndex, ref PlaceholderInfo placeholder) { placeholder = new PlaceholderInfo(); placeholder.hasNested = false; placeholder.placeholderStart = -1; placeholder.selectorLength = -1; List<string> selectorSplitList = new List<string>(); int lastSplitIndex = 0; int openCount = 0; // Dim endIndex% = format.Length while (startIndex < endIndex) { char c = format[startIndex]; if (placeholder.placeholderStart == -1) { // Looking for "{" if (c == '{') { placeholder.placeholderStart = startIndex; placeholder.selectorStart = startIndex + 1; lastSplitIndex = placeholder.selectorStart; } else if (c == Plugins.Core.ParsingServices.escapeCharacter) { // The next character is escaped startIndex++; } } else if (placeholder.selectorLength == -1) { // Looking for ":" or "}" ... // or an alpha-numeric or a selectorSplitter if (c == '}') { // Add this item to the list of Selectors (as long as it isn't empty) if (lastSplitIndex < startIndex) { selectorSplitList.Add(format.Substring(lastSplitIndex, (startIndex - lastSplitIndex))); lastSplitIndex = (startIndex + 1); } placeholder.selectors = selectorSplitList.ToArray(); placeholder.placeholderLength = startIndex + 1 - placeholder.placeholderStart; placeholder.selectorLength = startIndex - placeholder.selectorStart; placeholder.formatLength = 0; return true; } else if (c == ':') { // Add this item to the list of Selectors (as long as it isn't empty) if (lastSplitIndex < startIndex) { selectorSplitList.Add(format.Substring(lastSplitIndex, (startIndex - lastSplitIndex))); lastSplitIndex = startIndex + 1; } placeholder.selectors = selectorSplitList.ToArray(); placeholder.selectorLength = startIndex - placeholder.selectorStart; placeholder.formatStart = startIndex + 1; } else if (Plugins.Core.ParsingServices.selectorSplitters.IndexOf(c) >= 0) { // It is a valid splitter character // Add this item to the list of Selectors (as long as it isn't empty) if (lastSplitIndex < startIndex) { selectorSplitList.Add(format.Substring(lastSplitIndex, (startIndex - lastSplitIndex))); lastSplitIndex = (startIndex + 1); } } else if (char.IsLetterOrDigit(c) || Plugins.Core.ParsingServices.selectorCharacters.Contains(new string(c, 1))) { // It is a valid selector character, so let's just continue } else { // It is NOT a valid character!!! if (placeholder.selectorStart <= startIndex) { startIndex--; } placeholder.placeholderStart = -1; // Restart the search selectorSplitList.Clear(); } } else { // We are in the Format section: // Looking for a "}" if (c == '}') { if (openCount == 0) { // we're done! placeholder.placeholderLength = startIndex + (1 - placeholder.placeholderStart); placeholder.formatLength = startIndex - placeholder.formatStart; return true; } else { openCount--; } } else if (c == '{') { // It's a nested bracket openCount++; placeholder.hasNested = true; } else if (c == Plugins.Core.ParsingServices.escapeCharacter) { // The next character is escaped startIndex++; } else { // It's just part of the Format } } startIndex++; } return false; // We couldn't find a full placeholder. }
/// <summary> /// Returns True if the placeholder was formatted correctly; False if a placeholder couldn't be found. /// Outputs all relevant placeholder information. /// /// This function takes the place of the Regular Expression. /// It is faster and more direct, and does not suffer from Regex endless loops. /// In tests, this nearly doubles the speed vs Regex. /// </summary> private bool NextPlaceholder(string format, int startIndex, int endIndex, ref PlaceholderInfo placeholder) { placeholder = new PlaceholderInfo(); placeholder.hasNested = false; placeholder.placeholderStart = -1; placeholder.selectorLength = -1; List <string> selectorSplitList = new List <string>(); int lastSplitIndex = 0; int openCount = 0; // Dim endIndex% = format.Length while (startIndex < endIndex) { char c = format[startIndex]; if (placeholder.placeholderStart == -1) { // Looking for "{" if (c == '{') { placeholder.placeholderStart = startIndex; placeholder.selectorStart = startIndex + 1; lastSplitIndex = placeholder.selectorStart; } else if (c == Plugins.Core.ParsingServices.escapeCharacter) { // The next character is escaped startIndex++; } } else if (placeholder.selectorLength == -1) { // Looking for ":" or "}" ... // or an alpha-numeric or a selectorSplitter if (c == '}') { // Add this item to the list of Selectors (as long as it isn't empty) if (lastSplitIndex < startIndex) { selectorSplitList.Add(format.Substring(lastSplitIndex, (startIndex - lastSplitIndex))); lastSplitIndex = (startIndex + 1); } placeholder.selectors = selectorSplitList.ToArray(); placeholder.placeholderLength = startIndex + 1 - placeholder.placeholderStart; placeholder.selectorLength = startIndex - placeholder.selectorStart; placeholder.formatLength = 0; return(true); } else if (c == ':') { // Add this item to the list of Selectors (as long as it isn't empty) if (lastSplitIndex < startIndex) { selectorSplitList.Add(format.Substring(lastSplitIndex, (startIndex - lastSplitIndex))); lastSplitIndex = startIndex + 1; } placeholder.selectors = selectorSplitList.ToArray(); placeholder.selectorLength = startIndex - placeholder.selectorStart; placeholder.formatStart = startIndex + 1; } else if (Plugins.Core.ParsingServices.selectorSplitters.IndexOf(c) >= 0) { // It is a valid splitter character // Add this item to the list of Selectors (as long as it isn't empty) if (lastSplitIndex < startIndex) { selectorSplitList.Add(format.Substring(lastSplitIndex, (startIndex - lastSplitIndex))); lastSplitIndex = (startIndex + 1); } } else if (char.IsLetterOrDigit(c) || Plugins.Core.ParsingServices.selectorCharacters.Contains(new string(c, 1))) { // It is a valid selector character, so let's just continue } else { // It is NOT a valid character!!! if (placeholder.selectorStart <= startIndex) { startIndex--; } placeholder.placeholderStart = -1; // Restart the search selectorSplitList.Clear(); } } else { // We are in the Format section: // Looking for a "}" if (c == '}') { if (openCount == 0) { // we're done! placeholder.placeholderLength = startIndex + (1 - placeholder.placeholderStart); placeholder.formatLength = startIndex - placeholder.formatStart; return(true); } else { openCount--; } } else if (c == '{') { // It's a nested bracket openCount++; placeholder.hasNested = true; } else if (c == Plugins.Core.ParsingServices.escapeCharacter) { // The next character is escaped startIndex++; } else { // It's just part of the Format } } startIndex++; } return(false); // We couldn't find a full placeholder. }
/// <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)); }