using System;
using System.Collections.Generic;
using System.Globalization;
namespace Vanara
{
// Leveraged code from t3chb0t and Bogdan Yarema at https://codereview.stackexchange.com/questions/138747/custom-string-formatters
/// Binds multiple formatters together.
///
internal sealed class CompositeFormatter : Formatter
{
private readonly List _formatters;
/// Initializes a new instance of the class.
/// The culture.
/// The formatters.
public CompositeFormatter(CultureInfo culture = null, params Formatter[] formatters) : base(culture)
{
_formatters = new List(formatters);
}
/// Adds the specified formatter.
/// The formatter.
public void Add(Formatter formatter) => _formatters.Add(formatter);
///
/// Converts the value of a specified object to an equivalent string representation using specified format and culture-specific
/// formatting information.
///
/// A format string containing formatting specifications.
/// An object to format.
/// An object that supplies format information about the current instance.
///
/// The string representation of the value of , formatted as specified by and
/// .
///
public override string Format(string format, object arg, IFormatProvider formatProvider)
{
foreach (var formatter in _formatters)
{
var result = formatter.Format(format, arg, formatProvider);
if (result != null)
return result;
}
return null;
}
}
/// Extension method to combine formatter instances.
public static class FormatterComposer
{
/// Adds a chain of formatters with specific cultures to make a composite.
/// A derived type.
/// The formatter instance to start the chain.
/// The culture.
/// A composite formatter.
///
///
/// // Build composite formatter from custom formatters derived from Formatter
/// var formatter = Formatter.Default().Add<CustomFormatter1>().Add<CustomFormatter2>();
/// // Use custom format extensions defined in the custom formatters to format the string
/// var output = string.Format(formatter, "{0:cf1} = {0:cf2}", 512);
///
///
public static Formatter Add(this Formatter formatter, CultureInfo culture = null) where T : Formatter, new()
{
var newFormatter = new T();
if (!(formatter is CompositeFormatter compositeFormatter))
return new CompositeFormatter(culture, formatter, newFormatter);
compositeFormatter.Add(newFormatter);
return compositeFormatter;
}
}
/// Base class for expandable formatters.
public abstract class Formatter : IFormatProvider, ICustomFormatter
{
/// Initializes a new instance of the class.
/// The culture.
protected Formatter(CultureInfo culture = null)
{
Culture = culture ?? CultureInfo.InvariantCulture;
}
/// Gets a default instance of a composite formatter.
/// The culture.
/// A composite formatter.
public static Formatter Default(CultureInfo culture = null) => new CompositeFormatter(culture);
/// Gets the culture.
/// The culture.
public CultureInfo Culture { get; }
/// Returns an object that provides formatting services for the specified type.
/// An object that specifies the type of format object to return.
///
/// An instance of the object specified by , if the IFormatProvider implementation can supply that type
/// of object; otherwise, .
///
public virtual object GetFormat(Type formatType) => formatType == typeof(ICustomFormatter) ? this : null;
///
/// Converts the value of a specified object to an equivalent string representation using specified format and culture-specific
/// formatting information.
///
/// A format string containing formatting specifications.
/// An object to format.
/// An object that supplies format information about the current instance.
///
/// The string representation of the value of , formatted as specified by and
/// .
///
public abstract string Format(string format, object arg, IFormatProvider formatProvider);
/// Helper method that can be used inside the Format method to handle unrecognized formats.
/// A format string containing formatting specifications.
/// An object to format.
///
/// The string representation of the value of , formatted as specified by .
///
protected string HandleOtherFormats(string format, object arg) => (arg as IFormattable)?.ToString(format, Culture) ?? (arg?.ToString() ?? string.Empty);
}
}