/// <summary> /// Register a bunch of currencies, at once. /// </summary> /// <remarks> /// <para>For explanations on when and how to use this method, please see /// <see cref="RegisterCurrency(String, Int16?)"/>.</para> /// <para>This method is thread-safe and ensures that the registry is not modified /// if anything goes wrong.</para> /// </remarks> /// <param name="currencies">The <see cref="Dictionary{TKey, TValue}"/> that contains /// the codes and minor units for the new currencies.</param> /// <returns>true if ALL currencies has been added; otherwise, false.</returns> /// <exception cref="ArgumentException">Thrown if a candidate currency does not meet /// the requirements: its code must be of length 3 and made of ASCII /// uppercase letters, and its minor units, if not null, must be greater than /// or equal to zero and lower than or equal to 28.</exception> public static bool RegisterCurrencies(Dictionary <string, short?> currencies) { Require.NotNull(currencies, nameof(currencies)); // Before any actual work, we check if the input looks fine. foreach (var pair in currencies) { string code = pair.Key; if (code == null) { throw new ArgumentException(Strings_Money.Argument_CurrencyCodeIsNull, nameof(currencies)); } if (!ValidateCode(code)) { throw new ArgumentException( Format.Current(Strings_Money.Argument_InvalidCurrencyCode, code), nameof(currencies)); } short?minorUnits = pair.Value; if (!ValidateMinorUnits(minorUnits)) { throw new ArgumentException( Format.Current(Strings_Money.Argument_InvalidCurrencyMinorUnits, minorUnits), nameof(currencies)); } if (Codes.ContainsKey(code) || WithdrawnCodes.Contains(code)) { return(false); } } lock (s_UserCodesLock) { // We work on a temporary copy of s_UserCodes. This is not very efficient but // ensures that s_UserCodes does not end up in a broken state if anything wrong // happens. Performance-wise, it is not as bad as it looks. First, we do not expect // this method to be called more than a couple of times during the lifetime of the // application, if ever. Second, s_UserCodes should be very small, therefore the // copy should be pretty fast and the operation should be rather inexpensive // from a memory perspective. // NB: This manip has nothing to do with thread-safety, it is only done for // correctness. If Add() fails, the method behaves as advertised - it does not // change the registry. var tmpCopy = s_UserCodes.ToDictionary(pair => pair.Key, pair => pair.Value); foreach (var pair in currencies) { if (tmpCopy.ContainsKey(pair.Key)) { return(false); } tmpCopy.Add(pair.Key, pair.Value); } SwapReferences(ref s_UserCodes, ref tmpCopy); } return(true); }
public static string GetDescription(int ErrorCode) { if (Codes.ContainsKey(ErrorCode)) { return(Convert.ToString(Codes[ErrorCode])); } return(Convert.ToString(Codes[-1])); }
/// <summary> /// Register a currency not part of ISO 4217. /// <para>It can also be useful when the library is not up-to-date with the ISO 4217 list /// of active currencies.</para> /// </summary> /// <remarks> /// <para>All currencies registered via this method have the <see cref="CurrencyTypes.UserDefined"/> /// type. In particular, they inherit the <see cref="CurrencyTypes.Circulating"/> type; as a /// consequence, you can not register a withdrawn currency.</para> /// <para>If you have more than one currency to register, you should use /// <see cref="RegisterCurrencies(Dictionary{String, Int16?})"/> instead.</para> /// <para>This method is thread-safe.</para> /// </remarks> /// <param name="code">The three letters code.</param> /// <param name="minorUnits">The number of minor units; null if the currency does not have /// a minor currency unit and <see cref="UnknownMinorUnits"/> if the status is unknown.</param> /// <returns>true if the operation succeeded; otherwise, false.</returns> /// <exception cref="ArgumentException">Thrown if the candidate currency does not meet /// the requirements: a <paramref name="code"/> must be of length 3 and made of ASCII /// uppercase letters, and <paramref name="minorUnits"/>, if not null, must be greater than /// or equal to zero and lower than or equal to 28.</exception> public static bool RegisterCurrency(string code, short?minorUnits) { // REVIEW: Should we relax the constraints we impose on the code value for user-defined // currencies? JavaMoney does it, but I am not very convinced that there are enough // good reasons for the complications it will imply. Nevertheless, if we decided // to do this, there will a problem with MinorCurrencyCode and we would have to review // all guards wherever we accept a code as input. Require.NotNull(code, nameof(code)); // A currency code MUST be composed of exactly 3 letters. // A currency code MUST only contain uppercase ASCII letters. Require.True( ValidateCode(code), nameof(code), Format.Current(Strings_Money.Argument_InvalidCurrencyCode, code)); Require.True( ValidateMinorUnits(minorUnits), nameof(minorUnits), Format.Current(Strings_Money.Argument_InvalidCurrencyMinorUnits, minorUnits)); // Codes and WithdrawnCodes are immutable, so no concurrency problems here. if (Codes.ContainsKey(code) || WithdrawnCodes.Contains(code)) { return(false); } lock (s_UserCodesLock) { if (!s_UserCodes.ContainsKey(code)) { s_UserCodes.Add(code, minorUnits); return(true); } } return(false); }