Changed output and intermediate directories, tuned up compile parameters for Windows...
[pwlib.git] / src / ptclib / pxml.cxx
blob399cef140bb88f13012e039e01989dae0e212604
1 /*
2 * pxml.cxx
4 * XML parser support
6 * Portable Windows Library
8 * Copyright (c) 2002 Equivalence Pty. Ltd.
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
18 * under the License.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
26 * $Log$
27 * Revision 1.43 2005/11/30 12:47:41 csoutheren
28 * Removed tabs, reformatted some code, and changed tags for Doxygen
30 * Revision 1.42 2005/05/12 05:30:16 csoutheren
31 * Ensured error location is initialised
33 * Revision 1.41 2004/10/23 10:58:15 ykiryanov
34 * Added ifdef _WIN32_WCE for PocketPC 2003 SDK port
36 * Revision 1.40 2004/10/12 23:28:08 csoutheren
37 * Fixed problem with bogus DOCTYPE being output
39 * Revision 1.39 2004/04/21 00:35:02 csoutheren
40 * Added a stream parser for protocols like XMPP where each child of the root is to be considered a separate document/message.
41 * Thanks to Federico Pinna and Reitek S.p.A.
43 * Revision 1.38 2004/04/09 06:52:17 rjongbloed
44 * Removed #pargma linker command for /delayload of DLL as documentations sais that
45 * you cannot do this.
47 * Revision 1.37 2004/02/23 23:52:20 csoutheren
48 * Added pragmas to avoid every Windows application needing to include libs explicitly
50 * Revision 1.36 2004/01/17 18:12:59 csoutheren
51 * Changed to use PString::MakeEmpty
53 * Revision 1.35 2003/05/14 02:50:53 rjongbloed
54 * Simplified name space initialisation
56 * Revision 1.34 2003/05/06 07:55:25 craigs
57 * Fixed problem with initialising XML parser for namespaces
59 * Revision 1.33 2003/04/27 23:53:30 craigs
60 * Removed deprecated options
62 * Revision 1.32 2003/04/16 08:00:19 robertj
63 * Windoes psuedo autoconf support
65 * Revision 1.31 2003/04/08 12:47:07 craigs
66 * Fixed problem with handling of CDATA
68 * Revision 1.30 2003/04/08 05:08:41 craigs
69 * Fixed problems with additional spaces being included with metasequences
71 * Revision 1.29 2003/04/02 09:13:55 rogerh
72 * Add type casts because the variable 'expat' is now a void *
74 * Revision 1.28 2003/03/31 06:20:56 craigs
75 * Split the expat wrapper from the XML file handling to allow reuse of the parser
77 * Revision 1.27 2003/01/13 02:14:02 robertj
78 * Improved error logging for auto-loaded XML
80 * Revision 1.26 2002/12/16 06:38:59 robertj
81 * Added ability to specify certain elemets (by name) that are exempt from
82 * the indent formatting. Useful for XML/RPC where leading white space is
83 * not ignored by all servers.
85 * Revision 1.25 2002/12/10 04:41:16 robertj
86 * Added test for URL being empty, don't try and run auto load in background.
88 * Revision 1.24 2002/11/26 05:53:45 craigs
89 * Added ability to auto-reload from URL
91 * Revision 1.23 2002/11/21 08:08:52 craigs
92 * Changed to not overwrite XML data if load fails
94 * Revision 1.22 2002/11/19 07:37:25 craigs
95 * Added locking functions and LoadURL function
97 * Revision 1.21 2002/11/06 22:47:25 robertj
98 * Fixed header comment (copyright etc)
102 // This depends on the expat XML library by Jim Clark
103 // See http://www.jclark.com/xml/expat.html for more information
105 #include <ptlib.h>
107 #ifdef __GNUC__
108 #pragma implementation "pxml.h"
109 #endif
111 #include <ptclib/pxml.h>
113 #if P_EXPAT
115 #include <expat.h>
118 #define CACHE_BUFFER_SIZE 1024
119 #define XMLSETTINGS_OPTIONS (NewLineAfterElement)
122 #ifdef _MSC_VER
123 #ifndef _WIN32_WCE
124 #pragma comment(lib, P_EXPAT_LIBRARY)
125 #endif // !_WIN32_WCE
126 #endif
129 ////////////////////////////////////////////////////
131 static void PXML_StartElement(void * userData, const char * name, const char ** attrs)
133 ((PXMLParser *)userData)->StartElement(name, attrs);
136 static void PXML_EndElement(void * userData, const char * name)
138 ((PXMLParser *)userData)->EndElement(name);
141 static void PXML_CharacterDataHandler(void * userData, const char * data, int len)
143 ((PXMLParser *)userData)->AddCharacterData(data, len);
146 static void PXML_XmlDeclHandler(void * userData, const char * version, const char * encoding, int standalone)
148 ((PXMLParser *)userData)->XmlDecl(version, encoding, standalone);
151 static void PXML_StartDocTypeDecl(void * userData,
152 const char * docTypeName,
153 const char * sysid,
154 const char * pubid,
155 int hasInternalSubSet)
157 ((PXMLParser *)userData)->StartDocTypeDecl(docTypeName, sysid, pubid, hasInternalSubSet);
160 static void PXML_EndDocTypeDecl(void * userData)
162 ((PXMLParser *)userData)->EndDocTypeDecl();
165 static void PXML_StartNamespaceDeclHandler(void *userData,
166 const XML_Char *prefix,
167 const XML_Char *uri)
169 ((PXMLParser *)userData)->StartNamespaceDeclHandler(prefix, uri);
172 static void PXML_EndNamespaceDeclHandler(void *userData, const XML_Char *prefix)
174 ((PXMLParser *)userData)->EndNamespaceDeclHandler(prefix);
177 PXMLParser::PXMLParser(int _options)
178 : options(_options)
180 if (options < 0)
181 options = 0;
183 if ((options & WithNS) != 0)
184 expat = XML_ParserCreateNS(NULL, '|');
185 else
186 expat = XML_ParserCreate(NULL);
188 XML_SetUserData((XML_Parser)expat, this);
190 XML_SetElementHandler ((XML_Parser)expat, PXML_StartElement, PXML_EndElement);
191 XML_SetCharacterDataHandler((XML_Parser)expat, PXML_CharacterDataHandler);
192 XML_SetXmlDeclHandler ((XML_Parser)expat, PXML_XmlDeclHandler);
193 XML_SetDoctypeDeclHandler ((XML_Parser)expat, PXML_StartDocTypeDecl, PXML_EndDocTypeDecl);
194 XML_SetNamespaceDeclHandler((XML_Parser)expat, PXML_StartNamespaceDeclHandler, PXML_EndNamespaceDeclHandler);
196 rootElement = NULL;
197 currentElement = NULL;
198 lastElement = NULL;
201 PXMLParser::~PXMLParser()
203 XML_ParserFree((XML_Parser)expat);
206 PXMLElement * PXMLParser::GetXMLTree() const
208 return rootElement;
211 PXMLElement * PXMLParser::SetXMLTree(PXMLElement * newRoot)
213 PXMLElement * oldRoot = rootElement;
214 rootElement = newRoot;
215 return oldRoot;
218 BOOL PXMLParser::Parse(const char * data, int dataLen, BOOL final)
220 return XML_Parse((XML_Parser)expat, data, dataLen, final) != 0;
223 void PXMLParser::GetErrorInfo(PString & errorString, PINDEX & errorCol, PINDEX & errorLine)
225 XML_Error err = XML_GetErrorCode((XML_Parser)expat);
226 errorString = PString(XML_ErrorString(err));
227 errorCol = XML_GetCurrentColumnNumber((XML_Parser)expat);
228 errorLine = XML_GetCurrentLineNumber((XML_Parser)expat);
231 void PXMLParser::StartElement(const char * name, const char **attrs)
233 PXMLElement * newElement = new PXMLElement(currentElement, name);
234 if (currentElement != NULL)
235 currentElement->AddSubObject(newElement, FALSE);
237 while (attrs[0] != NULL) {
238 newElement->SetAttribute(PString(attrs[0]), PString(attrs[1]));
239 attrs += 2;
242 currentElement = newElement;
243 lastElement = NULL;
245 if (rootElement == NULL)
246 rootElement = currentElement;
249 void PXMLParser::EndElement(const char * /*name*/)
251 currentElement = currentElement->GetParent();
252 lastElement = NULL;
255 void PXMLParser::AddCharacterData(const char * data, int len)
257 PString str(data, len);
259 if (lastElement != NULL) {
260 PAssert(!lastElement->IsElement(), "lastElement set by non-data element");
261 lastElement->SetString(lastElement->GetString() + str, FALSE);
262 } else {
263 PXMLData * newElement = new PXMLData(currentElement, str);
264 if (currentElement != NULL)
265 currentElement->AddSubObject(newElement, FALSE);
266 lastElement = newElement;
271 void PXMLParser::XmlDecl(const char * _version, const char * _encoding, int _standAlone)
273 version = _version;
274 encoding = _encoding;
275 standAlone = _standAlone;
278 void PXMLParser::StartDocTypeDecl(const char * /*docTypeName*/,
279 const char * /*sysid*/,
280 const char * /*pubid*/,
281 int /*hasInternalSubSet*/)
285 void PXMLParser::EndDocTypeDecl()
289 void PXMLParser::StartNamespaceDeclHandler(const XML_Char * /*prefix*/,
290 const XML_Char * /*uri*/)
294 void PXMLParser::EndNamespaceDeclHandler(const XML_Char * /*prefix*/)
299 ///////////////////////////////////////////////////////////////////////////////////////////////
302 PXML::PXML(int options, const char * noIndentElements)
303 : PXMLBase(options)
305 Construct(options, noIndentElements);
308 PXML::PXML(const PString & data, int options, const char * noIndentElements)
309 : PXMLBase(options)
311 Construct(options, noIndentElements);
312 Load(data);
315 PXML::~PXML()
317 autoLoadTimer.Stop();
318 RemoveAll();
321 PXML::PXML(const PXML & xml)
322 : noIndentElements(xml.noIndentElements)
324 Construct(xml.options, NULL);
326 loadFromFile = xml.loadFromFile;
327 loadFilename = xml.loadFilename;
328 version = xml.version;
329 encoding = xml.encoding;
330 standAlone = xml.standAlone;
332 PWaitAndSignal m(xml.rootMutex);
334 PXMLElement * oldRootElement = xml.rootElement;
335 if (oldRootElement != NULL)
336 rootElement = (PXMLElement *)oldRootElement->Clone(NULL);
339 void PXML::Construct(int _options, const char * _noIndentElements)
341 rootElement = NULL;
342 options = _options > 0 ? _options : 0;
343 loadFromFile = FALSE;
344 standAlone = -2;
345 errorCol = 0;
346 errorLine = 0;
348 if (_noIndentElements != NULL)
349 noIndentElements = PString(_noIndentElements).Tokenise(' ', FALSE);
352 PXMLElement * PXML::SetRootElement(const PString & documentType)
354 PWaitAndSignal m(rootMutex);
356 if (rootElement != NULL)
357 delete rootElement;
359 rootElement = new PXMLElement(rootElement, documentType);
361 return rootElement;
364 PXMLElement * PXML::SetRootElement(PXMLElement * element)
366 PWaitAndSignal m(rootMutex);
368 if (rootElement != NULL)
369 delete rootElement;
371 rootElement = element;
373 return rootElement;
376 BOOL PXML::IsDirty() const
378 PWaitAndSignal m(rootMutex);
380 if (rootElement == NULL)
381 return FALSE;
383 return rootElement->IsDirty();
386 PCaselessString PXML::GetDocumentType() const
388 PWaitAndSignal m(rootMutex);
390 if (rootElement == NULL)
391 return PCaselessString();
392 return rootElement->GetName();
395 BOOL PXML::LoadFile(const PFilePath & fn, int _options)
397 PTRACE(4, "XML\tLoading file " << fn);
399 PWaitAndSignal m(rootMutex);
401 if (_options >= 0)
402 options = _options;
404 loadFilename = fn;
405 loadFromFile = TRUE;
407 PFile file;
408 if (!file.Open(fn, PFile::ReadOnly)) {
409 errorString = "File open error" & file.GetErrorText();
410 return FALSE;
413 off_t len = file.GetLength();
414 PString data;
415 if (!file.Read(data.GetPointer(len + 1), len)) {
416 errorString = "File read error" & file.GetErrorText();
417 return FALSE;
420 data[(PINDEX)len] = '\0';
422 return Load(data);
425 BOOL PXML::LoadURL(const PURL & url)
427 return LoadURL(url, PMaxTimeInterval, -1);
431 BOOL PXML::LoadURL(const PURL & url, const PTimeInterval & timeout, int _options)
433 if (url.IsEmpty()) {
434 errorString = "Cannot load empty URL";
435 errorCol = errorLine = 0;
436 return FALSE;
439 PTRACE(4, "XML\tLoading URL " << url);
441 PString data;
442 if (url.GetScheme() == "file")
443 return LoadFile(url.AsFilePath());
445 PHTTPClient client;
446 PINDEX contentLength;
447 PMIMEInfo outMIME, replyMIME;
449 // make sure we do not hang around for ever
450 client.SetReadTimeout(timeout);
452 // get the resource header information
453 if (!client.GetDocument(url, outMIME, replyMIME)) {
454 errorString = PString("Cannot load URL") & url.AsString();
455 errorCol = errorLine = 0;
456 return FALSE;
459 // get the length of the data
460 if (!replyMIME.Contains(PHTTPClient::ContentLengthTag))
461 contentLength = (PINDEX)replyMIME[PHTTPClient::ContentLengthTag].AsUnsigned();
462 else
463 contentLength = P_MAX_INDEX;
465 // download the resource into memory
466 PINDEX offs = 0;
467 for (;;) {
468 PINDEX len;
469 if (contentLength == P_MAX_INDEX)
470 len = CACHE_BUFFER_SIZE;
471 else if (offs == contentLength)
472 break;
473 else
474 len = PMIN(contentLength = offs, CACHE_BUFFER_SIZE);
476 if (!client.Read(offs + data.GetPointer(offs + len), len))
477 break;
479 len = client.GetLastReadCount();
481 offs += len;
484 return Load(data, _options);
487 BOOL PXML::StartAutoReloadURL(const PURL & url,
488 const PTimeInterval & timeout,
489 const PTimeInterval & refreshTime,
490 int _options)
492 if (url.IsEmpty()) {
493 autoLoadError = "Cannot auto-load empty URL";
494 return FALSE;
497 PWaitAndSignal m(autoLoadMutex);
498 autoLoadTimer.Stop();
500 SetOptions(_options);
501 autoloadURL = url;
502 autoLoadWaitTime = timeout;
503 autoLoadError.MakeEmpty();
504 autoLoadTimer.SetNotifier(PCREATE_NOTIFIER(AutoReloadTimeout));
506 BOOL stat = AutoLoadURL();
508 autoLoadTimer = refreshTime;
510 return stat;
513 void PXML::AutoReloadTimeout(PTimer &, INT)
515 PThread::Create(PCREATE_NOTIFIER(AutoReloadThread), PThread::AutoDeleteThread);
518 void PXML::AutoReloadThread(PThread &, INT)
520 PWaitAndSignal m(autoLoadMutex);
521 OnAutoLoad(AutoLoadURL());
522 autoLoadTimer.Reset();
525 void PXML::OnAutoLoad(BOOL ok)
527 PTRACE_IF(3, !ok, "XML\tFailed to load XML: " << GetErrorString());
530 BOOL PXML::AutoLoadURL()
532 BOOL stat = LoadURL(autoloadURL, autoLoadWaitTime);
533 if (stat)
534 autoLoadError.MakeEmpty();
535 else
536 autoLoadError = GetErrorString() + psprintf(" at line %i, column %i", GetErrorLine(), GetErrorColumn());
537 return stat;
540 BOOL PXML::StopAutoReloadURL()
542 PWaitAndSignal m(autoLoadMutex);
543 autoLoadTimer.Stop();
544 return TRUE;
548 BOOL PXML::Load(const PString & data, int _options)
550 if (_options >= 0)
551 options = _options;
553 BOOL stat = FALSE;
554 PXMLElement * loadingRootElement = NULL;
557 PXMLParser parser(options);
558 int done = 1;
559 stat = parser.Parse(data, data.GetLength(), done) != 0;
561 if (!stat)
562 parser.GetErrorInfo(errorString, errorCol, errorLine);
564 version = parser.GetVersion();
565 encoding = parser.GetEncoding();
566 standAlone = parser.GetStandAlone();
568 loadingRootElement = parser.GetXMLTree();
571 if (stat) {
572 if (loadingRootElement == NULL) {
573 errorString = "XML\tFailed to create root node in XML!";
574 return FALSE;
576 else {
577 PWaitAndSignal m(rootMutex);
578 if (rootElement != NULL) {
579 delete rootElement;
580 rootElement = NULL;
582 rootElement = loadingRootElement;
583 PTRACE(4, "XML\tLoaded XML " << rootElement->GetName());
585 OnLoaded();
588 return stat;
591 BOOL PXML::Save(int _options)
593 if (_options >= 0)
594 options = _options;
596 if (!loadFromFile || !IsDirty())
597 return FALSE;
599 return SaveFile(loadFilename);
602 BOOL PXML::SaveFile(const PFilePath & fn, int _options)
604 PWaitAndSignal m(rootMutex);
606 PFile file;
607 if (!file.Open(fn, PFile::WriteOnly))
608 return FALSE;
610 PString data;
611 if (!Save(data, _options))
612 return FALSE;
614 return file.Write((const char *)data, data.GetLength());
617 BOOL PXML::Save(PString & data, int _options)
619 PWaitAndSignal m(rootMutex);
621 if (_options >= 0)
622 options = _options;
624 PStringStream strm;
625 strm << *this;
626 data = strm;
627 return TRUE;
630 void PXML::RemoveAll()
632 PWaitAndSignal m(rootMutex);
634 if (rootElement != NULL) {
635 delete rootElement;
636 rootElement = NULL;
640 PXMLElement * PXML::GetElement(const PCaselessString & name, PINDEX idx) const
642 if (rootElement == NULL)
643 return NULL;
645 return rootElement->GetElement(name, idx);
648 PXMLElement * PXML::GetElement(PINDEX idx) const
650 if (rootElement == NULL)
651 return NULL;
652 if (idx >= rootElement->GetSize())
653 return NULL;
655 return (PXMLElement *)(rootElement->GetElement(idx));
658 BOOL PXML::RemoveElement(PINDEX idx)
660 if (rootElement == NULL)
661 return FALSE;
663 if (idx >= rootElement->GetSize())
664 return FALSE;
666 rootElement->RemoveElement(idx);
667 return TRUE;
671 PINDEX PXML::GetNumElements() const
673 if (rootElement == NULL)
674 return 0;
675 else
676 return rootElement->GetSize();
679 BOOL PXML::IsNoIndentElement(const PString & elementName) const
681 return noIndentElements.GetValuesIndex(elementName) != P_MAX_INDEX;
685 void PXML::PrintOn(ostream & strm) const
687 BOOL newLine = (options & (PXMLParser::Indent|PXMLParser::NewLineAfterElement)) != 0;
689 //<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
691 PString ver = version;
692 PString enc = encoding;
693 int salone = standAlone;
695 if (ver.IsEmpty())
696 ver= "1.0";
697 if (enc.IsEmpty())
698 enc = "UTF-8";
699 if (salone == -2)
700 salone = -1;
702 strm << "<?xml version=\"" << ver << "\" encoding=\"" << enc << "\"";
703 switch (salone) {
704 case 0:
705 strm << " standalone=\"no\"";
706 break;
707 case 1:
708 strm << " standalone=\"yes\"";
709 break;
710 default:
711 break;
714 strm << "?>";
715 if (newLine)
716 strm << endl;
718 if (rootElement != NULL) {
719 if (!docType.IsEmpty())
720 strm << "<!DOCTYPE " << docType << '>';
721 if (newLine)
722 strm << endl;
723 rootElement->Output(strm, *this, 2);
727 PString PXML::CreateStartTag(const PString & text)
729 return '<' + text + '>';
733 PString PXML::CreateEndTag(const PString & text)
735 return "</" + text + '>';
739 PString PXML::CreateTagNoData(const PString & text)
741 return '<' + text + "/>";
745 PString PXML::CreateTag(const PString & text, const PString & data)
747 return CreateStartTag(text) + data + CreateEndTag(text);
751 ///////////////////////////////////////////////////////
753 void PXMLObject::SetDirty()
755 dirty = TRUE;
756 if (parent != NULL)
757 parent->SetDirty();
760 PXMLObject * PXMLObject::GetNextObject()
762 if (parent == NULL)
763 return NULL;
765 // find our index in our parent's list
766 PINDEX idx = parent->FindObject(this);
767 if (idx == P_MAX_INDEX)
768 return NULL;
770 // get the next object
771 ++idx;
772 if (idx >= parent->GetSize())
773 return NULL;
775 return (*parent).GetElement(idx);
778 ///////////////////////////////////////////////////////
780 PXMLData::PXMLData(PXMLElement * _parent, const PString & _value)
781 : PXMLObject(_parent)
783 value = _value;
786 PXMLData::PXMLData(PXMLElement * _parent, const char * data, int len)
787 : PXMLObject(_parent)
789 value = PString(data, len);
792 void PXMLData::Output(ostream & strm, const PXMLBase & xml, int indent) const
794 int options = xml.GetOptions();
795 if (xml.IsNoIndentElement(parent->GetName()))
796 options &= ~PXMLParser::Indent;
798 if (options & PXMLParser::Indent)
799 strm << setw(indent-1) << " ";
801 strm << value;
803 if ((options & (PXMLParser::Indent|PXMLParser::NewLineAfterElement)) != 0)
804 strm << endl;
807 void PXMLData::SetString(const PString & str, BOOL setDirty)
809 value = str;
810 if (setDirty)
811 SetDirty();
814 PXMLObject * PXMLData::Clone(PXMLElement * _parent) const
816 return new PXMLData(_parent, value);
819 ///////////////////////////////////////////////////////
821 PXMLElement::PXMLElement(PXMLElement * _parent, const char * _name)
822 : PXMLObject(_parent)
824 dirty = FALSE;
825 if (_name != NULL)
826 name = _name;
829 PXMLElement::PXMLElement(PXMLElement * _parent, const PString & _name, const PString & data)
830 : PXMLObject(_parent), name(_name)
832 dirty = FALSE;
833 AddSubObject(new PXMLData(this, data));
836 PINDEX PXMLElement::FindObject(PXMLObject * ptr) const
838 return subObjects.GetObjectsIndex(ptr);
842 PXMLElement * PXMLElement::GetElement(const PCaselessString & name, PINDEX start) const
844 PINDEX idx;
845 PINDEX size = subObjects.GetSize();
846 PINDEX count = 0;
847 for (idx = 0; idx < size; idx++) {
848 if (subObjects[idx].IsElement()) {
849 PXMLElement & subElement = ((PXMLElement &)subObjects[idx]);
850 if (subElement.GetName() *= name) {
851 if (count++ == start)
852 return (PXMLElement *)&subObjects[idx];
856 return NULL;
859 PXMLObject * PXMLElement::GetElement(PINDEX idx) const
861 if (idx >= subObjects.GetSize())
862 return NULL;
864 return &subObjects[idx];
867 BOOL PXMLElement::RemoveElement(PINDEX idx)
869 if (idx >= subObjects.GetSize())
870 return FALSE;
872 subObjects.RemoveAt(idx);
873 return TRUE;
877 PString PXMLElement::GetAttribute(const PCaselessString & key) const
879 return attributes(key);
882 PString PXMLElement::GetKeyAttribute(PINDEX idx) const
884 if (idx < attributes.GetSize())
885 return attributes.GetKeyAt(idx);
886 else
887 return PString();
890 PString PXMLElement::GetDataAttribute(PINDEX idx) const
892 if (idx < attributes.GetSize())
893 return attributes.GetDataAt(idx);
894 else
895 return PString();
898 void PXMLElement::SetAttribute(const PCaselessString & key,
899 const PString & value,
900 BOOL setDirty)
902 attributes.SetAt(key, value);
903 if (setDirty)
904 SetDirty();
907 BOOL PXMLElement::HasAttribute(const PCaselessString & key)
909 return attributes.Contains(key);
912 void PXMLElement::PrintOn(ostream & strm) const
914 PXMLBase xml(-1);
915 Output(strm, xml, 0);
918 void PXMLElement::Output(ostream & strm, const PXMLBase & xml, int indent) const
920 int options = xml.GetOptions();
922 BOOL newLine = (options & (PXMLParser::Indent|PXMLParser::NewLineAfterElement)) != 0;
924 if ((options & PXMLParser::Indent) != 0)
925 strm << setw(indent-1) << " ";
927 strm << '<' << name;
929 PINDEX i;
930 if (attributes.GetSize() > 0) {
931 for (i = 0; i < attributes.GetSize(); i++) {
932 PCaselessString key = attributes.GetKeyAt(i);
933 strm << ' ' << key << "=\"" << attributes[key] << '"';
937 // this ensures empty elements use the shortened form
938 if (subObjects.GetSize() == 0) {
939 strm << "/>";
940 if (newLine)
941 strm << endl;
943 else {
944 BOOL indenting = (options & PXMLParser::Indent) != 0 && !xml.IsNoIndentElement(name);
946 strm << '>';
947 if (indenting)
948 strm << endl;
950 for (i = 0; i < subObjects.GetSize(); i++)
951 subObjects[i].Output(strm, xml, indent + 2);
953 if (indenting)
954 strm << setw(indent-1) << " ";
956 strm << "</" << name << '>';
957 if (newLine)
958 strm << endl;
962 PXMLObject * PXMLElement::AddSubObject(PXMLObject * elem, BOOL setDirty)
964 subObjects.SetAt(subObjects.GetSize(), elem);
965 if (setDirty)
966 SetDirty();
968 return elem;
971 PXMLElement * PXMLElement::AddChild(PXMLElement * elem, BOOL dirty)
973 return (PXMLElement *)AddSubObject(elem, dirty);
976 PXMLData * PXMLElement::AddChild(PXMLData * elem, BOOL dirty)
978 return (PXMLData *)AddSubObject(elem, dirty);
981 PXMLObject * PXMLElement::Clone(PXMLElement * _parent) const
983 PXMLElement * elem = new PXMLElement(_parent);
985 elem->SetName(name);
986 elem->attributes = attributes;
987 elem->dirty = dirty;
989 PINDEX idx;
990 for (idx = 0; idx < subObjects.GetSize(); idx++)
991 elem->AddSubObject(subObjects[idx].Clone(elem), FALSE);
993 return elem;
996 PString PXMLElement::GetData() const
998 PString str;
999 PINDEX idx;
1000 for (idx = 0; idx < subObjects.GetSize(); idx++) {
1001 if (!subObjects[idx].IsElement()) {
1002 PXMLData & dataElement = ((PXMLData &)subObjects[idx]);
1003 PStringArray lines = dataElement.GetString().Lines();
1004 PINDEX j;
1005 for (j = 0; j < lines.GetSize(); j++)
1006 str = str & lines[j];
1009 return str;
1013 ///////////////////////////////////////////////////////
1015 PXMLSettings::PXMLSettings(int options)
1016 :PXML(options)
1020 PXMLSettings::PXMLSettings(const PString & data, int options)
1021 : PXML(data,options)
1025 PXMLSettings::PXMLSettings(const PConfig & data, int options)
1026 : PXML(options)
1028 PStringList sects = data.GetSections();
1030 for (PINDEX i = 0;i < (PINDEX)sects.GetSize();++i) {
1031 PStringToString keyvals = data.GetAllKeyValues(sects[i]);
1032 for (PINDEX j = 0; j < (PINDEX)keyvals.GetSize(); ++j) {
1033 SetAttribute(sects[i],keyvals.GetKeyAt(j),keyvals.GetDataAt(j));
1038 BOOL PXMLSettings::Load(const PString & data)
1040 return PXML::Load(data);
1043 BOOL PXMLSettings::LoadFile(const PFilePath & fn)
1045 return PXML::LoadFile(fn);
1048 BOOL PXMLSettings::Save()
1050 return PXML::Save();
1053 BOOL PXMLSettings::Save(PString & data)
1055 return PXML::Save(data);
1058 BOOL PXMLSettings::SaveFile(const PFilePath & fn)
1060 return PXML::SaveFile(fn);
1063 PString PXMLSettings::GetAttribute(const PCaselessString & section, const PString & key) const
1065 if (rootElement == NULL)
1066 return PString();
1068 PXMLElement * element = rootElement->GetElement(section);
1069 if (element == NULL)
1070 return PString();
1072 return element->GetAttribute(key);
1075 void PXMLSettings::SetAttribute(const PCaselessString & section, const PString & key, const PString & value)
1077 if (rootElement == NULL)
1078 rootElement = new PXMLElement(NULL, "settings");
1080 PXMLElement * element = rootElement->GetElement(section);
1081 if (element == NULL) {
1082 element = new PXMLElement(rootElement, section);
1083 rootElement->AddSubObject(element);
1085 element->SetAttribute(key, value);
1088 BOOL PXMLSettings::HasAttribute(const PCaselessString & section, const PString & key) const
1090 if (rootElement == NULL)
1091 return FALSE;
1093 PXMLElement * element = rootElement->GetElement(section);
1094 if (element == NULL)
1095 return FALSE;
1097 return element->HasAttribute(key);
1100 void PXMLSettings::ToConfig(PConfig & cfg) const
1102 for (PINDEX i = 0;i < (PINDEX)GetNumElements();++i) {
1103 PXMLElement * el = GetElement(i);
1104 PString sectionName = el->GetName();
1105 for (PINDEX j = 0; j < (PINDEX)el->GetNumAttributes(); ++j) {
1106 PString key = el->GetKeyAttribute(j);
1107 PString dat = el->GetDataAttribute(j);
1108 if (!key && !dat)
1109 cfg.SetString(sectionName, key, dat);
1114 ///////////////////////////////////////////////////////
1116 PXMLStreamParser::PXMLStreamParser() :
1117 rootOpen(TRUE)
1122 void PXMLStreamParser::EndElement(const char * name)
1124 PXMLElement * element = currentElement;
1126 PXMLParser::EndElement(name);
1128 if (currentElement == rootElement) {
1129 if (element == rootElement) { // stream closed
1130 rootOpen = FALSE;
1132 else {
1133 PINDEX i = rootElement->FindObject(element);
1135 if (i != P_MAX_INDEX) {
1136 PXML tmp;
1137 element = (PXMLElement *)element->Clone(0);
1138 rootElement->RemoveElement(i);
1140 PXML * msg = new PXML;
1141 msg->SetRootElement(element);
1142 messages.Enqueue(msg);
1149 PXML * PXMLStreamParser::Read(PChannel * channel)
1151 char buf[256];
1153 channel->SetReadTimeout(1000);
1155 while (rootOpen) {
1156 if (messages.GetSize() != 0)
1157 return messages.Dequeue();
1159 if (!channel->Read(buf, sizeof(buf) - 1) || !channel->IsOpen())
1160 return 0;
1162 buf[channel->GetLastReadCount()] = 0;
1164 if (!Parse(buf, channel->GetLastReadCount(), FALSE))
1165 return 0;
1168 channel->Close();
1169 return 0;
1172 ///////////////////////////////////////////////////////
1174 #endif