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 SAXDocumentSerializer(boolean v)65 protected SAXDocumentSerializer(boolean v) { 66 super(v); 67 } 68 SAXDocumentSerializer()69 public SAXDocumentSerializer() { 70 } 71 72 reset()73 public void reset() { 74 super.reset(); 75 76 _elementHasNamespaces = false; 77 _charactersAsCDATA = false; 78 } 79 80 // ContentHandler 81 startDocument()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 endDocument()92 public final void endDocument() throws SAXException { 93 try { 94 encodeDocumentTermination(); 95 } catch (IOException e) { 96 throw new SAXException("endDocument", e); 97 } 98 } 99 startPrefixMapping(String prefix, String uri)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 startElement(String namespaceURI, String localName, String qName, Attributes atts)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 endElement(String namespaceURI, String localName, String qName)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 characters(char[] ch, int start, int length)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 ignorableWhitespace(char[] ch, int start, int length)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 processingInstruction(String target, String data)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 setDocumentLocator(org.xml.sax.Locator locator)210 public final void setDocumentLocator(org.xml.sax.Locator locator) { 211 } 212 skippedEntity(String name)213 public final void skippedEntity(String name) throws SAXException { 214 } 215 216 217 218 // LexicalHandler 219 comment(char[] ch, int start, int length)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 startCDATA()232 public final void startCDATA() throws SAXException { 233 _charactersAsCDATA = true; 234 } 235 endCDATA()236 public final void endCDATA() throws SAXException { 237 _charactersAsCDATA = false; 238 } 239 startDTD(String name, String publicId, String systemId)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 endDTD()253 public final void endDTD() throws SAXException { 254 } 255 startEntity(String name)256 public final void startEntity(String name) throws SAXException { 257 } 258 endEntity(String name)259 public final void endEntity(String name) throws SAXException { 260 } 261 262 263 // EncodingAlgorithmContentHandler 264 octets(String URI, int id, byte[] b, int start, int length)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 object(String URI, int id, Object data)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 bytes(byte[] b, int start, int length)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 shorts(short[] s, int start, int length)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 ints(int[] i, int start, int length)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 longs(long[] l, int start, int length)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 booleans(boolean[] b, int start, int length)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 floats(float[] f, int start, int length)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 doubles(double[] d, int start, int length)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 uuids(long[] msblsb, int start, int length)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 numericCharacters(char ch[], int start, int length)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 dateTimeCharacters(char ch[], int start, int length)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 alphabetCharacters(String alphabet, char ch[], int start, int length)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 characters(char[] ch, int start, int length, boolean index)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 countAttributes(Attributes atts)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 encodeAttributes(Attributes atts)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 encodeElement(String namespaceURI, String qName, String localName)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 encodeAttribute(String namespaceURI, String qName, String localName)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 } 598