Skip to content

Commit 6c43bab

Browse files
author
Robert Thompson
committed
Better namespace support for XML
Fix one place was still expecting C String XPath works with namespaces implement XMLElement(xmlString:) Add some tests, re-enable all tests Implement more namespaces support
1 parent 0bfc8d6 commit 6c43bab

File tree

6 files changed

+458
-98
lines changed

6 files changed

+458
-98
lines changed

CoreFoundation/Parsing.subproj/CFXMLInterface.c

Lines changed: 204 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <libxml/xmlmemory.h>
2121
#include <libxml/xmlsave.h>
2222
#include <libxml/xpath.h>
23+
#include <libxml/xpathInternals.h>
2324
#include <libxml/dict.h>
2425
#include "CFInternal.h"
2526

@@ -57,6 +58,7 @@ CFIndex _kCFXMLTypeElement = XML_ELEMENT_NODE;
5758
CFIndex _kCFXMLTypeAttribute = XML_ATTRIBUTE_NODE;
5859
CFIndex _kCFXMLTypeDTD = XML_DTD_NODE;
5960
CFIndex _kCFXMLDocTypeHTML = XML_DOC_HTML;
61+
CFIndex _kCFXMLTypeNamespace = 22; // libxml2 does not define namespaces as nodes, so we have to fake it
6062

6163
CFIndex _kCFXMLDTDNodeTypeEntity = XML_ENTITY_DECL;
6264
CFIndex _kCFXMLDTDNodeTypeAttribute = XML_ATTRIBUTE_DECL;
@@ -360,10 +362,6 @@ _CFXMLNodePtr _CFXMLNewProperty(_CFXMLNodePtr node, const unsigned char* name, c
360362
return xmlNewProp(node, name, value);
361363
}
362364

