diff --git a/SwiftyExpat.xcodeproj/project.pbxproj b/SwiftyExpat.xcodeproj/project.pbxproj index 53db085..de95c8f 100644 --- a/SwiftyExpat.xcodeproj/project.pbxproj +++ b/SwiftyExpat.xcodeproj/project.pbxproj @@ -30,7 +30,6 @@ E8FB77941971767500E0557D /* SwiftyExpatTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FB77931971767500E0557D /* SwiftyExpatTests.swift */; }; E8FB77951971767500E0557D /* SwiftyExpat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8FB773E1971609A00E0557D /* SwiftyExpat.framework */; }; E8FB779C1971E4AB00E0557D /* SwiftyExpat-Bridging-Header.h in Headers */ = {isa = PBXBuildFile; fileRef = E8FB779B1971E4AB00E0557D /* SwiftyExpat-Bridging-Header.h */; }; - E8FB779E1971E5AA00E0557D /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FB779D1971E5AA00E0557D /* main.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -93,7 +92,6 @@ E8FB77921971767500E0557D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E8FB77931971767500E0557D /* SwiftyExpatTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftyExpatTests.swift; sourceTree = ""; }; E8FB779B1971E4AB00E0557D /* SwiftyExpat-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SwiftyExpat-Bridging-Header.h"; sourceTree = ""; }; - E8FB779D1971E5AA00E0557D /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -137,7 +135,6 @@ isa = PBXGroup; children = ( E8C667EB19757405004EFA0C /* Expat.swift */, - E8FB779D1971E5AA00E0557D /* main.swift */, E8FB77431971609A00E0557D /* SwiftyExpat.h */, E8FB775E1971667F00E0557D /* expat */, E8FB77411971609A00E0557D /* Supporting Files */, @@ -343,7 +340,6 @@ files = ( E8FB77811971667F00E0557D /* xmltok.c in Sources */, E8FB777E1971667F00E0557D /* xmlparse.c in Sources */, - E8FB779E1971E5AA00E0557D /* main.swift in Sources */, E8C667EC19757405004EFA0C /* Expat.swift in Sources */, E8FB777F1971667F00E0557D /* xmlrole.c in Sources */, ); diff --git a/SwiftyExpat/Expat.swift b/SwiftyExpat/Expat.swift index 1ea6de4..118404f 100644 --- a/SwiftyExpat/Expat.swift +++ b/SwiftyExpat/Expat.swift @@ -14,7 +14,8 @@ */ class Expat : OutputStream { - let parser : XML_Parser + let parser : XML_Parser + var isClosed = false init(encoding: String = "UTF-8", nsSeparator: Character = ":") { let sepUTF8 = ("" + nsSeparator).utf8 @@ -31,6 +32,7 @@ class Expat : OutputStream { parser = newParser } deinit { + println("freeing parser ...") XML_ParserFree(parser) } @@ -38,9 +40,8 @@ class Expat : OutputStream { /* feed the parser */ func write(cs: CString) { - let cslen = strlen(cs) - let isFinal = cslen == 0 - XML_Parse(parser, cs, Int32(cslen), isFinal ? 1 : 0) + let cslen = strlen(cs) + XML_Parse(parser, cs, Int32(cslen), 0) } func write(s: String) { @@ -48,7 +49,32 @@ class Expat : OutputStream { } func close() { + if isClosed { return } + + let isFinal : Int32 = 1 + XML_Parse(parser, "", 0, isFinal) + resetCallbacks() + isClosed = true + } + + func resetCallbacks() { + // reset callbacks to fixup any potential cycles + XML_SetElementHandler (parser, nil, nil) + XML_SetCharacterDataHandler (parser, nil) + XML_SetProcessingInstructionHandler(parser, nil) + XML_SetCommentHandler (parser, nil) + XML_SetCdataSectionHandler (parser, nil, nil) + XML_SetDefaultHandler (parser, nil) + XML_SetDefaultHandlerExpand (parser, nil) + XML_SetDoctypeDeclHandler (parser, nil, nil) + XML_SetUnparsedEntityDeclHandler(parser, nil) + XML_SetNotationDeclHandler (parser, nil) + XML_SetNamespaceDeclHandler (parser, nil, nil) + XML_SetNotStandaloneHandler (parser, nil) + XML_SetExternalEntityRefHandler (parser, nil) + XML_SetSkippedEntityHandler (parser, nil) + XML_SetUnknownEncodingHandler (parser, nil, nil) } @@ -56,8 +82,7 @@ class Expat : OutputStream { func onStartElement(cb: ( String, [ String : String ] ) -> Void) -> Self { XML_SetStartElementHandler(parser) { - // void *userData, const XML_Char *name, const XML_Char **atts - ( userData, name, attrs ) in + _, name, attrs in let sName = String.fromCString(name)! // unwrap, must be set var sAttrs = [ String : String]() @@ -77,4 +102,30 @@ class Expat : OutputStream { return self } + func onEndElement(cb: ( String ) -> Void) -> Self { + XML_SetEndElementHandler(parser) { _, name in + let sName = String.fromCString(name)! // unwrap, must be set + cb(sName) + } + return self + } + + func onStartNamespace(cb: ( String?, String ) -> Void) -> Self { + XML_SetStartNamespaceDeclHandler(parser) { + _, prefix, uri in + let sPrefix = String.fromCString(prefix) + let sURI = String.fromCString(uri)! + cb(sPrefix, sURI) + } + return self + } + func onEndNamespace(cb: ( String? ) -> Void) -> Self { + XML_SetEndNamespaceDeclHandler(parser) { + _, prefix in + let sPrefix = String.fromCString(prefix) + cb(sPrefix) + } + return self + } + } diff --git a/SwiftyExpat/expat/xmlparse.c b/SwiftyExpat/expat/xmlparse.c index def003a..62a0fbf 100644 --- a/SwiftyExpat/expat/xmlparse.c +++ b/SwiftyExpat/expat/xmlparse.c @@ -1302,7 +1302,8 @@ XML_GetAttributeInfo(XML_Parser parser) #ifdef EXPAT_WITH_BLOCKS # define EXPAT_SET_CALLBACK(__VAR__, __TYPE__, __ARG__) \ -(__VAR__ = (__TYPE__)Block_copy(__ARG__)) + ({ if (__VAR__ != NULL) Block_release(__VAR__); \ + __VAR__ = (__TYPE__)Block_copy(__ARG__); }) #else # define EXPAT_SET_CALLBACK(__VAR__, __TYPE__, __ARG___) (__VAR == __ARG__) #endif diff --git a/SwiftyExpat/main.swift b/SwiftyExpat/main.swift deleted file mode 100644 index 98019b8..0000000 --- a/SwiftyExpat/main.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// main.swift -// SwiftyExpat -// -// Created by Helge Heß on 7/12/14. -// Copyright (c) 2014 Always Right Institute. All rights reserved. -// - -import Foundation - -func testit() { - println("testing it ...") - - var p = Expat() - - p.onStartElement { name, attrs in println("name: \(name) \(attrs)") } - - let testXML = "world" - p.write(testXML) - p.write("") // EOF - - // XML_SetElementHandler(p, startCB, endCB) -} diff --git a/SwiftyExpatTests/SwiftyExpatTests.swift b/SwiftyExpatTests/SwiftyExpatTests.swift index 6bfc29c..e621e18 100644 --- a/SwiftyExpatTests/SwiftyExpatTests.swift +++ b/SwiftyExpatTests/SwiftyExpatTests.swift @@ -11,26 +11,38 @@ import XCTest import SwiftyExpat class SwiftyExpatTests: XCTestCase { + + var p : Expat! = nil override func setUp() { super.setUp() + + p = Expat() + .onStartElement { name, attrs in println("<\(name) \(attrs)") } + .onEndElement { name in println(">\(name)") } + .onStartNamespace { prefix, uri in println("+NS[\(prefix)] = \(uri)") } + .onEndNamespace { prefix in println("-NS[\(prefix)]") } } override func tearDown() { + p = nil super.tearDown() } - func testExample() { - // This is an example of a functional test case. + func testSimpleParsing() { XCTAssert(true, "Pass") - SwiftyExpat.testit() + + let testXML = "world" + p.write(testXML) + p.close() // EOF } + /* func testPerformanceExample() { // This is an example of a performance test case. self.measureBlock() { // Put the code you want to measure the time of here. } } - + */ }