/* * pxml.cxx * * XML parser support * * Portable Windows Library * * Copyright (c) 2002 Equivalence Pty. Ltd. * * The contents of this file are subject to the Mozilla Public License * Version 1.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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Portable Windows Library. * * The Initial Developer of the Original Code is Equivalence Pty. Ltd. * * Contributor(s): ______________________________________. * * $Log: pxml.cxx,v $ * Revision 1.43 2005/11/30 12:47:41 csoutheren * Removed tabs, reformatted some code, and changed tags for Doxygen * * Revision 1.42 2005/05/12 05:30:16 csoutheren * Ensured error location is initialised * * Revision 1.41 2004/10/23 10:58:15 ykiryanov * Added ifdef _WIN32_WCE for PocketPC 2003 SDK port * * Revision 1.40 2004/10/12 23:28:08 csoutheren * Fixed problem with bogus DOCTYPE being output * * Revision 1.39 2004/04/21 00:35:02 csoutheren * Added a stream parser for protocols like XMPP where each child of the root is to be considered a separate document/message. * Thanks to Federico Pinna and Reitek S.p.A. * * Revision 1.38 2004/04/09 06:52:17 rjongbloed * Removed #pargma linker command for /delayload of DLL as documentations sais that * you cannot do this. * * Revision 1.37 2004/02/23 23:52:20 csoutheren * Added pragmas to avoid every Windows application needing to include libs explicitly * * Revision 1.36 2004/01/17 18:12:59 csoutheren * Changed to use PString::MakeEmpty * * Revision 1.35 2003/05/14 02:50:53 rjongbloed * Simplified name space initialisation * * Revision 1.34 2003/05/06 07:55:25 craigs * Fixed problem with initialising XML parser for namespaces * * Revision 1.33 2003/04/27 23:53:30 craigs * Removed deprecated options * * Revision 1.32 2003/04/16 08:00:19 robertj * Windoes psuedo autoconf support * * Revision 1.31 2003/04/08 12:47:07 craigs * Fixed problem with handling of CDATA * * Revision 1.30 2003/04/08 05:08:41 craigs * Fixed problems with additional spaces being included with metasequences * * Revision 1.29 2003/04/02 09:13:55 rogerh * Add type casts because the variable 'expat' is now a void * * * Revision 1.28 2003/03/31 06:20:56 craigs * Split the expat wrapper from the XML file handling to allow reuse of the parser * * Revision 1.27 2003/01/13 02:14:02 robertj * Improved error logging for auto-loaded XML * * Revision 1.26 2002/12/16 06:38:59 robertj * Added ability to specify certain elemets (by name) that are exempt from * the indent formatting. Useful for XML/RPC where leading white space is * not ignored by all servers. * * Revision 1.25 2002/12/10 04:41:16 robertj * Added test for URL being empty, don't try and run auto load in background. * * Revision 1.24 2002/11/26 05:53:45 craigs * Added ability to auto-reload from URL * * Revision 1.23 2002/11/21 08:08:52 craigs * Changed to not overwrite XML data if load fails * * Revision 1.22 2002/11/19 07:37:25 craigs * Added locking functions and LoadURL function * * Revision 1.21 2002/11/06 22:47:25 robertj * Fixed header comment (copyright etc) * */ // This depends on the expat XML library by Jim Clark // See http://www.jclark.com/xml/expat.html for more information #include #ifdef __GNUC__ #pragma implementation "pxml.h" #endif #include #if P_EXPAT #include #define CACHE_BUFFER_SIZE 1024 #define XMLSETTINGS_OPTIONS (NewLineAfterElement) #ifdef _MSC_VER #ifndef _WIN32_WCE #pragma comment(lib, P_EXPAT_LIBRARY) #endif // !_WIN32_WCE #endif //////////////////////////////////////////////////// static void PXML_StartElement(void * userData, const char * name, const char ** attrs) { ((PXMLParser *)userData)->StartElement(name, attrs); } static void PXML_EndElement(void * userData, const char * name) { ((PXMLParser *)userData)->EndElement(name); } static void PXML_CharacterDataHandler(void * userData, const char * data, int len) { ((PXMLParser *)userData)->AddCharacterData(data, len); } static void PXML_XmlDeclHandler(void * userData, const char * version, const char * encoding, int standalone) { ((PXMLParser *)userData)->XmlDecl(version, encoding, standalone); } static void PXML_StartDocTypeDecl(void * userData, const char * docTypeName, const char * sysid, const char * pubid, int hasInternalSubSet) { ((PXMLParser *)userData)->StartDocTypeDecl(docTypeName, sysid, pubid, hasInternalSubSet); } static void PXML_EndDocTypeDecl(void * userData) { ((PXMLParser *)userData)->EndDocTypeDecl(); } static void PXML_StartNamespaceDeclHandler(void *userData, const XML_Char *prefix, const XML_Char *uri) { ((PXMLParser *)userData)->StartNamespaceDeclHandler(prefix, uri); } static void PXML_EndNamespaceDeclHandler(void *userData, const XML_Char *prefix) { ((PXMLParser *)userData)->EndNamespaceDeclHandler(prefix); } PXMLParser::PXMLParser(int _options) : options(_options) { if (options < 0) options = 0; if ((options & WithNS) != 0) expat = XML_ParserCreateNS(NULL, '|'); else expat = XML_ParserCreate(NULL); XML_SetUserData((XML_Parser)expat, this); XML_SetElementHandler ((XML_Parser)expat, PXML_StartElement, PXML_EndElement); XML_SetCharacterDataHandler((XML_Parser)expat, PXML_CharacterDataHandler); XML_SetXmlDeclHandler ((XML_Parser)expat, PXML_XmlDeclHandler); XML_SetDoctypeDeclHandler ((XML_Parser)expat, PXML_StartDocTypeDecl, PXML_EndDocTypeDecl); XML_SetNamespaceDeclHandler((XML_Parser)expat, PXML_StartNamespaceDeclHandler, PXML_EndNamespaceDeclHandler); rootElement = NULL; currentElement = NULL; lastElement = NULL; } PXMLParser::~PXMLParser() { XML_ParserFree((XML_Parser)expat); } PXMLElement * PXMLParser::GetXMLTree() const { return rootElement; } PXMLElement * PXMLParser::SetXMLTree(PXMLElement * newRoot) { PXMLElement * oldRoot = rootElement; rootElement = newRoot; return oldRoot; } BOOL PXMLParser::Parse(const char * data, int dataLen, BOOL final) { return XML_Parse((XML_Parser)expat, data, dataLen, final) != 0; } void PXMLParser::GetErrorInfo(PString & errorString, PINDEX & errorCol, PINDEX & errorLine) { XML_Error err = XML_GetErrorCode((XML_Parser)expat); errorString = PString(XML_ErrorString(err)); errorCol = XML_GetCurrentColumnNumber((XML_Parser)expat); errorLine = XML_GetCurrentLineNumber((XML_Parser)expat); } void PXMLParser::StartElement(const char * name, const char **attrs) { PXMLElement * newElement = new PXMLElement(currentElement, name); if (currentElement != NULL) currentElement->AddSubObject(newElement, FALSE); while (attrs[0] != NULL) { newElement->SetAttribute(PString(attrs[0]), PString(attrs[1])); attrs += 2; } currentElement = newElement; lastElement = NULL; if (rootElement == NULL) rootElement = currentElement; } void PXMLParser::EndElement(const char * /*name*/) { currentElement = currentElement->GetParent(); lastElement = NULL; } void PXMLParser::AddCharacterData(const char * data, int len) { PString str(data, len); if (lastElement != NULL) { PAssert(!lastElement->IsElement(), "lastElement set by non-data element"); lastElement->SetString(lastElement->GetString() + str, FALSE); } else { PXMLData * newElement = new PXMLData(currentElement, str); if (currentElement != NULL) currentElement->AddSubObject(newElement, FALSE); lastElement = newElement; } } void PXMLParser::XmlDecl(const char * _version, const char * _encoding, int _standAlone) { version = _version; encoding = _encoding; standAlone = _standAlone; } void PXMLParser::StartDocTypeDecl(const char * /*docTypeName*/, const char * /*sysid*/, const char * /*pubid*/, int /*hasInternalSubSet*/) { } void PXMLParser::EndDocTypeDecl() { } void PXMLParser::StartNamespaceDeclHandler(const XML_Char * /*prefix*/, const XML_Char * /*uri*/) { } void PXMLParser::EndNamespaceDeclHandler(const XML_Char * /*prefix*/) { } /////////////////////////////////////////////////////////////////////////////////////////////// PXML::PXML(int options, const char * noIndentElements) : PXMLBase(options) { Construct(options, noIndentElements); } PXML::PXML(const PString & data, int options, const char * noIndentElements) : PXMLBase(options) { Construct(options, noIndentElements); Load(data); } PXML::~PXML() { autoLoadTimer.Stop(); RemoveAll(); } PXML::PXML(const PXML & xml) : noIndentElements(xml.noIndentElements) { Construct(xml.options, NULL); loadFromFile = xml.loadFromFile; loadFilename = xml.loadFilename; version = xml.version; encoding = xml.encoding; standAlone = xml.standAlone; PWaitAndSignal m(xml.rootMutex); PXMLElement * oldRootElement = xml.rootElement; if (oldRootElement != NULL) rootElement = (PXMLElement *)oldRootElement->Clone(NULL); } void PXML::Construct(int _options, const char * _noIndentElements) { rootElement = NULL; options = _options > 0 ? _options : 0; loadFromFile = FALSE; standAlone = -2; errorCol = 0; errorLine = 0; if (_noIndentElements != NULL) noIndentElements = PString(_noIndentElements).Tokenise(' ', FALSE); } PXMLElement * PXML::SetRootElement(const PString & documentType) { PWaitAndSignal m(rootMutex); if (rootElement != NULL) delete rootElement; rootElement = new PXMLElement(rootElement, documentType); return rootElement; } PXMLElement * PXML::SetRootElement(PXMLElement * element) { PWaitAndSignal m(rootMutex); if (rootElement != NULL) delete rootElement; rootElement = element; return rootElement; } BOOL PXML::IsDirty() const { PWaitAndSignal m(rootMutex); if (rootElement == NULL) return FALSE; return rootElement->IsDirty(); } PCaselessString PXML::GetDocumentType() const { PWaitAndSignal m(rootMutex); if (rootElement == NULL) return PCaselessString(); return rootElement->GetName(); } BOOL PXML::LoadFile(const PFilePath & fn, int _options) { PTRACE(4, "XML\tLoading file " << fn); PWaitAndSignal m(rootMutex); if (_options >= 0) options = _options; loadFilename = fn; loadFromFile = TRUE; PFile file; if (!file.Open(fn, PFile::ReadOnly)) { errorString = "File open error" & file.GetErrorText(); return FALSE; } off_t len = file.GetLength(); PString data; if (!file.Read(data.GetPointer(len + 1), len)) { errorString = "File read error" & file.GetErrorText(); return FALSE; } data[(PINDEX)len] = '\0'; return Load(data); } BOOL PXML::LoadURL(const PURL & url) { return LoadURL(url, PMaxTimeInterval, -1); } BOOL PXML::LoadURL(const PURL & url, const PTimeInterval & timeout, int _options) { if (url.IsEmpty()) { errorString = "Cannot load empty URL"; errorCol = errorLine = 0; return FALSE; } PTRACE(4, "XML\tLoading URL " << url); PString data; if (url.GetScheme() == "file") return LoadFile(url.AsFilePath()); PHTTPClient client; PINDEX contentLength; PMIMEInfo outMIME, replyMIME; // make sure we do not hang around for ever client.SetReadTimeout(timeout); // get the resource header information if (!client.GetDocument(url, outMIME, replyMIME)) { errorString = PString("Cannot load URL") & url.AsString(); errorCol = errorLine = 0; return FALSE; } // get the length of the data if (!replyMIME.Contains(PHTTPClient::ContentLengthTag)) contentLength = (PINDEX)replyMIME[PHTTPClient::ContentLengthTag].AsUnsigned(); else contentLength = P_MAX_INDEX; // download the resource into memory PINDEX offs = 0; for (;;) { PINDEX len; if (contentLength == P_MAX_INDEX) len = CACHE_BUFFER_SIZE; else if (offs == contentLength) break; else len = PMIN(contentLength = offs, CACHE_BUFFER_SIZE); if (!client.Read(offs + data.GetPointer(offs + len), len)) break; len = client.GetLastReadCount(); offs += len; } return Load(data, _options); } BOOL PXML::StartAutoReloadURL(const PURL & url, const PTimeInterval & timeout, const PTimeInterval & refreshTime, int _options) { if (url.IsEmpty()) { autoLoadError = "Cannot auto-load empty URL"; return FALSE; } PWaitAndSignal m(autoLoadMutex); autoLoadTimer.Stop(); SetOptions(_options); autoloadURL = url; autoLoadWaitTime = timeout; autoLoadError.MakeEmpty(); autoLoadTimer.SetNotifier(PCREATE_NOTIFIER(AutoReloadTimeout)); BOOL stat = AutoLoadURL(); autoLoadTimer = refreshTime; return stat; } void PXML::AutoReloadTimeout(PTimer &, INT) { PThread::Create(PCREATE_NOTIFIER(AutoReloadThread), PThread::AutoDeleteThread); } void PXML::AutoReloadThread(PThread &, INT) { PWaitAndSignal m(autoLoadMutex); OnAutoLoad(AutoLoadURL()); autoLoadTimer.Reset(); } void PXML::OnAutoLoad(BOOL ok) { PTRACE_IF(3, !ok, "XML\tFailed to load XML: " << GetErrorString()); } BOOL PXML::AutoLoadURL() { BOOL stat = LoadURL(autoloadURL, autoLoadWaitTime); if (stat) autoLoadError.MakeEmpty(); else autoLoadError = GetErrorString() + psprintf(" at line %i, column %i", GetErrorLine(), GetErrorColumn()); return stat; } BOOL PXML::StopAutoReloadURL() { PWaitAndSignal m(autoLoadMutex); autoLoadTimer.Stop(); return TRUE; } BOOL PXML::Load(const PString & data, int _options) { if (_options >= 0) options = _options; BOOL stat = FALSE; PXMLElement * loadingRootElement = NULL; { PXMLParser parser(options); int done = 1; stat = parser.Parse(data, data.GetLength(), done) != 0; if (!stat) parser.GetErrorInfo(errorString, errorCol, errorLine); version = parser.GetVersion(); encoding = parser.GetEncoding(); standAlone = parser.GetStandAlone(); loadingRootElement = parser.GetXMLTree(); } if (stat) { if (loadingRootElement == NULL) { errorString = "XML\tFailed to create root node in XML!"; return FALSE; } else { PWaitAndSignal m(rootMutex); if (rootElement != NULL) { delete rootElement; rootElement = NULL; } rootElement = loadingRootElement; PTRACE(4, "XML\tLoaded XML " << rootElement->GetName()); } OnLoaded(); } return stat; } BOOL PXML::Save(int _options) { if (_options >= 0) options = _options; if (!loadFromFile || !IsDirty()) return FALSE; return SaveFile(loadFilename); } BOOL PXML::SaveFile(const PFilePath & fn, int _options) { PWaitAndSignal m(rootMutex); PFile file; if (!file.Open(fn, PFile::WriteOnly)) return FALSE; PString data; if (!Save(data, _options)) return FALSE; return file.Write((const char *)data, data.GetLength()); } BOOL PXML::Save(PString & data, int _options) { PWaitAndSignal m(rootMutex); if (_options >= 0) options = _options; PStringStream strm; strm << *this; data = strm; return TRUE; } void PXML::RemoveAll() { PWaitAndSignal m(rootMutex); if (rootElement != NULL) { delete rootElement; rootElement = NULL; } } PXMLElement * PXML::GetElement(const PCaselessString & name, PINDEX idx) const { if (rootElement == NULL) return NULL; return rootElement->GetElement(name, idx); } PXMLElement * PXML::GetElement(PINDEX idx) const { if (rootElement == NULL) return NULL; if (idx >= rootElement->GetSize()) return NULL; return (PXMLElement *)(rootElement->GetElement(idx)); } BOOL PXML::RemoveElement(PINDEX idx) { if (rootElement == NULL) return FALSE; if (idx >= rootElement->GetSize()) return FALSE; rootElement->RemoveElement(idx); return TRUE; } PINDEX PXML::GetNumElements() const { if (rootElement == NULL) return 0; else return rootElement->GetSize(); } BOOL PXML::IsNoIndentElement(const PString & elementName) const { return noIndentElements.GetValuesIndex(elementName) != P_MAX_INDEX; } void PXML::PrintOn(ostream & strm) const { BOOL newLine = (options & (PXMLParser::Indent|PXMLParser::NewLineAfterElement)) != 0; // PString ver = version; PString enc = encoding; int salone = standAlone; if (ver.IsEmpty()) ver= "1.0"; if (enc.IsEmpty()) enc = "UTF-8"; if (salone == -2) salone = -1; strm << ""; if (newLine) strm << endl; if (rootElement != NULL) { if (!docType.IsEmpty()) strm << "'; if (newLine) strm << endl; rootElement->Output(strm, *this, 2); } } PString PXML::CreateStartTag(const PString & text) { return '<' + text + '>'; } PString PXML::CreateEndTag(const PString & text) { return "'; } PString PXML::CreateTagNoData(const PString & text) { return '<' + text + "/>"; } PString PXML::CreateTag(const PString & text, const PString & data) { return CreateStartTag(text) + data + CreateEndTag(text); } /////////////////////////////////////////////////////// // void PXMLObject::SetDirty() { dirty = TRUE; if (parent != NULL) parent->SetDirty(); } PXMLObject * PXMLObject::GetNextObject() { if (parent == NULL) return NULL; // find our index in our parent's list PINDEX idx = parent->FindObject(this); if (idx == P_MAX_INDEX) return NULL; // get the next object ++idx; if (idx >= parent->GetSize()) return NULL; return (*parent).GetElement(idx); } /////////////////////////////////////////////////////// PXMLData::PXMLData(PXMLElement * _parent, const PString & _value) : PXMLObject(_parent) { value = _value; } PXMLData::PXMLData(PXMLElement * _parent, const char * data, int len) : PXMLObject(_parent) { value = PString(data, len); } void PXMLData::Output(ostream & strm, const PXMLBase & xml, int indent) const { int options = xml.GetOptions(); if (xml.IsNoIndentElement(parent->GetName())) options &= ~PXMLParser::Indent; if (options & PXMLParser::Indent) strm << setw(indent-1) << " "; strm << value; if ((options & (PXMLParser::Indent|PXMLParser::NewLineAfterElement)) != 0) strm << endl; } void PXMLData::SetString(const PString & str, BOOL setDirty) { value = str; if (setDirty) SetDirty(); } PXMLObject * PXMLData::Clone(PXMLElement * _parent) const { return new PXMLData(_parent, value); } /////////////////////////////////////////////////////// PXMLElement::PXMLElement(PXMLElement * _parent, const char * _name) : PXMLObject(_parent) { dirty = FALSE; if (_name != NULL) name = _name; } PXMLElement::PXMLElement(PXMLElement * _parent, const PString & _name, const PString & data) : PXMLObject(_parent), name(_name) { dirty = FALSE; AddSubObject(new PXMLData(this, data)); } PINDEX PXMLElement::FindObject(PXMLObject * ptr) const { return subObjects.GetObjectsIndex(ptr); } PXMLElement * PXMLElement::GetElement(const PCaselessString & name, PINDEX start) const { PINDEX idx; PINDEX size = subObjects.GetSize(); PINDEX count = 0; for (idx = 0; idx < size; idx++) { if (subObjects[idx].IsElement()) { PXMLElement & subElement = ((PXMLElement &)subObjects[idx]); if (subElement.GetName() *= name) { if (count++ == start) return (PXMLElement *)&subObjects[idx]; } } } return NULL; } PXMLObject * PXMLElement::GetElement(PINDEX idx) const { if (idx >= subObjects.GetSize()) return NULL; return &subObjects[idx]; } BOOL PXMLElement::RemoveElement(PINDEX idx) { if (idx >= subObjects.GetSize()) return FALSE; subObjects.RemoveAt(idx); return TRUE; } PString PXMLElement::GetAttribute(const PCaselessString & key) const { return attributes(key); } PString PXMLElement::GetKeyAttribute(PINDEX idx) const { if (idx < attributes.GetSize()) return attributes.GetKeyAt(idx); else return PString(); } PString PXMLElement::GetDataAttribute(PINDEX idx) const { if (idx < attributes.GetSize()) return attributes.GetDataAt(idx); else return PString(); } void PXMLElement::SetAttribute(const PCaselessString & key, const PString & value, BOOL setDirty) { attributes.SetAt(key, value); if (setDirty) SetDirty(); } BOOL PXMLElement::HasAttribute(const PCaselessString & key) { return attributes.Contains(key); } void PXMLElement::PrintOn(ostream & strm) const { PXMLBase xml(-1); Output(strm, xml, 0); } void PXMLElement::Output(ostream & strm, const PXMLBase & xml, int indent) const { int options = xml.GetOptions(); BOOL newLine = (options & (PXMLParser::Indent|PXMLParser::NewLineAfterElement)) != 0; if ((options & PXMLParser::Indent) != 0) strm << setw(indent-1) << " "; strm << '<' << name; PINDEX i; if (attributes.GetSize() > 0) { for (i = 0; i < attributes.GetSize(); i++) { PCaselessString key = attributes.GetKeyAt(i); strm << ' ' << key << "=\"" << attributes[key] << '"'; } } // this ensures empty elements use the shortened form if (subObjects.GetSize() == 0) { strm << "/>"; if (newLine) strm << endl; } else { BOOL indenting = (options & PXMLParser::Indent) != 0 && !xml.IsNoIndentElement(name); strm << '>'; if (indenting) strm << endl; for (i = 0; i < subObjects.GetSize(); i++) subObjects[i].Output(strm, xml, indent + 2); if (indenting) strm << setw(indent-1) << " "; strm << "'; if (newLine) strm << endl; } } PXMLObject * PXMLElement::AddSubObject(PXMLObject * elem, BOOL setDirty) { subObjects.SetAt(subObjects.GetSize(), elem); if (setDirty) SetDirty(); return elem; } PXMLElement * PXMLElement::AddChild(PXMLElement * elem, BOOL dirty) { return (PXMLElement *)AddSubObject(elem, dirty); } PXMLData * PXMLElement::AddChild(PXMLData * elem, BOOL dirty) { return (PXMLData *)AddSubObject(elem, dirty); } PXMLObject * PXMLElement::Clone(PXMLElement * _parent) const { PXMLElement * elem = new PXMLElement(_parent); elem->SetName(name); elem->attributes = attributes; elem->dirty = dirty; PINDEX idx; for (idx = 0; idx < subObjects.GetSize(); idx++) elem->AddSubObject(subObjects[idx].Clone(elem), FALSE); return elem; } PString PXMLElement::GetData() const { PString str; PINDEX idx; for (idx = 0; idx < subObjects.GetSize(); idx++) { if (!subObjects[idx].IsElement()) { PXMLData & dataElement = ((PXMLData &)subObjects[idx]); PStringArray lines = dataElement.GetString().Lines(); PINDEX j; for (j = 0; j < lines.GetSize(); j++) str = str & lines[j]; } } return str; } /////////////////////////////////////////////////////// PXMLSettings::PXMLSettings(int options) :PXML(options) { } PXMLSettings::PXMLSettings(const PString & data, int options) : PXML(data,options) { } PXMLSettings::PXMLSettings(const PConfig & data, int options) : PXML(options) { PStringList sects = data.GetSections(); for (PINDEX i = 0;i < (PINDEX)sects.GetSize();++i) { PStringToString keyvals = data.GetAllKeyValues(sects[i]); for (PINDEX j = 0; j < (PINDEX)keyvals.GetSize(); ++j) { SetAttribute(sects[i],keyvals.GetKeyAt(j),keyvals.GetDataAt(j)); } } } BOOL PXMLSettings::Load(const PString & data) { return PXML::Load(data); } BOOL PXMLSettings::LoadFile(const PFilePath & fn) { return PXML::LoadFile(fn); } BOOL PXMLSettings::Save() { return PXML::Save(); } BOOL PXMLSettings::Save(PString & data) { return PXML::Save(data); } BOOL PXMLSettings::SaveFile(const PFilePath & fn) { return PXML::SaveFile(fn); } PString PXMLSettings::GetAttribute(const PCaselessString & section, const PString & key) const { if (rootElement == NULL) return PString(); PXMLElement * element = rootElement->GetElement(section); if (element == NULL) return PString(); return element->GetAttribute(key); } void PXMLSettings::SetAttribute(const PCaselessString & section, const PString & key, const PString & value) { if (rootElement == NULL) rootElement = new PXMLElement(NULL, "settings"); PXMLElement * element = rootElement->GetElement(section); if (element == NULL) { element = new PXMLElement(rootElement, section); rootElement->AddSubObject(element); } element->SetAttribute(key, value); } BOOL PXMLSettings::HasAttribute(const PCaselessString & section, const PString & key) const { if (rootElement == NULL) return FALSE; PXMLElement * element = rootElement->GetElement(section); if (element == NULL) return FALSE; return element->HasAttribute(key); } void PXMLSettings::ToConfig(PConfig & cfg) const { for (PINDEX i = 0;i < (PINDEX)GetNumElements();++i) { PXMLElement * el = GetElement(i); PString sectionName = el->GetName(); for (PINDEX j = 0; j < (PINDEX)el->GetNumAttributes(); ++j) { PString key = el->GetKeyAttribute(j); PString dat = el->GetDataAttribute(j); if (!key && !dat) cfg.SetString(sectionName, key, dat); } } } /////////////////////////////////////////////////////// PXMLStreamParser::PXMLStreamParser() : rootOpen(TRUE) { } void PXMLStreamParser::EndElement(const char * name) { PXMLElement * element = currentElement; PXMLParser::EndElement(name); if (currentElement == rootElement) { if (element == rootElement) { // stream closed rootOpen = FALSE; } else { PINDEX i = rootElement->FindObject(element); if (i != P_MAX_INDEX) { PXML tmp; element = (PXMLElement *)element->Clone(0); rootElement->RemoveElement(i); PXML * msg = new PXML; msg->SetRootElement(element); messages.Enqueue(msg); } } } } PXML * PXMLStreamParser::Read(PChannel * channel) { char buf[256]; channel->SetReadTimeout(1000); while (rootOpen) { if (messages.GetSize() != 0) return messages.Dequeue(); if (!channel->Read(buf, sizeof(buf) - 1) || !channel->IsOpen()) return 0; buf[channel->GetLastReadCount()] = 0; if (!Parse(buf, channel->GetLastReadCount(), FALSE)) return 0; } channel->Close(); return 0; } /////////////////////////////////////////////////////// #endif