|
1 /* |
|
2 * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Oracle designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Oracle in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 * or visit www.oracle.com if you need additional information or have any |
|
23 * questions. |
|
24 * |
|
25 * THIS FILE WAS MODIFIED BY SUN MICROSYSTEMS, INC. |
|
26 */ |
|
27 |
|
28 package com.sun.xml.internal.fastinfoset.sax; |
|
29 |
|
30 import com.sun.xml.internal.fastinfoset.Encoder; |
|
31 import com.sun.xml.internal.fastinfoset.EncodingConstants; |
|
32 import com.sun.xml.internal.fastinfoset.QualifiedName; |
|
33 import com.sun.xml.internal.org.jvnet.fastinfoset.sax.FastInfosetWriter; |
|
34 import com.sun.xml.internal.fastinfoset.util.LocalNameQualifiedNamesMap; |
|
35 import java.io.IOException; |
|
36 import com.sun.xml.internal.org.jvnet.fastinfoset.EncodingAlgorithmIndexes; |
|
37 import com.sun.xml.internal.org.jvnet.fastinfoset.FastInfosetException; |
|
38 import com.sun.xml.internal.org.jvnet.fastinfoset.RestrictedAlphabet; |
|
39 import com.sun.xml.internal.org.jvnet.fastinfoset.sax.EncodingAlgorithmAttributes; |
|
40 import org.xml.sax.Attributes; |
|
41 import org.xml.sax.SAXException; |
|
42 import com.sun.xml.internal.fastinfoset.CommonResourceBundle; |
|
43 |
|
44 /** |
|
45 * The Fast Infoset SAX serializer. |
|
46 * <p> |
|
47 * Instantiate this serializer to serialize a fast infoset document in accordance |
|
48 * with the SAX API. |
|
49 * <p> |
|
50 * This utilizes the SAX API in a reverse manner to that of parsing. It is the |
|
51 * responsibility of the client to call the appropriate event methods on the |
|
52 * SAX handlers, and to ensure that such a sequence of methods calls results |
|
53 * in the production well-formed fast infoset documents. The |
|
54 * SAXDocumentSerializer performs no well-formed checks. |
|
55 * |
|
56 * <p> |
|
57 * More than one fast infoset document may be encoded to the |
|
58 * {@link java.io.OutputStream}. |
|
59 */ |
|
60 public class SAXDocumentSerializer extends Encoder implements FastInfosetWriter { |
|
61 protected boolean _elementHasNamespaces = false; |
|
62 |
|
63 protected boolean _charactersAsCDATA = false; |
|
64 |
|
65 protected SAXDocumentSerializer(boolean v) { |
|
66 super(v); |
|
67 } |
|
68 |
|
69 public SAXDocumentSerializer() { |
|
70 } |
|
71 |
|
72 |
|
73 public void reset() { |
|
74 super.reset(); |
|
75 |
|
76 _elementHasNamespaces = false; |
|
77 _charactersAsCDATA = false; |
|
78 } |
|
79 |
|
80 // ContentHandler |
|
81 |
|
82 public final void startDocument() throws SAXException { |
|
83 try { |
|
84 reset(); |
|
85 encodeHeader(false); |
|
86 encodeInitialVocabulary(); |
|
87 } catch (IOException e) { |
|
88 throw new SAXException("startDocument", e); |
|
89 } |
|
90 } |
|
91 |
|
92 public final void endDocument() throws SAXException { |
|
93 try { |
|
94 encodeDocumentTermination(); |
|
95 } catch (IOException e) { |
|
96 throw new SAXException("endDocument", e); |
|
97 } |
|
98 } |
|
99 |
|
100 public void startPrefixMapping(String prefix, String uri) throws SAXException { |
|
101 try { |
|
102 if (_elementHasNamespaces == false) { |
|
103 encodeTermination(); |
|
104 |
|
105 // Mark the current buffer position to flag attributes if necessary |
|
106 mark(); |
|
107 _elementHasNamespaces = true; |
|
108 |
|
109 // Write out Element byte with namespaces |
|
110 write(EncodingConstants.ELEMENT | EncodingConstants.ELEMENT_NAMESPACES_FLAG); |
|
111 } |
|
112 |
|
113 encodeNamespaceAttribute(prefix, uri); |
|
114 } catch (IOException e) { |
|
115 throw new SAXException("startElement", e); |
|
116 } |
|
117 } |
|
118 |
|
119 public final void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { |
|
120 // TODO consider using buffer for encoding of attributes, then pre-counting is not necessary |
|
121 final int attributeCount = (atts != null && atts.getLength() > 0) |
|
122 ? countAttributes(atts) : 0; |
|
123 try { |
|
124 if (_elementHasNamespaces) { |
|
125 _elementHasNamespaces = false; |
|
126 |
|
127 if (attributeCount > 0) { |
|
128 // Flag the marked byte with attributes |
|
129 _octetBuffer[_markIndex] |= EncodingConstants.ELEMENT_ATTRIBUTE_FLAG; |
|
130 } |
|
131 resetMark(); |
|
132 |
|
133 write(EncodingConstants.TERMINATOR); |
|
134 |
|
135 _b = 0; |
|
136 } else { |
|
137 encodeTermination(); |
|
138 |
|
139 _b = EncodingConstants.ELEMENT; |
|
140 if (attributeCount > 0) { |
|
141 _b |= EncodingConstants.ELEMENT_ATTRIBUTE_FLAG; |
|
142 } |
|
143 } |
|
144 |
|
145 encodeElement(namespaceURI, qName, localName); |
|
146 |
|
147 if (attributeCount > 0) { |
|
148 encodeAttributes(atts); |
|
149 } |
|
150 } catch (IOException e) { |
|
151 throw new SAXException("startElement", e); |
|
152 } catch (FastInfosetException e) { |
|
153 throw new SAXException("startElement", e); |
|
154 } |
|
155 } |
|
156 |
|
157 public final void endElement(String namespaceURI, String localName, String qName) throws SAXException { |
|
158 try { |
|
159 encodeElementTermination(); |
|
160 } catch (IOException e) { |
|
161 throw new SAXException("endElement", e); |
|
162 } |
|
163 } |
|
164 |
|
165 public final void characters(char[] ch, int start, int length) throws SAXException { |
|
166 if (length <= 0) { |
|
167 return; |
|
168 } |
|
169 |
|
170 if (getIgnoreWhiteSpaceTextContent() && |
|
171 isWhiteSpace(ch, start, length)) return; |
|
172 |
|
173 try { |
|
174 encodeTermination(); |
|
175 |
|
176 if (!_charactersAsCDATA) { |
|
177 encodeCharacters(ch, start, length); |
|
178 } else { |
|
179 encodeCIIBuiltInAlgorithmDataAsCDATA(ch, start, length); |
|
180 } |
|
181 } catch (IOException e) { |
|
182 throw new SAXException(e); |
|
183 } catch (FastInfosetException e) { |
|
184 throw new SAXException(e); |
|
185 } |
|
186 } |
|
187 |
|
188 public final void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { |
|
189 if (getIgnoreWhiteSpaceTextContent()) return; |
|
190 |
|
191 characters(ch, start, length); |
|
192 } |
|
193 |
|
194 public final void processingInstruction(String target, String data) throws SAXException { |
|
195 try { |
|
196 if (getIgnoreProcesingInstructions()) return; |
|
197 |
|
198 if (target.length() == 0) { |
|
199 throw new SAXException(CommonResourceBundle.getInstance(). |
|
200 getString("message.processingInstructionTargetIsEmpty")); |
|
201 } |
|
202 encodeTermination(); |
|
203 |
|
204 encodeProcessingInstruction(target, data); |
|
205 } catch (IOException e) { |
|
206 throw new SAXException("processingInstruction", e); |
|
207 } |
|
208 } |
|
209 |
|
210 public final void setDocumentLocator(org.xml.sax.Locator locator) { |
|
211 } |
|
212 |
|
213 public final void skippedEntity(String name) throws SAXException { |
|
214 } |
|
215 |
|
216 |
|
217 |
|
218 // LexicalHandler |
|
219 |
|
220 public final void comment(char[] ch, int start, int length) throws SAXException { |
|
221 try { |
|
222 if (getIgnoreComments()) return; |
|
223 |
|
224 encodeTermination(); |
|
225 |
|
226 encodeComment(ch, start, length); |
|
227 } catch (IOException e) { |
|
228 throw new SAXException("startElement", e); |
|
229 } |
|
230 } |
|
231 |
|
232 public final void startCDATA() throws SAXException { |
|
233 _charactersAsCDATA = true; |
|
234 } |
|
235 |
|
236 public final void endCDATA() throws SAXException { |
|
237 _charactersAsCDATA = false; |
|
238 } |
|
239 |
|
240 public final void startDTD(String name, String publicId, String systemId) throws SAXException { |
|
241 if (getIgnoreDTD()) return; |
|
242 |
|
243 try { |
|
244 encodeTermination(); |
|
245 |
|
246 encodeDocumentTypeDeclaration(publicId, systemId); |
|
247 encodeElementTermination(); |
|
248 } catch (IOException e) { |
|
249 throw new SAXException("startDTD", e); |
|
250 } |
|
251 } |
|
252 |
|
253 public final void endDTD() throws SAXException { |
|
254 } |
|
255 |
|
256 public final void startEntity(String name) throws SAXException { |
|
257 } |
|
258 |
|
259 public final void endEntity(String name) throws SAXException { |
|
260 } |
|
261 |
|
262 |
|
263 // EncodingAlgorithmContentHandler |
|
264 |
|
265 public final void octets(String URI, int id, byte[] b, int start, int length) throws SAXException { |
|
266 if (length <= 0) { |
|
267 return; |
|
268 } |
|
269 |
|
270 try { |
|
271 encodeTermination(); |
|
272 |
|
273 encodeNonIdentifyingStringOnThirdBit(URI, id, b, start, length); |
|
274 } catch (IOException e) { |
|
275 throw new SAXException(e); |
|
276 } catch (FastInfosetException e) { |
|
277 throw new SAXException(e); |
|
278 } |
|
279 } |
|
280 |
|
281 public final void object(String URI, int id, Object data) throws SAXException { |
|
282 try { |
|
283 encodeTermination(); |
|
284 |
|
285 encodeNonIdentifyingStringOnThirdBit(URI, id, data); |
|
286 } catch (IOException e) { |
|
287 throw new SAXException(e); |
|
288 } catch (FastInfosetException e) { |
|
289 throw new SAXException(e); |
|
290 } |
|
291 } |
|
292 |
|
293 |
|
294 // PrimitiveTypeContentHandler |
|
295 |
|
296 public final void bytes(byte[] b, int start, int length) throws SAXException { |
|
297 if (length <= 0) { |
|
298 return; |
|
299 } |
|
300 |
|
301 try { |
|
302 encodeTermination(); |
|
303 |
|
304 encodeCIIOctetAlgorithmData(EncodingAlgorithmIndexes.BASE64, b, start, length); |
|
305 } catch (IOException e) { |
|
306 throw new SAXException(e); |
|
307 } |
|
308 } |
|
309 |
|
310 public final void shorts(short[] s, int start, int length) throws SAXException { |
|
311 if (length <= 0) { |
|
312 return; |
|
313 } |
|
314 |
|
315 try { |
|
316 encodeTermination(); |
|
317 |
|
318 encodeCIIBuiltInAlgorithmData(EncodingAlgorithmIndexes.SHORT, s, start, length); |
|
319 } catch (IOException e) { |
|
320 throw new SAXException(e); |
|
321 } catch (FastInfosetException e) { |
|
322 throw new SAXException(e); |
|
323 } |
|
324 } |
|
325 |
|
326 public final void ints(int[] i, int start, int length) throws SAXException { |
|
327 if (length <= 0) { |
|
328 return; |
|
329 } |
|
330 |
|
331 try { |
|
332 encodeTermination(); |
|
333 |
|
334 encodeCIIBuiltInAlgorithmData(EncodingAlgorithmIndexes.INT, i, start, length); |
|
335 } catch (IOException e) { |
|
336 throw new SAXException(e); |
|
337 } catch (FastInfosetException e) { |
|
338 throw new SAXException(e); |
|
339 } |
|
340 } |
|
341 |
|
342 public final void longs(long[] l, int start, int length) throws SAXException { |
|
343 if (length <= 0) { |
|
344 return; |
|
345 } |
|
346 |
|
347 try { |
|
348 encodeTermination(); |
|
349 |
|
350 encodeCIIBuiltInAlgorithmData(EncodingAlgorithmIndexes.LONG, l, start, length); |
|
351 } catch (IOException e) { |
|
352 throw new SAXException(e); |
|
353 } catch (FastInfosetException e) { |
|
354 throw new SAXException(e); |
|
355 } |
|
356 } |
|
357 |
|
358 public final void booleans(boolean[] b, int start, int length) throws SAXException { |
|
359 if (length <= 0) { |
|
360 return; |
|
361 } |
|
362 |
|
363 try { |
|
364 encodeTermination(); |
|
365 |
|
366 encodeCIIBuiltInAlgorithmData(EncodingAlgorithmIndexes.BOOLEAN, b, start, length); |
|
367 } catch (IOException e) { |
|
368 throw new SAXException(e); |
|
369 } catch (FastInfosetException e) { |
|
370 throw new SAXException(e); |
|
371 } |
|
372 } |
|
373 |
|
374 public final void floats(float[] f, int start, int length) throws SAXException { |
|
375 if (length <= 0) { |
|
376 return; |
|
377 } |
|
378 |
|
379 try { |
|
380 encodeTermination(); |
|
381 |
|
382 encodeCIIBuiltInAlgorithmData(EncodingAlgorithmIndexes.FLOAT, f, start, length); |
|
383 } catch (IOException e) { |
|
384 throw new SAXException(e); |
|
385 } catch (FastInfosetException e) { |
|
386 throw new SAXException(e); |
|
387 } |
|
388 } |
|
389 |
|
390 public final void doubles(double[] d, int start, int length) throws SAXException { |
|
391 if (length <= 0) { |
|
392 return; |
|
393 } |
|
394 |
|
395 try { |
|
396 encodeTermination(); |
|
397 |
|
398 encodeCIIBuiltInAlgorithmData(EncodingAlgorithmIndexes.DOUBLE, d, start, length); |
|
399 } catch (IOException e) { |
|
400 throw new SAXException(e); |
|
401 } catch (FastInfosetException e) { |
|
402 throw new SAXException(e); |
|
403 } |
|
404 } |
|
405 |
|
406 public void uuids(long[] msblsb, int start, int length) throws SAXException { |
|
407 if (length <= 0) { |
|
408 return; |
|
409 } |
|
410 |
|
411 try { |
|
412 encodeTermination(); |
|
413 |
|
414 encodeCIIBuiltInAlgorithmData(EncodingAlgorithmIndexes.UUID, msblsb, start, length); |
|
415 } catch (IOException e) { |
|
416 throw new SAXException(e); |
|
417 } catch (FastInfosetException e) { |
|
418 throw new SAXException(e); |
|
419 } |
|
420 } |
|
421 |
|
422 |
|
423 // RestrictedAlphabetContentHandler |
|
424 |
|
425 public void numericCharacters(char ch[], int start, int length) throws SAXException { |
|
426 if (length <= 0) { |
|
427 return; |
|
428 } |
|
429 |
|
430 try { |
|
431 encodeTermination(); |
|
432 |
|
433 final boolean addToTable = isCharacterContentChunkLengthMatchesLimit(length); |
|
434 encodeNumericFourBitCharacters(ch, start, length, addToTable); |
|
435 } catch (IOException e) { |
|
436 throw new SAXException(e); |
|
437 } catch (FastInfosetException e) { |
|
438 throw new SAXException(e); |
|
439 } |
|
440 } |
|
441 |
|
442 public void dateTimeCharacters(char ch[], int start, int length) throws SAXException { |
|
443 if (length <= 0) { |
|
444 return; |
|
445 } |
|
446 |
|
447 try { |
|
448 encodeTermination(); |
|
449 |
|
450 final boolean addToTable = isCharacterContentChunkLengthMatchesLimit(length); |
|
451 encodeDateTimeFourBitCharacters(ch, start, length, addToTable); |
|
452 } catch (IOException e) { |
|
453 throw new SAXException(e); |
|
454 } catch (FastInfosetException e) { |
|
455 throw new SAXException(e); |
|
456 } |
|
457 } |
|
458 |
|
459 public void alphabetCharacters(String alphabet, char ch[], int start, int length) throws SAXException { |
|
460 if (length <= 0) { |
|
461 return; |
|
462 } |
|
463 |
|
464 try { |
|
465 encodeTermination(); |
|
466 |
|
467 final boolean addToTable = isCharacterContentChunkLengthMatchesLimit(length); |
|
468 encodeAlphabetCharacters(alphabet, ch, start, length, addToTable); |
|
469 } catch (IOException e) { |
|
470 throw new SAXException(e); |
|
471 } catch (FastInfosetException e) { |
|
472 throw new SAXException(e); |
|
473 } |
|
474 } |
|
475 |
|
476 // ExtendedContentHandler |
|
477 |
|
478 public void characters(char[] ch, int start, int length, boolean index) throws SAXException { |
|
479 if (length <= 0) { |
|
480 return; |
|
481 } |
|
482 |
|
483 if (getIgnoreWhiteSpaceTextContent() && |
|
484 isWhiteSpace(ch, start, length)) return; |
|
485 |
|
486 try { |
|
487 encodeTermination(); |
|
488 |
|
489 if (!_charactersAsCDATA) { |
|
490 encodeNonIdentifyingStringOnThirdBit(ch, start, length, _v.characterContentChunk, index, true); |
|
491 } else { |
|
492 encodeCIIBuiltInAlgorithmDataAsCDATA(ch, start, length); |
|
493 } |
|
494 } catch (IOException e) { |
|
495 throw new SAXException(e); |
|
496 } catch (FastInfosetException e) { |
|
497 throw new SAXException(e); |
|
498 } |
|
499 } |
|
500 |
|
501 |
|
502 |
|
503 protected final int countAttributes(Attributes atts) { |
|
504 // Count attributes ignoring any in the XMLNS namespace |
|
505 // Note, such attributes may be produced when transforming from a DOM node |
|
506 int count = 0; |
|
507 for (int i = 0; i < atts.getLength(); i++) { |
|
508 final String uri = atts.getURI(i); |
|
509 if (uri == "http://www.w3.org/2000/xmlns/" || uri.equals("http://www.w3.org/2000/xmlns/")) { |
|
510 continue; |
|
511 } |
|
512 count++; |
|
513 } |
|
514 return count; |
|
515 } |
|
516 |
|
517 protected void encodeAttributes(Attributes atts) throws IOException, FastInfosetException { |
|
518 boolean addToTable; |
|
519 boolean mustBeAddedToTable; |
|
520 String value; |
|
521 if (atts instanceof EncodingAlgorithmAttributes) { |
|
522 final EncodingAlgorithmAttributes eAtts = (EncodingAlgorithmAttributes)atts; |
|
523 Object data; |
|
524 String alphabet; |
|
525 for (int i = 0; i < eAtts.getLength(); i++) { |
|
526 if (encodeAttribute(atts.getURI(i), atts.getQName(i), atts.getLocalName(i))) { |
|
527 data = eAtts.getAlgorithmData(i); |
|
528 // If data is null then there is no algorithm data |
|
529 if (data == null) { |
|
530 value = eAtts.getValue(i); |
|
531 addToTable = isAttributeValueLengthMatchesLimit(value.length()); |
|
532 mustBeAddedToTable = eAtts.getToIndex(i); |
|
533 |
|
534 alphabet = eAtts.getAlpababet(i); |
|
535 if (alphabet == null) { |
|
536 encodeNonIdentifyingStringOnFirstBit(value, _v.attributeValue, addToTable, mustBeAddedToTable); |
|
537 } else if (alphabet == RestrictedAlphabet.DATE_TIME_CHARACTERS) { |
|
538 encodeDateTimeNonIdentifyingStringOnFirstBit( |
|
539 value, addToTable, mustBeAddedToTable); |
|
540 } else if (alphabet == RestrictedAlphabet.NUMERIC_CHARACTERS) { |
|
541 encodeNumericNonIdentifyingStringOnFirstBit( |
|
542 value, addToTable, mustBeAddedToTable); |
|
543 } else { |
|
544 encodeNonIdentifyingStringOnFirstBit(value, _v.attributeValue, addToTable, mustBeAddedToTable); |
|
545 } |
|
546 } else { |
|
547 encodeNonIdentifyingStringOnFirstBit(eAtts.getAlgorithmURI(i), |
|
548 eAtts.getAlgorithmIndex(i), data); |
|
549 } |
|
550 } |
|
551 } |
|
552 } else { |
|
553 for (int i = 0; i < atts.getLength(); i++) { |
|
554 if (encodeAttribute(atts.getURI(i), atts.getQName(i), atts.getLocalName(i))) { |
|
555 value = atts.getValue(i); |
|
556 addToTable = isAttributeValueLengthMatchesLimit(value.length()); |
|
557 encodeNonIdentifyingStringOnFirstBit(value, _v.attributeValue, addToTable, false); |
|
558 } |
|
559 } |
|
560 } |
|
561 _b = EncodingConstants.TERMINATOR; |
|
562 _terminate = true; |
|
563 } |
|
564 |
|
565 protected void encodeElement(String namespaceURI, String qName, String localName) throws IOException { |
|
566 LocalNameQualifiedNamesMap.Entry entry = _v.elementName.obtainEntry(qName); |
|
567 if (entry._valueIndex > 0) { |
|
568 QualifiedName[] names = entry._value; |
|
569 for (int i = 0; i < entry._valueIndex; i++) { |
|
570 final QualifiedName n = names[i]; |
|
571 if ((namespaceURI == n.namespaceName || namespaceURI.equals(n.namespaceName))) { |
|
572 encodeNonZeroIntegerOnThirdBit(names[i].index); |
|
573 return; |
|
574 } |
|
575 } |
|
576 } |
|
577 |
|
578 encodeLiteralElementQualifiedNameOnThirdBit(namespaceURI, getPrefixFromQualifiedName(qName), |
|
579 localName, entry); |
|
580 } |
|
581 |
|
582 protected boolean encodeAttribute(String namespaceURI, String qName, String localName) throws IOException { |
|
583 LocalNameQualifiedNamesMap.Entry entry = _v.attributeName.obtainEntry(qName); |
|
584 if (entry._valueIndex > 0) { |
|
585 QualifiedName[] names = entry._value; |
|
586 for (int i = 0; i < entry._valueIndex; i++) { |
|
587 if ((namespaceURI == names[i].namespaceName || namespaceURI.equals(names[i].namespaceName))) { |
|
588 encodeNonZeroIntegerOnSecondBitFirstBitZero(names[i].index); |
|
589 return true; |
|
590 } |
|
591 } |
|
592 } |
|
593 |
|
594 return encodeLiteralAttributeQualifiedNameOnSecondBit(namespaceURI, getPrefixFromQualifiedName(qName), |
|
595 localName, entry); |
|
596 } |
|
597 } |