Skip to content

Commit

Permalink
Merge pull request #264 from codelynx/master
Browse files Browse the repository at this point in the history
bugfix:  supporting namespace for querying the same attribute name with different namespace
  • Loading branch information
tid-kijyun authored Jan 19, 2024
2 parents bf19cf5 + c050bda commit 86bca4f
Show file tree
Hide file tree
Showing 7 changed files with 225 additions and 13 deletions.
30 changes: 28 additions & 2 deletions Kanna.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
1EB4A01F204C1F240003D7A2 /* KannaCSSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EB4A01E204C1F240003D7A2 /* KannaCSSTests.swift */; };
1EB4A021204C20760003D7A2 /* KannaXMLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EB4A020204C20760003D7A2 /* KannaXMLTests.swift */; };
1EC805FA1FA2FB2F0067D3DA /* Deprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EC805F91FA2FB2F0067D3DA /* Deprecated.swift */; };
CC02AE172B5A1A210075B74A /* pptx-presentation.xml.rels in Resources */ = {isa = PBXBuildFile; fileRef = CC02AE162B5A1A210075B74A /* pptx-presentation.xml.rels */; };
CC02AE192B5A1A480075B74A /* pptx-presentation.xml in Resources */ = {isa = PBXBuildFile; fileRef = CC02AE182B5A1A480075B74A /* pptx-presentation.xml */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -66,6 +68,8 @@
1EB4A01E204C1F240003D7A2 /* KannaCSSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KannaCSSTests.swift; sourceTree = "<group>"; };
1EB4A020204C20760003D7A2 /* KannaXMLTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KannaXMLTests.swift; sourceTree = "<group>"; };
1EC805F91FA2FB2F0067D3DA /* Deprecated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Deprecated.swift; sourceTree = "<group>"; };
CC02AE162B5A1A210075B74A /* pptx-presentation.xml.rels */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = "pptx-presentation.xml.rels"; sourceTree = "<group>"; };
CC02AE182B5A1A480075B74A /* pptx-presentation.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = "pptx-presentation.xml"; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -171,6 +175,8 @@
1E7ADC9C1DF5907F006E1815 /* sample.html */,
1E7ADC9D1DF5907F006E1815 /* test_HTML4.html */,
1E7ADC9E1DF5907F006E1815 /* test_XML_ExcelWorkbook.xml */,
CC02AE162B5A1A210075B74A /* pptx-presentation.xml.rels */,
CC02AE182B5A1A480075B74A /* pptx-presentation.xml */,
1E7ADC9F1DF5907F006E1815 /* versions.xml */,
);
name = Data;
Expand Down Expand Up @@ -282,9 +288,11 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
CC02AE172B5A1A210075B74A /* pptx-presentation.xml.rels in Resources */,
1E7ADCA21DF5907F006E1815 /* test_HTML4.html in Resources */,
1E7ADCA11DF5907F006E1815 /* sample.html in Resources */,
1E7ADCA01DF5907F006E1815 /* libraries.xml in Resources */,
CC02AE192B5A1A480075B74A /* pptx-presentation.xml in Resources */,
1E7ADCA41DF5907F006E1815 /* versions.xml in Resources */,
1E7ADCA31DF5907F006E1815 /* test_XML_ExcelWorkbook.xml in Resources */,
);
Expand Down Expand Up @@ -403,16 +411,19 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.9;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_INCLUDE_PATHS = "";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TVOS_DEPLOYMENT_TARGET = 12.0;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
WATCHOS_DEPLOYMENT_TARGET = 4.0;
};
name = Debug;
};
Expand Down Expand Up @@ -462,14 +473,17 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.9;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_INCLUDE_PATHS = "";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 5.0;
TVOS_DEPLOYMENT_TARGET = 12.0;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
WATCHOS_DEPLOYMENT_TARGET = 4.0;
};
name = Release;
};
Expand All @@ -487,12 +501,16 @@
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = Sources/Kanna/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.13;
OTHER_LDFLAGS = "-lxml2";
PRODUCT_BUNDLE_IDENTIFIER = com.tid.Kanna;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TVOS_DEPLOYMENT_TARGET = 12.0;
WATCHOS_DEPLOYMENT_TARGET = 4.0;
};
name = Debug;
};
Expand All @@ -510,12 +528,16 @@
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = Sources/Kanna/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.13;
OTHER_LDFLAGS = "-lxml2";
PRODUCT_BUNDLE_IDENTIFIER = com.tid.Kanna;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TVOS_DEPLOYMENT_TARGET = 12.0;
WATCHOS_DEPLOYMENT_TARGET = 4.0;
};
name = Release;
};
Expand All @@ -528,10 +550,12 @@
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = DP9Q5R8635;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.13;
PRODUCT_BUNDLE_IDENTIFIER = com.tid.KannaTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TVOS_DEPLOYMENT_TARGET = 12.0;
};
name = Debug;
};
Expand All @@ -544,9 +568,11 @@
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = DP9Q5R8635;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.13;
PRODUCT_BUNDLE_IDENTIFIER = com.tid.KannaTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TVOS_DEPLOYMENT_TARGET = 12.0;
};
name = Release;
};
Expand Down
9 changes: 9 additions & 0 deletions Sources/Kanna/Kanna.swift
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,15 @@ public protocol XMLDocument: AnyObject, SearchableNode {
var namespaces: [Namespace] { get }
}

