diff options
Diffstat (limited to 'storage/connect/domdoc.cpp')
-rw-r--r-- | storage/connect/domdoc.cpp | 755 |
1 files changed, 755 insertions, 0 deletions
diff --git a/storage/connect/domdoc.cpp b/storage/connect/domdoc.cpp new file mode 100644 index 00000000..268ad771 --- /dev/null +++ b/storage/connect/domdoc.cpp @@ -0,0 +1,755 @@ +/******************************************************************/ +/* Implementation of XML document processing using MS DOM */ +/* Author: Olivier Bertrand 2007 - 2013 */ +/******************************************************************/ +#include "my_global.h" +#include <stdio.h> +#if defined(_WIN32) +//#include <windows.h> +#if defined(MSX2) +#import "msxml2.dll" //Does not exist on Vista +#elif defined(MSX3) +#import "msxml3.dll" //Causes error C2872: DOMNodeType: ambiguous symbol ?? +#elif defined(MSX4) +#import "msxml4.dll" //Causes error C2872: DOMNodeType: ambiguous symbol ?? +#elif defined(MSX6) +#pragma warning(suppress : 4192) +#import "msxml6.dll" //Causes error C2872: DOMNodeType: ambiguous symbol ?? +#else // MSX4 +#error MSX? is not defined +#endif // MSX +using namespace MSXML2; +#else +#error This is a Windows implementation only +#endif + +#define NODE_TYPE_LIST + +#include "global.h" +#include "plgdbsem.h" +#include "xobject.h" +#include "domdoc.h" + +inline bool TestHr(PGLOBAL g, HRESULT hr) + { + if FAILED(hr) { + snprintf(g->Message, sizeof(g->Message), "%s, hr=%d", MSG(COM_ERROR), hr); + return true; + } else + return false; + + } // end of TestHr + +/******************************************************************/ +/* Return a DOMDOC as a XMLDOC. */ +/******************************************************************/ +PXDOC GetDomDoc(PGLOBAL g, char *nsl, char *nsdf, + char *enc, PFBLOCK fp) + { + return (PXDOC) new(g) DOMDOC(nsl, nsdf, enc, fp); + } // end of GetDomDoc + +/***********************************************************************/ +/* Close a loaded DOM XML file. */ +/***********************************************************************/ +void CloseXMLFile(PGLOBAL g, PFBLOCK fp, bool all) + { + PXBLOCK xp = (PXBLOCK)fp; + + if (xp && xp->Count > 1 && !all) { + xp->Count--; + } else if (xp && xp->Count > 0) { + try { + if (xp->Docp) + xp->Docp->Release(); + + } catch(_com_error e) { + char *p = _com_util::ConvertBSTRToString(e.Description()); + snprintf(g->Message, sizeof(g->Message), "%s %s", MSG(COM_ERROR), p); + delete[] p; + } catch(...) {} + + CoUninitialize(); + xp->Count = 0; + } // endif + + } // end of CloseXMLFile + +/* ------------------------ class DOMDOC ------------------------ */ + +/******************************************************************/ +/* DOMDOC constructor. */ +/******************************************************************/ +DOMDOC::DOMDOC(char *nsl, char *nsdf, char *enc, PFBLOCK fp) + : XMLDOCUMENT(nsl, nsdf, enc) + { + assert (!fp || fp->Type == TYPE_FB_XML); + Docp = (fp) ? ((PXBLOCK)fp)->Docp : (MSXML2::IXMLDOMDocumentPtr)NULL; + Nlist = NULL; + Hr = 0; + } // end of DOMDOC constructor + +/******************************************************************/ +/* Initialize XML parser and check library compatibility. */ +/******************************************************************/ +bool DOMDOC::Initialize(PGLOBAL g, PCSZ entry, bool zipped) +{ + if (zipped && InitZip(g, entry)) + return true; + + if (TestHr(g, CoInitialize(NULL))) + return true; + + if (TestHr(g, Docp.CreateInstance("msxml2.domdocument"))) + return true; + + return MakeNSlist(g); +} // end of Initialize + +/******************************************************************/ +/* Parse the XML file and construct node tree in memory. */ +/******************************************************************/ +bool DOMDOC::ParseFile(PGLOBAL g, char *fn) +{ + bool b; + + Docp->async = false; + + if (zip) { + // Parse an in memory document + char *xdoc = GetMemDoc(g, fn); + + // This is not equivalent to load for UTF8 characters + // It is why get node content is not the same + b = (xdoc) ? (bool)Docp->loadXML((_bstr_t)xdoc) : false; + } else + // Load the document + b = (bool)Docp->load((_bstr_t)fn); + + if (!b) + return true; + + return false; +} // end of ParseFile + +/******************************************************************/ +/* Create or reuse an Xblock for this document. */ +/******************************************************************/ +PFBLOCK DOMDOC::LinkXblock(PGLOBAL g, MODE m, int rc, char *fn) + { + PDBUSER dup = (PDBUSER)g->Activityp->Aptr; + PXBLOCK xp = (PXBLOCK)PlugSubAlloc(g, NULL, sizeof(XBLOCK)); + + memset(xp, 0, sizeof(XBLOCK)); + xp->Next = (PXBLOCK)dup->Openlist; + dup->Openlist = (PFBLOCK)xp; + xp->Type = TYPE_FB_XML; + xp->Fname = (LPCSTR)PlugSubAlloc(g, NULL, strlen(fn) + 1); + strcpy((char*)xp->Fname, fn); + xp->Count = 1; + xp->Length = (m == MODE_READ) ? 1 : 0; + xp->Docp = Docp; + xp->Retcode = rc; + + // Return xp as a fp + return (PFBLOCK)xp; + } // end of LinkXblock + +/******************************************************************/ +/* Create the XML node. */ +/******************************************************************/ +bool DOMDOC::NewDoc(PGLOBAL g, PCSZ ver) + { + char buf[64]; + MSXML2::IXMLDOMProcessingInstructionPtr pip; + + sprintf(buf, "version=\"%s\" encoding=\"%s\"", ver, Encoding); + pip = Docp->createProcessingInstruction("xml", buf); + return(TestHr(g, Docp->appendChild(pip))); + } // end of NewDoc + +/******************************************************************/ +/* Add a comment to the document node. */ +/******************************************************************/ +void DOMDOC::AddComment(PGLOBAL g, char *com) + { + TestHr(g, Docp->appendChild(Docp->createComment(com))); + } // end of AddComment + +/******************************************************************/ +/* Return the node class of the root of the document. */ +/******************************************************************/ +PXNODE DOMDOC::GetRoot(PGLOBAL g) + { + MSXML2::IXMLDOMElementPtr root = Docp->documentElement; + + if (root == NULL) + return NULL; + + return new(g) DOMNODE(this, root); + } // end of GetRoot + +/******************************************************************/ +/* Create a new root element and return its class node. */ +/******************************************************************/ +PXNODE DOMDOC::NewRoot(PGLOBAL g, char *name) + { + MSXML2::IXMLDOMElementPtr ep = Docp->createElement(name); + + if (ep == NULL || TestHr(g, Docp->appendChild(ep))) + return NULL; + + return new(g) DOMNODE(this, ep); + } // end of NewRoot + +/******************************************************************/ +/* Return a void DOMNODE node class. */ +/******************************************************************/ +PXNODE DOMDOC::NewPnode(PGLOBAL g, char *name) + { + MSXML2::IXMLDOMElementPtr root = NULL; + + if (name) + if ((root = Docp->createElement(name)) == NULL) + return NULL; + + return new(g) DOMNODE(this, root); + } // end of NewPnode + +/******************************************************************/ +/* Return a void DOMATTR node class. */ +/******************************************************************/ +PXATTR DOMDOC::NewPattr(PGLOBAL g) + { + return new(g) DOMATTR(this, NULL); + } // end of NewPattr + +/******************************************************************/ +/* Return a void DOMATTR node class. */ +/******************************************************************/ +PXLIST DOMDOC::NewPlist(PGLOBAL g) + { + return new(g) DOMNODELIST(this, NULL); + } // end of NewPlist + +/******************************************************************/ +/* Dump the node tree to a new XML file. */ +/******************************************************************/ +int DOMDOC::DumpDoc(PGLOBAL g, char *ofn) + { + int rc = 0; + + try { + Docp->save(ofn); + } catch(_com_error e) { + snprintf(g->Message, sizeof(g->Message), "%s: %s", MSG(COM_ERROR), + _com_util::ConvertBSTRToString(e.Description())); + rc = -1; + } catch(...) {} + + return rc; + } // end of Dump + +/******************************************************************/ +/* Free the document, cleanup the XML library, and */ +/* debug memory for regression tests. */ +/******************************************************************/ +void DOMDOC::CloseDoc(PGLOBAL g, PFBLOCK xp) + { + CloseXMLFile(g, xp, false); + CloseZip(); + } // end of Close + +/* ----------------------- class DOMNODE ------------------------ */ + +/******************************************************************/ +/* DOMNODE constructor. */ +/******************************************************************/ +DOMNODE::DOMNODE(PXDOC dp, MSXML2::IXMLDOMNodePtr np) : XMLNODE(dp) + { + Docp = ((PDOMDOC)dp)->Docp; + Nodep = np; + Ws = NULL; + Len = 0; + Zip = (bool)dp->zip; + } // end of DOMNODE constructor + +/******************************************************************/ +/* Return the node name. */ +/******************************************************************/ +char *DOMNODE::GetName(PGLOBAL g) + { + if (!WideCharToMultiByte(CP_ACP, 0, Nodep->nodeName, -1, + Name, sizeof(Name), NULL, NULL)) { + strcpy(g->Message, MSG(NAME_CONV_ERR)); + return NULL; + } // endif + + return Name; + } // end of GetName + +/******************************************************************/ +/* Return the node class of next sibling of the node. */ +/******************************************************************/ +PXNODE DOMNODE::GetNext(PGLOBAL g) + { + if (Nodep->nextSibling == NULL) + Next = NULL; + else // if (!Next) + Next = new(g) DOMNODE(Doc, Nodep->nextSibling); + + return Next; + } // end of GetNext + +/******************************************************************/ +/* Return the node class of first children of the node. */ +/******************************************************************/ +PXNODE DOMNODE::GetChild(PGLOBAL g) + { + if (Nodep->firstChild == NULL) + Children = NULL; + else // if (!Children) + Children = new(g) DOMNODE(Doc, Nodep->firstChild); + + return Children; + } // end of GetChild + +/******************************************************************/ +/* Return the content of a node and subnodes. */ +/******************************************************************/ +RCODE DOMNODE::GetContent(PGLOBAL g, char *buf, int len) + { + RCODE rc = RC_OK; + + // Nodep can be null for a missing HTML table column + if (Nodep) { + if (Zip) { + strcpy(buf, Nodep->text); + } else if (!WideCharToMultiByte(CP_UTF8, 0, Nodep->text, -1, + buf, len, NULL, NULL)) { + DWORD lsr = GetLastError(); + + switch (lsr) { + case 0: + case ERROR_INSUFFICIENT_BUFFER: // 122L + snprintf(g->Message, sizeof(g->Message), "Truncated %s content", GetName(g)); + rc = RC_INFO; + break; + case ERROR_NO_UNICODE_TRANSLATION: // 1113L + snprintf(g->Message, sizeof(g->Message), "Invalid character(s) in %s content", + GetName(g)); + rc = RC_INFO; + break; + default: + snprintf(g->Message, sizeof(g->Message), "System error getting %s content", + GetName(g)); + rc = RC_FX; + break; + } // endswitch + + } // endif + + } else + *buf = '\0'; + + return rc; + } // end of GetContent + +/******************************************************************/ +/* Set the text content of an attribute. */ +/******************************************************************/ +bool DOMNODE::SetContent(PGLOBAL g, char *txtp, int len) + { + bool rc; + BSTR val; + + if (len > Len || !Ws) { + Ws = (WCHAR*)PlugSubAlloc(g, NULL, (len + 1) * 2); + Len = len; + } // endif len + + if (!MultiByteToWideChar(CP_UTF8, 0, txtp, strlen(txtp) + 1, + Ws, Len + 1)) { + snprintf(g->Message, sizeof(g->Message), MSG(WS_CONV_ERR), txtp); + return true; + } // endif + + val = SysAllocString(Ws); + rc = TestHr(g, Nodep->put_text(val)); + SysFreeString(val); + return rc; + } // end of SetContent + +/******************************************************************/ +/* Return a clone of this node. */ +/******************************************************************/ +PXNODE DOMNODE::Clone(PGLOBAL g, PXNODE np) + { + if (np) { + ((PDOMNODE)np)->Nodep = Nodep; + return np; + } else + return new(g) DOMNODE(Doc, Nodep); + + } // end of Clone + +/******************************************************************/ +/* Return the list of all or matching children that are elements.*/ +/******************************************************************/ +PXLIST DOMNODE::GetChildElements(PGLOBAL g, char *xp, PXLIST lp) + { + MSXML2::IXMLDOMNodeListPtr dnlp; + + if (xp) { + if (Nodep->nodeType == MSXML2::NODE_ELEMENT) { + MSXML2::IXMLDOMElementPtr ep = Nodep; + dnlp = ep->getElementsByTagName(xp); + } else + return NULL; + + } else + dnlp = Nodep->childNodes; + + if (lp) { + ((PDOMLIST)lp)->Listp = dnlp; + return lp; + } else + return new(g) DOMNODELIST(Doc, dnlp); + + } // end of GetChildElements + +/******************************************************************/ +/* Return the list of nodes verifying the passed Xapth. */ +/******************************************************************/ +PXLIST DOMNODE::SelectNodes(PGLOBAL g, char *xp, PXLIST lp) + { + MSXML2::IXMLDOMNodeListPtr dnlp = Nodep->selectNodes(xp); + + if (lp) { + ((PDOMLIST)lp)->Listp = dnlp; + return lp; + } else + return new(g) DOMNODELIST(Doc, dnlp); + + } // end of SelectNodes + +/******************************************************************/ +/* Return the first node verifying the passed Xapth. */ +/******************************************************************/ +PXNODE DOMNODE::SelectSingleNode(PGLOBAL g, char *xp, PXNODE np) + { + try { + MSXML2::IXMLDOMNodePtr dnp = Nodep->selectSingleNode(xp); + + if (dnp) { + if (np) { + ((PDOMNODE)np)->Nodep = dnp; + return np; + } else + return new(g) DOMNODE(Doc, dnp); + + } // endif dnp + + } catch(_com_error e) { + snprintf(g->Message, sizeof(g->Message), "%s: %s", MSG(COM_ERROR), + _com_util::ConvertBSTRToString(e.Description())); + } catch(...) {} + + return NULL; + } // end of SelectSingleNode + +/******************************************************************/ +/* Return the node attribute with the specified name. */ +/******************************************************************/ +PXATTR DOMNODE::GetAttribute(PGLOBAL g, char *name, PXATTR ap) + { + MSXML2::IXMLDOMElementPtr ep; + MSXML2::IXMLDOMNamedNodeMapPtr nmp; + MSXML2::IXMLDOMAttributePtr atp; + + if (name) { + ep = Nodep; + atp = ep->getAttributeNode(name); + nmp = NULL; + } else { + nmp = Nodep->Getattributes(); + atp = nmp->Getitem(0); + } // endif name + + if (atp) { + if (ap) { + ((PDOMATTR)ap)->Atrp = atp; + ((PDOMATTR)ap)->Nmp = nmp; + ((PDOMATTR)ap)->K = 0; + return ap; + } else + return new(g) DOMATTR(Doc, atp, nmp); + + } else + return NULL; + + } // end of GetAttribute + +/******************************************************************/ +/* Add a new element child node to this node and return it. */ +/******************************************************************/ +PXNODE DOMNODE::AddChildNode(PGLOBAL g, PCSZ name, PXNODE np) + { + const char *p, *pn; +// char *p, *pn, *epf, *pf = NULL; + MSXML2::IXMLDOMNodePtr ep; +// _bstr_t uri((wchar_t*)NULL); + +#if 0 + // Is a prefix specified ? + if ((p = strchr(name, ':'))) { + pf = BufAlloc(g, name, p - name); + + // Is it the pseudo default prefix + if (Doc->DefNs && !strcmp(pf, Doc->DefNs)) { + name = p + 1; // Suppress it from name + pf = NULL; // No real prefix + } // endif DefNs + + } // endif p + + // Look for matching namespace URI in context + for (ep = Nodep; ep; ep = ep->parentNode) { + epf = (_bstr_t)ep->prefix; + + if ((!pf && !epf) || (pf && epf && !strcmp(pf, epf))) { + uri = Nodep->namespaceURI; + break; + } // endif + + } // endfor ep + + if ((wchar_t*)uri == NULL) { + if (!pf) + pf = Doc->DefNs; + + // Look for the namespace URI corresponding to this node + if (pf) + for (PNS nsp = Doc->Namespaces; nsp; nsp = nsp->Next) + if (!strcmp(pf, nsp->Prefix)) { + uri = nsp->Uri; + break; + } // endfor nsp + + } // endif pns +#endif // 0 + + // If name has the format m[n] only m is taken as node name + if ((p = strchr(name, '['))) + pn = BufAlloc(g, name, (int)(p - name)); + else + pn = name; + + // Construct the element node with eventual namespace +// ep = Docp->createNode(_variant_t("Element"), pn, uri); + ep = Docp->createElement(pn); + + _bstr_t pfx = ep->prefix; + _bstr_t uri = ep->namespaceURI; + + if (ep == NULL || TestHr(g, Nodep->appendChild(ep))) + return NULL; + + if (np) + ((PDOMNODE)np)->Nodep = ep; + else + np = new(g) DOMNODE(Doc, ep); + + return NewChild(np); + } // end of AddChildNode + +/******************************************************************/ +/* Add a new property to this node and return it. */ +/******************************************************************/ +PXATTR DOMNODE::AddProperty(PGLOBAL g, char *name, PXATTR ap) + { + MSXML2::IXMLDOMAttributePtr atp = Docp->createAttribute(name); + + if (atp) { + MSXML2::IXMLDOMElementPtr ep = Nodep; + ep->setAttributeNode(atp); + + if (ap) { + ((PDOMATTR)ap)->Atrp = atp; + return ap; + } else + return new(g) DOMATTR(Doc, atp); + + } else + return NULL; + + } // end of AddProperty + +/******************************************************************/ +/* Add a new text node to this node. */ +/******************************************************************/ +void DOMNODE::AddText(PGLOBAL g, PCSZ txtp) + { + MSXML2::IXMLDOMTextPtr tp= Docp->createTextNode((_bstr_t)txtp); + + if (tp != NULL) + TestHr(g, Nodep->appendChild(tp)); + + } // end of AddText + +/******************************************************************/ +/* Remove a child node from this node. */ +/******************************************************************/ +void DOMNODE::DeleteChild(PGLOBAL g, PXNODE dnp) + { + TestHr(g, Nodep->removeChild(((PDOMNODE)dnp)->Nodep)); +// ((PDOMNODE)dnp)->Nodep->Release(); bad idea, causes a crash + Delete(dnp); + } // end of DeleteChild + +/* --------------------- class DOMNODELIST ---------------------- */ + +/******************************************************************/ +/* DOMNODELIST constructor. */ +/******************************************************************/ +DOMNODELIST::DOMNODELIST(PXDOC dp, MSXML2::IXMLDOMNodeListPtr lp) + : XMLNODELIST(dp) + { + Listp = lp; + } // end of DOMNODELIST constructor + +/******************************************************************/ +/* Return the nth element of the list. */ +/******************************************************************/ +PXNODE DOMNODELIST::GetItem(PGLOBAL g, int n, PXNODE np) + { + if (Listp == NULL || Listp->length <= n) + return NULL; + + if (np) { + ((PDOMNODE)np)->Nodep = Listp->item[n]; + return np; + } else + return new(g) DOMNODE(Doc, Listp->item[n]); + + } // end of GetItem + +/******************************************************************/ +/* Reset the pointer on the deleted item. */ +/******************************************************************/ +bool DOMNODELIST::DropItem(PGLOBAL g, int n) +{ + if (Listp == NULL || Listp->length < n) + return true; + + return false; +} // end of DeleteItem + +/* ----------------------- class DOMATTR ------------------------ */ + +/******************************************************************/ +/* DOMATTR constructor. */ +/******************************************************************/ +DOMATTR::DOMATTR(PXDOC dp, MSXML2::IXMLDOMAttributePtr ap, + MSXML2::IXMLDOMNamedNodeMapPtr nmp) + : XMLATTRIBUTE(dp) + { + Atrp = ap; + Nmp = nmp; + Ws = NULL; + Len = 0; + K = 0; + } // end of DOMATTR constructor + +/******************************************************************/ +/* Return the attribute name. */ +/******************************************************************/ +char *DOMATTR::GetName(PGLOBAL g) + { + if (!WideCharToMultiByte(CP_ACP, 0, Atrp->nodeName, -1, + Name, sizeof(Name), NULL, NULL)) { + strcpy(g->Message, MSG(NAME_CONV_ERR)); + return NULL; + } // endif + + return Name; + } // end of GetName + +/******************************************************************/ +/* Return the next attribute node. */ +/* This funtion is implemented as needed by XMLColumns. */ +/******************************************************************/ +PXATTR DOMATTR::GetNext(PGLOBAL g) + { + if (!Nmp) + return NULL; + + if (++K >= Nmp->Getlength()) { + Nmp->reset(); + Nmp = NULL; + K = 0; + return NULL; + } // endif K + + Atrp = Nmp->Getitem(K); + return this; + } // end of GetNext + +/******************************************************************/ +/* Return the content of a node and subnodes. */ +/******************************************************************/ +RCODE DOMATTR::GetText(PGLOBAL g, char *buf, int len) + { + RCODE rc = RC_OK; + + if (!WideCharToMultiByte(CP_UTF8, 0, Atrp->text, -1, + buf, len, NULL, NULL)) { + DWORD lsr = GetLastError(); + + switch (lsr) { + case 0: + case ERROR_INSUFFICIENT_BUFFER: // 122L + snprintf(g->Message, sizeof(g->Message), "Truncated %s content", GetName(g)); + rc = RC_INFO; + break; + case ERROR_NO_UNICODE_TRANSLATION: // 1113L + snprintf(g->Message, sizeof(g->Message), "Invalid character(s) in %s content", + GetName(g)); + rc = RC_INFO; + break; + default: + snprintf(g->Message, sizeof(g->Message), "System error getting %s content", + GetName(g)); + rc = RC_FX; + break; + } // endswitch + + } // endif + + return rc; + } // end of GetText + +/******************************************************************/ +/* Set the text content of an attribute. */ +/******************************************************************/ +bool DOMATTR::SetText(PGLOBAL g, char *txtp, int len) + { + bool rc; + BSTR val; + + if (len > Len || !Ws) { + Ws = (WCHAR*)PlugSubAlloc(g, NULL, (len + 1) * 2); + Len = len; + } // endif len + + if (!MultiByteToWideChar(CP_UTF8, 0, txtp, strlen(txtp) + 1, + Ws, Len + 1)) { + snprintf(g->Message, sizeof(g->Message), MSG(WS_CONV_ERR), txtp); + return true; + } // endif + + val = SysAllocString(Ws); + rc = TestHr(g, Atrp->put_text(val)); + SysFreeString(val); + return rc; + } // end of SetText |