363-
_CFXMLNamespacePtr _CFXMLNewNamespace(_CFXMLNodePtr node, const unsigned char* uri, const unsigned char* prefix) {
364-
return xmlNewNs(node, uri, prefix);
365-
}
366-
367365
CF_RETURNS_RETAINED CFStringRef _CFXMLNodeURI(_CFXMLNodePtr node) {
368366
xmlNodePtr nodePtr = (xmlNodePtr)node;
369367
switch (nodePtr->type) {
@@ -447,8 +445,44 @@ CFIndex _CFXMLNodeGetType(_CFXMLNodePtr node) {
447445
return ((xmlNodePtr)node)->type;
448446
}
449447

450-
const char* _CFXMLNodeGetName(_CFXMLNodePtr node) {
451-
return (const char*)(((xmlNodePtr)node)->name);
448+
static inline xmlChar* _getQName(xmlNodePtr node) {
449+
const xmlChar* prefix = NULL;
450+
const xmlChar* ncname = node->name;
451+
452+
switch (node->type) {
453+
case XML_NOTATION_NODE:
454+
case XML_DTD_NODE:
455+
case XML_ELEMENT_DECL:
456+
case XML_ATTRIBUTE_DECL:
457+
case XML_ENTITY_DECL:
458+
case XML_NAMESPACE_DECL:
459+
case XML_XINCLUDE_START:
460+
case XML_XINCLUDE_END:
461+
break;
462+
463+
default:
464+
if (node->ns != NULL) {
465+
prefix = node->ns->prefix;
466+
}
467+
}
468+
469+
return xmlBuildQName(ncname, prefix, NULL, 0);
470+
}
471+
472+
CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLNodeGetName(_CFXMLNodePtr node) {
473+
xmlNodePtr xmlNode = (xmlNodePtr)node;
474+
475+
xmlChar* qName = _getQName(xmlNode);
476+
477+
if (qName != NULL) {
478+
CFStringRef result = CFStringCreateWithCString(NULL, (const char*)qName, kCFStringEncodingUTF8);
479+
if (qName != xmlNode->name) {
480+
xmlFree(qName);
481+
}
482+
return result;
483+
} else {
484+
return NULL;
485+
}
452486
}
453487

454488
void _CFXMLNodeSetName(_CFXMLNodePtr node, const char* name) {
@@ -497,7 +531,7 @@ void _CFXMLNodeSetContent(_CFXMLNodePtr node, const unsigned char* _Nullable co
497531
// xmlElementContent structures, let's leverage what we've already got.
498532
CFMutableStringRef xmlString = CFStringCreateMutable(NULL, 0);
499533
CFStringAppend(xmlString, CFSTR("<!ELEMENT "));
500-
CFStringAppendCString(xmlString, _CFXMLNodeGetName(node), kCFStringEncodingUTF8);
534+
CFStringAppendCString(xmlString, (const char*)element->name, kCFStringEncodingUTF8);
501535
CFStringAppend(xmlString, CFSTR(" "));
502536
CFStringAppendCString(xmlString, (const char*)content, kCFStringEncodingUTF8);
503537
CFStringAppend(xmlString, CFSTR(">"));
@@ -837,12 +871,21 @@ CF_RETURNS_RETAINED CFArrayRef _CFXMLNodesForXPath(_CFXMLNodePtr node, const uns
837871
if (((xmlNodePtr)node)->doc == NULL) {
838872
return NULL;
839873
}
840-
874+
875+
if (((xmlNodePtr)node)->type == XML_DOCUMENT_NODE) {
876+
node = ((xmlDocPtr)node)->children;
877+
}
878+
841879
xmlXPathContextPtr context = xmlXPathNewContext(((xmlNodePtr)node)->doc);
880+
xmlNsPtr ns = ((xmlNodePtr)node)->ns;
881+
while (ns != NULL) {
882+
xmlXPathRegisterNs(context, ns->prefix, ns->href);
883+
ns = ns->next;
884+
}
842885
xmlXPathObjectPtr evalResult = xmlXPathNodeEval(node, xpath, context);
843886

844887
xmlNodeSetPtr nodes = evalResult->nodesetval;
845-
888+
846889
int count = nodes->nodeNr;
847890

848891
CFMutableArrayRef results = CFArrayCreateMutable(NULL, count, NULL);
@@ -856,8 +899,15 @@ CF_RETURNS_RETAINED CFArrayRef _CFXMLNodesForXPath(_CFXMLNodePtr node, const uns
856899
return results;
857900
}
858901

859-
_CFXMLNodePtr _CFXMLNodeHasProp(_CFXMLNodePtr node, const unsigned char* propertyName) {
860-
return xmlHasProp(node, propertyName);
902+
CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLPathForNode(_CFXMLNodePtr node) {
903+
xmlChar* path = xmlGetNodePath(node);
904+
CFStringRef result = CFStringCreateWithCString(NULL, (const char*)path, kCFStringEncodingUTF8);
905+
xmlFree(path);
906+
return result;
907+
}
908+
909+
_CFXMLNodePtr _CFXMLNodeHasProp(_CFXMLNodePtr node, const char* propertyName) {
910+
return xmlHasProp(node, (const xmlChar*)propertyName);
861911
}
862912

863913
_CFXMLDocPtr _CFXMLDocPtrFromDataWithOptions(CFDataRef data, int options) {
@@ -876,19 +926,26 @@ _CFXMLDocPtr _CFXMLDocPtrFromDataWithOptions(CFDataRef data, int options) {
876926
if (options & _kCFXMLNodeLoadExternalEntitiesAlways) {
877927
xmlOptions |= XML_PARSE_DTDLOAD;
878928
}
879-
929+
930+
xmlOptions |= XML_PARSE_RECOVER;
931+
xmlOptions |= XML_PARSE_NSCLEAN;
932+
880933
return xmlReadMemory((const char*)CFDataGetBytePtr(data), CFDataGetLength(data), NULL, NULL, xmlOptions);
881934
}
882935

883936
CF_RETURNS_RETAINED CFStringRef _CFXMLNodeLocalName(_CFXMLNodePtr node) {
884-
int length = 0;
885-
const xmlChar* result = xmlSplitQName3(((xmlNodePtr)node)->name, &length);
937+
xmlChar* prefix = NULL;
938+
const xmlChar* result = xmlSplitQName2(_getQName((xmlNodePtr)node), &prefix);
939+
if (result == NULL) {
940+
result = ((xmlNodePtr)node)->name;
941+
}
942+
886943
return CFStringCreateWithCString(NULL, (const char*)result, kCFStringEncodingUTF8);
887944
}
888945

889946
CF_RETURNS_RETAINED CFStringRef _CFXMLNodePrefix(_CFXMLNodePtr node) {
890947
xmlChar* result = NULL;
891-
xmlChar* unused = xmlSplitQName2(((xmlNodePtr)node)->name, &result);
948+
xmlChar* unused = xmlSplitQName2(_getQName((xmlNodePtr)node), &result);
892949

893950
CFStringRef resultString = CFStringCreateWithCString(NULL, (const char*)result, kCFStringEncodingUTF8);
894951
xmlFree(result);
@@ -1179,6 +1236,131 @@ void _CFXMLDTDNodeSetPublicID(_CFXMLDTDNodePtr node, const unsigned char* public
11791236
}
11801237
}
11811238

1239+
// Namespaces
1240+
CF_RETURNS_RETAINED _CFXMLNodePtr _Nonnull * _Nullable _CFXMLNamespaces(_CFXMLNodePtr node, CFIndex* count) {
1241+
*count = 0;
1242+
xmlNs* ns = ((xmlNode*)node)->ns;
1243+
while (ns != NULL) {
1244+
(*count)++;
1245+
ns = ns->next;
1246+
}
1247+
1248+
_CFXMLNodePtr* result = calloc(*count, sizeof(_CFXMLNodePtr));
1249+
ns = ((xmlNode*)node)->ns;
1250+
for (int i = 0; i < *count; i++) {
1251+
xmlNode* temp = xmlNewNode(ns, (unsigned char *)"");
1252+
1253+
temp->type = _kCFXMLTypeNamespace;
1254+
result[i] = temp;
1255+
ns = ns->next;
1256+
}
1257+
return result;
1258+
}
1259+
1260+
static inline void _removeAllNamespaces(xmlNodePtr node);
1261+
static inline void _removeAllNamespaces(xmlNodePtr node) {
1262+
xmlNsPtr ns = node->ns;
1263+
if (ns != NULL) {
1264+
xmlFreeNsList(ns);
1265+
node->ns = NULL;
1266+
}
1267+
}
1268+
1269+
void _CFXMLSetNamespaces(_CFXMLNodePtr node, _CFXMLNodePtr* _Nullable nodes, CFIndex count) {
1270+
_removeAllNamespaces(node);
1271+
1272+
if (nodes == NULL || count == 0) {
1273+
return;
1274+
}
1275+
1276+
xmlNodePtr nsNode = (xmlNodePtr)nodes[0];
1277+
((xmlNodePtr)node)->ns = xmlCopyNamespace(nsNode->ns);
1278+
xmlNsPtr currNs = ((xmlNodePtr)node)->ns;
1279+
for (CFIndex i = 1; i < count; i++) {
1280+
currNs->next = xmlCopyNamespace(((xmlNodePtr)nodes[i])->ns);
1281+
currNs = currNs->next;
1282+
}
1283+
}
1284+
1285+
CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLNamespaceGetValue(_CFXMLNodePtr node) {
1286+
xmlNsPtr ns = ((xmlNode*)node)->ns;
1287+
1288+
if (ns->href == NULL) {
1289+
return NULL;
1290+
}
1291+
1292+
return CFStringCreateWithCString(NULL, (const char*)ns->href, kCFStringEncodingUTF8);
1293+
}
1294+
1295+
void _CFXMLNamespaceSetValue(_CFXMLNodePtr node, const char* value, int64_t length) {
1296+
xmlNsPtr ns = ((xmlNodePtr)node)->ns;
1297+
ns->href = xmlStrndup((const xmlChar*)value, length);
1298+
}
1299+
1300+
CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLNamespaceGetPrefix(_CFXMLNodePtr node) {
1301+
xmlNsPtr ns = ((xmlNodePtr)node)->ns;
1302+
1303+
if (ns->prefix == NULL) {
1304+
return NULL;
1305+
}
1306+
1307+
return CFStringCreateWithCString(NULL, (const char*)ns->prefix, kCFStringEncodingUTF8);
1308+
}
1309+
1310+
void _CFXMLNamespaceSetPrefix(_CFXMLNodePtr node, const char* prefix, int64_t length) {
1311+
xmlNsPtr ns = ((xmlNodePtr)node)->ns;
1312+
1313+
ns->prefix = xmlStrndup((const xmlChar*)prefix, length);
1314+
}
1315+
1316+
_CFXMLNodePtr _CFXMLNewNamespace(const char* name, const char* stringValue) {
1317+
xmlNsPtr ns = xmlNewNs(NULL, (const xmlChar*)stringValue, (const xmlChar*)name);
1318+
xmlNodePtr node = xmlNewNode(ns, (const xmlChar*)"");
1319+
1320+
node->type = _kCFXMLTypeNamespace;
1321+
1322+
return node;
1323+
}
1324+
1325+
void _CFXMLAddNamespace(_CFXMLNodePtr node, _CFXMLNodePtr nsNode) {
1326+
xmlNodePtr nodePtr = (xmlNodePtr)node;
1327+
xmlNsPtr ns = xmlCopyNamespace(((xmlNodePtr)nsNode)->ns);
1328+
ns->context = nodePtr->doc;
1329+
1330+
xmlNsPtr currNs = nodePtr->ns;
1331+
if (currNs == NULL) {
1332+
nodePtr->ns = ns;
1333+
return;
1334+
}
1335+
1336+
while(currNs->next != NULL) {
1337+
currNs = currNs->next;
1338+
}
1339+
1340+
currNs->next = ns;
1341+
}
1342+
1343+
void _CFXMLRemoveNamespace(_CFXMLNodePtr node, const char* prefix) {
1344+
xmlNodePtr nodePtr = (xmlNodePtr)node;
1345+
xmlNsPtr ns = nodePtr->ns;
1346+
if (ns != NULL && xmlStrcmp((const xmlChar*)prefix, ns->prefix) == 0) {
1347+
nodePtr->ns = ns->next;
1348+
xmlFreeNs(ns);
1349+
return;
1350+
}
1351+
1352+
while (ns->next != NULL) {
1353+
if (xmlStrcmp(ns->next->prefix, (const xmlChar*)prefix) == 0) {
1354+
xmlNsPtr next = ns->next;
1355+
ns->next = ns->next->next;
1356+
xmlFreeNs(next);
1357+
return;
1358+
}
1359+
1360+
ns = ns->next;
1361+
}
1362+
}
1363+
11821364
void _CFXMLFreeNode(_CFXMLNodePtr node) {
11831365
if (!node) {
11841366
return;
@@ -1231,6 +1413,13 @@ void _CFXMLFreeNode(_CFXMLNodePtr node) {
12311413
}
12321414

12331415
default:
1416+
// we first need to check if this node is one of our custom
1417+
// namespace nodes, which don't actually exist in libxml2
1418+
if (((xmlNodePtr)node)->type == _kCFXMLTypeNamespace) {
1419+
// resetting its type to XML_ELEMENT_NODE will cause the enclosed namespace
1420+
// to be properly freed by libxml2
1421+
((xmlNodePtr)node)->type = XML_ELEMENT_NODE;
1422+
}
12341423
xmlFreeNode(node);
12351424
}
12361425
}

CoreFoundation/Parsing.subproj/CFXMLInterface.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ extern CFIndex _kCFXMLTypeElement;
5555
extern CFIndex _kCFXMLTypeAttribute;
5656
extern CFIndex _kCFXMLTypeDTD;
5757
extern CFIndex _kCFXMLDocTypeHTML;
58+
extern CFIndex _kCFXMLTypeNamespace;
5859

5960
extern CFIndex _kCFXMLDTDNodeTypeEntity;
6061
extern CFIndex _kCFXMLDTDNodeTypeAttribute;
@@ -144,7 +145,6 @@ _CFXMLNodePtr _CFXMLNewProcessingInstruction(const unsigned char* name, const un
144145
_CFXMLNodePtr _CFXMLNewTextNode(const unsigned char* value);
145146
_CFXMLNodePtr _CFXMLNewComment(const unsigned char* value);
146147
_CFXMLNodePtr _CFXMLNewProperty(_CFXMLNodePtr _Nullable node, const unsigned char* name, const unsigned char* value);
147-
_CFXMLNamespacePtr _CFXMLNewNamespace(_CFXMLNodePtr _Nullable node, const unsigned char* uri, const unsigned char* prefix);
148148

149149
CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLNodeURI(_CFXMLNodePtr node);
150150
void _CFXMLNodeSetURI(_CFXMLNodePtr node, const unsigned char* _Nullable URI);
@@ -153,7 +153,7 @@ void _CFXMLNodeSetPrivateData(_CFXMLNodePtr node, void* data);
153153
void* _Nullable _CFXMLNodeGetPrivateData(_CFXMLNodePtr node);
154154
_CFXMLNodePtr _Nullable _CFXMLNodeProperties(_CFXMLNodePtr node);
155155
CFIndex _CFXMLNodeGetType(_CFXMLNodePtr node);
156-
const char* _Nullable _CFXMLNodeGetName(_CFXMLNodePtr node);
156+
CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLNodeGetName(_CFXMLNodePtr node);
157157
void _CFXMLNodeSetName(_CFXMLNodePtr node, const char* name);
158158
CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLNodeGetContent(_CFXMLNodePtr node);
159159
void _CFXMLNodeSetContent(_CFXMLNodePtr node, const unsigned char* _Nullable content);
@@ -195,8 +195,9 @@ CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLGetEntityContent(_CFXMLEntityPtr
195195
CF_RETURNS_RETAINED CFStringRef _CFXMLStringWithOptions(_CFXMLNodePtr node, uint32_t options);
196196

197197
CF_RETURNS_RETAINED CFArrayRef _Nullable _CFXMLNodesForXPath(_CFXMLNodePtr node, const unsigned char* xpath);
198+
CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLPathForNode(_CFXMLNodePtr node);
198199

199-
_CFXMLNodePtr _Nullable _CFXMLNodeHasProp(_CFXMLNodePtr node, const unsigned char* propertyName);
200+
_CFXMLNodePtr _Nullable _CFXMLNodeHasProp(_CFXMLNodePtr node, const char* propertyName);
200201

201202
_CFXMLDocPtr _CFXMLDocPtrFromDataWithOptions(CFDataRef data, int options);
202203

@@ -232,6 +233,17 @@ void _CFXMLDTDNodeSetSystemID(_CFXMLDTDNodePtr node, const unsigned char* _Nulla
232233
CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLDTDNodeGetPublicID(_CFXMLDTDNodePtr node);
233234
void _CFXMLDTDNodeSetPublicID(_CFXMLDTDNodePtr node, const unsigned char* _Nullable publicID);
234235

236+
// Namespaces
237+
CF_RETURNS_RETAINED _CFXMLNodePtr _Nonnull * _Nullable _CFXMLNamespaces(_CFXMLNodePtr node, CFIndex* count);
238+
void _CFXMLSetNamespaces(_CFXMLNodePtr node, _CFXMLNodePtr _Nonnull * _Nullable nodes, CFIndex count);
239+
CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLNamespaceGetValue(_CFXMLNodePtr node);
240+
void _CFXMLNamespaceSetValue(_CFXMLNodePtr node, const char* _Nullable value, int64_t length);
241+
CF_RETURNS_RETAINED CFStringRef _Nullable _CFXMLNamespaceGetPrefix(_CFXMLNodePtr node);
242+
void _CFXMLNamespaceSetPrefix(_CFXMLNodePtr node, const char* _Nullable prefix, int64_t length);
243+
_CFXMLNodePtr _CFXMLNewNamespace(const char* name, const char* stringValue);
244+
void _CFXMLAddNamespace(_CFXMLNodePtr node, _CFXMLNodePtr nsNode);
245+
void _CFXMLRemoveNamespace(_CFXMLNodePtr node, const char* prefix);
246+
235247
void _CFXMLFreeNode(_CFXMLNodePtr node);
236248
void _CFXMLFreeDocument(_CFXMLDocPtr doc);
237249
void _CFXMLFreeDTD(_CFXMLDTDPtr dtd);

Foundation.xcodeproj/xcshareddata/xcschemes/TestFoundation.xcscheme

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@
8585
</TestAction>
8686
<LaunchAction
8787
buildConfiguration = "Debug"
88-
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
89-
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
88+
selectedDebuggerIdentifier = ""
89+
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
9090
launchStyle = "0"
9191
useCustomWorkingDirectory = "NO"
9292
ignoresPersistentStateOnLaunch = "NO"

0 commit comments

Comments
 (0)