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 | |
24 | namespace Poco { |
25 | namespace XML { |
26 | |
27 | |
28 | const std::string XMLWriter::NEWLINE_DEFAULT; |
29 | const std::string XMLWriter::NEWLINE_CR = "\r" ; |
30 | const std::string XMLWriter::NEWLINE_CRLF = "\r\n" ; |
31 | const std::string XMLWriter::NEWLINE_LF = "\n" ; |
32 | const std::string XMLWriter::MARKUP_QUOTENC = """ ; |
33 | const std::string XMLWriter::MARKUP_AMPENC = "&" ; |
34 | const std::string XMLWriter::MARKUP_LTENC = "<" ; |
35 | const std::string XMLWriter::MARKUP_GTENC = ">" ; |
36 | const std::string XMLWriter::MARKUP_TABENC = "	" ; |
37 | const std::string XMLWriter::MARKUP_CRENC = "
" ; |
38 | const std::string XMLWriter::MARKUP_LFENC = "
" ; |
39 | const std::string XMLWriter::MARKUP_LT = "<" ; |
40 | const std::string XMLWriter::MARKUP_GT = ">" ; |
41 | const std::string XMLWriter::MARKUP_SLASHGT = "/>" ; |
42 | const std::string XMLWriter::MARKUP_LTSLASH = "</" ; |
43 | const std::string XMLWriter::MARKUP_COLON = ":" ; |
44 | const std::string XMLWriter::MARKUP_EQQUOT = "=\"" ; |
45 | const std::string XMLWriter::MARKUP_QUOT = "\"" ; |
46 | const std::string XMLWriter::MARKUP_SPACE = " " ; |
47 | const std::string XMLWriter::MARKUP_TAB = "\t" ; |
48 | const std::string XMLWriter::MARKUP_BEGIN_CDATA = "<![CDATA[" ; |
49 | const 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 | |
59 | XMLWriter::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 | |
82 | XMLWriter::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 | |
105 | XMLWriter::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 | |
137 | XMLWriter::~XMLWriter() |
138 | { |
139 | delete _pTextConverter; |
140 | delete _pInEncoding; |
141 | delete _pOutEncoding; |
142 | } |
143 | |
144 | |
145 | void XMLWriter::setDocumentLocator(const Locator* loc) |
146 | { |
147 | } |
148 | |
149 | |
150 | void 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 | |
164 | const std::string& XMLWriter::getNewLine() const |
165 | { |
166 | return _newLine; |
167 | } |
168 | |
169 | |
170 | void XMLWriter::setIndent(const std::string& indent) |
171 | { |
172 | _indent = indent; |
173 | } |
174 | |
175 | |
176 | const std::string& XMLWriter::getIndent() const |
177 | { |
178 | return _indent; |
179 | } |
180 | |
181 | |
182 | void 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 | |
203 | void 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 | |
217 | void 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 | |
233 | void 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 | |
244 | void 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 | |
251 | void 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 | |
268 | void 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 | |
286 | void 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 | |
293 | void 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 | |
311 | void 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 | |
347 | void XMLWriter::characters(const XMLString& str) |
348 | { |
349 | characters(str.data(), 0, (int) str.length()); |
350 | } |
351 | |
352 | |
353 | void XMLWriter::rawCharacters(const XMLString& str) |
354 | { |
355 | if (_unclosedStartTag) closeStartTag(); |
356 | _contentWritten = _contentWritten || !str.empty(); |
357 | writeXML(str); |
358 | } |
359 | |
360 | |
361 | void XMLWriter::ignorableWhitespace(const XMLChar ch[], int start, int length) |
362 | { |
363 | characters(ch, start, length); |
364 | } |
365 | |
366 | |
367 | void 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 | |
384 | namespace |
385 | { |
386 | static const XMLString CDATA = toXMLString("CDATA" ); |
387 | } |
388 | |
389 | |
390 | void 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 | |
413 | void 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 | |
427 | void XMLWriter::endPrefixMapping(const XMLString& prefix) |
428 | { |
429 | // Note: prefix removed by popContext() at element closing tag |
430 | } |
431 | |
432 | |
433 | void XMLWriter::skippedEntity(const XMLString& name) |
434 | { |
435 | } |
436 | |
437 | |
438 | void 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 | |
447 | void XMLWriter::endCDATA() |
448 | { |
449 | poco_assert (_inCDATA); |
450 | _inCDATA = false; |
451 | writeMarkup(MARKUP_END_CDATA); |
452 | } |
453 | |
454 | |
455 | void XMLWriter::(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 | |
466 | void 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 | |
490 | void 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 | |
505 | void XMLWriter::startEntity(const XMLString& name) |
506 | { |
507 | } |
508 | |
509 | |
510 | void XMLWriter::endEntity(const XMLString& name) |
511 | { |
512 | } |
513 | |
514 | |
515 | void 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 | |
546 | void 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 | |
582 | void XMLWriter::prettyPrint() const |
583 | { |
584 | if ((_options & PRETTY_PRINT) && !_contentWritten) |
585 | { |
586 | writeNewLine(); |
587 | writeIndent(); |
588 | } |
589 | } |
590 | |
591 | |
592 | void 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 | |
640 | void 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 | |
670 | void 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 | |
700 | void XMLWriter::closeStartTag() |
701 | { |
702 | _unclosedStartTag = false; |
703 | writeMarkup(MARKUP_GT); |
704 | } |
705 | |
706 | |
707 | void 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 prefix = it->first; |
747 | if (prefix.empty()) |
748 | prefix = _namespaces.getPrefix(*itURI); |
749 | if (prefix.empty() && !_namespaces.isMapped(*itURI)) |
750 | { |
751 | if (defaultNameSpaceUsed) |
752 | { |
753 | if (*itURI != defaultNamespaceURI) |
754 | prefix = uniquePrefix(); |
755 | } |
756 | else |
757 | { |
758 | defaultNamespaceURI = *itURI; |
759 | defaultNameSpaceUsed = true; |
760 | } |
761 | |
762 | } |
763 | const XMLString& uri = _namespaces.getURI(prefix); |
764 | if ((uri.empty() || uri != *itURI) && !itURI->empty()) |
765 | { |
766 | _namespaces.declarePrefix(prefix, *itURI); |
767 | } |
768 | } |
769 | } |
770 | } |
771 | |
772 | |
773 | void 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 | |
802 | void 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 | |
822 | void 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 | |
842 | void 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 | |
867 | void 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 | |
899 | void 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 | |
938 | void 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 | |
977 | void 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 | |
988 | void XMLWriter::writeXML(const XMLString& str) const |
989 | { |
990 | _pTextConverter->write((const char*) str.data(), (int) str.size()*sizeof(XMLChar)); |
991 | } |
992 | |
993 | |
994 | void XMLWriter::writeXML(XMLChar ch) const |
995 | { |
996 | _pTextConverter->write((const char*) &ch, sizeof(ch)); |
997 | } |
998 | |
999 | |
1000 | void 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 | |
1015 | void XMLWriter::writeNewLine() const |
1016 | { |
1017 | if (_options & PRETTY_PRINT) |
1018 | writeMarkup(_newLine); |
1019 | } |
1020 | |
1021 | |
1022 | void XMLWriter::writeIndent() const |
1023 | { |
1024 | writeIndent(_depth); |
1025 | } |
1026 | |
1027 | |
1028 | void XMLWriter::writeIndent(int depth) const |
1029 | { |
1030 | for (int i = 0; i < depth; ++i) |
1031 | writeMarkup(_indent); |
1032 | } |
1033 | |
1034 | |
1035 | void 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 | |
1049 | std::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 | |
1058 | XMLString XMLWriter::uniquePrefix() |
1059 | { |
1060 | std::ostringstream str; |
1061 | str << "ns" << ++_prefix; |
1062 | return toXMLString(str.str()); |
1063 | } |
1064 | |
1065 | |
1066 | bool XMLWriter::isNamespaceMapped(const XMLString& namespc) const |
1067 | { |
1068 | return _namespaces.isMapped(namespc); |
1069 | } |
1070 | |
1071 | |
1072 | } } // namespace Poco::XML |
1073 | |