public extension XMLDocument {
var namespaceDictionary: [String: String]? {
let dictionary = self.namespaces.reduce(into: [:]) {
// when prefix is blank, treat prefix "" as "xmlns", or xpath cannot specify "" as prefix
$0[$1.prefix == "" ? "xmlns": $1.prefix] = $1.name
}
return dictionary.count > 0 ? dictionary : nil
}
}
/**
HTMLDocument
*/
Expand Down
2 changes: 2 additions & 0 deletions Sources/Kanna/libxmlHTMLDocument.swift
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ final class libxmlXMLDocument: XMLDocument {
}

func xpath(_ xpath: String, namespaces: [String: String]? = nil) -> XPathObject {
let namespaces = namespaces ?? self.namespaceDictionary
guard let docPtr = docPtr else { return .none }
return XPath(doc: self, docPtr: docPtr).xpath(xpath, namespaces: namespaces)
}
Expand Down Expand Up @@ -336,6 +337,7 @@ struct XPath {
guard let ctxt = xmlXPathNewContext(docPtr) else { return .none }
defer { xmlXPathFreeContext(ctxt) }

let namespaces = namespaces ?? self.doc.namespaceDictionary
if let nsDictionary = namespaces {
for (ns, name) in nsDictionary {
xmlXPathRegisterNs(ctxt, ns, name)
Expand Down
29 changes: 18 additions & 11 deletions Sources/Kanna/libxmlHTMLNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,17 +115,17 @@ final class libxmlHTMLNode: XMLElement {
get {
var attr = nodePtr.pointee.properties
while attr != nil {
let mem = attr?.pointee
if let tagName = String(validatingUTF8: UnsafeRawPointer((mem?.name)!).assumingMemoryBound(to: CChar.self)) {
if attributeName == tagName {
if let children = mem?.children {
return libxmlGetNodeContent(children)
} else {
return ""
}
}
}
attr = attr?.pointee.next
let mem = attr!.pointee
let prefix = mem.ns.flatMap { $0.pointee.prefix.string }
let tagName = [prefix, mem.name.string].compactMap { $0 }.joined(separator: ":")
if attributeName == tagName {
if let children = mem.children {
return libxmlGetNodeContent(children)
} else {
return ""
}
}
attr = attr!.pointee.next
}
return nil
}
Expand Down Expand Up @@ -233,3 +233,10 @@ private func escape(_ str: String) -> String {
}
return newStr
}

fileprivate extension UnsafePointer<UInt8> {
var string: String? {
let string = String(validatingUTF8: UnsafePointer<CChar>(OpaquePointer(self)))
return string
}
}
118 changes: 118 additions & 0 deletions Tests/KannaTests/Data/pptx-presentation.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<p:presentation xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" saveSubsetFonts="1"
autoCompressPictures="0">
<p:sldMasterIdLst>
<p:sldMasterId id="2147483648" r:id="rId1" />
</p:sldMasterIdLst>
<p:sldIdLst>
<p:sldId id="256" r:id="rId2" />
</p:sldIdLst>
<p:sldSz cx="12192000" cy="6858000" />
<p:notesSz cx="6858000" cy="9144000" />
<p:defaultTextStyle>
<a:defPPr>
<a:defRPr lang="en-US" />
</a:defPPr>
<a:lvl1pPr marL="0" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0"
hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill>
<a:schemeClr val="tx1" />
</a:solidFill>
<a:latin typeface="+mn-lt" />
<a:ea typeface="+mn-ea" />
<a:cs typeface="+mn-cs" />
</a:defRPr>
</a:lvl1pPr>
<a:lvl2pPr marL="457200" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0"
hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill>
<a:schemeClr val="tx1" />
</a:solidFill>
<a:latin typeface="+mn-lt" />
<a:ea typeface="+mn-ea" />
<a:cs typeface="+mn-cs" />
</a:defRPr>
</a:lvl2pPr>
<a:lvl3pPr marL="914400" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0"
hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill>
<a:schemeClr val="tx1" />
</a:solidFill>
<a:latin typeface="+mn-lt" />
<a:ea typeface="+mn-ea" />
<a:cs typeface="+mn-cs" />
</a:defRPr>
</a:lvl3pPr>
<a:lvl4pPr marL="1371600" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0"
hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill>
<a:schemeClr val="tx1" />
</a:solidFill>
<a:latin typeface="+mn-lt" />
<a:ea typeface="+mn-ea" />
<a:cs typeface="+mn-cs" />
</a:defRPr>
</a:lvl4pPr>
<a:lvl5pPr marL="1828800" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0"
hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill>
<a:schemeClr val="tx1" />
</a:solidFill>
<a:latin typeface="+mn-lt" />
<a:ea typeface="+mn-ea" />
<a:cs typeface="+mn-cs" />
</a:defRPr>
</a:lvl5pPr>
<a:lvl6pPr marL="2286000" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0"
hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill>
<a:schemeClr val="tx1" />
</a:solidFill>
<a:latin typeface="+mn-lt" />
<a:ea typeface="+mn-ea" />
<a:cs typeface="+mn-cs" />
</a:defRPr>
</a:lvl6pPr>
<a:lvl7pPr marL="2743200" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0"
hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill>
<a:schemeClr val="tx1" />
</a:solidFill>
<a:latin typeface="+mn-lt" />
<a:ea typeface="+mn-ea" />
<a:cs typeface="+mn-cs" />
</a:defRPr>
</a:lvl7pPr>
<a:lvl8pPr marL="3200400" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0"
hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill>
<a:schemeClr val="tx1" />
</a:solidFill>
<a:latin typeface="+mn-lt" />
<a:ea typeface="+mn-ea" />
<a:cs typeface="+mn-cs" />
</a:defRPr>
</a:lvl8pPr>
<a:lvl9pPr marL="3657600" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0"
hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill>
<a:schemeClr val="tx1" />
</a:solidFill>
<a:latin typeface="+mn-lt" />
<a:ea typeface="+mn-ea" />
<a:cs typeface="+mn-cs" />
</a:defRPr>
</a:lvl9pPr>
</p:defaultTextStyle>
</p:presentation>
21 changes: 21 additions & 0 deletions Tests/KannaTests/Data/pptx-presentation.xml.rels
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId3"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/presProps"
Target="presProps.xml" />
<Relationship Id="rId2"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide"
Target="slides/slide1.xml" />
<Relationship Id="rId1"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster"
Target="slideMasters/slideMaster1.xml" />
<Relationship Id="rId6"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/tableStyles"
Target="tableStyles.xml" />
<Relationship Id="rId5"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme"
Target="theme/theme1.xml" />
<Relationship Id="rId4"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/viewProps"
Target="viewProps.xml" />
</Relationships>
29 changes: 29 additions & 0 deletions Tests/KannaTests/KannaXMLTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,35 @@ class KannaXMLTests: XCTestCase {
XCTAssertEqual(namespaces.sorted(), arry.sorted())
}
}

func testNamespaces_multipleNamespaces() {
// namespaces: "xmlns:a", "xmlns:r", "xmlns:p"
let url = Bundle(for: KannaXMLTests.self).url(forResource: "pptx-presentation", withExtension: "xml")
XCTAssertNotNil(url)
let doc = try? XML(url: url!, encoding: .utf8)
XCTAssertNotNil(doc)
let nodes = Array(doc!.xpath("//p:sldId"))
XCTAssert(nodes.count == 1)
let sldId = nodes[0]
XCTAssert(sldId.tagName == "sldId")
XCTAssert(sldId["id"] == "256")
XCTAssert(sldId["r:id"] == "rId2")
}

func testNamespaces_singleNamespace() {
// namespaces: "xmlns"
let url = Bundle(for: KannaXMLTests.self).url(forResource: "pptx-presentation", withExtension: "xml.rels")
XCTAssertNotNil(url)
let doc = try? XML(url: url!, encoding: .utf8)
XCTAssertNotNil(doc)
let nodes1 = Array(doc!.xpath("//Relationship"))
XCTAssert(nodes1.count == 0)
let nodes2 = Array(doc!.xpath("//xmlns:Relationship"))
XCTAssert(nodes2.count == 6)
let (relationship0, relationship1) = (nodes2[0], nodes2[1])
XCTAssert(relationship0["Id"] == "rId3")
XCTAssert(relationship1["Id"] == "rId2")
}
}

extension KannaXMLTests {
Expand Down

0 comments on commit 86bca4f

Please sign in to comment.