/* * Copyright 1999-2001,2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * $Id: XMLDOMUtil.cpp 176383 2005-04-22 08:27:51Z amassari $ */ #include "stdafx.h" #include "xml4com.h" #include "XMLDOMElement.h" #include "XMLDOMAttribute.h" #include "XMLDOMText.h" #include "XMLDOMCDATASection.h" #include "XMLDOMEntityReference.h" #include "XMLDOMEntity.h" #include "XMLDOMProcessingInstruction.h" #include "XMLDOMComment.h" #include "XMLDOMDocument.h" #include "XMLDOMDocumentType.h" #include "XMLDOMDocumentFragment.h" #include "XMLDOMNotation.h" #include "XMLDOMUtil.h" #include #include #include const OLECHAR* g_DomNodeName[] = { OLESTR("invalid"), OLESTR("element"), OLESTR("attribute"), OLESTR("text"), OLESTR("cdatasection"), OLESTR("entityreference"), OLESTR("entity"), OLESTR("processinginstruction"), OLESTR("comment"), OLESTR("document"), OLESTR("documenttype"), OLESTR("documentfragment"), OLESTR("notation") }; const int g_DomNodeNameSize = sizeof(g_DomNodeName) / sizeof(OLECHAR*); template class CComObjectPool { public: CComObjectPool(unsigned long poolSize); virtual ~CComObjectPool(); HRESULT WINAPI CreateInstance(Base** pp); HRESULT Deactivate(Base* obj); private: Base** m_pool; unsigned long m_size; unsigned long m_hit; unsigned long m_attempt; HRESULT Activate(Base* obj); }; template class CPooledComObject : public Base { public: typedef Base _BaseClass; CPooledComObject(void* = NULL) { _Module.Lock(); } // Set refcount to 1 to protect destruction ~CPooledComObject() { m_dwRef = 1L; FinalRelease(); #ifdef _ATL_DEBUG_INTERFACES _Module.DeleteNonAddRefThunk(_GetRawUnknown()); #endif _Module.Unlock(); } //If InternalAddRef or InternalRelease is undefined then your class //doesn't derive from CComObjectRoot STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();} STDMETHOD_(ULONG, Release)() { ULONG l = InternalRelease(); if (l == 0) { if(SUCCEEDED(m_pool.Deactivate(this))) { FinalRelease(); } else delete this; } return l; } //if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) {return _InternalQueryInterface(iid, ppvObject);} template HRESULT STDMETHODCALLTYPE QueryInterface(Q** pp) { return QueryInterface(__uuidof(Q), (void**)pp); } static HRESULT WINAPI CreateInstance(Base** pp) { return m_pool.CreateInstance(pp); } private: static CComObjectPool m_pool; }; template CComObjectPool::CComObjectPool(unsigned long poolSize) { m_pool = NULL; m_size = poolSize; m_pool = new Base*[m_size]; for(unsigned long i = 0; i < m_size; i++) m_pool[i] = NULL; m_hit= 0; m_attempt = 0; } template CComObjectPool::~CComObjectPool() { for(unsigned long i = 0; i < m_size; i++) { if(m_pool[i]) delete m_pool[i]; } delete [] m_pool; } template HRESULT CComObjectPool::Deactivate(Base* obj) { for(unsigned long i = 0; i < m_size; i++) { if(m_pool[i] == NULL) { m_pool[i] = obj; return S_OK; } } return E_FAIL; } template HRESULT CComObjectPool::Activate(Base* p) { p->SetVoid(NULL); p->InternalFinalConstructAddRef(); HRESULT hRes = p->FinalConstruct(); p->InternalFinalConstructRelease(); return hRes; } template HRESULT WINAPI CComObjectPool::CreateInstance(Base** pp) { ATLASSERT(pp != NULL); HRESULT hRes = E_OUTOFMEMORY; Base* p = NULL; m_attempt++; for(unsigned long i = 0; i < m_size; i++) { if(m_pool[i]) { p = m_pool[i]; m_pool[i] = NULL; hRes = Activate(p); if (SUCCEEDED(hRes)) { m_hit++; break; } else { delete p; p = NULL; } } } if(FAILED(hRes)) { ATLTRY(p = new CPooledComObject()) if (p != NULL) { hRes = Activate(p); if (hRes != S_OK) { delete p; p = NULL; } } } *pp = p; return hRes; } CComObjectPool CPooledComObject::m_pool(6); typedef CPooledComObject CPooledXMLDOMElementObj; CComObjectPool CPooledComObject::m_pool(6); typedef CPooledComObject CPooledXMLDOMAttributeObj; CComObjectPool CPooledComObject::m_pool(6); typedef CPooledComObject CPooledXMLDOMTextObj; CComObjectPool CPooledComObject::m_pool(6); typedef CPooledComObject CPooledXMLDOMCDATASectionObj; CComObjectPool CPooledComObject::m_pool(6); typedef CPooledComObject CPooledXMLDOMEntityReferenceObj; CComObjectPool CPooledComObject::m_pool(6); typedef CPooledComObject CPooledXMLDOMEntityObj; CComObjectPool CPooledComObject::m_pool(6); typedef CPooledComObject CPooledXMLDOMProcessingInstructionObj; CComObjectPool CPooledComObject::m_pool(6); typedef CPooledComObject CPooledXMLDOMCommentObj; HRESULT wrapNode(IXMLDOMDocument *pDoc, DOMNode* node, REFIID iid, LPVOID *pVal) { HRESULT hr = S_OK; if (NULL == pVal) return E_POINTER; *pVal = NULL; short type = node->getNodeType(); // the way we are constructing the wrappers is kind of fishy but oh well... // the various IBM DOM wrapper classes don't ever add any members or have // any v-tables so what we are doing should be safe. There isn't any other // way as far as I can tell to do this.... switch(type) { case DOMNode::ELEMENT_NODE: { CXMLDOMElement *pObj = NULL; hr = CPooledXMLDOMElementObj::CreateInstance(&pObj); if (S_OK != hr) return hr; pObj->AddRef(); pObj->SetOwnerDoc(pDoc); try { pObj->element = static_cast (node); } catch(DOMException& ex) { pObj->Release(); return MakeHRESULT(ex); } catch(...) { pObj->Release(); return E_FAIL; } hr = pObj->QueryInterface(iid, pVal); if (S_OK != hr) *pVal = NULL; pObj->Release(); break; } case DOMNode::ATTRIBUTE_NODE: { CXMLDOMAttribute *pObj = NULL; hr = CPooledXMLDOMAttributeObj::CreateInstance(&pObj); if (S_OK != hr) return hr; pObj->AddRef(); pObj->SetOwnerDoc(pDoc); try { pObj->attr = static_cast (node); } catch(DOMException& ex) { pObj->Release(); return MakeHRESULT(ex); } catch(...) { pObj->Release(); return E_FAIL; } hr = pObj->QueryInterface(iid, pVal); if (S_OK != hr) *pVal = NULL; pObj->Release(); break; } case DOMNode::TEXT_NODE: { CXMLDOMText *pObj = NULL; hr = CPooledXMLDOMTextObj::CreateInstance(&pObj); if (S_OK != hr) return hr; pObj->AddRef(); pObj->SetOwnerDoc(pDoc); try { pObj->text = static_cast (node); } catch(DOMException& ex) { pObj->Release(); return MakeHRESULT(ex); } catch(...) { pObj->Release(); return E_FAIL; } hr = pObj->QueryInterface(iid, pVal); if (S_OK != hr) *pVal = NULL; pObj->Release(); break; } case DOMNode::CDATA_SECTION_NODE: { CXMLDOMCDATASection *pObj = NULL; hr = CPooledXMLDOMCDATASectionObj::CreateInstance(&pObj); if (S_OK != hr) return hr; pObj->AddRef(); pObj->SetOwnerDoc(pDoc); try { pObj->cdataSection = static_cast (node); } catch(DOMException& ex) { pObj->Release(); return MakeHRESULT(ex); } catch(...) { pObj->Release(); return E_FAIL; } hr = pObj->QueryInterface(iid, pVal); if (S_OK != hr) *pVal = NULL; pObj->Release(); break; } case DOMNode::ENTITY_REFERENCE_NODE: { CXMLDOMEntityReference *pObj = NULL; hr = CPooledXMLDOMEntityReferenceObj::CreateInstance(&pObj); if (S_OK != hr) return hr; pObj->AddRef(); pObj->SetOwnerDoc(pDoc); try { pObj->entityReference = static_cast (node); } catch(DOMException& ex) { pObj->Release(); return MakeHRESULT(ex); } catch(...) { pObj->Release(); return E_FAIL; } hr = pObj->QueryInterface(iid, pVal); if (S_OK != hr) *pVal = NULL; pObj->Release(); break; } case DOMNode::ENTITY_NODE: { CXMLDOMEntity *pObj = NULL; hr = CPooledXMLDOMEntityObj::CreateInstance(&pObj); if (S_OK != hr) return hr; pObj->AddRef(); pObj->SetOwnerDoc(pDoc); try { pObj->entity = static_cast (node); } catch(DOMException& ex) { pObj->Release(); return MakeHRESULT(ex); } catch(...) { pObj->Release(); return E_FAIL; } hr = pObj->QueryInterface(iid, pVal); if (S_OK != hr) *pVal = NULL; pObj->Release(); break; } case DOMNode::PROCESSING_INSTRUCTION_NODE: { CXMLDOMProcessingInstruction *pObj = NULL; hr = CPooledXMLDOMProcessingInstructionObj::CreateInstance(&pObj); if (S_OK != hr) return hr; pObj->AddRef(); pObj->SetOwnerDoc(pDoc); try { pObj->processingInstruction = static_cast (node); } catch(DOMException& ex) { pObj->Release(); return MakeHRESULT(ex); } catch(...) { pObj->Release(); return E_FAIL; } hr = pObj->QueryInterface(iid, pVal); if (S_OK != hr) *pVal = NULL; pObj->Release(); break; } case DOMNode::COMMENT_NODE: { CXMLDOMComment *pObj = NULL; hr = CPooledXMLDOMCommentObj::CreateInstance(&pObj); if (S_OK != hr) return hr; pObj->AddRef(); pObj->SetOwnerDoc(pDoc); try { pObj->comment = static_cast (node); } catch(DOMException& ex) { pObj->Release(); return MakeHRESULT(ex); } catch(...) { pObj->Release(); return E_FAIL; } hr = pObj->QueryInterface(iid, pVal); if (S_OK != hr) *pVal = NULL; pObj->Release(); break; } case DOMNode::DOCUMENT_NODE: { CXMLDOMDocumentObj *pObj = NULL; hr = CXMLDOMDocumentObj::CreateInstance(&pObj); if (S_OK != hr) return hr; pObj->AddRef(); pObj->SetOwnerDoc(pDoc); try { pObj->m_Document = static_cast (node); } catch(DOMException& ex) { pObj->Release(); return MakeHRESULT(ex); } catch(...) { pObj->Release(); return E_FAIL; } hr = pObj->QueryInterface(iid, pVal); if (S_OK != hr) *pVal = NULL; pObj->Release(); break; } case DOMNode::DOCUMENT_TYPE_NODE: { CXMLDOMDocumentTypeObj *pObj = NULL; hr = CXMLDOMDocumentTypeObj::CreateInstance(&pObj); if (S_OK != hr) return hr; pObj->AddRef(); pObj->SetOwnerDoc(pDoc); try { pObj->documentType = static_cast (node); } catch(DOMException& ex) { pObj->Release(); return MakeHRESULT(ex); } catch(...) { pObj->Release(); return E_FAIL; } hr = pObj->QueryInterface(iid, pVal); if (S_OK != hr) *pVal = NULL; pObj->Release(); break; } case DOMNode::DOCUMENT_FRAGMENT_NODE: { CXMLDOMDocumentFragmentObj *pObj = NULL; hr = CXMLDOMDocumentFragmentObj::CreateInstance(&pObj); if (S_OK != hr) return hr; pObj->AddRef(); pObj->SetOwnerDoc(pDoc); try { pObj->documentFragment = static_cast (node); } catch(DOMException& ex) { pObj->Release(); return MakeHRESULT(ex); } catch(...) { pObj->Release(); return E_FAIL; } hr = pObj->QueryInterface(iid, pVal); if (S_OK != hr) *pVal = NULL; pObj->Release(); break; } case DOMNode::NOTATION_NODE: { CXMLDOMNotationObj *pObj = NULL; hr = CXMLDOMNotationObj::CreateInstance(&pObj); if (S_OK != hr) return hr; pObj->AddRef(); pObj->SetOwnerDoc(pDoc); try { pObj->notation = static_cast (node); } catch(DOMException& ex) { pObj->Release(); return MakeHRESULT(ex); } catch(...) { pObj->Release(); return E_FAIL; } hr = pObj->QueryInterface(iid, pVal); if (S_OK != hr) *pVal = NULL; pObj->Release(); break; } default: hr = E_NOTIMPL; break; } return hr; } class xmlstream { public: xmlstream() { m_length = 0; m_alloc = 0; m_buffer = 0; m_next = 0; } ~xmlstream() { delete [] m_buffer; } xmlstream& operator<<(const XMLCh* other) { // // get length of string // unsigned long len = 0; for(const XMLCh* source = other; *source; source++,len++); // // append to stream // append(other,len); return *this; } xmlstream& operator<<(const XMLCh other) { append(&other,1); return *this; } BSTR SysAllocString() { if(m_length > 0) return SysAllocStringLen(m_buffer,m_length); return 0; } private: void append(const XMLCh* other,unsigned long length) { const XMLCh* source = NULL; if(m_length + length > m_alloc) { unsigned long chunk = 4096; if(length > chunk) chunk += length; XMLCh* newbuf = new XMLCh[m_alloc+ chunk]; m_alloc += chunk; m_next = newbuf + m_length; // // copy old content into new buffer // XMLCh* dest = newbuf; source = m_buffer; for(unsigned long i = 0; i < m_length; i++,dest++,source++) { *dest = *source; } delete [] m_buffer; m_buffer = newbuf; } source = other; for(unsigned long i = 0; i < length; i++,source++,m_next++) { *m_next = *source; } m_length += length; } unsigned long m_length; unsigned long m_alloc; XMLCh* m_buffer; XMLCh* m_next; }; // --------------------------------------------------------------------------- // outputContent // // Write document content from a string to a C++ ostream. Escape the // XML special characters (<, &, etc.) unless this is suppressed by the // command line option. // --------------------------------------------------------------------------- void outputContent(xmlstream& target, const XMLCh* toWrite) { int length = XMLString::stringLen(toWrite); int index; for (index = 0; index < length; index++) { switch (toWrite[index]) { case chAmpersand : target << XMLStrL("&"); break; case chOpenAngle : target << XMLStrL("<"); break; case chCloseAngle: target << XMLStrL(">"); break; case chDoubleQuote : target << XMLStrL("""); break; default: // If it is none of the special characters, print it as such target << toWrite[index]; break; } } return; } xmlstream& operator<<(xmlstream& target, const DOMNode* toWrite) { // Get the name and value out for convenience const XMLCh* nodeName = toWrite->getNodeName(); const XMLCh* nodeValue = toWrite->getNodeValue(); switch (toWrite->getNodeType()) { case DOMNode::TEXT_NODE: { outputContent(target, nodeValue); break; } case DOMNode::PROCESSING_INSTRUCTION_NODE : { target << XMLStrL(""); break; } case DOMNode::DOCUMENT_NODE : { // // Bug here: we need to find a way to get the encoding name // for the default code page on the system where the program // is running, and plug that in for the encoding name. // XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* document=(XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument*)toWrite; target << XMLStrL("getVersion() << XMLStrL("\""); const XMLCh* str = document->getEncoding(); if (str != 0) target << XMLStrL(" encoding=\"") << str << XMLStrL("\""); if(document->getStandalone()) target << XMLStrL(" standalone=\"yes\""); target << XMLStrL("?>"); DOMNode* child = toWrite->getFirstChild(); while( child != 0) { target << child; child = child->getNextSibling(); } break; } case DOMNode::ELEMENT_NODE : { // Output the element start tag. target << XMLStrL('<') << nodeName; // Output any attributes on this element DOMNamedNodeMap* attributes = toWrite->getAttributes(); int attrCount = attributes->getLength(); for (int i = 0; i < attrCount; i++) { DOMNode* attribute = attributes->item(i); target << XMLStrL(' ') << attribute->getNodeName() << XMLStrL(" = \""); // Note that "<" must be escaped in attribute values. outputContent(target, attribute->getNodeValue()); target << XMLStrL('"'); } // // Test for the presence of children, which includes both // text content and nested elements. // DOMNode* child = toWrite->getFirstChild(); if (child != 0) { // There are children. Close start-tag, and output children. target << XMLStrL(">"); while( child != 0) { target << child; child = child->getNextSibling(); } // Done with children. Output the end tag. target << XMLStrL(""); } else { // // There were no children. Output the short form close of // the element start tag, making it an empty-element tag. // target << XMLStrL("/>"); } break; } case DOMNode::ENTITY_REFERENCE_NODE: { DOMNode* child; for (child = toWrite->getFirstChild(); child != 0; child = child->getNextSibling()) target << child; break; } case DOMNode::CDATA_SECTION_NODE: { target << XMLStrL(""); break; } case DOMNode::COMMENT_NODE: { target << XMLStrL(""); break; } case DOMNode::DOCUMENT_TYPE_NODE: { DOMDocumentType* doctype = (DOMDocumentType*)toWrite;; target << XMLStrL("getPublicId(); if (id != 0) target << XMLStrL(" PUBLIC \"") << id << XMLStrL("\""); id = doctype->getSystemId(); if (id != 0) target << XMLStrL(" SYSTEM \"") << id << XMLStrL("\""); id = doctype->getInternalSubset(); if (id !=0) target << XMLStrL(" [ ") << id << XMLStrL("]"); target << XMLStrL(">"); break; } case DOMNode::ENTITY_NODE: { DOMEntity* entity = (DOMEntity*)toWrite;; target << XMLStrL("getPublicId(); if (id != 0) target << XMLStrL("PUBLIC \"") << id << XMLStrL("\""); id = entity->getSystemId(); if (id != 0) target << XMLStrL("SYSTEM \"") << id << XMLStrL("\""); id = entity->getNotationName(); if (id != 0) target << XMLStrL("NDATA \"") << id << XMLStrL("\""); break; } default: target << XMLStrL(""); } return target; } void GetXML(const DOMNode* node, _bstr_t &text) { xmlstream stream; stream << node; text = _bstr_t(stream.SysAllocString(),false); }