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, "*")
+    }
 }