/src/xerces-c/src/xercesc/dom/impl/DOMLSSerializerImpl.cpp
Line | Count | Source |
1 | | /* |
2 | | * Licensed to the Apache Software Foundation (ASF) under one or more |
3 | | * contributor license agreements. See the NOTICE file distributed with |
4 | | * this work for additional information regarding copyright ownership. |
5 | | * The ASF licenses this file to You under the Apache License, Version 2.0 |
6 | | * (the "License"); you may not use this file except in compliance with |
7 | | * the License. You may obtain a copy of the License at |
8 | | * |
9 | | * http://www.apache.org/licenses/LICENSE-2.0 |
10 | | * |
11 | | * Unless required by applicable law or agreed to in writing, software |
12 | | * distributed under the License is distributed on an "AS IS" BASIS, |
13 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 | | * See the License for the specific language governing permissions and |
15 | | * limitations under the License. |
16 | | */ |
17 | | |
18 | | /* |
19 | | * $Id: DOMLSSerializerImpl.cpp 1824086 2018-02-13 00:54:01Z scantor $ |
20 | | */ |
21 | | |
22 | | #include "DOMLSSerializerImpl.hpp" |
23 | | #include "DOMLSOutputImpl.hpp" |
24 | | #include "DOMErrorImpl.hpp" |
25 | | #include "DOMLocatorImpl.hpp" |
26 | | #include "DOMImplementationImpl.hpp" |
27 | | #include "DOMStringListImpl.hpp" |
28 | | |
29 | | #include <xercesc/framework/MemBufFormatTarget.hpp> |
30 | | #include <xercesc/framework/LocalFileFormatTarget.hpp> |
31 | | |
32 | | #include <xercesc/util/TransService.hpp> |
33 | | #include <xercesc/util/TranscodingException.hpp> |
34 | | #include <xercesc/util/Janitor.hpp> |
35 | | #include <xercesc/util/XMLString.hpp> |
36 | | #include <xercesc/util/XMLUniDefs.hpp> |
37 | | #include <xercesc/util/XMLMsgLoader.hpp> |
38 | | #include <xercesc/dom/StDOMNode.hpp> |
39 | | #include <xercesc/util/OutOfMemoryException.hpp> |
40 | | #include <xercesc/util/XMLChar.hpp> |
41 | | |
42 | | XERCES_CPP_NAMESPACE_BEGIN |
43 | | |
44 | | |
45 | | // --------------------------------------------------------------------------- |
46 | | // Local const data |
47 | | // |
48 | | // --------------------------------------------------------------------------- |
49 | | |
50 | | static const int INVALID_FEATURE_ID = -1; |
51 | | static const int CANONICAL_FORM_ID = 0x0; |
52 | | static const int DISCARD_DEFAULT_CONTENT_ID = 0x1; |
53 | | static const int ENTITIES_ID = 0x2; |
54 | | static const int FORMAT_PRETTY_PRINT_ID = 0x3; |
55 | | static const int NORMALIZE_CHARACTERS_ID = 0x4; |
56 | | static const int SPLIT_CDATA_SECTIONS_ID = 0x5; |
57 | | static const int VALIDATION_ID = 0x6; |
58 | | static const int WHITESPACE_IN_ELEMENT_CONTENT_ID = 0x7; |
59 | | static const int BYTE_ORDER_MARK_ID = 0x8; |
60 | | static const int XML_DECLARATION = 0x9; |
61 | | static const int FORMAT_PRETTY_PRINT_1ST_LEVEL_ID = 0xA; |
62 | | |
63 | | // feature true false |
64 | | // ================================================================================ |
65 | | //canonical-form [optional] Not Supported [required] (default) |
66 | | //discard-default-content [required] (default) [required] |
67 | | //entity [required] (default) [optional] |
68 | | //format-pretty-print [optional] Partially Supported [required] (default) |
69 | | //normalize-characters [optional] Not Supported [required] (default) |
70 | | //split-cdata-sections [required] (default) [required] |
71 | | //validation [optional] Not Supported [required] (default) |
72 | | //whitespace-in-element-content [requierd] (default) [optional] Not Supported |
73 | | // |
74 | | |
75 | | // |
76 | | // Each feature has 2 entries in this array, |
77 | | // the first for "true", |
78 | | // the second for "false". |
79 | | // |
80 | | static const bool featuresSupported[] = { |
81 | | false, true, // canonical-form |
82 | | true, true, // discard-default-content |
83 | | true, true, // entity |
84 | | true, true, // format-pretty-print |
85 | | false, true, // normalize-characters |
86 | | true, true, // split-cdata-sections |
87 | | false, true, // validation |
88 | | true, false, // whitespace-in-element-content |
89 | | true, true, // http://apache.org/xml/features/dom/byte-order-mark |
90 | | true, true, // xml-declaration |
91 | | true, true // http://apache.org/xml/features/pretty-print/space-first-level-elements |
92 | | }; |
93 | | |
94 | | // default end-of-line sequence |
95 | | static const XMLCh gEOLSeq[] = |
96 | | { |
97 | | chLF, chNull |
98 | | }; |
99 | | |
100 | | //UTF-8 |
101 | | static const XMLCh gUTF8[] = |
102 | | { |
103 | | chLatin_U, chLatin_T, chLatin_F, chDash, chDigit_8, chNull |
104 | | }; |
105 | | |
106 | | //</ |
107 | | static const XMLCh gEndElement[] = |
108 | | { |
109 | | chOpenAngle, chForwardSlash, chNull |
110 | | }; |
111 | | |
112 | | //?> |
113 | | static const XMLCh gEndPI[] = |
114 | | { |
115 | | chQuestion, chCloseAngle, chNull |
116 | | }; |
117 | | |
118 | | //<? |
119 | | static const XMLCh gStartPI[] = |
120 | | { |
121 | | chOpenAngle, chQuestion, chNull |
122 | | }; |
123 | | |
124 | | //<?xml version=" |
125 | | static const XMLCh gXMLDecl_VersionInfo[] = |
126 | | { |
127 | | chOpenAngle, chQuestion, chLatin_x, chLatin_m, chLatin_l, chSpace, |
128 | | chLatin_v, chLatin_e, chLatin_r, chLatin_s, chLatin_i, chLatin_o, |
129 | | chLatin_n, chEqual, chDoubleQuote, chNull |
130 | | }; |
131 | | |
132 | | //encoding=" |
133 | | static const XMLCh gXMLDecl_EncodingDecl[] = |
134 | | { |
135 | | chLatin_e, chLatin_n, chLatin_c, chLatin_o, chLatin_d, chLatin_i, |
136 | | chLatin_n, chLatin_g, chEqual, chDoubleQuote, chNull |
137 | | }; |
138 | | |
139 | | //" standalone=" |
140 | | static const XMLCh gXMLDecl_SDDecl[] = |
141 | | { |
142 | | chLatin_s, chLatin_t, chLatin_a, chLatin_n, chLatin_d, chLatin_a, |
143 | | chLatin_l, chLatin_o, chLatin_n, chLatin_e, chEqual, chDoubleQuote, |
144 | | chNull |
145 | | }; |
146 | | |
147 | | //" |
148 | | static const XMLCh gXMLDecl_separator[] = |
149 | | { |
150 | | chDoubleQuote, chSpace, chNull |
151 | | }; |
152 | | |
153 | | //?> |
154 | | static const XMLCh gXMLDecl_endtag[] = |
155 | | { |
156 | | chQuestion, chCloseAngle, chNull |
157 | | }; |
158 | | |
159 | | //<![CDATA[ |
160 | | static const XMLCh gStartCDATA[] = |
161 | | { |
162 | | chOpenAngle, chBang, chOpenSquare, chLatin_C, chLatin_D, |
163 | | chLatin_A, chLatin_T, chLatin_A, chOpenSquare, chNull |
164 | | }; |
165 | | |
166 | | //]]> |
167 | | static const XMLCh gEndCDATA[] = |
168 | | { |
169 | | // chCloseSquare, chCloseAngle, chCloseAngle, chNull // test only: ]>> |
170 | | chCloseSquare, chCloseSquare, chCloseAngle, chNull |
171 | | }; |
172 | | |
173 | | //<!-- |
174 | | static const XMLCh gStartComment[] = |
175 | | { |
176 | | chOpenAngle, chBang, chDash, chDash, chNull |
177 | | }; |
178 | | |
179 | | //--> |
180 | | static const XMLCh gEndComment[] = |
181 | | { |
182 | | chDash, chDash, chCloseAngle, chNull |
183 | | }; |
184 | | |
185 | | //<!DOCTYPE |
186 | | static const XMLCh gStartDoctype[] = |
187 | | { |
188 | | chOpenAngle, chBang, chLatin_D, chLatin_O, chLatin_C, chLatin_T, |
189 | | chLatin_Y, chLatin_P, chLatin_E, chSpace, chNull |
190 | | }; |
191 | | |
192 | | //PUBLIC " |
193 | | static const XMLCh gPublic[] = |
194 | | { |
195 | | chLatin_P, chLatin_U, chLatin_B, chLatin_L, chLatin_I, |
196 | | chLatin_C, chSpace, chDoubleQuote, chNull |
197 | | }; |
198 | | |
199 | | //SYSTEM " |
200 | | static const XMLCh gSystem[] = |
201 | | { |
202 | | chLatin_S, chLatin_Y, chLatin_S, chLatin_T, chLatin_E, |
203 | | chLatin_M, chSpace, chDoubleQuote, chNull |
204 | | }; |
205 | | |
206 | | //<!ENTITY |
207 | | static const XMLCh gStartEntity[] = |
208 | | { |
209 | | chOpenAngle, chBang, chLatin_E, chLatin_N, chLatin_T, chLatin_I, |
210 | | chLatin_T, chLatin_Y, chSpace, chNull |
211 | | }; |
212 | | |
213 | | //NDATA " |
214 | | static const XMLCh gNotation[] = |
215 | | { |
216 | | chLatin_N, chLatin_D, chLatin_A, chLatin_T, chLatin_A, |
217 | | chSpace, chDoubleQuote, chNull |
218 | | }; |
219 | | |
220 | | static const XMLByte BOM_utf8[] = {(XMLByte)0xEF, (XMLByte)0xBB, (XMLByte)0xBF, (XMLByte) 0}; |
221 | | static const XMLByte BOM_utf16be[] = {(XMLByte)0xFE, (XMLByte)0xFF, (XMLByte) 0}; |
222 | | static const XMLByte BOM_utf16le[] = {(XMLByte)0xFF, (XMLByte)0xFE, (XMLByte) 0}; |
223 | | static const XMLByte BOM_ucs4be[] = {(XMLByte)0x00, (XMLByte)0x00, (XMLByte)0xFE, (XMLByte)0xFF, (XMLByte) 0}; |
224 | | static const XMLByte BOM_ucs4le[] = {(XMLByte)0xFF, (XMLByte)0xFE, (XMLByte)0x00, (XMLByte)0x00, (XMLByte) 0}; |
225 | | |
226 | | // |
227 | | // Notification of the error though error handler |
228 | | // |
229 | | // The application may instruct the engine to abort serialization |
230 | | // by returning "false". |
231 | | // |
232 | | // REVISIT: update the locator ctor once the line#, col#, uri and offset |
233 | | // are available from DOM3 core |
234 | | // |
235 | | // REVISIT: use throwing exception to abort serialization is an interesting |
236 | | // thing here, since the serializer is a recusive function, we |
237 | | // can't use return, obviously. However we may have multiple try/catch |
238 | | // along its way going back to write(). So far we don't come up with a |
239 | | // "short-cut" to go "directly" back. |
240 | | // |
241 | 0 | #define TRY_CATCH_THROW(action) \ |
242 | 0 | fFormatter->setUnRepFlags(XMLFormatter::UnRep_Fail); \ |
243 | 0 | try \ |
244 | 0 | { \ |
245 | 0 | action; \ |
246 | 0 | } \ |
247 | 0 | catch(TranscodingException const &e) \ |
248 | 0 | { \ |
249 | 0 | reportError(nodeToWrite, DOMError::DOM_SEVERITY_FATAL_ERROR, e.getMessage()); \ |
250 | 0 | throw e; \ |
251 | 0 | } |
252 | | |
253 | | DOMLSSerializerImpl::~DOMLSSerializerImpl() |
254 | 0 | { |
255 | 0 | fMemoryManager->deallocate(fNewLine);//delete [] fNewLine; |
256 | 0 | delete fNamespaceStack; |
257 | 0 | delete fSupportedParameters; |
258 | | // we don't own/adopt error handler and filter |
259 | 0 | } |
260 | | |
261 | | DOMLSSerializerImpl::DOMLSSerializerImpl(MemoryManager* const manager) |
262 | 0 | :fFeatures(0) |
263 | 0 | ,fNewLine(0) |
264 | 0 | ,fErrorHandler(0) |
265 | 0 | ,fFilter(0) |
266 | 0 | ,fDocumentVersion(XMLUni::fgVersion1_0) |
267 | 0 | ,fSupportedParameters(0) |
268 | 0 | ,fEncodingUsed(0) |
269 | 0 | ,fNewLineUsed(0) |
270 | 0 | ,fFormatter(0) |
271 | 0 | ,fErrorCount(0) |
272 | 0 | ,fCurrentLine(0) |
273 | 0 | ,fLineFeedInTextNodePrinted(false) |
274 | 0 | ,fLastWhiteSpaceInTextNode(0) |
275 | 0 | ,fIsXml11(false) |
276 | 0 | ,fNamespaceStack(0) |
277 | 0 | ,fMemoryManager(manager) |
278 | 0 | { |
279 | 0 | fNamespaceStack=new (fMemoryManager) RefVectorOf< RefHashTableOf<XMLCh> >(1,true, fMemoryManager); |
280 | | |
281 | | // |
282 | | // set features to default setting |
283 | | // |
284 | 0 | setFeature(CANONICAL_FORM_ID, false); |
285 | 0 | setFeature(DISCARD_DEFAULT_CONTENT_ID, true ); |
286 | 0 | setFeature(ENTITIES_ID, true ); |
287 | 0 | setFeature(FORMAT_PRETTY_PRINT_ID, false); |
288 | 0 | setFeature(NORMALIZE_CHARACTERS_ID, false); |
289 | 0 | setFeature(SPLIT_CDATA_SECTIONS_ID, true ); |
290 | 0 | setFeature(VALIDATION_ID, false); |
291 | 0 | setFeature(WHITESPACE_IN_ELEMENT_CONTENT_ID, true ); |
292 | 0 | setFeature(BYTE_ORDER_MARK_ID, false); |
293 | 0 | setFeature(XML_DECLARATION, true ); |
294 | 0 | setFeature(FORMAT_PRETTY_PRINT_1ST_LEVEL_ID, true ); |
295 | |
|
296 | 0 | fSupportedParameters=new (fMemoryManager) DOMStringListImpl(12, fMemoryManager); |
297 | 0 | fSupportedParameters->add(XMLUni::fgDOMErrorHandler); |
298 | 0 | fSupportedParameters->add(XMLUni::fgDOMWRTCanonicalForm); |
299 | 0 | fSupportedParameters->add(XMLUni::fgDOMWRTDiscardDefaultContent); |
300 | 0 | fSupportedParameters->add(XMLUni::fgDOMWRTEntities); |
301 | 0 | fSupportedParameters->add(XMLUni::fgDOMWRTFormatPrettyPrint); |
302 | 0 | fSupportedParameters->add(XMLUni::fgDOMWRTNormalizeCharacters); |
303 | 0 | fSupportedParameters->add(XMLUni::fgDOMWRTSplitCdataSections); |
304 | 0 | fSupportedParameters->add(XMLUni::fgDOMWRTValidation); |
305 | 0 | fSupportedParameters->add(XMLUni::fgDOMWRTWhitespaceInElementContent); |
306 | 0 | fSupportedParameters->add(XMLUni::fgDOMWRTBOM); |
307 | 0 | fSupportedParameters->add(XMLUni::fgDOMXMLDeclaration); |
308 | 0 | fSupportedParameters->add(XMLUni::fgDOMWRTXercesPrettyPrint); |
309 | 0 | } |
310 | | |
311 | | bool DOMLSSerializerImpl::canSetParameter(const XMLCh* featName |
312 | | , const void* /*value*/) const |
313 | 0 | { |
314 | 0 | if(XMLString::compareIStringASCII(featName, XMLUni::fgDOMErrorHandler)==0) |
315 | 0 | return true; |
316 | 0 | return false; |
317 | 0 | } |
318 | | |
319 | | bool DOMLSSerializerImpl::canSetParameter(const XMLCh* featName |
320 | | , bool state) const |
321 | 0 | { |
322 | 0 | int featureId = INVALID_FEATURE_ID; |
323 | 0 | return checkFeature(featName, false, featureId) ? canSetFeature(featureId, state) : false; |
324 | 0 | } |
325 | | |
326 | | void DOMLSSerializerImpl::setParameter(const XMLCh* featName |
327 | | , const void* value) |
328 | 0 | { |
329 | 0 | if(XMLString::compareIStringASCII(featName, XMLUni::fgDOMErrorHandler)==0) |
330 | 0 | fErrorHandler = (DOMErrorHandler*)value; |
331 | 0 | else |
332 | 0 | throw DOMException(DOMException::NOT_SUPPORTED_ERR, 0, fMemoryManager); |
333 | 0 | } |
334 | | |
335 | | void DOMLSSerializerImpl::setParameter(const XMLCh* featName |
336 | | , bool state) |
337 | 0 | { |
338 | 0 | int featureId = INVALID_FEATURE_ID; |
339 | 0 | checkFeature(featName, true, featureId); |
340 | |
|
341 | 0 | if (!canSetFeature(featureId, state)) |
342 | 0 | throw DOMException(DOMException::NOT_SUPPORTED_ERR, 0, fMemoryManager); |
343 | | |
344 | 0 | setFeature(featureId, state); |
345 | | |
346 | | // |
347 | | // setting "canonical-form" to true will set the parameters "format-pretty-print", |
348 | | // "discard-default-content", and "xml-declaration", to false |
349 | | // |
350 | 0 | if ((featureId == CANONICAL_FORM_ID) && state) |
351 | 0 | { |
352 | 0 | setFeature(FORMAT_PRETTY_PRINT_ID, false); |
353 | 0 | setFeature(FORMAT_PRETTY_PRINT_1ST_LEVEL_ID, false); |
354 | 0 | setFeature(DISCARD_DEFAULT_CONTENT_ID, false); |
355 | 0 | setFeature(XML_DECLARATION, false); |
356 | 0 | } |
357 | | // Setting one of those parameters to true will set "canonical-form" to false. |
358 | 0 | if ((featureId == FORMAT_PRETTY_PRINT_ID || featureId == DISCARD_DEFAULT_CONTENT_ID || featureId == XML_DECLARATION) && state) |
359 | 0 | setFeature(CANONICAL_FORM_ID, false); |
360 | 0 | } |
361 | | |
362 | | const void* DOMLSSerializerImpl::getParameter(const XMLCh* featName) const |
363 | 0 | { |
364 | 0 | if(XMLString::compareIStringASCII(featName, XMLUni::fgDOMErrorHandler)==0) |
365 | 0 | { |
366 | 0 | return (void*)fErrorHandler; |
367 | 0 | } |
368 | 0 | else |
369 | 0 | { |
370 | 0 | int featureId = INVALID_FEATURE_ID; |
371 | 0 | checkFeature(featName, true, featureId); |
372 | 0 | return (void*)getFeature(featureId); |
373 | 0 | } |
374 | 0 | } |
375 | | |
376 | | const DOMStringList* DOMLSSerializerImpl::getParameterNames() const |
377 | 0 | { |
378 | 0 | return fSupportedParameters; |
379 | 0 | } |
380 | | |
381 | | void DOMLSSerializerImpl::setNewLine(const XMLCh* const newLine) |
382 | 0 | { |
383 | 0 | fMemoryManager->deallocate(fNewLine);//delete [] fNewLine; |
384 | 0 | fNewLine = XMLString::replicate(newLine, fMemoryManager); |
385 | 0 | } |
386 | | |
387 | | const XMLCh* DOMLSSerializerImpl::getNewLine() const |
388 | 0 | { |
389 | 0 | return fNewLine; |
390 | 0 | } |
391 | | |
392 | | void DOMLSSerializerImpl::setFilter(DOMLSSerializerFilter *filter) |
393 | 0 | { |
394 | 0 | fFilter = filter; |
395 | 0 | } |
396 | | |
397 | | DOMLSSerializerFilter* DOMLSSerializerImpl::getFilter() const |
398 | 0 | { |
399 | 0 | return fFilter; |
400 | 0 | } |
401 | | |
402 | | // |
403 | | // |
404 | | // |
405 | | bool DOMLSSerializerImpl::write(const DOMNode* nodeToWrite, |
406 | | DOMLSOutput* const destination) |
407 | 0 | { |
408 | 0 | XMLFormatTarget* pTarget=destination->getByteStream(); |
409 | 0 | Janitor<XMLFormatTarget> janTarget(0); |
410 | 0 | if(!pTarget) |
411 | 0 | { |
412 | 0 | const XMLCh* szSystemId=destination->getSystemId(); |
413 | 0 | if(!szSystemId) |
414 | 0 | { |
415 | | //TODO: report error "missing target" |
416 | 0 | return false; |
417 | 0 | } |
418 | 0 | pTarget=new LocalFileFormatTarget(szSystemId, fMemoryManager); |
419 | 0 | janTarget.reset(pTarget); |
420 | 0 | } |
421 | | /** |
422 | | * When writing to a LSOutput, the encoding is found by looking at the encoding information |
423 | | * that is reachable through the LSOutput and the item to be written (or its owner document) in this order: |
424 | | * |
425 | | * 1. LSOutput.encoding, |
426 | | * 2. Document.inputEncoding, |
427 | | * 3. Document.xmlEncoding. |
428 | | * |
429 | | * If no encoding is reachable through the above properties, a default encoding of "UTF-8" will be used. |
430 | | * If the specified encoding is not supported an "unsupported-encoding" fatal error is raised. |
431 | | */ |
432 | 0 | fEncodingUsed = gUTF8; |
433 | |
|
434 | 0 | const DOMDocument *docu = (nodeToWrite->getNodeType() == DOMNode::DOCUMENT_NODE)? |
435 | 0 | (const DOMDocument*)nodeToWrite : nodeToWrite->getOwnerDocument(); |
436 | |
|
437 | 0 | const XMLCh* lsEncoding=destination->getEncoding(); |
438 | 0 | if (lsEncoding && *lsEncoding) |
439 | 0 | { |
440 | 0 | fEncodingUsed = lsEncoding; |
441 | 0 | } |
442 | 0 | else if (docu) |
443 | 0 | { |
444 | 0 | const XMLCh* tmpEncoding = docu->getInputEncoding(); |
445 | |
|
446 | 0 | if ( tmpEncoding && *tmpEncoding) |
447 | 0 | { |
448 | 0 | fEncodingUsed = tmpEncoding; |
449 | 0 | } |
450 | 0 | else |
451 | 0 | { |
452 | 0 | tmpEncoding = docu->getXmlEncoding(); |
453 | |
|
454 | 0 | if ( tmpEncoding && *tmpEncoding) |
455 | 0 | { |
456 | 0 | fEncodingUsed = tmpEncoding; |
457 | 0 | } |
458 | 0 | } |
459 | 0 | } |
460 | | |
461 | | |
462 | | /** |
463 | | * The end-of-line sequence of characters to be used in the XML being |
464 | | * written out. The only permitted values are these: |
465 | | * . null |
466 | | * |
467 | | * Use a default end-of-line sequence. DOM implementations should choose |
468 | | * the default to match the usual convention for text files in the |
469 | | * environment being used. Implementations must choose a default |
470 | | * sequence that matches one of those allowed by 2.11 "End-of-Line |
471 | | * Handling". |
472 | | * |
473 | | * CR The carriage-return character (#xD) |
474 | | * CR-LF The carriage-return and line-feed characters (#xD #xA) |
475 | | * LF The line-feed character (#xA) |
476 | | * |
477 | | * The default value for this attribute is null |
478 | | */ |
479 | 0 | fNewLineUsed = (fNewLine && *fNewLine)? fNewLine : gEOLSeq; |
480 | | |
481 | | /** |
482 | | * get Document Version |
483 | | */ |
484 | 0 | fDocumentVersion = (docu && docu->getXmlVersion() && *(docu->getXmlVersion()))?docu->getXmlVersion():XMLUni::fgVersion1_0; |
485 | 0 | fIsXml11 = XMLString::equals(fDocumentVersion, XMLUni::fgVersion1_1); |
486 | |
|
487 | 0 | fErrorCount = 0; |
488 | |
|
489 | 0 | fLineFeedInTextNodePrinted = false; |
490 | 0 | fLastWhiteSpaceInTextNode = 0; |
491 | |
|
492 | 0 | try |
493 | 0 | { |
494 | 0 | fFormatter = new (fMemoryManager) XMLFormatter( fEncodingUsed |
495 | 0 | ,fDocumentVersion |
496 | 0 | ,pTarget |
497 | 0 | ,XMLFormatter::NoEscapes |
498 | 0 | ,XMLFormatter::UnRep_CharRef |
499 | 0 | ,fMemoryManager); |
500 | 0 | } |
501 | 0 | catch (const TranscodingException& e) |
502 | 0 | { |
503 | 0 | reportError(nodeToWrite, DOMError::DOM_SEVERITY_FATAL_ERROR, e.getMessage()); |
504 | 0 | return false; |
505 | 0 | } |
506 | | |
507 | 0 | try |
508 | 0 | { |
509 | 0 | Janitor<XMLFormatter> janName(fFormatter); |
510 | 0 | processNode(nodeToWrite); |
511 | 0 | pTarget->flush(); |
512 | 0 | } |
513 | | |
514 | | // |
515 | | // The serialize engine (processNode) throws an exception to abort |
516 | | // serialization if |
517 | | // |
518 | | // . A fatal error occurs which renders the output ill-formed, or |
519 | | // . Instructed by the application's error handler |
520 | | // |
521 | 0 | catch (const TranscodingException&) |
522 | 0 | { |
523 | 0 | pTarget->flush(); |
524 | 0 | return false; |
525 | 0 | } |
526 | |
|
527 | 0 | catch (const XMLDOMMsg::Codes) |
528 | 0 | { |
529 | 0 | pTarget->flush(); |
530 | 0 | return false; |
531 | 0 | } |
532 | 0 | catch(const OutOfMemoryException&) |
533 | 0 | { |
534 | 0 | throw; |
535 | 0 | } |
536 | 0 | catch (...) |
537 | 0 | { |
538 | 0 | pTarget->flush(); |
539 | 0 | throw; |
540 | 0 | } |
541 | | |
542 | | // |
543 | | // true if node was successfully serialized and |
544 | | // false in case a failure occured and the |
545 | | // failure wasn't canceled by the error handler. |
546 | | // |
547 | 0 | return ((fErrorCount == 0)? true : false); |
548 | 0 | } |
549 | | |
550 | | bool DOMLSSerializerImpl::writeToURI(const DOMNode* nodeToWrite, const XMLCh* uri) |
551 | 0 | { |
552 | 0 | DOMLSOutputImpl output(fMemoryManager); |
553 | 0 | output.setSystemId(uri); |
554 | 0 | return write(nodeToWrite, &output); |
555 | 0 | } |
556 | | |
557 | | // |
558 | | // We don't throw DOMSTRING_SIZE_ERR since we are no longer |
559 | | // using DOMString. |
560 | | // |
561 | | XMLCh* DOMLSSerializerImpl::writeToString(const DOMNode* nodeToWrite, MemoryManager* manager /*= NULL*/) |
562 | 0 | { |
563 | 0 | if(manager==NULL) |
564 | 0 | manager = fMemoryManager; |
565 | 0 | MemBufFormatTarget destination(1023, manager); |
566 | 0 | bool retVal; |
567 | |
|
568 | 0 | bool bBOMFlag=getFeature(BYTE_ORDER_MARK_ID); |
569 | 0 | setFeature(BYTE_ORDER_MARK_ID, false); |
570 | 0 | try |
571 | 0 | { |
572 | 0 | DOMLSOutputImpl output(manager); |
573 | 0 | output.setByteStream(&destination); |
574 | 0 | output.setEncoding(XMLUni::fgUTF16EncodingString); |
575 | 0 | retVal = write(nodeToWrite, &output); |
576 | 0 | } |
577 | 0 | catch(const OutOfMemoryException&) |
578 | 0 | { |
579 | 0 | throw; |
580 | 0 | } |
581 | 0 | catch (...) |
582 | 0 | { |
583 | | // |
584 | | // there is a possibility that memory allocation |
585 | | // exception thrown in XMLBuffer class |
586 | | // |
587 | 0 | setFeature(BYTE_ORDER_MARK_ID, bBOMFlag); |
588 | 0 | return 0; |
589 | 0 | } |
590 | | |
591 | 0 | setFeature(BYTE_ORDER_MARK_ID, bBOMFlag); |
592 | 0 | return (retVal ? XMLString::replicate(reinterpret_cast<const XMLCh*>(destination.getRawBuffer()), manager) : 0); |
593 | 0 | } |
594 | | |
595 | | // |
596 | | // Characters not representable in output encoding, |
597 | | // |
598 | | // 1. CHARACTER DATA (outside of markup) --- no error |
599 | | // ordinary character -> numeric character reference |
600 | | // '<' and '&' -> < and & |
601 | | // |
602 | | // 2. Within MARKUP, but outside of attributes |
603 | | // reported as an error --- ERROR |
604 | | // markup: |
605 | | // start tag done |
606 | | // end tag done |
607 | | // empty element tag done |
608 | | // entity references done |
609 | | // character references // REVISIT |
610 | | // comments done |
611 | | // CDATA section delimiters done, done |
612 | | // document type declarartions done |
613 | | // processing instructions (PI) done |
614 | | // |
615 | | // 3. With in ATTRIBUTE |
616 | | // -> numeric character reference |
617 | | // no quotes -> in quotes |
618 | | // with quotes, no apostrophe -> in apostrophe |
619 | | // with quotes and apostrophe -> in quotes and " |
620 | | // |
621 | | // 4. CDATA sections |
622 | | // "split_cdata_section" true --- char ref |
623 | | // false --- ERROR |
624 | | // |
625 | | // --------------------------------------------------------------------------- |
626 | | // Stream out a DOM node, and, recursively, all of its children. This |
627 | | // function is the heart of writing a DOM tree out as XML source. Give it |
628 | | // a document node and it will do the whole thing. |
629 | | // --------------------------------------------------------------------------- |
630 | | |
631 | | void DOMLSSerializerImpl::processNode(const DOMNode* const nodeToWrite, int level) |
632 | 0 | { |
633 | | |
634 | | // Get the name and value out for convenience |
635 | 0 | const XMLCh* nodeName = nodeToWrite->getNodeName(); |
636 | 0 | const XMLCh* nodeValue = nodeToWrite->getNodeValue(); |
637 | 0 | XMLSize_t lent = XMLString::stringLen(nodeValue); |
638 | |
|
639 | 0 | switch (nodeToWrite->getNodeType()) |
640 | 0 | { |
641 | 0 | case DOMNode::TEXT_NODE: |
642 | 0 | { |
643 | 0 | if (checkFilter(nodeToWrite) != DOMNodeFilter::FILTER_ACCEPT) |
644 | 0 | break; |
645 | | |
646 | 0 | ensureValidString(nodeToWrite, nodeValue); |
647 | 0 | if (getFeature(FORMAT_PRETTY_PRINT_ID)) |
648 | 0 | { |
649 | 0 | fLineFeedInTextNodePrinted = false; |
650 | 0 | fLastWhiteSpaceInTextNode = 0; |
651 | |
|
652 | 0 | if(XMLChar1_0::isAllSpaces(nodeValue, XMLString::stringLen(nodeValue))) |
653 | 0 | { |
654 | | // skips whitespace-only text nodes unless whitespace-in-element is set. |
655 | 0 | if (!getFeature(WHITESPACE_IN_ELEMENT_CONTENT_ID)) |
656 | 0 | { |
657 | 0 | break; |
658 | 0 | } |
659 | 0 | else |
660 | 0 | { |
661 | | // |
662 | | // we need to trace if newline(s) have been printed out |
663 | | // to avoid generate extra newline for pretty printing, |
664 | | // as well as the number of whitespaces after the last |
665 | | // newline character to do indentation properly. |
666 | | // |
667 | 0 | int pos = XMLString::lastIndexOf(nodeValue, chLF); |
668 | 0 | if (-1 != pos) |
669 | 0 | { |
670 | 0 | fLineFeedInTextNodePrinted = true; |
671 | 0 | fLastWhiteSpaceInTextNode = (unsigned int)(lent - pos); |
672 | 0 | } |
673 | 0 | else |
674 | 0 | { |
675 | | // for those platforms using chCR alone as |
676 | | // a newline character |
677 | 0 | pos = XMLString::lastIndexOf(nodeValue, chCR); |
678 | 0 | if (-1 != pos) |
679 | 0 | { |
680 | 0 | fLineFeedInTextNodePrinted = true; |
681 | 0 | fLastWhiteSpaceInTextNode = (unsigned int)(lent - pos); |
682 | 0 | } |
683 | 0 | } |
684 | 0 | } |
685 | 0 | } |
686 | 0 | } |
687 | | |
688 | 0 | setURCharRef(); // character data |
689 | 0 | fFormatter->formatBuf(nodeValue, lent, XMLFormatter::CharEscapes); |
690 | 0 | break; |
691 | 0 | } |
692 | | |
693 | 0 | case DOMNode::PROCESSING_INSTRUCTION_NODE: |
694 | 0 | { |
695 | 0 | if (checkFilter(nodeToWrite) != DOMNodeFilter::FILTER_ACCEPT) |
696 | 0 | break; |
697 | | |
698 | 0 | ensureValidString(nodeToWrite, nodeName); |
699 | 0 | ensureValidString(nodeToWrite, nodeValue); |
700 | |
|
701 | 0 | if(level == 1 && getFeature(FORMAT_PRETTY_PRINT_1ST_LEVEL_ID)) |
702 | 0 | printNewLine(); |
703 | |
|
704 | 0 | printNewLine(); |
705 | 0 | printIndent(level); |
706 | |
|
707 | 0 | TRY_CATCH_THROW |
708 | 0 | ( |
709 | 0 | *fFormatter << XMLFormatter::NoEscapes << gStartPI << nodeName; |
710 | 0 | if (lent > 0) |
711 | 0 | { |
712 | 0 | *fFormatter << chSpace << nodeValue; |
713 | 0 | } |
714 | 0 | *fFormatter << gEndPI; |
715 | 0 | ) |
716 | 0 | break; |
717 | 0 | } |
718 | | |
719 | 0 | case DOMNode::DOCUMENT_NODE: // Not to be shown to Filter |
720 | 0 | { |
721 | | |
722 | | // output BOM if needed |
723 | 0 | processBOM(); |
724 | |
|
725 | 0 | setURCharRef(); |
726 | 0 | const DOMDocument *docu = (const DOMDocument*)nodeToWrite; |
727 | | |
728 | | //[23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' |
729 | | //[24] VersionInfo ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"') |
730 | | //[80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName |
731 | | //[32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"')) |
732 | | // |
733 | |
|
734 | 0 | if (getFeature(XML_DECLARATION)) { |
735 | | // use the version and encoding resolved |
736 | 0 | *fFormatter << gXMLDecl_VersionInfo << fDocumentVersion << gXMLDecl_separator; |
737 | 0 | *fFormatter << gXMLDecl_EncodingDecl << fEncodingUsed << gXMLDecl_separator; |
738 | |
|
739 | 0 | const XMLCh* st = (docu->getXmlStandalone())? XMLUni::fgYesString : XMLUni::fgNoString; |
740 | 0 | *fFormatter << gXMLDecl_SDDecl << st << gXMLDecl_separator; |
741 | |
|
742 | 0 | *fFormatter << gXMLDecl_endtag; |
743 | 0 | } |
744 | |
|
745 | 0 | DOMNodeSPtr child = nodeToWrite->getFirstChild(); |
746 | 0 | while( child != 0) |
747 | 0 | { |
748 | 0 | processNode(child, level); |
749 | 0 | child = child->getNextSibling(); |
750 | 0 | } |
751 | 0 | printNewLine(); |
752 | 0 | break; |
753 | 0 | } |
754 | | |
755 | 0 | case DOMNode::DOCUMENT_FRAGMENT_NODE: |
756 | 0 | { |
757 | |
|
758 | 0 | setURCharRef(); |
759 | |
|
760 | 0 | DOMNode *child = nodeToWrite->getFirstChild(); |
761 | 0 | while( child != 0) |
762 | 0 | { |
763 | 0 | processNode(child, level); |
764 | 0 | child = child->getNextSibling(); |
765 | 0 | } |
766 | 0 | printNewLine(); |
767 | 0 | break; |
768 | 0 | } |
769 | | |
770 | 0 | case DOMNode::ELEMENT_NODE: |
771 | 0 | { |
772 | 0 | DOMNodeFilter::FilterAction filterAction = checkFilter(nodeToWrite); |
773 | |
|
774 | 0 | if ( filterAction == DOMNodeFilter::FILTER_REJECT) |
775 | 0 | break; |
776 | | |
777 | 0 | if (!fLineFeedInTextNodePrinted) |
778 | 0 | { |
779 | 0 | if(level == 1 && getFeature(FORMAT_PRETTY_PRINT_1ST_LEVEL_ID)) |
780 | 0 | printNewLine(); |
781 | |
|
782 | 0 | printNewLine(); |
783 | 0 | } |
784 | 0 | else |
785 | 0 | { |
786 | 0 | fLineFeedInTextNodePrinted = false; |
787 | 0 | } |
788 | |
|
789 | 0 | printIndent(level); |
790 | | |
791 | | //track the line number the current node begins on |
792 | 0 | int nodeLine = fCurrentLine; |
793 | | |
794 | | // add an entry in the namespace stack |
795 | 0 | RefHashTableOf<XMLCh>* namespaceMap=NULL; |
796 | |
|
797 | 0 | if ( filterAction == DOMNodeFilter::FILTER_ACCEPT) |
798 | 0 | { |
799 | | // this element attributes child elements |
800 | | // accept yes yes yes |
801 | | // skip no no yes |
802 | | // |
803 | 0 | TRY_CATCH_THROW |
804 | 0 | ( |
805 | | // The name has to be representable without any escapes |
806 | 0 | *fFormatter << XMLFormatter::NoEscapes |
807 | 0 | << chOpenAngle << nodeName; |
808 | 0 | ) |
809 | | |
810 | | // Output any attributes on this element |
811 | 0 | setURCharRef(); |
812 | 0 | DOMNamedNodeMap *attributes = nodeToWrite->getAttributes(); |
813 | 0 | XMLSize_t attrCount = attributes->getLength(); |
814 | | |
815 | | // check if the namespace for the current node is already defined |
816 | 0 | const XMLCh* prefix = nodeToWrite->getPrefix(); |
817 | 0 | const XMLCh* uri = nodeToWrite->getNamespaceURI(); |
818 | 0 | if((uri && uri[0]) || ((prefix==0 || prefix[0]==0) && isDefaultNamespacePrefixDeclared())) |
819 | 0 | { |
820 | 0 | if(prefix==0 || prefix[0]==0) |
821 | 0 | prefix=XMLUni::fgZeroLenString; |
822 | 0 | if(!isNamespaceBindingActive(prefix, uri)) |
823 | 0 | { |
824 | 0 | if(namespaceMap==NULL) |
825 | 0 | { |
826 | 0 | namespaceMap=new (fMemoryManager) RefHashTableOf<XMLCh>(12, false, fMemoryManager); |
827 | 0 | fNamespaceStack->addElement(namespaceMap); |
828 | 0 | } |
829 | 0 | namespaceMap->put((void*)prefix,(XMLCh*)uri); |
830 | 0 | *fFormatter << XMLFormatter::NoEscapes |
831 | 0 | << chSpace << XMLUni::fgXMLNSString; |
832 | 0 | if(!XMLString::equals(prefix,XMLUni::fgZeroLenString)) |
833 | 0 | *fFormatter << chColon << prefix; |
834 | 0 | *fFormatter << chEqual << chDoubleQuote |
835 | 0 | << XMLFormatter::AttrEscapes |
836 | 0 | << uri |
837 | 0 | << XMLFormatter::NoEscapes |
838 | 0 | << chDoubleQuote; |
839 | 0 | } |
840 | 0 | } |
841 | |
|
842 | 0 | bool discard = getFeature(DISCARD_DEFAULT_CONTENT_ID); |
843 | 0 | for (XMLSize_t i = 0; i < attrCount; i++) |
844 | 0 | { |
845 | 0 | DOMAttrSPtr attribute = (DOMAttr*)attributes->item(i); |
846 | | |
847 | | // Not to be shown to Filter |
848 | | |
849 | | // |
850 | | //"discard-default-content" |
851 | | // true |
852 | | // [required] (default) |
853 | | // Use whatever information available to the implementation |
854 | | // (i.e. XML schema, DTD, the specified flag on Attr nodes, |
855 | | // and so on) to decide what attributes and content should be |
856 | | // discarded or not. |
857 | | // Note that the specified flag on Attr nodes in itself is |
858 | | // not always reliable, it is only reliable when it is set |
859 | | // to false since the only case where it can be set to false |
860 | | // is if the attribute was created by the implementation. |
861 | | // The default content won't be removed if an implementation |
862 | | // does not have any information available. |
863 | | // false |
864 | | // [required] |
865 | | // Keep all attributes and all content. |
866 | | // |
867 | 0 | if (discard && !((DOMAttr*)attribute )->getSpecified()) |
868 | 0 | continue; |
869 | | // |
870 | | // Again the name has to be completely representable. But the |
871 | | // attribute can have refs and requires the attribute style |
872 | | // escaping. |
873 | | // |
874 | | |
875 | | // if this attribute is a namespace declaration, add it to the namespace map for the current level |
876 | 0 | const XMLCh* ns = attribute->getNamespaceURI(); |
877 | 0 | if (ns != 0 ) |
878 | 0 | { |
879 | 0 | if(XMLString::equals(ns, XMLUni::fgXMLNSURIName)) |
880 | 0 | { |
881 | 0 | if(namespaceMap==NULL) |
882 | 0 | { |
883 | 0 | namespaceMap=new (fMemoryManager) RefHashTableOf<XMLCh>(12, false, fMemoryManager); |
884 | 0 | fNamespaceStack->addElement(namespaceMap); |
885 | 0 | } |
886 | 0 | const XMLCh* nsPrefix = attribute->getLocalName(); |
887 | 0 | if(XMLString::equals(attribute->getNodeName(),XMLUni::fgXMLNSString)) |
888 | 0 | nsPrefix = XMLUni::fgZeroLenString; |
889 | 0 | if(namespaceMap->containsKey((void*)nsPrefix)) |
890 | 0 | continue; |
891 | 0 | namespaceMap->put((void*)attribute->getLocalName(),(XMLCh*)attribute->getNodeValue()); |
892 | 0 | } |
893 | 0 | else if(!XMLString::equals(ns, XMLUni::fgXMLURIName)) |
894 | 0 | { |
895 | | // check if the namespace for the current node is already defined |
896 | 0 | const XMLCh* prefix = attribute->getPrefix(); |
897 | 0 | if(prefix && prefix[0]) |
898 | 0 | { |
899 | 0 | const XMLCh* uri = attribute->getNamespaceURI(); |
900 | 0 | if(!isNamespaceBindingActive(prefix, uri)) |
901 | 0 | { |
902 | 0 | if(namespaceMap==NULL) |
903 | 0 | { |
904 | 0 | namespaceMap=new (fMemoryManager) RefHashTableOf<XMLCh>(12, false, fMemoryManager); |
905 | 0 | fNamespaceStack->addElement(namespaceMap); |
906 | 0 | } |
907 | 0 | namespaceMap->put((void*)prefix,(XMLCh*)uri); |
908 | 0 | *fFormatter << XMLFormatter::NoEscapes |
909 | 0 | << chSpace << XMLUni::fgXMLNSString << chColon << prefix |
910 | 0 | << chEqual << chDoubleQuote |
911 | 0 | << XMLFormatter::AttrEscapes |
912 | 0 | << uri |
913 | 0 | << XMLFormatter::NoEscapes |
914 | 0 | << chDoubleQuote; |
915 | 0 | } |
916 | 0 | } |
917 | 0 | } |
918 | 0 | } |
919 | 0 | if (XMLString::equals(ns, XMLUni::fgXMLNSURIName) || checkFilter(attribute) == DOMNodeFilter::FILTER_ACCEPT) |
920 | 0 | { |
921 | 0 | *fFormatter << XMLFormatter::NoEscapes |
922 | 0 | << chSpace << attribute->getNodeName() |
923 | 0 | << chEqual << chDoubleQuote |
924 | 0 | << XMLFormatter::AttrEscapes; |
925 | 0 | if (getFeature(ENTITIES_ID)) |
926 | 0 | { |
927 | 0 | DOMNodeSPtr child = attribute->getFirstChild(); |
928 | 0 | while( child != 0) |
929 | 0 | { |
930 | 0 | if(child->getNodeType()==DOMNode::TEXT_NODE) |
931 | 0 | { |
932 | 0 | ensureValidString(attribute, child->getNodeValue()); |
933 | 0 | *fFormatter << child->getNodeValue(); |
934 | 0 | } |
935 | 0 | else if(child->getNodeType()==DOMNode::ENTITY_REFERENCE_NODE) |
936 | 0 | *fFormatter << XMLFormatter::NoEscapes |
937 | 0 | << chAmpersand << child->getNodeName() << chSemiColon |
938 | 0 | << XMLFormatter::AttrEscapes; |
939 | 0 | child = child->getNextSibling(); |
940 | 0 | } |
941 | 0 | } |
942 | 0 | else |
943 | 0 | { |
944 | 0 | ensureValidString(attribute, attribute->getNodeValue()); |
945 | 0 | *fFormatter << attribute->getNodeValue(); |
946 | 0 | } |
947 | 0 | *fFormatter << XMLFormatter::NoEscapes |
948 | 0 | << chDoubleQuote; |
949 | 0 | } |
950 | 0 | } // end of for |
951 | 0 | } // end of FILTER_ACCEPT |
952 | | |
953 | 0 | level++; |
954 | | |
955 | | // FILTER_SKIP may start from here |
956 | | |
957 | | // |
958 | | // Test for the presence of children, which includes both |
959 | | // text content and nested elements. |
960 | | // |
961 | 0 | DOMNodeSPtr child = nodeToWrite->getFirstChild(); |
962 | 0 | if (child != 0) |
963 | 0 | { |
964 | | // There are children. Close start-tag, and output children. |
965 | | // No escapes are legal here |
966 | 0 | if (filterAction == DOMNodeFilter::FILTER_ACCEPT) |
967 | 0 | *fFormatter << XMLFormatter::NoEscapes << chCloseAngle; |
968 | |
|
969 | 0 | while( child != 0) |
970 | 0 | { |
971 | 0 | processNode(child, level); |
972 | 0 | child = child->getNextSibling(); |
973 | 0 | } |
974 | |
|
975 | 0 | level--; |
976 | |
|
977 | 0 | if (filterAction == DOMNodeFilter::FILTER_ACCEPT) |
978 | 0 | { |
979 | | //if we are not on the same line as when we started |
980 | | //this node then print a new line and indent |
981 | 0 | if(nodeLine != fCurrentLine) |
982 | 0 | { |
983 | 0 | if (!fLineFeedInTextNodePrinted) |
984 | 0 | { |
985 | 0 | printNewLine(); |
986 | 0 | } |
987 | 0 | else |
988 | 0 | { |
989 | 0 | fLineFeedInTextNodePrinted = false; |
990 | 0 | } |
991 | |
|
992 | 0 | if(nodeLine != fCurrentLine && level == 0 && getFeature(FORMAT_PRETTY_PRINT_1ST_LEVEL_ID)) |
993 | 0 | printNewLine(); |
994 | |
|
995 | 0 | printIndent(level); |
996 | 0 | } |
997 | 0 | TRY_CATCH_THROW |
998 | 0 | ( |
999 | 0 | *fFormatter << XMLFormatter::NoEscapes << gEndElement |
1000 | 0 | << nodeName << chCloseAngle; |
1001 | 0 | ) |
1002 | |
|
1003 | 0 | } |
1004 | 0 | } |
1005 | 0 | else |
1006 | 0 | { |
1007 | 0 | level--; |
1008 | | |
1009 | | // |
1010 | | // There were no children. Output the short form close of |
1011 | | // the element start tag, making it an empty-element tag. |
1012 | | // |
1013 | 0 | if (filterAction == DOMNodeFilter::FILTER_ACCEPT) |
1014 | 0 | { |
1015 | 0 | TRY_CATCH_THROW |
1016 | 0 | ( |
1017 | 0 | *fFormatter << XMLFormatter::NoEscapes << chForwardSlash << chCloseAngle; |
1018 | 0 | ) |
1019 | 0 | } |
1020 | 0 | } |
1021 | | |
1022 | | // remove the namespace map at this level |
1023 | 0 | if(namespaceMap!=NULL) |
1024 | 0 | fNamespaceStack->removeLastElement(); |
1025 | |
|
1026 | 0 | break; |
1027 | 0 | } |
1028 | 0 | case DOMNode::ATTRIBUTE_NODE: |
1029 | 0 | { |
1030 | 0 | if (checkFilter(nodeToWrite) != DOMNodeFilter::FILTER_ACCEPT) |
1031 | 0 | break; |
1032 | | |
1033 | 0 | const XMLCh* localName = nodeToWrite->getLocalName(); |
1034 | | |
1035 | | // check if this is a DOM Level 1 Node |
1036 | 0 | if(localName == 0) |
1037 | 0 | *fFormatter << XMLFormatter::NoEscapes |
1038 | 0 | << nodeToWrite->getNodeName(); |
1039 | 0 | else |
1040 | 0 | *fFormatter << XMLFormatter::NoEscapes |
1041 | 0 | << chOpenCurly << nodeToWrite->getNamespaceURI() |
1042 | 0 | << chCloseCurly << localName; |
1043 | 0 | *fFormatter << chEqual << chDoubleQuote |
1044 | 0 | << XMLFormatter::AttrEscapes; |
1045 | 0 | if (getFeature(ENTITIES_ID)) |
1046 | 0 | { |
1047 | 0 | DOMNodeSPtr child = nodeToWrite->getFirstChild(); |
1048 | 0 | while( child != 0) |
1049 | 0 | { |
1050 | 0 | if(child->getNodeType()==DOMNode::TEXT_NODE) |
1051 | 0 | { |
1052 | 0 | ensureValidString(nodeToWrite, child->getNodeValue()); |
1053 | 0 | *fFormatter << child->getNodeValue(); |
1054 | 0 | } |
1055 | 0 | else if(child->getNodeType()==DOMNode::ENTITY_REFERENCE_NODE) |
1056 | 0 | *fFormatter << XMLFormatter::NoEscapes |
1057 | 0 | << chAmpersand << child->getNodeName() << chSemiColon |
1058 | 0 | << XMLFormatter::AttrEscapes; |
1059 | 0 | child = child->getNextSibling(); |
1060 | 0 | } |
1061 | 0 | } |
1062 | 0 | else |
1063 | 0 | { |
1064 | 0 | ensureValidString(nodeToWrite, nodeValue); |
1065 | 0 | *fFormatter << nodeValue; |
1066 | 0 | } |
1067 | 0 | *fFormatter << XMLFormatter::NoEscapes |
1068 | 0 | << chDoubleQuote; |
1069 | |
|
1070 | 0 | break; |
1071 | 0 | } |
1072 | 0 | case DOMNode::ENTITY_REFERENCE_NODE: |
1073 | 0 | { |
1074 | | //"entities" |
1075 | | //true |
1076 | | //[required] (default) |
1077 | | //Keep EntityReference and Entity nodes in the document. |
1078 | | |
1079 | | //false |
1080 | | //[optional] |
1081 | | //Remove all EntityReference and Entity nodes from the document, |
1082 | | // putting the entity expansions directly in their place. |
1083 | | // Text nodes are into "normal" form. |
1084 | | //Only EntityReference nodes to non-defined entities are kept in the document. |
1085 | |
|
1086 | 0 | if (checkFilter(nodeToWrite) != DOMNodeFilter::FILTER_ACCEPT) |
1087 | 0 | break; |
1088 | | |
1089 | 0 | if (getFeature(ENTITIES_ID)) |
1090 | 0 | { |
1091 | 0 | TRY_CATCH_THROW |
1092 | 0 | ( |
1093 | 0 | *fFormatter << XMLFormatter::NoEscapes << chAmpersand |
1094 | 0 | << nodeName << chSemiColon; |
1095 | 0 | ) |
1096 | 0 | } |
1097 | 0 | else |
1098 | 0 | { |
1099 | | // check if the referenced entity is defined or not |
1100 | 0 | if (nodeToWrite->getOwnerDocument()->getDoctype()->getEntities()->getNamedItem(nodeName)) |
1101 | 0 | { |
1102 | 0 | DOMNodeSPtr child; |
1103 | 0 | for (child = nodeToWrite->getFirstChild(); |
1104 | 0 | child != 0; |
1105 | 0 | child = child->getNextSibling()) |
1106 | 0 | { |
1107 | 0 | processNode(child, level); |
1108 | 0 | } |
1109 | 0 | } |
1110 | 0 | else |
1111 | 0 | { |
1112 | 0 | TRY_CATCH_THROW |
1113 | 0 | ( |
1114 | 0 | *fFormatter<<XMLFormatter::NoEscapes<<chAmpersand<<nodeName<<chSemiColon; |
1115 | 0 | ) |
1116 | 0 | } |
1117 | 0 | } |
1118 | 0 | break; |
1119 | 0 | } |
1120 | | |
1121 | | // |
1122 | | // feature:split_cdata_sections occurence of ]]> unrep-char |
1123 | | // =============================================================== |
1124 | | // true split split |
1125 | | // false fails fails |
1126 | | // |
1127 | 0 | case DOMNode::CDATA_SECTION_NODE: |
1128 | 0 | { |
1129 | 0 | if (checkFilter(nodeToWrite) != DOMNodeFilter::FILTER_ACCEPT) |
1130 | 0 | break; |
1131 | | |
1132 | 0 | if (getFeature(SPLIT_CDATA_SECTIONS_ID)) |
1133 | 0 | { |
1134 | | // it is fairly complicated and we process this |
1135 | | // in a separate function. |
1136 | 0 | procCdataSection(nodeValue, nodeToWrite); |
1137 | 0 | } |
1138 | 0 | else |
1139 | 0 | { |
1140 | 0 | ensureValidString(nodeToWrite, nodeValue); |
1141 | | // search for "]]>", the node value is not supposed to have this |
1142 | 0 | if (XMLString::patternMatch(nodeValue, gEndCDATA) != -1) |
1143 | 0 | { |
1144 | 0 | reportError(nodeToWrite, DOMError::DOM_SEVERITY_FATAL_ERROR, XMLDOMMsg::Writer_NestedCDATA); |
1145 | 0 | } |
1146 | |
|
1147 | 0 | TRY_CATCH_THROW |
1148 | 0 | ( |
1149 | | // transcoder throws exception for unrep chars |
1150 | 0 | *fFormatter << XMLFormatter::NoEscapes << gStartCDATA << nodeValue << gEndCDATA; |
1151 | 0 | ) |
1152 | 0 | } |
1153 | | |
1154 | 0 | break; |
1155 | 0 | } |
1156 | | |
1157 | 0 | case DOMNode::COMMENT_NODE: |
1158 | 0 | { |
1159 | 0 | if (checkFilter(nodeToWrite) != DOMNodeFilter::FILTER_ACCEPT) |
1160 | 0 | break; |
1161 | | |
1162 | 0 | ensureValidString(nodeToWrite, nodeValue); |
1163 | | |
1164 | | // Figure out if we want pretty-printing for this comment. |
1165 | | // If this comment node does not have any element siblings |
1166 | | // (i.e., it is a text node) then we don't want to add any |
1167 | | // whitespaces since that might be significant to the |
1168 | | // application. Otherwise we want pretty-printing. |
1169 | | // |
1170 | |
|
1171 | 0 | bool pretty = (level == 0); // Document-level comments. |
1172 | |
|
1173 | 0 | if (!pretty) |
1174 | 0 | { |
1175 | | // See if we have any element siblings. |
1176 | | // |
1177 | 0 | const DOMNode* s = nodeToWrite->getNextSibling (); |
1178 | |
|
1179 | 0 | while (s != 0 && s->getNodeType () != DOMNode::ELEMENT_NODE) |
1180 | 0 | s = s->getNextSibling (); |
1181 | |
|
1182 | 0 | if (s != 0) |
1183 | 0 | pretty = true; |
1184 | 0 | else |
1185 | 0 | { |
1186 | 0 | s = nodeToWrite->getPreviousSibling (); |
1187 | |
|
1188 | 0 | while (s != 0 && s->getNodeType () != DOMNode::ELEMENT_NODE) |
1189 | 0 | s = s->getPreviousSibling (); |
1190 | |
|
1191 | 0 | if (s != 0) |
1192 | 0 | pretty = true; |
1193 | 0 | } |
1194 | 0 | } |
1195 | |
|
1196 | 0 | if (pretty) |
1197 | 0 | { |
1198 | 0 | if(level == 1 && getFeature(FORMAT_PRETTY_PRINT_1ST_LEVEL_ID)) |
1199 | 0 | printNewLine(); |
1200 | |
|
1201 | 0 | printNewLine(); |
1202 | 0 | printIndent(level); |
1203 | 0 | } |
1204 | |
|
1205 | 0 | TRY_CATCH_THROW |
1206 | 0 | ( |
1207 | 0 | *fFormatter << XMLFormatter::NoEscapes << gStartComment |
1208 | 0 | << nodeValue << gEndComment; |
1209 | 0 | ) |
1210 | 0 | break; |
1211 | 0 | } |
1212 | | |
1213 | 0 | case DOMNode::DOCUMENT_TYPE_NODE: // Not to be shown to Filter |
1214 | 0 | { |
1215 | 0 | const DOMDocumentType *doctype = (const DOMDocumentType *)nodeToWrite; |
1216 | |
|
1217 | 0 | fFormatter->setEscapeFlags(XMLFormatter::NoEscapes); |
1218 | |
|
1219 | 0 | printNewLine(); |
1220 | 0 | printIndent(level); |
1221 | |
|
1222 | 0 | TRY_CATCH_THROW |
1223 | 0 | ( |
1224 | 0 | *fFormatter << gStartDoctype << nodeName; |
1225 | |
|
1226 | 0 | const XMLCh *id = doctype->getPublicId(); |
1227 | 0 | if (id && *id) |
1228 | 0 | { |
1229 | 0 | *fFormatter << chSpace << gPublic << id << chDoubleQuote; |
1230 | |
|
1231 | 0 | id = doctype->getSystemId(); |
1232 | 0 | if (id && *id) |
1233 | 0 | { |
1234 | 0 | *fFormatter << chSpace << chDoubleQuote << id << chDoubleQuote; |
1235 | 0 | } |
1236 | 0 | else |
1237 | 0 | { |
1238 | | // |
1239 | | // 4.2.2 External Entities |
1240 | | // [Definition: If the entity is not internal, |
1241 | | // it is an external entity, declared as follows:] |
1242 | | // External Entity Declaration |
1243 | | // [75] ExternalID ::= 'SYSTEM' S SystemLiteral |
1244 | | // | 'PUBLIC' S PubidLiteral S SystemLiteral |
1245 | | // |
1246 | 0 | reportError(nodeToWrite, DOMError::DOM_SEVERITY_FATAL_ERROR, XMLDOMMsg::Writer_NotRecognizedType); |
1247 | | // systemLiteral not found |
1248 | 0 | } |
1249 | 0 | } |
1250 | 0 | else |
1251 | 0 | { |
1252 | 0 | id = doctype->getSystemId(); |
1253 | 0 | if (id && *id) |
1254 | 0 | { |
1255 | 0 | *fFormatter << chSpace << gSystem << id << chDoubleQuote; |
1256 | 0 | } |
1257 | 0 | } |
1258 | |
|
1259 | 0 | id = doctype->getInternalSubset(); |
1260 | 0 | if (id && *id) |
1261 | 0 | { |
1262 | 0 | *fFormatter << chSpace << chOpenSquare << id << chCloseSquare; |
1263 | 0 | } |
1264 | |
|
1265 | 0 | *fFormatter << chCloseAngle; |
1266 | | ) // end of TRY_CATCH_THROW |
1267 | 0 | |
1268 | 0 | break; |
1269 | 0 | } |
1270 | | |
1271 | 0 | case DOMNode::ENTITY_NODE: // Not to be shown to Filter |
1272 | 0 | { |
1273 | | // |
1274 | | // REVISIT: how does the feature "entities" impact |
1275 | | // entity node? |
1276 | | // |
1277 | 0 | printNewLine(); |
1278 | 0 | printIndent(level); |
1279 | |
|
1280 | 0 | fFormatter->setEscapeFlags(XMLFormatter::NoEscapes); |
1281 | 0 | *fFormatter << gStartEntity << nodeName; |
1282 | |
|
1283 | 0 | const XMLCh * id = ((const DOMEntity*)nodeToWrite)->getPublicId(); |
1284 | 0 | if (id) |
1285 | 0 | *fFormatter << gPublic << id << chDoubleQuote; |
1286 | |
|
1287 | 0 | id = ((const DOMEntity*)nodeToWrite)->getSystemId(); |
1288 | 0 | if (id) |
1289 | 0 | *fFormatter << gSystem << id << chDoubleQuote; |
1290 | |
|
1291 | 0 | id = ((const DOMEntity*)nodeToWrite)->getNotationName(); |
1292 | 0 | if (id) |
1293 | 0 | *fFormatter << gNotation << id << chDoubleQuote; |
1294 | |
|
1295 | 0 | *fFormatter << chCloseAngle; |
1296 | |
|
1297 | 0 | break; |
1298 | 0 | } |
1299 | | |
1300 | 0 | default: |
1301 | | /*** |
1302 | | This is an implementation specific behaviour, we abort if a user derived class has not dealt with |
1303 | | this node type. |
1304 | | ***/ |
1305 | 0 | { |
1306 | 0 | if(!customNodeSerialize(nodeToWrite, level)) { |
1307 | 0 | reportError(nodeToWrite, DOMError::DOM_SEVERITY_FATAL_ERROR, XMLDOMMsg::Writer_NotRecognizedType); |
1308 | | // UnreognizedNodeType; |
1309 | 0 | } |
1310 | 0 | } |
1311 | |
|
1312 | 0 | break; |
1313 | 0 | } |
1314 | |
|
1315 | 0 | } |
1316 | | |
1317 | 0 | bool DOMLSSerializerImpl::customNodeSerialize(const DOMNode* const, int) { |
1318 | 0 | return false; |
1319 | 0 | } |
1320 | | |
1321 | | // |
1322 | | // |
1323 | | DOMNodeFilter::FilterAction DOMLSSerializerImpl::checkFilter(const DOMNode* const node) const |
1324 | 0 | { |
1325 | 0 | if (!fFilter || |
1326 | 0 | ((fFilter->getWhatToShow() & (1 << (node->getNodeType() - 1))) == 0)) |
1327 | 0 | return DOMNodeFilter::FILTER_ACCEPT; |
1328 | | |
1329 | | // |
1330 | | // if and only if there is a filter, and it is interested |
1331 | | // in the node type, then we pass the node to the filter |
1332 | | // for examination |
1333 | | // |
1334 | 0 | return (DOMNodeFilter::FilterAction) fFilter->acceptNode(node); |
1335 | 0 | } |
1336 | | |
1337 | | |
1338 | | bool DOMLSSerializerImpl::checkFeature(const XMLCh* const featName |
1339 | | , bool toThrow |
1340 | | , int& featureId) const |
1341 | 0 | { |
1342 | | // check for null and/or empty feature name |
1343 | 0 | if (!featName || !*featName) |
1344 | 0 | { |
1345 | 0 | if (toThrow) |
1346 | 0 | throw DOMException(DOMException::NOT_FOUND_ERR, 0, fMemoryManager); |
1347 | | |
1348 | 0 | return false; |
1349 | 0 | } |
1350 | | |
1351 | 0 | featureId = INVALID_FEATURE_ID; |
1352 | |
|
1353 | 0 | if (XMLString::equals(featName, XMLUni::fgDOMWRTCanonicalForm)) |
1354 | 0 | featureId = CANONICAL_FORM_ID; |
1355 | 0 | else if (XMLString::equals(featName, XMLUni::fgDOMWRTDiscardDefaultContent)) |
1356 | 0 | featureId = DISCARD_DEFAULT_CONTENT_ID; |
1357 | 0 | else if (XMLString::equals(featName, XMLUni::fgDOMWRTEntities)) |
1358 | 0 | featureId = ENTITIES_ID; |
1359 | 0 | else if (XMLString::equals(featName, XMLUni::fgDOMWRTFormatPrettyPrint)) |
1360 | 0 | featureId = FORMAT_PRETTY_PRINT_ID; |
1361 | 0 | else if (XMLString::equals(featName, XMLUni::fgDOMWRTNormalizeCharacters)) |
1362 | 0 | featureId = NORMALIZE_CHARACTERS_ID; |
1363 | 0 | else if (XMLString::equals(featName, XMLUni::fgDOMWRTSplitCdataSections)) |
1364 | 0 | featureId = SPLIT_CDATA_SECTIONS_ID; |
1365 | 0 | else if (XMLString::equals(featName, XMLUni::fgDOMWRTValidation)) |
1366 | 0 | featureId = VALIDATION_ID; |
1367 | 0 | else if (XMLString::equals(featName, XMLUni::fgDOMWRTWhitespaceInElementContent)) |
1368 | 0 | featureId = WHITESPACE_IN_ELEMENT_CONTENT_ID; |
1369 | 0 | else if (XMLString::equals(featName, XMLUni::fgDOMWRTBOM)) |
1370 | 0 | featureId = BYTE_ORDER_MARK_ID; |
1371 | 0 | else if (XMLString::equals(featName, XMLUni::fgDOMXMLDeclaration)) |
1372 | 0 | featureId = XML_DECLARATION; |
1373 | 0 | else if (XMLString::equals(featName, XMLUni::fgDOMWRTXercesPrettyPrint)) |
1374 | 0 | featureId = FORMAT_PRETTY_PRINT_1ST_LEVEL_ID; |
1375 | | |
1376 | | |
1377 | | //feature name not resolvable |
1378 | 0 | if (featureId == INVALID_FEATURE_ID) |
1379 | 0 | { |
1380 | 0 | if (toThrow) |
1381 | 0 | throw DOMException(DOMException::NOT_FOUND_ERR, 0, fMemoryManager); |
1382 | | |
1383 | 0 | return false; |
1384 | 0 | } |
1385 | | |
1386 | 0 | return true; |
1387 | 0 | } |
1388 | | |
1389 | | bool DOMLSSerializerImpl::reportError(const DOMNode* const errorNode |
1390 | | , DOMError::ErrorSeverity errorType |
1391 | | , const XMLCh* const errorMsg) |
1392 | 0 | { |
1393 | 0 | bool toContinueProcess = true; // default value for no error handler |
1394 | |
|
1395 | 0 | if (fErrorHandler) |
1396 | 0 | { |
1397 | 0 | DOMLocatorImpl locator(0, 0, (DOMNode*) errorNode, 0); |
1398 | 0 | DOMErrorImpl domError(errorType , errorMsg, &locator); |
1399 | 0 | try |
1400 | 0 | { |
1401 | 0 | toContinueProcess = fErrorHandler->handleError(domError); |
1402 | 0 | } |
1403 | 0 | catch(...) |
1404 | 0 | { |
1405 | 0 | } |
1406 | 0 | } |
1407 | |
|
1408 | 0 | if (errorType != DOMError::DOM_SEVERITY_WARNING) |
1409 | 0 | fErrorCount++; |
1410 | |
|
1411 | 0 | return toContinueProcess; |
1412 | 0 | } |
1413 | | |
1414 | | bool DOMLSSerializerImpl::reportError(const DOMNode* const errorNode |
1415 | | , DOMError::ErrorSeverity errorType |
1416 | | , XMLDOMMsg::Codes toEmit) |
1417 | 0 | { |
1418 | 0 | const XMLSize_t msgSize = 1023; |
1419 | 0 | XMLCh errText[msgSize + 1]; |
1420 | |
|
1421 | 0 | DOMImplementationImpl::getMsgLoader4DOM()->loadMsg(toEmit, errText, msgSize); |
1422 | |
|
1423 | 0 | bool toContinueProcess = true; // default value for no error handler |
1424 | |
|
1425 | 0 | if (fErrorHandler) |
1426 | 0 | { |
1427 | 0 | DOMLocatorImpl locator(0, 0, (DOMNode*) errorNode, 0); |
1428 | 0 | DOMErrorImpl domError(errorType , errText, &locator); |
1429 | 0 | try |
1430 | 0 | { |
1431 | 0 | toContinueProcess = fErrorHandler->handleError(domError); |
1432 | 0 | } |
1433 | 0 | catch(...) |
1434 | 0 | { |
1435 | 0 | } |
1436 | 0 | } |
1437 | |
|
1438 | 0 | if (errorType != DOMError::DOM_SEVERITY_WARNING) |
1439 | 0 | fErrorCount++; |
1440 | |
|
1441 | 0 | if (errorType == DOMError::DOM_SEVERITY_FATAL_ERROR || !toContinueProcess) |
1442 | 0 | throw DOMLSException(DOMLSException::SERIALIZE_ERR, toEmit, fMemoryManager); |
1443 | | |
1444 | 0 | return toContinueProcess; |
1445 | 0 | } |
1446 | | |
1447 | | // |
1448 | | // |
1449 | | // |
1450 | | void DOMLSSerializerImpl::procCdataSection(const XMLCh* const nodeValue |
1451 | | , const DOMNode* const nodeToWrite) |
1452 | 0 | { |
1453 | 0 | static const XMLSize_t offset = XMLString::stringLen(gEndCDATA); |
1454 | | |
1455 | | /*** |
1456 | | * Append a ']]>' at the end |
1457 | | */ |
1458 | 0 | XMLSize_t len = XMLString::stringLen(nodeValue); |
1459 | 0 | XMLCh* repNodeValue = (XMLCh*) fMemoryManager->allocate |
1460 | 0 | ( |
1461 | 0 | (len + offset + 1) * sizeof(XMLCh) |
1462 | 0 | );//new XMLCh [len + offset + 1]; |
1463 | 0 | XMLString::copyString(repNodeValue, nodeValue); |
1464 | 0 | XMLString::catString(repNodeValue, gEndCDATA); |
1465 | 0 | ArrayJanitor<XMLCh> jName(repNodeValue, fMemoryManager); |
1466 | |
|
1467 | 0 | XMLCh* curPtr = (XMLCh*) repNodeValue; |
1468 | 0 | XMLCh* nextPtr = 0; |
1469 | 0 | int endTagPos = -1; |
1470 | |
|
1471 | 0 | bool endTagFound = true; |
1472 | |
|
1473 | 0 | while (endTagFound) |
1474 | 0 | { |
1475 | 0 | endTagPos = XMLString::patternMatch(curPtr, gEndCDATA); |
1476 | 0 | if (endTagPos != -1) |
1477 | 0 | { |
1478 | 0 | nextPtr = curPtr + endTagPos + offset; // skip the ']]>' |
1479 | 0 | *(curPtr + endTagPos) = chNull; //nullify the first ']' |
1480 | 0 | if (XMLSize_t(endTagPos) != len) |
1481 | 0 | reportError(nodeToWrite, DOMError::DOM_SEVERITY_WARNING, XMLDOMMsg::Writer_NestedCDATA); |
1482 | 0 | len = len - endTagPos - offset; |
1483 | 0 | } |
1484 | 0 | else |
1485 | 0 | { |
1486 | 0 | endTagFound = false; |
1487 | 0 | } |
1488 | | |
1489 | | /*** |
1490 | | to check ]]>]]> |
1491 | | ***/ |
1492 | 0 | if (endTagPos == 0) |
1493 | 0 | { |
1494 | 0 | TRY_CATCH_THROW |
1495 | 0 | ( |
1496 | 0 | *fFormatter << XMLFormatter::NoEscapes << gStartCDATA << gEndCDATA; |
1497 | 0 | ) |
1498 | 0 | } |
1499 | 0 | else |
1500 | 0 | { |
1501 | 0 | procUnrepCharInCdataSection(curPtr, nodeToWrite); |
1502 | 0 | } |
1503 | | |
1504 | 0 | if (endTagFound) |
1505 | 0 | { |
1506 | 0 | *(nextPtr - offset) = chCloseSquare; //restore the first ']' |
1507 | 0 | curPtr = nextPtr; |
1508 | 0 | } |
1509 | 0 | } |
1510 | 0 | } |
1511 | | |
1512 | | // |
1513 | | // |
1514 | | // |
1515 | | void DOMLSSerializerImpl::procUnrepCharInCdataSection(const XMLCh* const nodeValue |
1516 | | , const DOMNode* const nodeToWrite) |
1517 | 0 | { |
1518 | | // |
1519 | | // We have to check each character and see if it could be represented. |
1520 | | // As long as it can, we just keep up with where we started and how |
1521 | | // many chars we've checked. When we hit an unrepresentable one, we |
1522 | | // stop, transcode everything we've collected, then start handling |
1523 | | // the unrepresentables via char refs. We repeat this until we get all |
1524 | | // the chars done. |
1525 | | // |
1526 | 0 | const XMLCh* srcPtr = nodeValue; |
1527 | 0 | const XMLCh* endPtr = nodeValue + XMLString::stringLen(nodeValue); |
1528 | | |
1529 | | // Set up the common part of the buffer that we build char refs into |
1530 | 0 | XMLCh tmpBuf[32]; |
1531 | 0 | tmpBuf[0] = chAmpersand; |
1532 | 0 | tmpBuf[1] = chPound; |
1533 | 0 | tmpBuf[2] = chLatin_x; |
1534 | |
|
1535 | 0 | while (srcPtr < endPtr) |
1536 | 0 | { |
1537 | 0 | const XMLCh* tmpPtr = srcPtr; |
1538 | 0 | while (tmpPtr < endPtr) |
1539 | 0 | { |
1540 | 0 | if (fFormatter->getTranscoder()->canTranscodeTo(*tmpPtr)) |
1541 | 0 | tmpPtr++; |
1542 | 0 | else |
1543 | 0 | break; |
1544 | 0 | } |
1545 | |
|
1546 | 0 | if (tmpPtr > srcPtr) |
1547 | 0 | { |
1548 | 0 | TRY_CATCH_THROW |
1549 | 0 | ( |
1550 | 0 | *fFormatter << XMLFormatter::NoEscapes << gStartCDATA; |
1551 | 0 | ) |
1552 | | |
1553 | | // We got at least some chars that can be done normally |
1554 | 0 | fFormatter->formatBuf |
1555 | 0 | ( |
1556 | 0 | srcPtr |
1557 | 0 | , tmpPtr - srcPtr |
1558 | 0 | , XMLFormatter::NoEscapes |
1559 | 0 | , XMLFormatter::UnRep_Fail |
1560 | 0 | ); |
1561 | |
|
1562 | 0 | TRY_CATCH_THROW |
1563 | 0 | ( |
1564 | 0 | *fFormatter << XMLFormatter::NoEscapes << gEndCDATA; |
1565 | 0 | ) |
1566 | | |
1567 | | // Update the source pointer to our new spot |
1568 | 0 | srcPtr = tmpPtr; |
1569 | 0 | } |
1570 | 0 | else |
1571 | 0 | { |
1572 | | // |
1573 | | // We hit something unrepresentable. So continue forward doing |
1574 | | // char refs until we hit something representable again or the |
1575 | | // end of input. |
1576 | | // |
1577 | | |
1578 | | // one warning for consective unrep chars |
1579 | 0 | reportError(nodeToWrite, DOMError::DOM_SEVERITY_WARNING, XMLDOMMsg::Writer_NotRepresentChar); |
1580 | |
|
1581 | 0 | while (srcPtr < endPtr) |
1582 | 0 | { |
1583 | | // Build a char ref for the current char |
1584 | 0 | XMLString::binToText(*srcPtr, &tmpBuf[3], 8, 16, fMemoryManager); |
1585 | 0 | const XMLSize_t bufLen = XMLString::stringLen(tmpBuf); |
1586 | 0 | tmpBuf[bufLen] = chSemiColon; |
1587 | 0 | tmpBuf[bufLen+1] = chNull; |
1588 | | |
1589 | | // And now call recursively back to our caller to format this |
1590 | 0 | fFormatter->formatBuf |
1591 | 0 | ( |
1592 | 0 | tmpBuf |
1593 | 0 | , bufLen + 1 |
1594 | 0 | , XMLFormatter::NoEscapes |
1595 | 0 | , XMLFormatter::UnRep_Fail |
1596 | 0 | ); |
1597 | | |
1598 | | // Move up the source pointer and break out if needed |
1599 | 0 | srcPtr++; |
1600 | 0 | if (fFormatter->getTranscoder()->canTranscodeTo(*srcPtr)) |
1601 | 0 | break; |
1602 | 0 | } |
1603 | 0 | } |
1604 | 0 | } |
1605 | 0 | } |
1606 | | |
1607 | | void DOMLSSerializerImpl::processNode(const DOMNode* const nodeToWrite) |
1608 | 0 | { |
1609 | 0 | processNode(nodeToWrite, 0); |
1610 | 0 | } |
1611 | | |
1612 | | bool DOMLSSerializerImpl::canSetFeature(const int featureId |
1613 | | , bool val) const |
1614 | 0 | { |
1615 | 0 | return featuresSupported[2*featureId + (val? 0: 1)]; |
1616 | 0 | } |
1617 | | |
1618 | | void DOMLSSerializerImpl::printNewLine() |
1619 | 0 | { |
1620 | 0 | if (getFeature(FORMAT_PRETTY_PRINT_ID)) |
1621 | 0 | { |
1622 | 0 | fCurrentLine++; |
1623 | 0 | *fFormatter << fNewLineUsed; |
1624 | 0 | } |
1625 | 0 | } |
1626 | | |
1627 | | void DOMLSSerializerImpl::printIndent(unsigned int level) |
1628 | 0 | { |
1629 | 0 | if (getFeature(FORMAT_PRETTY_PRINT_ID)) |
1630 | 0 | { |
1631 | 0 | if (fLastWhiteSpaceInTextNode) |
1632 | 0 | { |
1633 | 0 | unsigned int indentLevel = fLastWhiteSpaceInTextNode/2; // two chSpaces equals one indent level |
1634 | 0 | fLastWhiteSpaceInTextNode = 0; |
1635 | | // if fLastWhiteSpaceInTextNode/2 is greater than level, then |
1636 | | // it means too many spaces have been written to the |
1637 | | // output stream and we can no longer indent properly |
1638 | 0 | if(indentLevel < level) |
1639 | 0 | level -= indentLevel; |
1640 | 0 | else |
1641 | 0 | level = 0; |
1642 | 0 | } |
1643 | |
|
1644 | 0 | for(unsigned int i = 0; i < level; i++) |
1645 | 0 | *fFormatter << chSpace << chSpace; |
1646 | 0 | } |
1647 | 0 | } |
1648 | | |
1649 | | void DOMLSSerializerImpl::release() |
1650 | 0 | { |
1651 | 0 | DOMLSSerializerImpl* writer = (DOMLSSerializerImpl*) this; |
1652 | 0 | delete writer; |
1653 | 0 | } |
1654 | | |
1655 | | void DOMLSSerializerImpl::processBOM() |
1656 | 0 | { |
1657 | | // if the feature is not set, don't output bom |
1658 | 0 | if (!getFeature(BYTE_ORDER_MARK_ID)) |
1659 | 0 | return; |
1660 | | |
1661 | 0 | if ((XMLString::compareIStringASCII(fEncodingUsed, XMLUni::fgUTF8EncodingString) == 0) || |
1662 | 0 | (XMLString::compareIStringASCII(fEncodingUsed, XMLUni::fgUTF8EncodingString2) == 0) ) |
1663 | 0 | { |
1664 | 0 | fFormatter->writeBOM(BOM_utf8, 3); |
1665 | 0 | } |
1666 | 0 | else if ((XMLString::compareIStringASCII(fEncodingUsed, XMLUni::fgUTF16LEncodingString) == 0) || |
1667 | 0 | (XMLString::compareIStringASCII(fEncodingUsed, XMLUni::fgUTF16LEncodingString2) == 0) ) |
1668 | 0 | { |
1669 | 0 | fFormatter->writeBOM(BOM_utf16le, 2); |
1670 | 0 | } |
1671 | 0 | else if ((XMLString::compareIStringASCII(fEncodingUsed, XMLUni::fgUTF16BEncodingString) == 0) || |
1672 | 0 | (XMLString::compareIStringASCII(fEncodingUsed, XMLUni::fgUTF16BEncodingString2) == 0) ) |
1673 | 0 | { |
1674 | 0 | fFormatter->writeBOM(BOM_utf16be, 2); |
1675 | 0 | } |
1676 | 0 | else if ((XMLString::compareIStringASCII(fEncodingUsed, XMLUni::fgUTF16EncodingString) == 0) || |
1677 | 0 | (XMLString::compareIStringASCII(fEncodingUsed, XMLUni::fgUTF16EncodingString2) == 0) || |
1678 | 0 | (XMLString::compareIStringASCII(fEncodingUsed, XMLUni::fgUTF16EncodingString3) == 0) || |
1679 | 0 | (XMLString::compareIStringASCII(fEncodingUsed, XMLUni::fgUTF16EncodingString4) == 0) || |
1680 | 0 | (XMLString::compareIStringASCII(fEncodingUsed, XMLUni::fgUTF16EncodingString5) == 0) || |
1681 | 0 | (XMLString::compareIStringASCII(fEncodingUsed, XMLUni::fgUTF16EncodingString6) == 0) || |
1682 | 0 | (XMLString::compareIStringASCII(fEncodingUsed, XMLUni::fgUTF16EncodingString7) == 0) ) |
1683 | 0 | { |
1684 | 0 | if (XMLPlatformUtils::fgXMLChBigEndian) |
1685 | 0 | fFormatter->writeBOM(BOM_utf16be, 2); |
1686 | 0 | else |
1687 | 0 | fFormatter->writeBOM(BOM_utf16le, 2); |
1688 | 0 | } |
1689 | 0 | else if ((XMLString::compareIStringASCII(fEncodingUsed, XMLUni::fgUCS4LEncodingString) == 0) || |
1690 | 0 | (XMLString::compareIStringASCII(fEncodingUsed, XMLUni::fgUCS4LEncodingString2) == 0) ) |
1691 | 0 | { |
1692 | 0 | fFormatter->writeBOM(BOM_ucs4le, 4); |
1693 | 0 | } |
1694 | 0 | else if ((XMLString::compareIStringASCII(fEncodingUsed, XMLUni::fgUCS4BEncodingString) == 0) || |
1695 | 0 | (XMLString::compareIStringASCII(fEncodingUsed, XMLUni::fgUCS4BEncodingString2) == 0) ) |
1696 | 0 | { |
1697 | 0 | fFormatter->writeBOM(BOM_ucs4be, 4); |
1698 | 0 | } |
1699 | 0 | else if ((XMLString::compareIStringASCII(fEncodingUsed, XMLUni::fgUCS4EncodingString) == 0) || |
1700 | 0 | (XMLString::compareIStringASCII(fEncodingUsed, XMLUni::fgUCS4EncodingString2) == 0) || |
1701 | 0 | (XMLString::compareIStringASCII(fEncodingUsed, XMLUni::fgUCS4EncodingString3) == 0) || |
1702 | 0 | (XMLString::compareIStringASCII(fEncodingUsed, XMLUni::fgUCS4EncodingString4) == 0) || |
1703 | 0 | (XMLString::compareIStringASCII(fEncodingUsed, XMLUni::fgUCS4EncodingString5) == 0) ) |
1704 | 0 | { |
1705 | 0 | if (XMLPlatformUtils::fgXMLChBigEndian) |
1706 | 0 | fFormatter->writeBOM(BOM_ucs4be, 4); |
1707 | 0 | else |
1708 | 0 | fFormatter->writeBOM(BOM_ucs4le, 4); |
1709 | 0 | } |
1710 | 0 | } |
1711 | | |
1712 | | bool DOMLSSerializerImpl::isDefaultNamespacePrefixDeclared() const |
1713 | 0 | { |
1714 | 0 | for(XMLSize_t i=fNamespaceStack->size();i>0;i--) |
1715 | 0 | { |
1716 | 0 | RefHashTableOf<XMLCh>* curNamespaceMap=fNamespaceStack->elementAt(i-1); |
1717 | 0 | const XMLCh* thisUri=curNamespaceMap->get((void*)XMLUni::fgZeroLenString); |
1718 | 0 | if(thisUri) |
1719 | 0 | return true; |
1720 | 0 | } |
1721 | 0 | return false; |
1722 | 0 | } |
1723 | | |
1724 | | bool DOMLSSerializerImpl::isNamespaceBindingActive(const XMLCh* prefix, const XMLCh* uri) const |
1725 | 0 | { |
1726 | 0 | for(XMLSize_t i=fNamespaceStack->size();i>0;i--) |
1727 | 0 | { |
1728 | 0 | RefHashTableOf<XMLCh>* curNamespaceMap=fNamespaceStack->elementAt(i-1); |
1729 | 0 | const XMLCh* thisUri=curNamespaceMap->get((void*)prefix); |
1730 | | // if the prefix has been declared, check if it binds to the correct namespace, otherwise, reports it isn't bound |
1731 | 0 | if(thisUri) |
1732 | 0 | return XMLString::equals(thisUri,uri); |
1733 | 0 | } |
1734 | 0 | return false; |
1735 | 0 | } |
1736 | | |
1737 | | void DOMLSSerializerImpl::ensureValidString(const DOMNode* nodeToWrite, const XMLCh* string) |
1738 | 0 | { |
1739 | | // XERCESC-1854: prevent illegal characters from being written |
1740 | | // XERCESC-2130: allow surrogates |
1741 | 0 | if(string==0) |
1742 | 0 | return; |
1743 | 0 | const XMLCh* cursor=string; |
1744 | 0 | while(*cursor!=0) |
1745 | 0 | { |
1746 | 0 | if((fIsXml11 && !XMLChar1_1::isXMLChar(*cursor)) || (!fIsXml11 && !XMLChar1_0::isXMLChar(*cursor))) |
1747 | 0 | { |
1748 | 0 | if((*cursor >= 0xD800) && (*cursor <= 0xDBFF)) |
1749 | 0 | { |
1750 | 0 | XMLCh leadingSurrogate = *cursor; |
1751 | 0 | cursor++; |
1752 | 0 | if(0==*cursor || (fIsXml11 && !XMLChar1_1::isXMLChar(leadingSurrogate, *cursor)) || (!fIsXml11 && !XMLChar1_0::isXMLChar(leadingSurrogate, *cursor))) |
1753 | 0 | { |
1754 | 0 | reportError(nodeToWrite, DOMError::DOM_SEVERITY_FATAL_ERROR, XMLDOMMsg::INVALID_CHARACTER_ERR); |
1755 | 0 | return; // leave if reportError does not throw |
1756 | 0 | } |
1757 | 0 | } |
1758 | 0 | else |
1759 | 0 | { |
1760 | 0 | reportError(nodeToWrite, DOMError::DOM_SEVERITY_FATAL_ERROR, XMLDOMMsg::INVALID_CHARACTER_ERR); |
1761 | 0 | } |
1762 | 0 | } |
1763 | 0 | cursor++; |
1764 | 0 | } |
1765 | 0 | } |
1766 | | |
1767 | | XERCES_CPP_NAMESPACE_END |