1//
2// XMLWriter.cpp
3//
4// Library: XML
5// Package: XML
6// Module: XMLWriter
7//
8// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/XML/XMLWriter.h"
16#include "Poco/XML/XMLString.h"
17#include "Poco/XML/XMLException.h"
18#include "Poco/SAX/AttributesImpl.h"
19#include "Poco/UTF8Encoding.h"
20#include "Poco/UTF16Encoding.h"
21#include <sstream>
22
23
24namespace Poco {
25namespace XML {
26
27
28const std::string XMLWriter::NEWLINE_DEFAULT;
29const std::string XMLWriter::NEWLINE_CR = "\r";
30const std::string XMLWriter::NEWLINE_CRLF = "\r\n";
31const std::string XMLWriter::NEWLINE_LF = "\n";
32const std::string XMLWriter::MARKUP_QUOTENC = "&quot;";
33const std::string XMLWriter::MARKUP_AMPENC = "&amp;";
34const std::string XMLWriter::MARKUP_LTENC = "&lt;";
35const std::string XMLWriter::MARKUP_GTENC = "&gt;";
36const std::string XMLWriter::MARKUP_TABENC = "&#x9;";
37const std::string XMLWriter::MARKUP_CRENC = "&#xD;";
38const std::string XMLWriter::MARKUP_LFENC = "&#xA;";
39const std::string XMLWriter::MARKUP_LT = "<";
40const std::string XMLWriter::MARKUP_GT = ">";
41const std::string XMLWriter::MARKUP_SLASHGT = "/>";
42const std::string XMLWriter::MARKUP_LTSLASH = "</";
43const std::string XMLWriter::MARKUP_COLON = ":";
44const std::string XMLWriter::MARKUP_EQQUOT = "=\"";
45const std::string XMLWriter::MARKUP_QUOT = "\"";
46const std::string XMLWriter::MARKUP_SPACE = " ";
47const std::string XMLWriter::MARKUP_TAB = "\t";
48const std::string XMLWriter::MARKUP_BEGIN_CDATA = "<![CDATA[";
49const std::string XMLWriter::MARKUP_END_CDATA = "]]>";
50
51
52#if defined(XML_UNICODE_WCHAR_T)
53 #define NATIVE_ENCODING Poco::UTF16Encoding
54#else
55 #define NATIVE_ENCODING Poco::UTF8Encoding
56#endif
57
58
59XMLWriter::XMLWriter(XMLByteOutputStream& str, int options):
60 _pTextConverter(0),
61 _pInEncoding(new NATIVE_ENCODING),
62 _pOutEncoding(new Poco::UTF8Encoding),
63 _options(options),
64 _encoding("UTF-8"),
65 _depth(-1),
66 _elementCount(0),
67 _inFragment(false),
68 _inCDATA(false),
69 _inDTD(false),
70 _inInternalDTD(false),
71 _contentWritten(false),
72 _unclosedStartTag(false),
73 _prefix(0),
74 _nsContextPushed(false),
75 _indent(MARKUP_TAB)
76{
77 _pTextConverter = new Poco::OutputStreamConverter(str, *_pInEncoding, *_pOutEncoding);
78 setNewLine((_options & CANONICAL_XML) ? NEWLINE_LF : NEWLINE_DEFAULT);
79}
80
81
82XMLWriter::XMLWriter(XMLByteOutputStream& str, int options, const std::string& encodingName, Poco::TextEncoding& textEncoding):
83 _pTextConverter(0),
84 _pInEncoding(new NATIVE_ENCODING),
85 _pOutEncoding(0),
86 _options(options),
87 _encoding(encodingName),
88 _depth(-1),
89 _elementCount(0),
90 _inFragment(false),
91 _inCDATA(false),
92 _inDTD(false),
93 _inInternalDTD(false),
94 _contentWritten(false),
95 _unclosedStartTag(false),
96 _prefix(0),
97 _nsContextPushed(false),
98 _indent(MARKUP_TAB)
99{
100 _pTextConverter = new Poco::OutputStreamConverter(str, *_pInEncoding, textEncoding);
101 setNewLine((_options & CANONICAL_XML) ? NEWLINE_LF : NEWLINE_DEFAULT);
102}
103
104
105XMLWriter::XMLWriter(XMLByteOutputStream& str, int options, const std::string& encodingName, Poco::TextEncoding* pTextEncoding):
106 _pTextConverter(0),
107 _pInEncoding(new NATIVE_ENCODING),
108 _pOutEncoding(0),
109 _options(options),
110 _encoding(encodingName),
111 _depth(-1),
112 _elementCount(0),
113 _inFragment(false),
114 _inCDATA(false),
115 _inDTD(false),
116 _inInternalDTD(false),
117 _contentWritten(false),
118 _unclosedStartTag(false),
119 _prefix(0),
120 _nsContextPushed(false),
121 _indent(MARKUP_TAB)
122{
123 if (pTextEncoding)
124 {
125 _pTextConverter = new Poco::OutputStreamConverter(str, *_pInEncoding, *pTextEncoding);
126 }
127 else
128 {
129 _encoding = "UTF-8";
130 _pOutEncoding = new Poco::UTF8Encoding;
131 _pTextConverter = new Poco::OutputStreamConverter(str, *_pInEncoding, *_pOutEncoding);
132 }
133 setNewLine((_options & CANONICAL_XML) ? NEWLINE_LF : NEWLINE_DEFAULT);
134}
135
136
137XMLWriter::~XMLWriter()
138{
139 delete _pTextConverter;
140 delete _pInEncoding;
141 delete _pOutEncoding;
142}
143
144
145void XMLWriter::setDocumentLocator(const Locator* /*loc*/)
146{
147}
148
149
150void XMLWriter::setNewLine(const std::string& newLineCharacters)
151{
152 if (newLineCharacters.empty())
153 {
154#if defined(_WIN32)
155 _newLine = NEWLINE_CRLF;
156#else
157 _newLine = NEWLINE_LF;
158#endif
159 }
160 else _newLine = newLineCharacters;
161}
162
163
164const std::string& XMLWriter::getNewLine() const
165{
166 return _newLine;
167}
168
169
170void XMLWriter::setIndent(const std::string& indent)
171{
172 _indent = indent;
173}
174
175
176const std::string& XMLWriter::getIndent() const
177{
178 return _indent;
179}
180
181
182void XMLWriter::startDocument()
183{
184 if (_depth != -1)
185 throw XMLException("Cannot start a document in another document");
186
187 _inFragment = false;
188 _depth = 0;
189 _elementCount = 0;
190 _inDTD = false;
191 _inInternalDTD = false;
192 _prefix = 0;
193
194 if (_options & WRITE_XML_DECLARATION)
195 writeXMLDeclaration();
196
197 _contentWritten = true;
198 _namespaces.reset();
199 _namespaces.pushContext();
200}
201
202
203void XMLWriter::endDocument()
204{
205 if (_depth > 0)
206 throw XMLException("Not well-formed (at least one tag has no matching end tag)");
207 if (_elementCount == 0)
208 throw XMLException("No document element");
209
210 poco_assert_dbg (!_unclosedStartTag);
211
212 _elementCount = 0;
213 _depth = -1;
214}
215
216
217void XMLWriter::startFragment()
218{
219 if (_depth != -1)
220 throw XMLException("Cannot start a fragment in another fragment or document");
221
222 _inFragment = true;
223 _depth = 0;
224 _elementCount = 0;
225 _prefix = 0;
226
227 _contentWritten = true;
228 _namespaces.reset();
229 _namespaces.pushContext();
230}
231
232
233void XMLWriter::endFragment()
234{
235 if (_depth > 1)
236 throw XMLException("Not well-formed (at least one tag has no matching end tag)");
237
238 _inFragment = false;
239 _elementCount = 0;
240 _depth = -1;
241}
242
243
244void XMLWriter::startElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname)
245{
246 const AttributesImpl attributes;
247 startElement(namespaceURI, localName, qname, attributes);
248}
249
250
251void XMLWriter::startElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
252{
253 if (_depth == 0 && !_inFragment && _elementCount > 1)
254 throw XMLException("Not well-formed. Second root element found", nameToString(localName, qname));
255
256 if (_unclosedStartTag) closeStartTag();
257 prettyPrint();
258 if (_options & CANONICAL_XML)
259 writeCanonicalStartElement(namespaceURI, localName, qname, attributes);
260 else
261 writeStartElement(namespaceURI, localName, qname, attributes);
262 _elementStack.push_back(Name(qname, namespaceURI, localName));
263 _contentWritten = false;
264 ++_depth;
265}
266
267
268void XMLWriter::endElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname)
269{
270 if (_depth < 1)
271 throw XMLException("No unclosed tag");
272
273 if (!_elementStack.back().equalsWeakly(qname, namespaceURI, localName))
274 throw XMLException("End tag does not match start tag", nameToString(localName, qname));
275
276 _elementStack.pop_back();
277 --_depth;
278 if (!_unclosedStartTag) prettyPrint();
279 writeEndElement(namespaceURI, localName, qname);
280 _contentWritten = false;
281 if (_depth == 0)
282 writeNewLine();
283}
284
285
286void XMLWriter::emptyElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname)
287{
288 const AttributesImpl attributes;
289 emptyElement(namespaceURI, localName, qname, attributes);
290}
291
292
293void XMLWriter::emptyElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
294{
295 if (_depth == 0 && _elementCount > 1)
296 throw XMLException("Not well-formed. Second root element found.");
297
298 if (_unclosedStartTag) closeStartTag();
299 prettyPrint();
300 if (_options & CANONICAL_XML)
301 writeCanonicalStartElement(namespaceURI, localName, qname, attributes);
302 else
303 writeStartElement(namespaceURI, localName, qname, attributes);
304 _contentWritten = false;
305 writeMarkup("/");
306 closeStartTag();
307 _namespaces.popContext();
308}
309
310
311void XMLWriter::characters(const XMLChar ch[], int start, int length)
312{
313 if (length == 0) return;
314
315 if (_unclosedStartTag) closeStartTag();
316 _contentWritten = _contentWritten || length > 0;
317 if (_inCDATA)
318 {
319 while (length-- > 0) writeXML(ch[start++]);
320 }
321 else
322 {
323 while (length-- > 0)
324 {
325 XMLChar c = ch[start++];
326 switch (c)
327 {
328 case '"': writeMarkup(MARKUP_QUOTENC); break;
329 case '&': writeMarkup(MARKUP_AMPENC); break;
330 case '<': writeMarkup(MARKUP_LTENC); break;
331 case '>': writeMarkup(MARKUP_GTENC); break;
332 default:
333 if (c >= 0 && c < 32)
334 {
335 if (c == '\t' || c == '\r' || c == '\n')
336 writeXML(c);
337 else
338 throw XMLException("Invalid character token.");
339 }
340 else writeXML(c);
341 }
342 }
343 }
344}
345
346
347void XMLWriter::characters(const XMLString& str)
348{
349 characters(str.data(), 0, (int) str.length());
350}
351
352
353void XMLWriter::rawCharacters(const XMLString& str)
354{
355 if (_unclosedStartTag) closeStartTag();
356 _contentWritten = _contentWritten || !str.empty();
357 writeXML(str);
358}
359
360
361void XMLWriter::ignorableWhitespace(const XMLChar ch[], int start, int length)
362{
363 characters(ch, start, length);
364}
365
366
367void XMLWriter::processingInstruction(const XMLString& target, const XMLString& data)
368{
369 if (_unclosedStartTag) closeStartTag();
370 prettyPrint();
371 writeMarkup("<?");
372 writeXML(target);
373 if (!data.empty())
374 {
375 writeMarkup(MARKUP_SPACE);
376 writeXML(data);
377 }
378 writeMarkup("?>");
379 if (_depth == 0)
380 writeNewLine();
381}
382
383
384namespace
385{
386 static const XMLString CDATA = toXMLString("CDATA");
387}
388
389
390void XMLWriter::dataElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname,
391 const XMLString& data,
392 const XMLString& attr1, const XMLString& value1,
393 const XMLString& attr2, const XMLString& value2,
394 const XMLString& attr3, const XMLString& value3)
395{
396 AttributesImpl attributes;
397 if (!attr1.empty()) attributes.addAttribute(XMLString(), XMLString(), attr1, CDATA, value1);
398 if (!attr2.empty()) attributes.addAttribute(XMLString(), XMLString(), attr2, CDATA, value2);
399 if (!attr3.empty()) attributes.addAttribute(XMLString(), XMLString(), attr3, CDATA, value3);
400 if (data.empty())
401 {
402 emptyElement(namespaceURI, localName, qname, attributes);
403 }
404 else
405 {
406 startElement(namespaceURI, localName, qname, attributes);
407 characters(data);
408 endElement(namespaceURI, localName, qname);
409 }
410}
411
412
413void XMLWriter::startPrefixMapping(const XMLString& prefix, const XMLString& namespaceURI)
414{
415 if (prefix != NamespaceSupport::XML_NAMESPACE_PREFIX)
416 {
417 if (!_nsContextPushed)
418 {
419 _namespaces.pushContext();
420 _nsContextPushed = true;
421 }
422 _namespaces.declarePrefix(prefix, namespaceURI);
423 }
424}
425
426
427void XMLWriter::endPrefixMapping(const XMLString& /*prefix*/)
428{
429 // Note: prefix removed by popContext() at element closing tag
430}
431
432
433void XMLWriter::skippedEntity(const XMLString& /*name*/)
434{
435}
436
437
438void XMLWriter::startCDATA()
439{
440 if (_inCDATA) throw XMLException("Cannot nest CDATA sections");
441 if (_unclosedStartTag) closeStartTag();
442 _inCDATA = true;
443 writeMarkup(MARKUP_BEGIN_CDATA);
444}
445
446
447void XMLWriter::endCDATA()
448{
449 poco_assert (_inCDATA);
450 _inCDATA = false;
451 writeMarkup(MARKUP_END_CDATA);
452}
453
454
455void XMLWriter::comment(const XMLChar ch[], int start, int length)
456{
457 if (_unclosedStartTag) closeStartTag();
458 prettyPrint();
459 writeMarkup("<!--");
460 while (length-- > 0) writeXML(ch[start++]);
461 writeMarkup("-->");
462 _contentWritten = false;
463}
464
465
466void XMLWriter::startDTD(const XMLString& name, const XMLString& publicId, const XMLString& systemId)
467{
468 writeMarkup("<!DOCTYPE ");
469 writeXML(name);
470 if (!publicId.empty())
471 {
472 writeMarkup(" PUBLIC \"");
473 writeXML(publicId);
474 writeMarkup("\"");
475 }
476 if (!systemId.empty())
477 {
478 if (publicId.empty())
479 {
480 writeMarkup(" SYSTEM");
481 }
482 writeMarkup(" \"");
483 writeXML(systemId);
484 writeMarkup("\"");
485 }
486 _inDTD = true;
487}
488
489
490void XMLWriter::endDTD()
491{
492 poco_assert (_inDTD);
493 if (_inInternalDTD)
494 {
495 writeNewLine();
496 writeMarkup("]");
497 _inInternalDTD = false;
498 }
499 writeMarkup(">");
500 writeNewLine();
501 _inDTD = false;
502}
503
504
505void XMLWriter::startEntity(const XMLString& /*name*/)
506{
507}
508
509
510void XMLWriter::endEntity(const XMLString& /*name*/)
511{
512}
513
514
515void XMLWriter::notationDecl(const XMLString& name, const XMLString* publicId, const XMLString* systemId)
516{
517 if (!_inDTD) throw XMLException("Notation declaration not within DTD");
518 if (!_inInternalDTD)
519 {
520 writeMarkup(" [");
521 _inInternalDTD = true;
522 }
523 if (_options & PRETTY_PRINT)
524 {
525 writeNewLine();
526 writeMarkup(_indent);
527 }
528 writeMarkup("<!NOTATION ");
529 writeXML(name);
530 if (systemId && !systemId->empty())
531 {
532 writeMarkup(" SYSTEM \"");
533 writeXML(*systemId);
534 writeMarkup("\"");
535 }
536 if (publicId && !publicId->empty())
537 {
538 writeMarkup(" PUBLIC \"");
539 writeXML(*publicId);
540 writeMarkup("\"");
541 }
542 writeMarkup(">");
543}
544
545
546void XMLWriter::unparsedEntityDecl(const XMLString& name, const XMLString* publicId, const XMLString& systemId, const XMLString& notationName)
547{
548 if (!_inDTD) throw XMLException("Entity declaration not within DTD");
549 if (!_inInternalDTD)
550 {
551 writeMarkup(" [");
552 _inInternalDTD = true;
553 }
554 if (_options & PRETTY_PRINT)
555 {
556 writeNewLine();
557 writeMarkup(_indent);
558 }
559 writeMarkup("<!ENTITY ");
560 writeXML(name);
561 if (!systemId.empty())
562 {
563 writeMarkup(" SYSTEM \"");
564 writeXML(systemId);
565 writeMarkup("\"");
566 }
567 if (publicId && !publicId->empty())
568 {
569 writeMarkup(" PUBLIC \"");
570 writeXML(*publicId);
571 writeMarkup("\"");
572 }
573 if (!notationName.empty())
574 {
575 writeMarkup(" NDATA ");
576 writeXML(notationName);
577 }
578 writeMarkup(">");
579}
580
581
582void XMLWriter::prettyPrint() const
583{
584 if ((_options & PRETTY_PRINT) && !_contentWritten)
585 {
586 writeNewLine();
587 writeIndent();
588 }
589}
590
591
592void XMLWriter::writeStartElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
593{
594 if (!_nsContextPushed)
595 _namespaces.pushContext();
596 _nsContextPushed = false;
597 ++_elementCount;
598
599 declareAttributeNamespaces(attributes);
600
601 writeMarkup(MARKUP_LT);
602 if (!localName.empty() && (qname.empty() || localName == qname))
603 {
604 XMLString prefix;
605 if (!namespaceURI.empty() && !_namespaces.isMapped(namespaceURI))
606 {
607 prefix = uniquePrefix();
608 _namespaces.declarePrefix(prefix, namespaceURI);
609 }
610 else prefix = _namespaces.getPrefix(namespaceURI);
611 writeName(prefix, localName);
612 }
613 else if (namespaceURI.empty() && localName.empty() && !qname.empty())
614 {
615 writeXML(qname);
616 }
617 else if (!localName.empty() && !qname.empty())
618 {
619 XMLString local;
620 XMLString prefix;
621 Name::split(qname, prefix, local);
622 if (prefix.empty()) prefix = _namespaces.getPrefix(namespaceURI);
623 const XMLString& uri = _namespaces.getURI(prefix);
624 if ((uri.empty() || uri != namespaceURI) && !namespaceURI.empty())
625 {
626 _namespaces.declarePrefix(prefix, namespaceURI);
627 }
628 writeName(prefix, localName);
629 }
630 else throw XMLException("Tag mismatch", nameToString(localName, qname));
631
632 AttributeMap attributeMap;
633 addNamespaceAttributes(attributeMap);
634 addAttributes(attributeMap, attributes, namespaceURI);
635 writeAttributes(attributeMap);
636 _unclosedStartTag = true;
637}
638
639
640void XMLWriter::writeCanonicalStartElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
641{
642 if (!_nsContextPushed)
643 _namespaces.pushContext();
644 _nsContextPushed = false;
645 ++_elementCount;
646
647 declareNamespaces(namespaceURI, localName, qname, attributes);
648
649 writeMarkup(MARKUP_LT);
650 if (!localName.empty())
651 {
652 writeName(_namespaces.getPrefix(namespaceURI), localName);
653 }
654 else if (namespaceURI.empty() && !qname.empty())
655 {
656 writeXML(qname);
657 }
658 else throw XMLException("Tag mismatch", nameToString(localName, qname));
659
660 CanonicalAttributeMap namespaceAttributeMap;
661 addNamespaceAttributes(namespaceAttributeMap);
662 writeAttributes(namespaceAttributeMap);
663 CanonicalAttributeMap attributeMap;
664 addAttributes(attributeMap, attributes, namespaceURI);
665 writeAttributes(attributeMap);
666 _unclosedStartTag = true;
667}
668
669
670void XMLWriter::writeEndElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname)
671{
672 if (_unclosedStartTag && !(_options & CANONICAL_XML))
673 {
674 writeMarkup(MARKUP_SLASHGT);
675 _unclosedStartTag = false;
676 }
677 else
678 {
679 if (_unclosedStartTag)
680 {
681 writeMarkup(MARKUP_GT);
682 _unclosedStartTag = false;
683 }
684 writeMarkup(MARKUP_LTSLASH);
685 if (!localName.empty())
686 {
687 XMLString prefix = _namespaces.getPrefix(namespaceURI);
688 writeName(prefix, localName);
689 }
690 else
691 {
692 writeXML(qname);
693 }
694 writeMarkup(MARKUP_GT);
695 }
696 _namespaces.popContext();
697}
698
699
700void XMLWriter::closeStartTag()
701{
702 _unclosedStartTag = false;
703 writeMarkup(MARKUP_GT);
704}
705
706
707void XMLWriter::declareNamespaces(const XMLString& namespaceURI, const XMLString& /*localName*/, const XMLString& qname, const Attributes& attributes)
708{
709 std::map<XMLString, std::set<XMLString> > usedNamespaces;
710 bool defaultNameSpaceUsed = false;
711 XMLString defaultNamespaceURI = _namespaces.getURI(XMLString());
712 XMLString local;
713 XMLString prefix;
714 XMLString elementNamespaceURI = namespaceURI;
715 Name::split(qname, prefix, local);
716 if (elementNamespaceURI.empty())
717 elementNamespaceURI = _namespaces.getURI(prefix);
718 if (!elementNamespaceURI.empty())
719 {
720 usedNamespaces[prefix].insert(elementNamespaceURI);
721 if (!defaultNamespaceURI.empty() && elementNamespaceURI == defaultNamespaceURI)
722 defaultNameSpaceUsed = true;
723 }
724 for (int i = 0; i < attributes.getLength(); i++)
725 {
726 XMLString attributeNamespaceURI = attributes.getURI(i);
727 XMLString attributeLocalName = attributes.getLocalName(i);
728 XMLString attributeQName = attributes.getQName(i);
729
730 XMLString attributePrefix;
731 XMLString attributeLocal;
732 Name::split(attributeQName, attributePrefix, attributeLocal);
733 if (attributeNamespaceURI.empty())
734 attributeNamespaceURI = _namespaces.getURI(prefix);
735 if (!attributeNamespaceURI.empty())
736 {
737 usedNamespaces[attributePrefix].insert(attributeNamespaceURI);
738 defaultNameSpaceUsed = defaultNameSpaceUsed || (!defaultNamespaceURI.empty() && attributeNamespaceURI == defaultNamespaceURI);
739 }
740 }
741 for (std::map<XMLString, std::set<XMLString> >::const_iterator it = usedNamespaces.begin(); it != usedNamespaces.end(); ++it)
742 {
743 const std::set<XMLString> namespaceURIs = it->second;
744 for (std::set<XMLString>::const_iterator itURI = namespaceURIs.begin(); itURI != namespaceURIs.end(); ++itURI)
745 {
746 XMLString prefix2 = it->first;
747 if (prefix2.empty())
748 prefix2 = _namespaces.getPrefix(*itURI);
749 if (prefix2.empty() && !_namespaces.isMapped(*itURI))
750 {
751 if (defaultNameSpaceUsed)
752 {
753 if (*itURI != defaultNamespaceURI)
754 prefix2 = uniquePrefix();
755 }
756 else
757 {
758 defaultNamespaceURI = *itURI;
759 defaultNameSpaceUsed = true;
760 }
761
762 }
763 const XMLString& uri = _namespaces.getURI(prefix2);
764 if ((uri.empty() || uri != *itURI) && !itURI->empty())
765 {
766 _namespaces.declarePrefix(prefix2, *itURI);
767 }
768 }
769 }
770}
771
772
773void XMLWriter::declareAttributeNamespaces(const Attributes& attributes)
774{
775 for (int i = 0; i < attributes.getLength(); i++)
776 {
777 XMLString namespaceURI = attributes.getURI(i);
778 XMLString localName = attributes.getLocalName(i);
779 XMLString qname = attributes.getQName(i);
780 if (!localName.empty())
781 {
782 XMLString prefix;
783 XMLString splitLocalName;
784 Name::split(qname, prefix, splitLocalName);
785 if (prefix.empty()) prefix = _namespaces.getPrefix(namespaceURI);
786 if (prefix.empty() && !namespaceURI.empty() && !_namespaces.isMapped(namespaceURI))
787 {
788 prefix = uniquePrefix();
789 _namespaces.declarePrefix(prefix, namespaceURI);
790 }
791
792 const XMLString& uri = _namespaces.getURI(prefix);
793 if ((uri.empty() || uri != namespaceURI) && !namespaceURI.empty())
794 {
795 _namespaces.declarePrefix(prefix, namespaceURI);
796 }
797 }
798 }
799}
800
801
802void XMLWriter::addNamespaceAttributes(AttributeMap& attributeMap)
803{
804 NamespaceSupport::PrefixSet prefixes;
805 _namespaces.getDeclaredPrefixes(prefixes);
806 for (NamespaceSupport::PrefixSet::const_iterator it = prefixes.begin(); it != prefixes.end(); ++it)
807 {
808 XMLString prefix = *it;
809 XMLString uri = _namespaces.getURI(prefix);
810 XMLString qname = NamespaceSupport::XMLNS_NAMESPACE_PREFIX;
811
812 if (!prefix.empty())
813 {
814 qname.append(toXMLString(MARKUP_COLON));
815 qname.append(prefix);
816 }
817 attributeMap[qname] = uri;
818 }
819}
820
821
822void XMLWriter::addNamespaceAttributes(CanonicalAttributeMap& attributeMap)
823{
824 NamespaceSupport::PrefixSet prefixes;
825 _namespaces.getDeclaredPrefixes(prefixes);
826 for (NamespaceSupport::PrefixSet::const_iterator it = prefixes.begin(); it != prefixes.end(); ++it)
827 {
828 XMLString prefix = *it;
829 XMLString uri = _namespaces.getURI(prefix);
830 XMLString qname = NamespaceSupport::XMLNS_NAMESPACE_PREFIX;
831
832 if (!prefix.empty())
833 {
834 qname.append(toXMLString(MARKUP_COLON));
835 qname.append(prefix);
836 }
837 attributeMap.insert(std::make_pair(qname, std::make_pair(qname, uri)));
838 }
839}
840
841
842void XMLWriter::addAttributes(AttributeMap& attributeMap, const Attributes& attributes, const XMLString& /*elementNamespaceURI*/)
843{
844 for (int i = 0; i < attributes.getLength(); i++)
845 {
846 XMLString namespaceURI = attributes.getURI(i);
847 XMLString localName = attributes.getLocalName(i);
848 XMLString qname = attributes.getQName(i);
849 if (!localName.empty())
850 {
851 XMLString prefix;
852 if (!namespaceURI.empty())
853 prefix = _namespaces.getPrefix(namespaceURI);
854 if (!prefix.empty())
855 {
856 qname = prefix;
857 qname.append(toXMLString(MARKUP_COLON));
858 }
859 else qname.clear();
860 qname.append(localName);
861 }
862 attributeMap[qname] = attributes.getValue(i);
863 }
864}
865
866
867void XMLWriter::addAttributes(CanonicalAttributeMap& attributeMap, const Attributes& attributes, const XMLString& /*elementNamespaceURI*/)
868{
869 for (int i = 0; i < attributes.getLength(); i++)
870 {
871 XMLString namespaceURI = attributes.getURI(i);
872 XMLString localName = attributes.getLocalName(i);
873 XMLString qname = attributes.getQName(i);
874 XMLString fullQName = qname;
875 if (!localName.empty())
876 {
877 XMLString prefix;
878 if (!namespaceURI.empty())
879 {
880 prefix = _namespaces.getPrefix(namespaceURI);
881 fullQName = namespaceURI;
882 fullQName.append(toXMLString(MARKUP_COLON));
883 }
884 else fullQName.clear();
885 if (!prefix.empty())
886 {
887 qname = prefix;
888 qname.append(toXMLString(MARKUP_COLON));
889 }
890 else qname.clear();
891 qname.append(localName);
892 fullQName.append(localName);
893 }
894 attributeMap.insert(std::make_pair(fullQName, std::make_pair(qname, attributes.getValue(i))));
895 }
896}
897
898
899void XMLWriter::writeAttributes(const AttributeMap& attributeMap)
900{
901 for (AttributeMap::const_iterator it = attributeMap.begin(); it != attributeMap.end(); ++it)
902 {
903 if ((_options & PRETTY_PRINT) && (_options & PRETTY_PRINT_ATTRIBUTES))
904 {
905 writeNewLine();
906 writeIndent(_depth + 1);
907 }
908 else
909 {
910 writeMarkup(MARKUP_SPACE);
911 }
912 writeXML(it->first);
913 writeMarkup(MARKUP_EQQUOT);
914 for (XMLString::const_iterator itc = it->second.begin(); itc != it->second.end(); ++itc)
915 {
916 XMLChar c = *itc;
917 switch (c)
918 {
919 case '"': writeMarkup(MARKUP_QUOTENC); break;
920 case '&': writeMarkup(MARKUP_AMPENC); break;
921 case '<': writeMarkup(MARKUP_LTENC); break;
922 case '>': writeMarkup(MARKUP_GTENC); break;
923 case '\t': writeMarkup(MARKUP_TABENC); break;
924 case '\r': writeMarkup(MARKUP_CRENC); break;
925 case '\n': writeMarkup(MARKUP_LFENC); break;
926 default:
927 if (c >= 0 && c < 32)
928 throw XMLException("Invalid character token.");
929 else
930 writeXML(c);
931 }
932 }
933 writeMarkup(MARKUP_QUOT);
934 }
935}
936
937
938void XMLWriter::writeAttributes(const CanonicalAttributeMap& attributeMap)
939{
940 for (CanonicalAttributeMap::const_iterator it = attributeMap.begin(); it != attributeMap.end(); ++it)
941 {
942 if ((_options & PRETTY_PRINT) && (_options & PRETTY_PRINT_ATTRIBUTES))
943 {
944 writeNewLine();
945 writeIndent(_depth + 1);
946 }
947 else
948 {
949 writeMarkup(MARKUP_SPACE);
950 }
951 writeXML(it->second.first);
952 writeMarkup(MARKUP_EQQUOT);
953 for (XMLString::const_iterator itc = it->second.second.begin(); itc != it->second.second.end(); ++itc)
954 {
955 XMLChar c = *itc;
956 switch (c)
957 {
958 case '"': writeMarkup(MARKUP_QUOTENC); break;
959 case '&': writeMarkup(MARKUP_AMPENC); break;
960 case '<': writeMarkup(MARKUP_LTENC); break;
961 case '>': writeMarkup(MARKUP_GTENC); break;
962 case '\t': writeMarkup(MARKUP_TABENC); break;
963 case '\r': writeMarkup(MARKUP_CRENC); break;
964 case '\n': writeMarkup(MARKUP_LFENC); break;
965 default:
966 if (c >= 0 && c < 32)
967 throw XMLException("Invalid character token.");
968 else
969 writeXML(c);
970 }
971 }
972 writeMarkup(MARKUP_QUOT);
973 }
974}
975
976
977void XMLWriter::writeMarkup(const std::string& str) const
978{
979#if defined(XML_UNICODE_WCHAR_T)
980 const XMLString xmlString = toXMLString(str);
981 writeXML(xmlString);
982#else
983 _pTextConverter->write(str.data(), (int) str.size());
984#endif
985}
986
987
988void XMLWriter::writeXML(const XMLString& str) const
989{
990 _pTextConverter->write((const char*) str.data(), (int) str.size()*sizeof(XMLChar));
991}
992
993
994void XMLWriter::writeXML(XMLChar ch) const
995{
996 _pTextConverter->write((const char*) &ch, sizeof(ch));
997}
998
999
1000void XMLWriter::writeName(const XMLString& prefix, const XMLString& localName)
1001{
1002 if (prefix.empty())
1003 {
1004 writeXML(localName);
1005 }
1006 else
1007 {
1008 writeXML(prefix);
1009 writeMarkup(MARKUP_COLON);
1010 writeXML(localName);
1011 }
1012}
1013
1014
1015void XMLWriter::writeNewLine() const
1016{
1017 if (_options & PRETTY_PRINT)
1018 writeMarkup(_newLine);
1019}
1020
1021
1022void XMLWriter::writeIndent() const
1023{
1024 writeIndent(_depth);
1025}
1026
1027
1028void XMLWriter::writeIndent(int depth) const
1029{
1030 for (int i = 0; i < depth; ++i)
1031 writeMarkup(_indent);
1032}
1033
1034
1035void XMLWriter::writeXMLDeclaration()
1036{
1037 writeMarkup("<?xml version=\"1.0\"");
1038 if (!_encoding.empty())
1039 {
1040 writeMarkup(" encoding=\"");
1041 writeMarkup(_encoding);
1042 writeMarkup("\"");
1043 }
1044 writeMarkup("?>");
1045 writeNewLine();
1046}
1047
1048
1049std::string XMLWriter::nameToString(const XMLString& localName, const XMLString& qname)
1050{
1051 if (qname.empty())
1052 return fromXMLString(localName);
1053 else
1054 return fromXMLString(qname);
1055}
1056
1057
1058XMLString XMLWriter::uniquePrefix()
1059{
1060 std::ostringstream str;
1061 str << "ns" << ++_prefix;
1062 return toXMLString(str.str());
1063}
1064
1065
1066bool XMLWriter::isNamespaceMapped(const XMLString& namespc) const
1067{
1068 return _namespaces.isMapped(namespc);
1069}
1070
1071
1072} } // namespace Poco::XML
1073