Merge pull request #684 from e78l/numfmt
NSNumberFormatter implementation progress
diff --git a/CoreFoundation/Locale.subproj/CFLocale.c b/CoreFoundation/Locale.subproj/CFLocale.c
index b502faf..69dda52 100644
--- a/CoreFoundation/Locale.subproj/CFLocale.c
+++ b/CoreFoundation/Locale.subproj/CFLocale.c
@@ -280,7 +280,12 @@
#if DEPLOYMENT_TARGET_MACOSX
+// Specify a default locale on Mac for Swift
+#if DEPLOYMENT_RUNTIME_SWIFT
+#define FALLBACK_LOCALE_NAME CFSTR("en_US")
+#else
#define FALLBACK_LOCALE_NAME CFSTR("")
+#endif /* DEPLOYMENT_RUNTIME_SWIFT */
#elif DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
#define FALLBACK_LOCALE_NAME CFSTR("en_US")
#elif DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
diff --git a/Foundation/NSNumberFormatter.swift b/Foundation/NSNumberFormatter.swift
index d0cf61e..fe319d1 100644
--- a/Foundation/NSNumberFormatter.swift
+++ b/Foundation/NSNumberFormatter.swift
@@ -70,6 +70,9 @@
let obj = CFNumberFormatterCreate(kCFAllocatorSystemDefault, locale._cfObject, numberStyle)!
_setFormatterAttributes(obj)
+ if let format = _format {
+ CFNumberFormatterSetFormat(obj, format._cfObject)
+ }
_currentCfFormatter = obj
return obj
}
@@ -99,12 +102,13 @@
open func number(from string: String) -> NSNumber? {
var range = CFRange(location: 0, length: string.length)
let number = withUnsafeMutablePointer(to: &range) { (rangePointer: UnsafeMutablePointer<CFRange>) -> NSNumber? in
-
+
#if os(OSX) || os(iOS)
- let result = CFNumberFormatterCreateNumberFromString(kCFAllocatorSystemDefault, _cfFormatter, string._cfObject, rangePointer, CFNumberFormatterOptionFlags.parseIntegersOnly.rawValue)
+ let parseOption = allowsFloats ? 0 : CFNumberFormatterOptionFlags.parseIntegersOnly.rawValue
#else
- let result = CFNumberFormatterCreateNumberFromString(kCFAllocatorSystemDefault, _cfFormatter, string._cfObject, rangePointer, CFOptionFlags(kCFNumberFormatterParseIntegersOnly))
+ let parseOption = allowsFloats ? 0 : CFOptionFlags(kCFNumberFormatterParseIntegersOnly)
#endif
+ let result = CFNumberFormatterCreateNumberFromString(kCFAllocatorSystemDefault, _cfFormatter, string._cfObject, rangePointer, parseOption)
return result?._nsObject
}
@@ -157,7 +161,7 @@
_setFormatterAttribute(formatter, attributeName: kCFNumberFormatterPerMillSymbol, value: _percentSymbol?._cfObject)
_setFormatterAttribute(formatter, attributeName: kCFNumberFormatterInternationalCurrencySymbol, value: _internationalCurrencySymbol?._cfObject)
_setFormatterAttribute(formatter, attributeName: kCFNumberFormatterCurrencyGroupingSeparator, value: _currencyGroupingSeparator?._cfObject)
- _setFormatterAttribute(formatter, attributeName: kCFNumberFormatterIsLenient, value: kCFBooleanTrue)
+ _setFormatterAttribute(formatter, attributeName: kCFNumberFormatterIsLenient, value: _lenient._cfObject)
_setFormatterAttribute(formatter, attributeName: kCFNumberFormatterUseSignificantDigits, value: _usesSignificantDigits._cfObject)
if _usesSignificantDigits {
_setFormatterAttribute(formatter, attributeName: kCFNumberFormatterMinSignificantDigits, value: _minimumSignificantDigits._bridgeToObjectiveC()._cfObject)
@@ -182,7 +186,7 @@
switch newValue {
case .none, .ordinal, .spellOut:
_usesSignificantDigits = false
-
+
case .currency, .currencyPlural, .currencyISOCode, .currencyAccounting:
_usesSignificantDigits = false
_usesGroupingSeparator = true
@@ -838,10 +842,10 @@
//
- internal var _format: String = "#;0;#"
+ internal var _format: String?
open var format: String {
get {
- return _format
+ return _format ?? "#;0;#"
}
set {
_reset()
diff --git a/TestFoundation/TestNSLocale.swift b/TestFoundation/TestNSLocale.swift
index 3d18791..f83edf3 100644
--- a/TestFoundation/TestNSLocale.swift
+++ b/TestFoundation/TestNSLocale.swift
@@ -19,9 +19,24 @@
static var allTests: [(String, (TestNSLocale) -> () throws -> Void)] {
return [
("test_constants", test_constants),
+ ("test_Identifier", test_Identifier),
("test_copy", test_copy)
]
}
+
+ func test_Identifier() {
+ // Current locale identifier should not be empty
+ // Or things like NSNumberFormatter spellOut style won't work
+ XCTAssertFalse(Locale.current.identifier.isEmpty)
+
+ let enUSID = "en_US"
+ let locale = Locale(identifier: enUSID)
+ XCTAssertEqual(enUSID, locale.identifier)
+
+ let deDEID = "de_DE"
+ let germanLocale = Locale(identifier: deDEID)
+ XCTAssertEqual(deDEID, germanLocale.identifier)
+ }
func test_constants() {
XCTAssertEqual(NSCurrentLocaleDidChangeNotification, "kCFLocaleCurrentLocaleDidChangeNotification",
diff --git a/TestFoundation/TestNSNumberFormatter.swift b/TestFoundation/TestNSNumberFormatter.swift
index 9511770..222b3d2 100644
--- a/TestFoundation/TestNSNumberFormatter.swift
+++ b/TestFoundation/TestNSNumberFormatter.swift
@@ -53,7 +53,9 @@
("test_lenient", test_lenient),
("test_minimumSignificantDigits", test_minimumSignificantDigits),
("test_maximumSignificantDigits", test_maximumSignificantDigits),
- ("test_stringFor", test_stringFor)
+ ("test_stringFor", test_stringFor),
+ ("test_numberFrom", test_numberFrom),
+ //("test_en_US_initialValues", test_en_US_initialValues)
]
}
@@ -68,11 +70,15 @@
XCTAssertEqual(formattedString, "T 42_00")
*/
}
-
+
func test_decimalSeparator() {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
- numberFormatter.decimalSeparator = "-"
+
+ let separator = "-"
+ numberFormatter.decimalSeparator = separator
+ XCTAssertEqual(numberFormatter.decimalSeparator, separator)
+
let formattedString = numberFormatter.string(from: 42.42)
XCTAssertEqual(formattedString, "42-42")
}
@@ -147,14 +153,28 @@
}
func test_plusSignSymbol() {
- //FIXME: How do we show the plus sign from a NSNumberFormatter?
+ // ex. 1.0E+1 in scientific notation
+ let numberFormatter = NumberFormatter()
+ let format = "#E+0"
+ numberFormatter.format = format
+ XCTAssertEqual(numberFormatter.format, format)
-// let numberFormatter = NumberFormatter()
-// numberFormatter.plusSign = "π"
-// let formattedString = numberFormatter.stringFromNumber(42)
-// XCTAssertEqual(formattedString, "π42")
+ let sign = "π"
+ numberFormatter.plusSign = sign
+ XCTAssertEqual(numberFormatter.plusSign, sign)
+
+ let formattedString = numberFormatter.string(from: 420000000000000000)
+ XCTAssertNotNil(formattedString)
+ XCTAssertEqual(formattedString, "4.2Eπ17")
+
+ // Verify a negative exponent does not have the π
+ let noPlusString = numberFormatter.string(from: -0.420)
+ XCTAssertNotNil(noPlusString)
+ if let fmt = noPlusString {
+ XCTAssertFalse(fmt.contains(sign), "Expected format of -0.420 (-4.2E-1) shouldn't have a plus sign which was set as \(sign)")
+ }
}
-
+
func test_currencySymbol() {
// Disabled due to [SR-250]
/*
@@ -316,14 +336,30 @@
*/
}
- //FIXME: Something is wrong with numberFromString implementation, I don't know exactly why, but it's not working.
func test_lenient() {
-// let numberFormatter = NumberFormatter()
-// numberFormatter.numberStyle = .CurrencyStyle
-// let nilNumberBeforeLenient = numberFormatter.numberFromString("42")
+ let numberFormatter = NumberFormatter()
+ // Not lenient by default
+ XCTAssertFalse(numberFormatter.isLenient)
+
+ // Lenient allows wrong style -- not lenient here
+ numberFormatter.numberStyle = .spellOut
+ XCTAssertEqual(numberFormatter.numberStyle, .spellOut)
+// let nilNumber = numberFormatter.number(from: "2.22")
+ // FIXME: Not nil on Linux?
+ //XCTAssertNil(nilNumber)
+ // Lenient allows wrong style
+ numberFormatter.isLenient = true
+ XCTAssertTrue(numberFormatter.isLenient)
+ let number = numberFormatter.number(from: "2.22")
+ XCTAssertEqual(number, 2.22)
+
+ // TODO: Add some tests with currency after [SR-250] resolved
+// numberFormatter.numberStyle = .currency
+// let nilNumberBeforeLenient = numberFormatter.number(from: "42")
+//
// XCTAssertNil(nilNumberBeforeLenient)
-// numberFormatter.lenient = true
-// let numberAfterLenient = numberFormatter.numberFromString("42.42")
+// numberFormatter.isLenient = true
+// let numberAfterLenient = numberFormatter.number(from: "42.42")
// XCTAssertEqual(numberAfterLenient, 42.42)
}
@@ -353,7 +389,76 @@
XCTAssertEqual(numberFormatter.string(for: NSNumber(value: 99.1))!, "99")
XCTAssertNil(numberFormatter.string(for: "NaN"))
XCTAssertNil(numberFormatter.string(for: NSString(string: "NaN")))
+
+ numberFormatter.numberStyle = .spellOut
+ XCTAssertEqual(numberFormatter.string(for: 234), "two hundred thirty-four")
+ XCTAssertEqual(numberFormatter.string(for: 2007), "two thousand seven")
+ XCTAssertEqual(numberFormatter.string(for: 3), "three")
+ XCTAssertEqual(numberFormatter.string(for: 0.3), "zero point three")
+
+ numberFormatter.locale = Locale(identifier: "zh_CN")
+ numberFormatter.numberStyle = .spellOut
+ XCTAssertEqual(numberFormatter.string(from: 11.4), "εδΈηΉε")
+
+ numberFormatter.locale = Locale(identifier: "fr_FR")
+ numberFormatter.numberStyle = .spellOut
+ XCTAssertEqual(numberFormatter.string(from: 11.4), "onze virgule quatre")
+
}
-
+
+ func test_numberFrom() {
+ let numberFormatter = NumberFormatter()
+ XCTAssertEqual(numberFormatter.number(from: "10"), 10)
+ XCTAssertEqual(numberFormatter.number(from: "3.14"), 3.14)
+ XCTAssertEqual(numberFormatter.number(from: "0.01"), 0.01)
+ XCTAssertEqual(numberFormatter.number(from: ".01"), 0.01)
+
+ // These don't work unless lenient/style set
+ numberFormatter.numberStyle = .decimal
+ XCTAssertEqual(numberFormatter.number(from: "1,001"), 1001)
+ XCTAssertEqual(numberFormatter.number(from: "1,050,001"), 1050001)
+
+ numberFormatter.numberStyle = .spellOut
+ XCTAssertEqual(numberFormatter.number(from: "two thousand and seven"), 2007)
+ XCTAssertEqual(numberFormatter.number(from: "one point zero"), 1.0)
+ XCTAssertEqual(numberFormatter.number(from: "one hundred million"), 1E8)
+
+ numberFormatter.locale = Locale(identifier: "zh_CN")
+ numberFormatter.numberStyle = .spellOut
+ XCTAssertEqual(numberFormatter.number(from: "εδΈηΉε"), 11.4)
+
+ numberFormatter.locale = Locale(identifier: "fr_FR")
+ numberFormatter.numberStyle = .spellOut
+ XCTAssertEqual(numberFormatter.number(from: "onze virgule quatre"), 11.4)
+ }
+
+ func test_en_US_initialValues() {
+ // Symbols should be extractable
+ // At one point, none of this passed!
+
+ let numberFormatter = NumberFormatter();
+ numberFormatter.locale = Locale(identifier: "en_US")
+
+ // TODO: Check if this is true for all versions...
+ XCTAssertEqual(numberFormatter.format, "#;0;#")
+
+ XCTAssertEqual(numberFormatter.plusSign, "+")
+ XCTAssertEqual(numberFormatter.minusSign, "-")
+ XCTAssertEqual(numberFormatter.decimalSeparator, ".")
+ XCTAssertEqual(numberFormatter.groupingSeparator, ",")
+ XCTAssertEqual(numberFormatter.nilSymbol, "")
+ XCTAssertEqual(numberFormatter.notANumberSymbol, "NaN")
+ XCTAssertEqual(numberFormatter.positiveInfinitySymbol, "+∞")
+ XCTAssertEqual(numberFormatter.negativeInfinitySymbol, "-∞")
+ XCTAssertEqual(numberFormatter.positivePrefix, "")
+ XCTAssertEqual(numberFormatter.negativePrefix, "-")
+ XCTAssertEqual(numberFormatter.positiveSuffix, "")
+ XCTAssertEqual(numberFormatter.negativeSuffix, "")
+ XCTAssertEqual(numberFormatter.percentSymbol, "%")
+ XCTAssertEqual(numberFormatter.perMillSymbol, "‰")
+ XCTAssertEqual(numberFormatter.exponentSymbol, "E")
+ XCTAssertEqual(numberFormatter.groupingSeparator, ",")
+ XCTAssertEqual(numberFormatter.paddingCharacter, "*")
+ }
}