Merge branch 'pr/334'
diff --git a/CoreFoundation/Base.subproj/SwiftRuntime/CoreFoundation.h b/CoreFoundation/Base.subproj/SwiftRuntime/CoreFoundation.h
index 1334f30..41eb6c1 100644
--- a/CoreFoundation/Base.subproj/SwiftRuntime/CoreFoundation.h
+++ b/CoreFoundation/Base.subproj/SwiftRuntime/CoreFoundation.h
@@ -87,6 +87,7 @@
#include <CoreFoundation/CFURLPriv.h>
#include <CoreFoundation/CFURLComponents.h>
+#include <CoreFoundation/CFRunArray.h>
#include <CoreFoundation/ForSwiftFoundationOnly.h>
diff --git a/Foundation.xcodeproj/project.pbxproj b/Foundation.xcodeproj/project.pbxproj
index 65c5ffb..0fcd123 100755
--- a/Foundation.xcodeproj/project.pbxproj
+++ b/Foundation.xcodeproj/project.pbxproj
@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
+ 294E3C1D1CC5E19300E4F44C /* TestNSAttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 294E3C1C1CC5E19300E4F44C /* TestNSAttributedString.swift */; };
2EBE67A51C77BF0E006583D5 /* TestNSDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBE67A31C77BF05006583D5 /* TestNSDateFormatter.swift */; };
528776141BF2629700CB0090 /* FoundationErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522C253A1BF16E1600804FC6 /* FoundationErrors.swift */; };
528776191BF27D9500CB0090 /* Test.plist in Resources */ = {isa = PBXBuildFile; fileRef = 528776181BF27D9500CB0090 /* Test.plist */; };
@@ -66,9 +67,9 @@
5B5D89761BBDADD300234F36 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B5D89751BBDADD300234F36 /* libicucore.dylib */; };
5B5D89781BBDADDB00234F36 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B5D89771BBDADDB00234F36 /* libz.dylib */; };
5B6228BB1C179041009587FE /* CFRunArray.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B6228BA1C179041009587FE /* CFRunArray.c */; };
- 5B6228BD1C179049009587FE /* CFRunArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 5B6228BC1C179049009587FE /* CFRunArray.h */; };
+ 5B6228BD1C179049009587FE /* CFRunArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 5B6228BC1C179049009587FE /* CFRunArray.h */; settings = {ATTRIBUTES = (Private, ); }; };
5B6228BF1C179052009587FE /* CFAttributedString.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B6228BE1C179052009587FE /* CFAttributedString.c */; };
- 5B6228C11C17905B009587FE /* CFAttributedString.h in Headers */ = {isa = PBXBuildFile; fileRef = 5B6228C01C17905B009587FE /* CFAttributedString.h */; };
+ 5B6228C11C17905B009587FE /* CFAttributedString.h in Headers */ = {isa = PBXBuildFile; fileRef = 5B6228C01C17905B009587FE /* CFAttributedString.h */; settings = {ATTRIBUTES = (Public, ); }; };
5B7C8A721BEA7FCE00C5B690 /* CFBase.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B5D895D1BBDABBF00234F36 /* CFBase.c */; };
5B7C8A731BEA7FCE00C5B690 /* CFFileUtilities.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B5D89851BBDB18D00234F36 /* CFFileUtilities.c */; };
5B7C8A741BEA7FCE00C5B690 /* CFPlatform.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B5D897B1BBDAE0800234F36 /* CFPlatform.c */; };
@@ -253,9 +254,9 @@
61E0117F1C1B5990000037DD /* CFRunLoop.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B5D88D81BBC9AD800234F36 /* CFRunLoop.c */; };
61E011811C1B5998000037DD /* CFMessagePort.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B5D88DC1BBC9AEC00234F36 /* CFMessagePort.c */; };
61E011821C1B599A000037DD /* CFMachPort.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B5D88D01BBC9AAC00234F36 /* CFMachPort.c */; };
- AE35A1861CBAC85E0042DB84 /* SwiftFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = AE35A1851CBAC85E0042DB84 /* SwiftFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; };
7900433B1CACD33E00ECCBF1 /* TestNSCompoundPredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 790043391CACD33E00ECCBF1 /* TestNSCompoundPredicate.swift */; };
7900433C1CACD33E00ECCBF1 /* TestNSPredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7900433A1CACD33E00ECCBF1 /* TestNSPredicate.swift */; };
+ AE35A1861CBAC85E0042DB84 /* SwiftFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = AE35A1851CBAC85E0042DB84 /* SwiftFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; };
CE19A88C1C23AA2300B4CB6A /* NSStringTestData.txt in Resources */ = {isa = PBXBuildFile; fileRef = CE19A88B1C23AA2300B4CB6A /* NSStringTestData.txt */; };
D31302011C30CEA900295652 /* NSConcreteValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D31302001C30CEA900295652 /* NSConcreteValue.swift */; };
D370696E1C394FBF00295652 /* NSKeyedUnarchiver-RangeTest.plist in Resources */ = {isa = PBXBuildFile; fileRef = D370696D1C394FBF00295652 /* NSKeyedUnarchiver-RangeTest.plist */; };
@@ -386,6 +387,7 @@
/* Begin PBXFileReference section */
22B9C1E01C165D7A00DECFF9 /* TestNSDate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSDate.swift; sourceTree = "<group>"; };
+ 294E3C1C1CC5E19300E4F44C /* TestNSAttributedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSAttributedString.swift; sourceTree = "<group>"; };
2EBE67A31C77BF05006583D5 /* TestNSDateFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSDateFormatter.swift; sourceTree = "<group>"; };
400E22641C1A4E58007C5933 /* TestNSProcessInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSProcessInfo.swift; sourceTree = "<group>"; };
4AE109261C17CCBF007367B5 /* TestNSIndexPath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSIndexPath.swift; sourceTree = "<group>"; };
@@ -1213,6 +1215,7 @@
5B40F9F11C125187000E72E3 /* TestNSXMLParser.swift */,
5B6F17961C48631C00935030 /* TestUtils.swift */,
E19E17DB1C2225930023AF4D /* TestNSXMLDocument.swift */,
+ 294E3C1C1CC5E19300E4F44C /* TestNSAttributedString.swift */,
);
name = Tests;
sourceTree = "<group>";
@@ -1520,7 +1523,6 @@
5B7C8ACC1BEA80FC00C5B690 /* CFDateFormatter.h in Headers */,
5B7C8AD41BEA80FC00C5B690 /* CFXMLParser.h in Headers */,
5B7C8AD11BEA80FC00C5B690 /* CFTimeZone.h in Headers */,
- 5B6228C11C17905B009587FE /* CFAttributedString.h in Headers */,
5B7C8AC31BEA80FC00C5B690 /* CFBag.h in Headers */,
5B7C8AE01BEA80FC00C5B690 /* CFStringEncodingExt.h in Headers */,
5B7C8AE11BEA80FC00C5B690 /* CFURL.h in Headers */,
@@ -1555,12 +1557,12 @@
5B7C8AD21BEA80FC00C5B690 /* CFPropertyList.h in Headers */,
5B7C8AE51BEA81AC00C5B690 /* ForFoundationOnly.h in Headers */,
5B7C8AD51BEA80FC00C5B690 /* CFBundle.h in Headers */,
+ 5B6228C11C17905B009587FE /* CFAttributedString.h in Headers */,
5B7C8AE21BEA80FC00C5B690 /* CFURLAccess.h in Headers */,
5B7C8ACA1BEA80FC00C5B690 /* CFError.h in Headers */,
5B7C8AE91BEA81AC00C5B690 /* CFLocaleInternal.h in Headers */,
5B7C8AE61BEA81AC00C5B690 /* ForSwiftFoundationOnly.h in Headers */,
5B7C8AC61BEA80FC00C5B690 /* CFData.h in Headers */,
- 5B6228BD1C179049009587FE /* CFRunArray.h in Headers */,
5B7C8AC01BEA807A00C5B690 /* CFUUID.h in Headers */,
5B7C8AD31BEA80FC00C5B690 /* CFXMLNode.h in Headers */,
5B7C8AC21BEA80FC00C5B690 /* CFArray.h in Headers */,
@@ -1590,6 +1592,7 @@
5B7C8AF71BEA81AC00C5B690 /* CFStringEncodingConverterExt.h in Headers */,
5B7C8AE31BEA81AC00C5B690 /* CFLogUtilities.h in Headers */,
5B7C8AD01BEA80FC00C5B690 /* CFNumber.h in Headers */,
+ 5B6228BD1C179049009587FE /* CFRunArray.h in Headers */,
5B7C8ACE1BEA80FC00C5B690 /* CFNumberFormatter.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1985,6 +1988,7 @@
5B13B3321C582D4C00651CE2 /* TestNSIndexSet.swift in Sources */,
5B13B3511C582D4C00651CE2 /* TestNSByteCountFormatter.swift in Sources */,
5B13B3501C582D4C00651CE2 /* TestUtils.swift in Sources */,
+ 294E3C1D1CC5E19300E4F44C /* TestNSAttributedString.swift in Sources */,
5B13B3431C582D4C00651CE2 /* TestNSScanner.swift in Sources */,
5B13B3401C582D4C00651CE2 /* TestNSRange.swift in Sources */,
5B13B3371C582D4C00651CE2 /* TestNSNotificationCenter.swift in Sources */,
diff --git a/Foundation/NSAttributedString.swift b/Foundation/NSAttributedString.swift
index a354e9b..49cca6c 100644
--- a/Foundation/NSAttributedString.swift
+++ b/Foundation/NSAttributedString.swift
@@ -7,9 +7,14 @@
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
+import CoreFoundation
public class NSAttributedString : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
+ private let _cfinfo = _CFInfo(typeID: CFAttributedStringGetTypeID())
+ private let _string: NSString
+ private let _attributeArray: CFRunArrayRef
+
public required init?(coder aDecoder: NSCoder) {
NSUnimplemented()
}
@@ -38,11 +43,58 @@
NSUnimplemented()
}
- public var string: String { NSUnimplemented() }
- public func attributesAtIndex(_ location: Int, effectiveRange range: NSRangePointer) -> [String : AnyObject] { NSUnimplemented() }
+ public var string: String {
+ return _string._swiftObject
+ }
+
+ public func attributesAtIndex(_ location: Int, effectiveRange range: NSRangePointer) -> [String : AnyObject] {
+ var cfRange = CFRange()
+ return withUnsafeMutablePointer(&cfRange) { (rangePointer: UnsafeMutablePointer<CFRange>) -> [String : AnyObject] in
+ // Get attributes value using CoreFoundation function
+ let value = CFAttributedStringGetAttributes(_cfObject, location, rangePointer)
+
+ // Convert the value to [String : AnyObject]
+ let dictionary = unsafeBitCast(value, to: NSDictionary.self)
+ var results = [String : AnyObject]()
+ for (key, value) in dictionary {
+ guard let stringKey = (key as? NSString)?._swiftObject else {
+ continue
+ }
+ results[stringKey] = value
+ }
+
+ // Update effective range
+ let hasAttrs = results.count > 0
+ range.pointee.location = hasAttrs ? rangePointer.pointee.location : NSNotFound
+ range.pointee.length = hasAttrs ? rangePointer.pointee.length : 0
+
+ return results
+ }
+ }
- public var length: Int { NSUnimplemented() }
- public func attribute(_ attrName: String, atIndex location: Int, effectiveRange range: NSRangePointer) -> AnyObject? { NSUnimplemented() }
+ public var length: Int {
+ return CFAttributedStringGetLength(_cfObject)
+ }
+
+ public func attribute(_ attrName: String, atIndex location: Int, effectiveRange range: NSRangePointer) -> AnyObject? {
+ var cfRange = CFRange()
+ return withUnsafeMutablePointer(&cfRange) { (rangePointer: UnsafeMutablePointer<CFRange>) -> AnyObject? in
+ // Get attribute value using CoreFoundation function
+ let attribute = CFAttributedStringGetAttribute(_cfObject, location, attrName._cfObject, rangePointer)
+
+ // Update effective range and return the result
+ if let attribute = attribute {
+ range.pointee.location = rangePointer.pointee.location
+ range.pointee.length = rangePointer.pointee.length
+ return attribute
+ } else {
+ range.pointee.location = NSNotFound
+ range.pointee.length = 0
+ return nil
+ }
+ }
+ }
+
public func attributedSubstringFromRange(_ range: NSRange) -> NSAttributedString { NSUnimplemented() }
public func attributesAtIndex(_ location: Int, longestEffectiveRange range: NSRangePointer, inRange rangeLimit: NSRange) -> [String : AnyObject] { NSUnimplemented() }
@@ -50,14 +102,46 @@
public func isEqualToAttributedString(_ other: NSAttributedString) -> Bool { NSUnimplemented() }
- public init(string str: String) { NSUnimplemented() }
- public init(string str: String, attributes attrs: [String : AnyObject]?) { NSUnimplemented() }
+ public init(string str: String) {
+ _string = str._nsObject
+ _attributeArray = CFRunArrayCreate(kCFAllocatorDefault)
+
+ super.init()
+ addAttributesToAttributeArray(attrs: nil)
+ }
+
+ public init(string str: String, attributes attrs: [String : AnyObject]?) {
+ _string = str._nsObject
+ _attributeArray = CFRunArrayCreate(kCFAllocatorDefault)
+
+ super.init()
+ addAttributesToAttributeArray(attrs: attrs)
+ }
+
public init(attributedString attrStr: NSAttributedString) { NSUnimplemented() }
+ private func addAttributesToAttributeArray(attrs: [String : AnyObject]?) {
+ guard _string.length > 0 else {
+ return
+ }
+
+ let range = CFRange(location: 0, length: _string.length)
+ if let attrs = attrs {
+ CFRunArrayInsert(_attributeArray, range, attrs._cfObject)
+ } else {
+ let emptyAttrs = [String : AnyObject]()
+ CFRunArrayInsert(_attributeArray, range, emptyAttrs._cfObject)
+ }
+ }
+
public func enumerateAttributesInRange(_ enumerationRange: NSRange, options opts: NSAttributedStringEnumerationOptions, usingBlock block: ([String : AnyObject], NSRange, UnsafeMutablePointer<ObjCBool>) -> Void) { NSUnimplemented() }
public func enumerateAttribute(_ attrName: String, inRange enumerationRange: NSRange, options opts: NSAttributedStringEnumerationOptions, usingBlock block: (AnyObject?, NSRange, UnsafeMutablePointer<ObjCBool>) -> Void) { NSUnimplemented() }
}
+extension NSAttributedString: _CFBridgable {
+ internal var _cfObject: CFAttributedString { return unsafeBitCast(self, to: CFAttributedString.self) }
+}
+
public struct NSAttributedStringEnumerationOptions : OptionSet {
public let rawValue : UInt
public init(rawValue: UInt) { self.rawValue = rawValue }
diff --git a/TestFoundation/TestNSAttributedString.swift b/TestFoundation/TestNSAttributedString.swift
new file mode 100644
index 0000000..04a66fd
--- /dev/null
+++ b/TestFoundation/TestNSAttributedString.swift
@@ -0,0 +1,82 @@
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See http://swift.org/LICENSE.txt for license information
+// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
+//
+
+
+
+#if DEPLOYMENT_RUNTIME_OBJC || os(Linux)
+ import Foundation
+ import XCTest
+#else
+ import SwiftFoundation
+ import SwiftXCTest
+#endif
+
+
+
+class TestNSAttributedString : XCTestCase {
+
+ static var allTests: [(String, TestNSAttributedString -> () throws -> Void)] {
+ return [
+ ("test_initWithString", test_initWithString),
+ ("test_initWithStringAndAttributes", test_initWithStringAndAttributes)
+ ]
+ }
+
+ func test_initWithString() {
+ let string = "Lorem 😀 ipsum dolor sit amet, consectetur adipiscing elit. ⌘ Phasellus consectetur et sem vitae consectetur. Nam venenatis lectus a laoreet blandit. ಠ_ರೃ"
+ let attrString = NSAttributedString(string: string)
+ XCTAssertEqual(attrString.string, string)
+ XCTAssertEqual(attrString.length, string.utf16Count)
+
+ var range = NSRange()
+ let attrs = attrString.attributesAtIndex(0, effectiveRange: &range)
+ XCTAssertEqual(range.location, NSNotFound)
+ XCTAssertEqual(range.length, 0)
+ XCTAssertEqual(attrs.count, 0)
+
+ let attribute = attrString.attribute("invalid", atIndex: 0, effectiveRange: &range)
+ XCTAssertNil(attribute)
+ XCTAssertEqual(range.location, NSNotFound)
+ XCTAssertEqual(range.length, 0)
+ }
+
+ func test_initWithStringAndAttributes() {
+ let string = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus consectetur et sem vitae consectetur. Nam venenatis lectus a laoreet blandit."
+ let attributes: [String : AnyObject] = ["attribute.placeholder.key" : "attribute.placeholder.value" as NSString]
+
+ let attrString = NSAttributedString(string: string, attributes: attributes)
+ XCTAssertEqual(attrString.string, string)
+ XCTAssertEqual(attrString.length, string.utf16Count)
+
+ var range = NSRange()
+ let attrs = attrString.attributesAtIndex(0, effectiveRange: &range)
+ guard let value = attrs["attribute.placeholder.key"] as? NSString else {
+ XCTAssert(false, "attribute value not found")
+ return
+ }
+ XCTAssertEqual(range.location, 0)
+ XCTAssertEqual(range.length, attrString.length)
+ XCTAssertEqual(value, "attribute.placeholder.value")
+
+ let invalidAttribute = attrString.attribute("invalid", atIndex: 0, effectiveRange: &range)
+ XCTAssertNil(invalidAttribute)
+ XCTAssertEqual(range.location, NSNotFound)
+ XCTAssertEqual(range.length, 0)
+
+ let attribute = attrString.attribute("attribute.placeholder.key", atIndex: 0, effectiveRange: &range)
+ XCTAssertEqual(range.location, 0)
+ XCTAssertEqual(range.length, attrString.length)
+ guard let validAttribute = attribute as? NSString else {
+ XCTAssert(false, "attribuet not found")
+ return
+ }
+ XCTAssertEqual(validAttribute, "attribute.placeholder.value")
+ }
+
+}
\ No newline at end of file
diff --git a/TestFoundation/main.swift b/TestFoundation/main.swift
index b6f86d9..aaee90d 100644
--- a/TestFoundation/main.swift
+++ b/TestFoundation/main.swift
@@ -74,4 +74,5 @@
testCase(TestNSUserDefaults.allTests),
testCase(TestNSXMLParser.allTests),
testCase(TestNSXMLDocument.allTests),
+ testCase(TestNSAttributedString.allTests),
])