public void EvaluateSelector(object current, Selector selector, ref bool handled, ref object result, FormatDetails formatDetails) { // See if current is a IDictionary and contains the selector: var rawDict = current as IDictionary; if (rawDict != null) { foreach (DictionaryEntry entry in rawDict) { var key = (entry.Key as string) ?? entry.Key.ToString(); if (key.Equals(selector.Text, Smart.Settings.GetCaseSensitivityComparison())) { result = entry.Value; handled = true; return; } } } // this check is for dynamics and generic dictionaries var dict = current as IDictionary<string, object>; if (dict != null) { var val = dict.FirstOrDefault(x => x.Key.Equals(selector.Text, Smart.Settings.GetCaseSensitivityComparison())).Value; if (val != null) { result = val; handled = true; } } }
/// <summary> /// Performs the default index-based selector, same as String.Format. /// </summary> public void EvaluateSelector(object current, Selector selector, ref bool handled, ref object result, FormatDetails formatDetails) { int selectorValue; if (int.TryParse(selector.Text, out selectorValue)) { // Argument Index: // Just like String.Format, the arg index must be in-range, // should be the first item, and shouldn't have any operator: if (selector.SelectorIndex == 0 && selectorValue < formatDetails.OriginalArgs.Length && selector.Operator == "") { // This selector is an argument index. result = formatDetails.OriginalArgs[selectorValue]; handled = true; } // Alignment: // An alignment item should be preceeded by a comma else if (selector.Operator == ",") { // This selector is actually an Alignment modifier. result = current; // (don't change the current item) formatDetails.Placeholder.Alignment = selectorValue; // Set the alignment handled = true; } } }
public void EvaluateFormat(object current, Format format, ref bool handled, IOutput output, FormatDetails formatDetails) { if (format != null && format.HasNested) return; var formatText = format != null ? format.Text : ""; TimeSpan fromTime; if (current is TimeSpan) { fromTime = (TimeSpan)current; } else if (current is DateTime && formatText.StartsWith("timestring")) { formatText = formatText.Substring(10); fromTime = DateTime.Now.Subtract((DateTime)current); } else { return; } var timeTextInfo = GetTimeTextInfo(formatDetails.Provider); if (timeTextInfo == null) { return; } var formattingOptions = TimeSpanFormatOptionsConverter.Parse(formatText); var timeString = TimeSpanUtility.ToTimeString(fromTime, formattingOptions, timeTextInfo); output.Write(timeString, formatDetails); handled = true; }
public void EvaluateSelector(object current, Selector selector, ref bool handled, ref object result, FormatDetails formatDetails) { // See if current is a IDictionary and contains the selector: var dict = current as IDictionary; if (dict != null && dict.Contains(selector.Text)) { result = dict[selector.Text]; handled = true; } }
public void EvaluateFormat(object current, Format format, ref bool handled, IOutput output, FormatDetails formatDetails) { if (!(current is Guid)) return; var guid = (Guid)current; output.Write(guid.ToString().Substring(0, 6), formatDetails); output.Write("...", formatDetails); handled = true; }
public void EvaluateFormat(object current, Format format, ref bool handled, IOutput output, FormatDetails formatDetails) { Expected expected = current as Expected; if (expected == null) return; handled = true; if (!string.IsNullOrEmpty(expected.Expression)) OutputExpectedExpression(expected, output, formatDetails); if (format == null) OutputExpectedValue(expected, output, formatDetails); else OutputFormattedExpected(current, format, output, formatDetails, expected); }
public void EvaluateFormat(object current, Format format, ref bool handled, IOutput output, FormatDetails formatDetails) { // Ignore formats that start with "?" (this can be used to bypass this extension) if (format == null || format.baseString[format.startIndex] == ':') { return; } // Extract the plural words from the format string: var pluralWords = format.Split("|"); // This extension requires at least two plural words: if (pluralWords.Count == 1) return; // See if the value is a number: var currentIsNumber = current is byte || current is short || current is int || current is long || current is float || current is double || current is decimal; // This extension only formats numbers: if (!currentIsNumber) return; // Normalize the number to decimal: var value = Convert.ToDecimal(current); // Get the plural rule: var provider = formatDetails.Provider; var pluralRule = GetPluralRule(provider); if (pluralRule == null) { // Not a supported language. return; } var pluralCount = pluralWords.Count; var pluralIndex = pluralRule(value, pluralCount); if (pluralIndex < 0 || pluralWords.Count <= pluralIndex) { // The plural rule should always return a value in-range! throw new FormatException(format, "Invalid number of plural parameters", pluralWords.Last().endIndex); } // Output the selected word (allowing for nested formats): var pluralForm = pluralWords[pluralIndex]; formatDetails.Formatter.Format(output, pluralForm, current, formatDetails); handled = true; }
/// <summary>Takes the current object and writes it to the output, using the specified format.</summary> /// <param name="current">The object to be formatted.</param> /// <param name="format">Represents a parsed format string.</param> /// <param name="handled">Set to indicate whether the formatter has formatted the object.</param> /// <param name="output">The output to write to.</param> /// <param name="formatDetails">Contains extra information about the item being formatted.</param> public void EvaluateFormat(object current, Format format, ref bool handled, IOutput output, FormatDetails formatDetails) { // validate if (!current.IsNumber() || format == null) return; if (format.baseString[format.startIndex] == ':') return; // ':' is a magic prefix to bypass pluralisation // parse format decimal count = Convert.ToDecimal(current); var pluralWords = format.Split("|"); // extract plural words int pluralCount = pluralWords.Count; if (pluralCount == 1) return; // get pluralisation rules CultureInfo culture = (formatDetails.Provider as CultureInfo) ?? this.DefaultCulture ?? CultureInfo.CurrentUICulture; PluralRuleset ruleset = this.Rulesets.GetRuleset(culture.TwoLetterISOLanguageName); if (ruleset == null) return; // unknown language if ((pluralCount < 0 || pluralCount > ruleset.Forms) && !ruleset.AlternativeFormSelectors.ContainsKey(pluralCount)) { // determine the accepted number of forms string expected = String.Join( ", ", new[] { ruleset.Forms } .Union(ruleset.AlternativeFormSelectors.Keys) .Select(p => p.ToString()) .ToArray() ); throw new FormatException(format, String.Format("There are an invalid number of plural parameters (got {0}, expected one of: {1}).", pluralCount, expected), pluralWords.Last().endIndex); } // get plural form int pluralIndex = ruleset.SelectForm(count, pluralCount); if (pluralIndex < 0 || pluralIndex > pluralCount - 1) throw new FormatException(format, String.Format("The ruleset unexpectedly returned an invalid plural form (expected 0-{0}, got {1}).", pluralCount - 1, pluralIndex), pluralWords.Last().endIndex); // output the selected word (allowing for nested formats): var pluralForm = pluralWords[pluralIndex]; formatDetails.Formatter.Format(output, pluralForm, current, formatDetails); handled = true; }
public void Write(string text, int startIndex, int length, FormatDetails formatDetails) { // Depending on the nested level, we will color this item differently: if (formatDetails.FormatError != null) { output.BackColor(errorColor).Append(text, startIndex, length); } else if (formatDetails.Placeholder == null) { // There is no "nesting" so just output plain text: output.Append(text, startIndex, length); } else { var nestedDepth = formatDetails.Placeholder.NestedDepth; var backcolor = this.nestedColors[nestedDepth % nestedColors.Length]; output.BackColor(backcolor).Append(text, startIndex, length); } }
public void EvaluateSelector(object current, Selector selector, ref bool handled, ref object result, FormatDetails formatDetails) { // See if current is a IDictionary and contains the selector var dict = current as IDictionary; if (dict != null && dict.Contains(selector.Text)) { result = dict[selector.Text]; handled = true; return; } // See if current is an IDictionary<string, object> (also an "expando" object) // and contains the selector. var genericDictionary = current as IDictionary<string, object>; if (genericDictionary != null && genericDictionary.ContainsKey(selector.Text)) { result = genericDictionary[selector.Text]; handled = true; } }
/// <summary> /// This allows an integer to be used as a selector to index an array (or list). /// /// This is better described using an example: /// CustomFormat("{Dates.2.Year}", {#1/1/2000#, #12/31/2999#, #9/9/9999#}) = "9999" /// The ".2" selector is used to reference Dates[2]. /// </summary> public void EvaluateSelector(object current, Selector selector, ref bool handled, ref object result, FormatDetails formatDetails) { // See if we're trying to access a specific index: int itemIndex; var currentList = current as IList; var isAbsolute = (selector.SelectorIndex == 0 && selector.Operator.Length == 0); if (!isAbsolute && currentList != null && int.TryParse(selector.Text, out itemIndex) && itemIndex < currentList.Count) { // The current is a List, and the selector is a number; // let's return the List item: // Example: {People[2].Name} // ^List ^itemIndex result = currentList[itemIndex]; handled = true; } // We want to see if there is an "Index" property that was supplied. if (selector.Text.Equals("index", StringComparison.OrdinalIgnoreCase)) { // Looking for "{Index}" if (selector.SelectorIndex == 0) { result = CollectionIndex; handled = true; return; } // Looking for 2 lists to sync: "{List1: {List2[Index]} }" if (currentList != null && 0 <= CollectionIndex && CollectionIndex < currentList.Count) { result = currentList[CollectionIndex]; handled = true; } } }
private static void OutputExpectedValue(Expected expected, IOutput output, FormatDetails formatDetails) { output.Write(string.Empty + expected.Value, formatDetails); }
public void Write(string text, FormatDetails formatDetails) { Write(text, 0, text.Length, formatDetails); }
/// <summary> /// Do the default formatting, same logic as "String.Format". /// </summary> public void EvaluateFormat(object current, Format format, ref bool handled, IOutput output, FormatDetails formatDetails) { // This function always handles the method: handled = true; // If the format has nested placeholders, we process those first // instead of formatting the item: if (format != null && format.HasNested) { formatDetails.Formatter.Format(output, format, current, formatDetails); return; } // If the object is null, we shouldn't write anything if (current == null) { return; } // (The following code was adapted from the built-in String.Format code) // We will try using IFormatProvider, IFormattable, and if all else fails, ToString. var formatter = formatDetails.Formatter; if (formatter == null) {} // Force the compiler not to complain about unused variable string result = null; ICustomFormatter cFormatter; IFormattable formattable; // Use the provider to see if a CustomFormatter is available: if (formatDetails.Provider != null && (cFormatter = formatDetails.Provider.GetFormat(typeof(ICustomFormatter)) as ICustomFormatter) != null) { var formatText = format == null ? null : format.GetText(); result = cFormatter.Format(formatText, current, formatDetails.Provider); } // IFormattable: else if ((formattable = current as IFormattable) != null) { var formatText = format == null ? null : format.ToString(); result = formattable.ToString(formatText, formatDetails.Provider); } // ToString: else { result = current.ToString(); } // Now that we have the result, let's output it (and consider alignment): // See if there's a pre-alignment to consider: if (formatDetails.Placeholder.Alignment > 0) { var spaces = formatDetails.Placeholder.Alignment - result.Length; if (spaces > 0) { output.Write(new String(' ', spaces), formatDetails); } } // Output the result: output.Write(result, formatDetails); // See if there's a post-alignment to consider: if (formatDetails.Placeholder.Alignment < 0) { var spaces = -formatDetails.Placeholder.Alignment - result.Length; if (spaces > 0) { output.Write(new String(' ', spaces), formatDetails); } } }
public void Write(string text, int startIndex, int length, FormatDetails formatDetails) { Output.Write(text.Substring(startIndex, length)); }
public void Write(string text, FormatDetails formatDetails) { Output.Write(text); }
public void EvaluateSelector(object current, Selector selector, ref bool handled, ref object result, FormatDetails formatDetails) { if (current == null) { return; } // REFLECTION: // Let's see if the argSelector is a Selectors/Field/ParseFormat: var sourceType = current.GetType(); var members = sourceType.GetMember(selector.Text); foreach (var member in members) { switch (member.MemberType) { case MemberTypes.Field: // Selector is a Field; retrieve the value: var field = (FieldInfo) member; result = field.GetValue(current); handled = true; return; case MemberTypes.Property: case MemberTypes.Method: MethodInfo method; if (member.MemberType == MemberTypes.Property) { // Selector is a Property var prop = (PropertyInfo) member; // Make sure the property is not WriteOnly: if (prop.CanRead) { method = prop.GetGetMethod(); } else { continue; } } else { // Selector is a method method = (MethodInfo) member; } // Check that this method is valid -- it needs to return a value and has to be parameterless: // We are only looking for a parameterless Function/Property: if (method.GetParameters().Length > 0) { continue; } // Make sure that this method is not void! It has to be a Function! if (method.ReturnType == typeof(void)) { continue; } // Retrieve the Selectors/ParseFormat value: result = method.Invoke(current, new object[0]); handled = true; return; } } }
public void Write(string text, FormatDetails formatDetails) { output.Append(text); }
private static void OutputExpectedExpression(Expected expected, IOutput output, FormatDetails formatDetails) { output.Write(expected.Expression + Environment.NewLine + NewLineIndent(output), formatDetails); }
public void Write(string text, int startIndex, int length, FormatDetails formatDetails) { output.Append(text, startIndex, length); }
public void EvaluateFormat(object current, Format format, ref bool handled, IOutput output, FormatDetails formatDetails) { if (format == null) return; // Ignore a leading ":", which is used to bypass the PluralLocalizationExtension if (format.baseString[format.startIndex] == ':') { format = format.Substring(1); } // See if the format string contains un-nested "|": var parameters = format.Split("|"); if (parameters.Count == 1) return; // There are no parameters found. // See if the value is a number: var currentIsNumber = current is byte || current is short || current is int || current is long || current is float || current is double || current is decimal; // An Enum is a number too: if (currentIsNumber == false && current != null && current.GetType().IsEnum) { currentIsNumber = true; } var currentNumber = currentIsNumber ? Convert.ToDecimal(current) : 0; int paramIndex; // Determines which parameter to use for output // First, we'll see if we are using "complex conditions": if (currentIsNumber) { paramIndex = -1; while (true) { paramIndex++; if (paramIndex == parameters.Count) { // We reached the end of our parameters, // so we output nothing handled = true; return; } bool conditionWasTrue; Format outputItem; if (!TryEvaluateCondition(parameters[paramIndex], currentNumber, out conditionWasTrue, out outputItem)) { // This parameter doesn't have a // complex condition (making it a "else" condition) // Only do "complex conditions" if the first item IS a "complex condition". if (paramIndex == 0) { break; } // Otherwise, output the "else" section: conditionWasTrue = true; } // If the conditional statement was true, then we can break. if (conditionWasTrue) { formatDetails.Formatter.Format(output, outputItem, current, formatDetails); handled = true; return; } } // We don't have any "complex conditions", // so let's do the normal conditional formatting: } var paramCount = parameters.Count; // Determine the Current item's Type: if (currentIsNumber) { if (currentNumber < 0) { paramIndex = paramCount - 1; } else { paramIndex = Math.Min((int)Math.Floor(currentNumber), paramCount - 1); } } else if (current is bool) { // Bool: True|False bool arg = (bool)current; if (arg) { paramIndex = 0; } else { paramIndex = 1; } } else if (current is DateTime) { // Date: Past|Present|Future or Past/Present|Future System.DateTime arg = (DateTime)current; if (paramCount == 3 && arg.Date == DateTime.Today) { paramIndex = 1; } else if (arg <= DateTime.Now) { paramIndex = 0; } else { paramIndex = paramCount - 1; } } else if (current is TimeSpan) { // TimeSpan: Negative|Zero|Positive or Negative/Zero|Positive TimeSpan arg = (TimeSpan)current; if (paramCount == 3 && arg == TimeSpan.Zero) { paramIndex = 1; } else if (arg.CompareTo(TimeSpan.Zero) <= 0) { paramIndex = 0; } else { paramIndex = paramCount - 1; } } else if (current is string) { // String: Value|NullOrEmpty var arg = (string)current; if (!string.IsNullOrEmpty(arg)) { paramIndex = 0; } else { paramIndex = 1; } } else { // Object: Something|Nothing object arg = current; if (arg != null) { paramIndex = 0; } else { paramIndex = 1; } } // Now, output the selected parameter: var selectedParameter = parameters[paramIndex]; // Output the selectedParameter: formatDetails.Formatter.Format(output, selectedParameter, current, formatDetails); handled = true; }
/// <summary> /// If the source value is an array (or supports ICollection), /// then each item will be custom formatted. /// /// /// Syntax: /// #1: "format|spacer" /// #2: "format|spacer|last spacer" /// #3: "format|spacer|last spacer|two spacer" /// /// The format will be used for each item in the collection, the spacer will be between all items, and the last spacer will replace the spacer for the last item only. /// /// Example: /// CustomFormat("{Dates:D|; |; and }", {#1/1/2000#, #12/31/2999#, #9/9/9999#}) = "January 1, 2000; December 31, 2999; and September 9, 9999" /// In this example, format = "D", spacer = "; ", and last spacer = "; and " /// /// /// /// Advanced: /// Composite Formatting is allowed in the format by using nested braces. /// If a nested item is detected, Composite formatting will be used. /// /// Example: /// CustomFormat("{Sizes:{Width}x{Height}|, }", {new Size(4,3), new Size(16,9)}) = "4x3, 16x9" /// In this example, format = "{Width}x{Height}". Notice the nested braces. /// /// </summary> public void EvaluateFormat(object current, Format format, ref bool handled, IOutput output, FormatDetails formatDetails) { // This method needs the Highest priority so that it comes before the PluralLocalizationExtension and ConditionalExtension // This extension requires at least IEnumerable var enumerable = current as IEnumerable; if (enumerable == null) return; // Ignore Strings, because they're IEnumerable. // This issue might actually need a solution // for other objects that are IEnumerable. if (current is string) return; // If the object is IFormattable, ignore it if (current is IFormattable) return; // This extension requires a | to specify the spacer: if (format == null) return; var parameters = format.Split("|", 4); if (parameters.Count < 2) return; // Grab all formatting options: // They must be in one of these formats: // itemFormat|spacer // itemFormat|spacer|lastSpacer // itemFormat|spacer|lastSpacer|twoSpacer var itemFormat = parameters[0]; var spacer = (parameters.Count >= 2) ? parameters[1].Text : ""; var lastSpacer = (parameters.Count >= 3) ? parameters[2].Text : spacer; var twoSpacer = (parameters.Count >= 4) ? parameters[3].Text : lastSpacer; if (!itemFormat.HasNested) { // The format is not nested, // so we will treat it as an itemFormat: var newItemFormat = new Format(itemFormat.baseString); newItemFormat.startIndex = itemFormat.startIndex; newItemFormat.endIndex = itemFormat.endIndex; newItemFormat.HasNested = true; var newPlaceholder = new Placeholder(newItemFormat, itemFormat.startIndex, formatDetails.Placeholder.NestedDepth); newPlaceholder.Format = itemFormat; newPlaceholder.endIndex = itemFormat.endIndex; newItemFormat.Items.Add(newPlaceholder); itemFormat = newItemFormat; } // Let's buffer all items from the enumerable (to ensure the Count without double-enumeration): ICollection items = current as ICollection; if (items == null) { var allItems = new List<object>(); foreach (var item in enumerable) { allItems.Add(item); } items = allItems; } 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 // Determine which spacer to write: if (spacer == null || CollectionIndex == 0) { // Don't write the spacer. } else if (CollectionIndex < items.Count - 1) { output.Write(spacer, formatDetails); } else if (CollectionIndex == 1) { output.Write(twoSpacer, formatDetails); } else { output.Write(lastSpacer, formatDetails); } // Output the nested format for this item: formatDetails.Formatter.Format(output, itemFormat, item, formatDetails); } CollectionIndex = oldCollectionIndex; // Restore the CollectionIndex handled = true; }
private static void OutputFormattedExpected(object current, Format format, IOutput output, FormatDetails formatDetails, Expected expected) { formatDetails.Formatter.Format(output, format, current, formatDetails); }