I am looking over some existing code (see below). The problem of grouping in the same place the presentation logic of some fields (like price, currency) was solved as extension methods on int
, double
, and so on...
I do not really like this. What do you think? How could it be done more elegantly and even unit testable?
namespace RestApi.Services.Helpers
{
using System;
using System.Globalization;
using System.Web.Mvc;
using Contracts.Fg;
using FrontEnd.CommonServices.Localization;
public enum QuoteDisplayType
{
BidAsk,
Difference,
Other
}
public static class CommonFormatterExtensions
{
private static ILocalizationService localizationService;
private static ILocalizationService LocalizationService
{
get { return localizationService ?? (localizationService = DependencyResolver.Current.GetService<ILocalizationService>()); }
}
private const string NoInformationText = "---";
private const string MarketLocalizationKey = "FormatAmountExtensions.MarketQuote";
private const string PercentageSymbol = "%";
private const string CurrencyCodeEuro = "EUR";
private const string CurrencyCodeUsDollar = "USD";
private const string CurrencyCodeBritishPound = "GBP";
private const string CurrencyCodeDutchGuilder = "NLG";
private const string SymbolEuro = "€";
private const string SymbolDollar = "$";
private const string SymbolDutchGuilders = "fl";
private const string SymbolBritishPound = "£";
private const string FormatWithMinTwoDecimals = "#,##0.00###";
private const string FormatWithAllTrailingDecimalZeroesSupressed = "#,##0.#####";
/// <summary>
/// Returns a formatted number for securities. When no fractions (decimals) are present, the number is formatted without decimals.
/// Note that altough we (topline) currrently do not support fractions (decimals) in security holdings, we do receive the number as a 'double' from Topline, and therefore also support the formatting of fractions.
/// </summary>
/// <param name="amount">amount to be formatted</param>
/// <returns>a string represented the formatted number</returns>
public static string FormattedNumberOfSecurities(this double amount)
{
return amount.Equals(0)
? NoInformationText
: amount.ToString(FormatWithAllTrailingDecimalZeroesSupressed, CultureInfo.CurrentCulture);
}
public static string FormattedNumberOfSecurities(this double? amount)
{
return amount.HasValue ? amount.Value.FormattedNumberOfSecurities() : NoInformationText;
}
public static string FormattedNumberOfSecurities(this int amount)
{
return amount.Equals(0)
? NoInformationText
: amount.ToString(FormatWithAllTrailingDecimalZeroesSupressed, CultureInfo.CurrentCulture);
}
public static string FormattedNumberOfSecurities(this int? amount)
{
return amount.HasValue ? amount.Value.FormattedNumberOfSecurities() : NoInformationText;
}
/// <summary>
/// Returns a formatted price for a security. Result has at least 2 decimals and max 5 decimals.
/// </summary>
/// <param name="amount">amount (price) to be formatted</param>
/// <param name="mainsecurityType">main security type of the security for which the price is to be formatted</param>
/// <param name="amountCurrencyCode">currency in which the security is traded</param>
/// <param name="quoteDisplayType">whether or not it is a bid/ask quote</param>
/// <returns>a string represented the formatted price</returns>
public static string FormattedSecurityPrice(this double amount, Hoofdfondstype mainsecurityType, string amountCurrencyCode, QuoteDisplayType quoteDisplayType = QuoteDisplayType.Other)
{
string formattedAmount;
if (amount.Equals(0))
{
//should only be applicable to Bid/Aks quotes.
formattedAmount = quoteDisplayType == QuoteDisplayType.BidAsk ? LocalizationService.Localize(MarketLocalizationKey) : NoInformationText;
}
else
{
formattedAmount = amount.ToString(FormatWithMinTwoDecimals, CultureInfo.CurrentCulture);
if (amountCurrencyCode != CurrencyCodeEuro && mainsecurityType != Hoofdfondstype.Indices && quoteDisplayType != QuoteDisplayType.Difference)
// Security prices and quotes are always shown with a currency indication, except when in EURO or when security is an index.
// A price difference is never displayed with a currency indication.
{
formattedAmount = AddCurrencyIndication(amountCurrencyCode, formattedAmount);
}
if ((quoteDisplayType != QuoteDisplayType.Difference) &&
(mainsecurityType == Hoofdfondstype.Coupons || mainsecurityType == Hoofdfondstype.Obligatie ||
mainsecurityType == Hoofdfondstype.ObligatieEmissies))
// Price of coupons and bonds is displayed as percentage, except when the price reflects a difference.
{
formattedAmount = String.Format(CultureInfo.CurrentCulture, "{0} {1}", formattedAmount,
PercentageSymbol);
}
}
return formattedAmount;
}
/// <summary>
/// Returns a formatted price for a security. Result has at least 2 decimals and max 5 decimals.
/// </summary>
/// <param name="amount">nullable amount (price) to be formatted</param>
/// <param name="mainsecurityType">main security type of the security for which the price is to be formatted</param>
/// <param name="amountCurrencyCode">currency in which the security is traded</param>
/// <returns>a string represented the formatted price or three dashes if no value is available</returns>
public static string FormattedSecurityPrice(this double? amount, Hoofdfondstype mainsecurityType, string amountCurrencyCode)
{
return amount.HasValue
? amount.Value.FormattedSecurityPrice(mainsecurityType, amountCurrencyCode)
: NoInformationText;
}
private static string AddCurrencyIndication(string amountCurrencyCode, string formattedAmount)
{
switch (amountCurrencyCode)
{
case CurrencyCodeUsDollar:
formattedAmount = SymbolDollar + " " + formattedAmount;
break;
case CurrencyCodeEuro:
formattedAmount = SymbolEuro + " " + formattedAmount;
break;
case CurrencyCodeDutchGuilder:
formattedAmount = SymbolDutchGuilders + " " + formattedAmount;
break;
case CurrencyCodeBritishPound:
formattedAmount = SymbolBritishPound + " " + formattedAmount;
break;
default:
formattedAmount = amountCurrencyCode + " " + formattedAmount;
break;
}
return formattedAmount;
}
}
}