001 /* 002 * Copyright (c) 2007 Henri Sivonen 003 * Copyright (c) 2008-2009 Mozilla Foundation 004 * 005 * Permission is hereby granted, free of charge, to any person obtaining a 006 * copy of this software and associated documentation files (the "Software"), 007 * to deal in the Software without restriction, including without limitation 008 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 009 * and/or sell copies of the Software, and to permit persons to whom the 010 * Software is furnished to do so, subject to the following conditions: 011 * 012 * The above copyright notice and this permission notice shall be included in 013 * all copies or substantial portions of the Software. 014 * 015 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 016 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 017 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 018 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 019 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 020 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 021 * DEALINGS IN THE SOFTWARE. 022 */ 023 024 package nu.validator.htmlparser.sax; 025 026 import java.io.IOException; 027 import java.io.OutputStream; 028 import java.io.OutputStreamWriter; 029 import java.io.UnsupportedEncodingException; 030 import java.io.Writer; 031 import java.nio.charset.Charset; 032 import java.nio.charset.CharsetEncoder; 033 import java.nio.charset.CodingErrorAction; 034 import java.util.HashMap; 035 import java.util.HashSet; 036 import java.util.LinkedList; 037 import java.util.Map; 038 import java.util.Set; 039 040 import org.xml.sax.Attributes; 041 import org.xml.sax.ContentHandler; 042 import org.xml.sax.Locator; 043 import org.xml.sax.SAXException; 044 import org.xml.sax.ext.LexicalHandler; 045 046 public class XmlSerializer implements ContentHandler, LexicalHandler { 047 048 private final class PrefixMapping { 049 public final String uri; 050 051 public final String prefix; 052 053 /** 054 * @param uri 055 * @param prefix 056 */ 057 public PrefixMapping(String uri, String prefix) { 058 this.uri = uri; 059 this.prefix = prefix; 060 } 061 062 /** 063 * @see java.lang.Object#equals(java.lang.Object) 064 */ 065 @Override public final boolean equals(Object obj) { 066 if (obj instanceof PrefixMapping) { 067 PrefixMapping other = (PrefixMapping) obj; 068 return this.prefix.equals(other.prefix); 069 } else { 070 return false; 071 } 072 } 073 074 /** 075 * @see java.lang.Object#hashCode() 076 */ 077 @Override public final int hashCode() { 078 return prefix.hashCode(); 079 } 080 081 } 082 083 private final class StackNode { 084 public final String uri; 085 086 public final String prefix; 087 088 public final String qName; 089 090 public final Set<PrefixMapping> mappings = new HashSet<PrefixMapping>(); 091 092 /** 093 * @param uri 094 * @param qName 095 */ 096 public StackNode(String uri, String qName, String prefix) { 097 this.uri = uri; 098 this.qName = qName; 099 this.prefix = prefix; 100 } 101 } 102 103 private final static Map<String, String> WELL_KNOWN_ATTRIBUTE_PREFIXES = new HashMap<String, String>(); 104 105 static { 106 WELL_KNOWN_ATTRIBUTE_PREFIXES.put("adobe:ns:meta/", "x"); 107 WELL_KNOWN_ATTRIBUTE_PREFIXES.put( 108 "http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd", 109 "sodipodi"); 110 WELL_KNOWN_ATTRIBUTE_PREFIXES.put( 111 "http://ns.adobe.com/AdobeIllustrator/10.0/", "i"); 112 WELL_KNOWN_ATTRIBUTE_PREFIXES.put( 113 "http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/", "a"); 114 WELL_KNOWN_ATTRIBUTE_PREFIXES.put( 115 "http://ns.adobe.com/Extensibility/1.0/", "x"); 116 WELL_KNOWN_ATTRIBUTE_PREFIXES.put( 117 "http://ns.adobe.com/illustrator/1.0/", "illustrator"); 118 WELL_KNOWN_ATTRIBUTE_PREFIXES.put("http://ns.adobe.com/pdf/1.3/", "pdf"); 119 WELL_KNOWN_ATTRIBUTE_PREFIXES.put("http://ns.adobe.com/photoshop/1.0/", 120 "photoshop"); 121 WELL_KNOWN_ATTRIBUTE_PREFIXES.put("http://ns.adobe.com/tiff/1.0/", 122 "tiff"); 123 WELL_KNOWN_ATTRIBUTE_PREFIXES.put("http://ns.adobe.com/xap/1.0/", "xap"); 124 WELL_KNOWN_ATTRIBUTE_PREFIXES.put("http://ns.adobe.com/xap/1.0/g/", 125 "xapG"); 126 WELL_KNOWN_ATTRIBUTE_PREFIXES.put("http://ns.adobe.com/xap/1.0/mm/", 127 "xapMM"); 128 WELL_KNOWN_ATTRIBUTE_PREFIXES.put( 129 "http://ns.adobe.com/xap/1.0/rights/", "xapRights"); 130 WELL_KNOWN_ATTRIBUTE_PREFIXES.put( 131 "http://ns.adobe.com/xap/1.0/sType/Dimensions#", "stDim"); 132 WELL_KNOWN_ATTRIBUTE_PREFIXES.put( 133 "http://ns.adobe.com/xap/1.0/sType/ResourceRef#", "stRef"); 134 WELL_KNOWN_ATTRIBUTE_PREFIXES.put("http://ns.adobe.com/xap/1.0/t/pg/", 135 "xapTPg"); 136 WELL_KNOWN_ATTRIBUTE_PREFIXES.put("http://purl.org/dc/elements/1.1/", 137 "dc"); 138 WELL_KNOWN_ATTRIBUTE_PREFIXES.put( 139 "http://schemas.microsoft.com/visio/2003/SVGExtensions/", "v"); 140 WELL_KNOWN_ATTRIBUTE_PREFIXES.put( 141 "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd", 142 "sodipodi"); 143 WELL_KNOWN_ATTRIBUTE_PREFIXES.put("http://w3.org/1999/xlink", "xlink"); 144 WELL_KNOWN_ATTRIBUTE_PREFIXES.put("http://www.carto.net/attrib/", 145 "attrib"); 146 WELL_KNOWN_ATTRIBUTE_PREFIXES.put( 147 "http://www.iki.fi/pav/software/textext/", "textext"); 148 WELL_KNOWN_ATTRIBUTE_PREFIXES.put( 149 "http://www.inkscape.org/namespaces/inkscape", "inkscape"); 150 WELL_KNOWN_ATTRIBUTE_PREFIXES.put( 151 "http://www.justsystem.co.jp/hanako13/svg", "jsh"); 152 WELL_KNOWN_ATTRIBUTE_PREFIXES.put( 153 "http://www.w3.org/1999/02/22-rdf-syntax-ns#", "rdf"); 154 WELL_KNOWN_ATTRIBUTE_PREFIXES.put("http://www.w3.org/1999/xlink", 155 "xlink"); 156 WELL_KNOWN_ATTRIBUTE_PREFIXES.put( 157 "http://www.w3.org/2001/XMLSchema-instance", "xsi"); 158 WELL_KNOWN_ATTRIBUTE_PREFIXES.put("http://www.w3org/1999/xlink", 159 "xlink"); 160 } 161 162 private final static Map<String, String> WELL_KNOWN_ELEMENT_PREFIXES = new HashMap<String, String>(); 163 164 static { 165 WELL_KNOWN_ELEMENT_PREFIXES.put("http://www.w3.org/1999/XSL/Transform", 166 "xsl"); 167 WELL_KNOWN_ELEMENT_PREFIXES.put( 168 "http://www.w3.org/1999/02/22-rdf-syntax-ns#", "rdf"); 169 WELL_KNOWN_ELEMENT_PREFIXES.put("http://purl.org/dc/elements/1.1/", 170 "dc"); 171 WELL_KNOWN_ELEMENT_PREFIXES.put( 172 "http://www.w3.org/2001/XMLSchema-instance", "xsi"); 173 WELL_KNOWN_ELEMENT_PREFIXES.put("http://www.ascc.net/xml/schematron", 174 "sch"); 175 WELL_KNOWN_ELEMENT_PREFIXES.put("http://purl.oclc.org/dsdl/schematron", 176 "sch"); 177 WELL_KNOWN_ELEMENT_PREFIXES.put( 178 "http://www.inkscape.org/namespaces/inkscape", "inkscape"); 179 WELL_KNOWN_ELEMENT_PREFIXES.put( 180 "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd", 181 "sodipodi"); 182 WELL_KNOWN_ELEMENT_PREFIXES.put( 183 "http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/", "a"); 184 WELL_KNOWN_ELEMENT_PREFIXES.put( 185 "http://ns.adobe.com/AdobeIllustrator/10.0/", "i"); 186 WELL_KNOWN_ELEMENT_PREFIXES.put("adobe:ns:meta/", "x"); 187 WELL_KNOWN_ELEMENT_PREFIXES.put("http://ns.adobe.com/xap/1.0/", "xap"); 188 WELL_KNOWN_ELEMENT_PREFIXES.put("http://ns.adobe.com/pdf/1.3/", "pdf"); 189 WELL_KNOWN_ELEMENT_PREFIXES.put("http://ns.adobe.com/tiff/1.0/", "tiff"); 190 WELL_KNOWN_ELEMENT_PREFIXES.put("http://creativecommons.org/ns#", "cc"); 191 WELL_KNOWN_ELEMENT_PREFIXES.put( 192 "http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd", 193 "sodipodi"); 194 WELL_KNOWN_ELEMENT_PREFIXES.put( 195 "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/", "Iptc4xmpCore"); 196 WELL_KNOWN_ELEMENT_PREFIXES.put("http://ns.adobe.com/exif/1.0/", "exif"); 197 WELL_KNOWN_ELEMENT_PREFIXES.put( 198 "http://ns.adobe.com/Extensibility/1.0/", "x"); 199 WELL_KNOWN_ELEMENT_PREFIXES.put("http://ns.adobe.com/illustrator/1.0/", 200 "illustrator"); 201 WELL_KNOWN_ELEMENT_PREFIXES.put("http://ns.adobe.com/pdfx/1.3/", "pdfx"); 202 WELL_KNOWN_ELEMENT_PREFIXES.put("http://ns.adobe.com/photoshop/1.0/", 203 "photoshop"); 204 WELL_KNOWN_ELEMENT_PREFIXES.put("http://ns.adobe.com/Variables/1.0/", 205 "v"); 206 WELL_KNOWN_ELEMENT_PREFIXES.put("http://ns.adobe.com/xap/1.0/g/", 207 "xapG"); 208 WELL_KNOWN_ELEMENT_PREFIXES.put("http://ns.adobe.com/xap/1.0/g/img/", 209 "xapGImg"); 210 WELL_KNOWN_ELEMENT_PREFIXES.put("http://ns.adobe.com/xap/1.0/mm/", 211 "xapMM"); 212 WELL_KNOWN_ELEMENT_PREFIXES.put("http://ns.adobe.com/xap/1.0/rights/", 213 "xapRights"); 214 WELL_KNOWN_ELEMENT_PREFIXES.put( 215 "http://ns.adobe.com/xap/1.0/sType/Dimensions#", "stDim"); 216 WELL_KNOWN_ELEMENT_PREFIXES.put( 217 "http://ns.adobe.com/xap/1.0/sType/Font#", "stFnt"); 218 WELL_KNOWN_ELEMENT_PREFIXES.put( 219 "http://ns.adobe.com/xap/1.0/sType/ResourceRef#", "stRef"); 220 WELL_KNOWN_ELEMENT_PREFIXES.put("http://ns.adobe.com/xap/1.0/t/pg/", 221 "xapTPg"); 222 WELL_KNOWN_ELEMENT_PREFIXES.put( 223 "http://product.corel.com/CGS/11/cddns/", "odm"); 224 WELL_KNOWN_ELEMENT_PREFIXES.put( 225 "http://schemas.microsoft.com/visio/2003/SVGExtensions/", "v"); 226 WELL_KNOWN_ELEMENT_PREFIXES.put("http://web.resource.org/cc/", "cc"); 227 WELL_KNOWN_ELEMENT_PREFIXES.put( 228 "http://www.freesoftware.fsf.org/bkchem/cdml", "cdml"); 229 WELL_KNOWN_ELEMENT_PREFIXES.put("http://www.opengis.net/gml", "gml"); 230 WELL_KNOWN_ELEMENT_PREFIXES.put("http://www.svgmaker.com/svgns", 231 "svgmaker"); 232 WELL_KNOWN_ELEMENT_PREFIXES.put( 233 "http://www.w3.org/2000/01/rdf-schema#", "rdfs"); 234 WELL_KNOWN_ELEMENT_PREFIXES.put("http://xmlns.com/foaf/0.1/", "foaf"); 235 WELL_KNOWN_ELEMENT_PREFIXES.put("http://www.xml-cml.org/schema/stmml", 236 "stm"); 237 WELL_KNOWN_ELEMENT_PREFIXES.put("http://www.iupac.org/foo/ichi", "ichi"); 238 } 239 240 private final static Writer wrap(OutputStream out) { 241 Charset charset = Charset.forName("utf-8"); 242 CharsetEncoder encoder = charset.newEncoder(); 243 encoder.onMalformedInput(CodingErrorAction.REPLACE); 244 encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); 245 try { 246 encoder.replaceWith("\uFFFD".getBytes("utf-8")); 247 } catch (UnsupportedEncodingException e) { 248 throw new RuntimeException(e); 249 } 250 return new OutputStreamWriter(out, encoder); 251 } 252 253 // grows from head 254 private final LinkedList<StackNode> stack = new LinkedList<StackNode>(); 255 256 private final Writer writer; 257 258 public XmlSerializer(OutputStream out) { 259 this(wrap(out)); 260 } 261 262 public XmlSerializer(Writer out) { 263 this.writer = out; 264 } 265 266 protected void checkNCName(String name) throws SAXException { 267 268 } 269 270 private final void push(String uri, String local, String prefix) { 271 stack.addFirst(new StackNode(uri, local, prefix)); 272 } 273 274 private final String pop() { 275 String rv = stack.removeFirst().qName; 276 stack.getFirst().mappings.clear(); 277 return rv; 278 } 279 280 private final String lookupPrefixAttribute(String ns) { 281 if ("http://www.w3.org/XML/1998/namespace".equals(ns)) { 282 return "xml"; 283 } 284 Set<String> hidden = new HashSet<String>(); 285 for (StackNode node : stack) { 286 for (PrefixMapping mapping : node.mappings) { 287 if (mapping.prefix.length() != 0 && mapping.uri.equals(ns) 288 && !hidden.contains(mapping.prefix)) { 289 return mapping.prefix; 290 } 291 hidden.add(mapping.prefix); 292 } 293 } 294 return null; 295 } 296 297 private final String lookupUri(String prefix) { 298 for (StackNode node : stack) { 299 for (PrefixMapping mapping : node.mappings) { 300 if (mapping.prefix.equals(prefix)) { 301 return mapping.uri; 302 } 303 } 304 } 305 return null; 306 } 307 308 private final boolean xmlNsQname(String name) { 309 if (name == null) { 310 return false; 311 } else if ("xmlns".equals(name)) { 312 return true; 313 } else if (name.startsWith("xmlns:")) { 314 return true; 315 } else { 316 return false; 317 } 318 } 319 320 private final void writeAttributeValue(String val) throws IOException { 321 boolean prevWasSpace = true; 322 int last = val.length() - 1; 323 for (int i = 0; i <= last; i++) { 324 char c = val.charAt(i); 325 switch (c) { 326 case '<': 327 writer.write("<"); 328 prevWasSpace = false; 329 break; 330 case '>': 331 writer.write(">"); 332 prevWasSpace = false; 333 break; 334 case '&': 335 writer.write("&"); 336 prevWasSpace = false; 337 break; 338 case '"': 339 writer.write("""); 340 prevWasSpace = false; 341 break; 342 case '\r': 343 writer.write("
"); 344 prevWasSpace = false; 345 break; 346 case '\t': 347 writer.write("	"); 348 prevWasSpace = false; 349 break; 350 case '\n': 351 writer.write("
"); 352 prevWasSpace = false; 353 break; 354 case ' ': 355 if (prevWasSpace || i == last) { 356 writer.write(" "); 357 prevWasSpace = false; 358 } else { 359 writer.write(' '); 360 prevWasSpace = true; 361 } 362 break; 363 case '\uFFFE': 364 writer.write('\uFFFD'); 365 prevWasSpace = false; 366 break; 367 case '\uFFFF': 368 writer.write('\uFFFD'); 369 prevWasSpace = false; 370 break; 371 default: 372 if (c < ' ') { 373 writer.write('\uFFFD'); 374 } else { 375 writer.write(c); 376 } 377 prevWasSpace = false; 378 break; 379 } 380 } 381 } 382 383 private final void generatePrefix(String uri) throws SAXException { 384 int counter = 0; 385 String candidate = WELL_KNOWN_ATTRIBUTE_PREFIXES.get(uri); 386 if (candidate == null) { 387 candidate = "p" + (counter++); 388 } 389 while (lookupUri(candidate) != null) { 390 candidate = "p" + (counter++); 391 } 392 startPrefixMappingPrivate(candidate, uri); 393 } 394 395 public final void characters(char[] ch, int start, int length) 396 throws SAXException { 397 try { 398 for (int i = start; i < start + length; i++) { 399 char c = ch[i]; 400 switch (c) { 401 case '<': 402 writer.write("<"); 403 break; 404 case '>': 405 writer.write(">"); 406 break; 407 case '&': 408 writer.write("&"); 409 break; 410 case '\r': 411 writer.write("
"); 412 break; 413 case '\t': 414 writer.write('\t'); 415 break; 416 case '\n': 417 writer.write('\n'); 418 break; 419 case '\uFFFE': 420 writer.write('\uFFFD'); 421 break; 422 case '\uFFFF': 423 writer.write('\uFFFD'); 424 break; 425 default: 426 if (c < ' ') { 427 writer.write('\uFFFD'); 428 } else { 429 writer.write(c); 430 } 431 break; 432 } 433 } 434 } catch (IOException e) { 435 throw new SAXException(e); 436 } 437 } 438 439 public final void endDocument() throws SAXException { 440 try { 441 stack.clear(); 442 writer.flush(); 443 writer.close(); 444 } catch (IOException e) { 445 throw new SAXException(e); 446 } 447 } 448 449 public final void endElement(String uri, String localName, String qName) 450 throws SAXException { 451 try { 452 writer.write('<'); 453 writer.write('/'); 454 writer.write(pop()); 455 writer.write('>'); 456 } catch (IOException e) { 457 throw new SAXException(e); 458 } 459 } 460 461 public final void ignorableWhitespace(char[] ch, int start, int length) 462 throws SAXException { 463 characters(ch, start, length); 464 } 465 466 public final void processingInstruction(String target, String data) 467 throws SAXException { 468 try { 469 checkNCName(target); 470 writer.write("<?"); 471 writer.write(target); 472 writer.write(' '); 473 boolean prevWasQuestionmark = false; 474 for (int i = 0; i < data.length(); i++) { 475 char c = data.charAt(i); 476 switch (c) { 477 case '?': 478 writer.write('?'); 479 prevWasQuestionmark = true; 480 break; 481 case '>': 482 if (prevWasQuestionmark) { 483 writer.write(" >"); 484 } else { 485 writer.write('>'); 486 } 487 prevWasQuestionmark = false; 488 break; 489 case '\t': 490 writer.write('\t'); 491 prevWasQuestionmark = false; 492 break; 493 case '\r': 494 case '\n': 495 writer.write('\n'); 496 prevWasQuestionmark = false; 497 break; 498 case '\uFFFE': 499 writer.write('\uFFFD'); 500 prevWasQuestionmark = false; 501 break; 502 case '\uFFFF': 503 writer.write('\uFFFD'); 504 prevWasQuestionmark = false; 505 break; 506 default: 507 if (c < ' ') { 508 writer.write('\uFFFD'); 509 } else { 510 writer.write(c); 511 } 512 prevWasQuestionmark = false; 513 break; 514 } 515 } 516 writer.write("?>"); 517 } catch (IOException e) { 518 throw new SAXException(e); 519 } 520 } 521 522 public final void setDocumentLocator(Locator locator) { 523 } 524 525 public final void startDocument() throws SAXException { 526 try { 527 writer.write("<?xml version='1.0' encoding='utf-8'?>\n"); 528 } catch (IOException e) { 529 throw new SAXException(e); 530 } 531 stack.clear(); 532 push(null, null, null); 533 } 534 535 public final void startElement(String uri, String localName, String q, 536 Attributes atts) throws SAXException { 537 checkNCName(localName); 538 String prefix; 539 String qName; 540 if (uri.length() == 0) { 541 prefix = ""; 542 qName = localName; 543 // generate xmlns 544 startPrefixMappingPrivate(prefix, uri); 545 } else { 546 prefix = WELL_KNOWN_ELEMENT_PREFIXES.get(uri); 547 if (prefix == null) { 548 prefix = ""; 549 } 550 String lookup = lookupUri(prefix); 551 if (lookup != null && !lookup.equals(uri)) { 552 prefix = ""; 553 } 554 startPrefixMappingPrivate(prefix, uri); 555 if (prefix.length() == 0) { 556 qName = localName; 557 } else { 558 qName = prefix + ':' + localName; 559 } 560 } 561 562 int attLen = atts.getLength(); 563 for (int i = 0; i < attLen; i++) { 564 String attUri = atts.getURI(i); 565 if (attUri.length() == 0 566 || "http://www.w3.org/XML/1998/namespace".equals(attUri) 567 || "http://www.w3.org/2000/xmlns/".equals(attUri) 568 || atts.getLocalName(i).length() == 0 569 || xmlNsQname(atts.getQName(i))) { 570 continue; 571 } 572 if (lookupPrefixAttribute(attUri) == null) { 573 generatePrefix(attUri); 574 } 575 } 576 577 try { 578 writer.write('<'); 579 writer.write(qName); 580 for (PrefixMapping mapping : stack.getFirst().mappings) { 581 writer.write(' '); 582 if (mapping.prefix.length() == 0) { 583 writer.write("xmlns"); 584 } else { 585 writer.write("xmlns:"); 586 writer.write(mapping.prefix); 587 } 588 writer.write('='); 589 writer.write('"'); 590 writeAttributeValue(mapping.uri); 591 writer.write('"'); 592 } 593 594 for (int i = 0; i < attLen; i++) { 595 String attUri = atts.getURI(i); 596 if ("http://www.w3.org/XML/1998/namespace".equals(attUri) 597 || "http://www.w3.org/2000/xmlns/".equals(attUri) 598 || atts.getLocalName(i).length() == 0 599 || xmlNsQname(atts.getQName(i))) { 600 continue; 601 } 602 writer.write(' '); 603 if (attUri.length() != 0) { 604 writer.write(lookupPrefixAttribute(attUri)); 605 writer.write(':'); 606 } 607 String attLocal = atts.getLocalName(i); 608 checkNCName(attLocal); 609 writer.write(attLocal); 610 writer.write('='); 611 writer.write('"'); 612 writeAttributeValue(atts.getValue(i)); 613 writer.write('"'); 614 } 615 writer.write('>'); 616 } catch (IOException e) { 617 throw new SAXException(e); 618 } 619 push(uri, qName, prefix); 620 } 621 622 public final void comment(char[] ch, int start, int length) throws SAXException { 623 try { 624 boolean prevWasHyphen = false; 625 writer.write("<!--"); 626 for (int i = start; i < start + length; i++) { 627 char c = ch[i]; 628 switch (c) { 629 case '-': 630 if (prevWasHyphen) { 631 writer.write(" -"); 632 } else { 633 writer.write('-'); 634 prevWasHyphen = true; 635 } 636 break; 637 case '\t': 638 writer.write('\t'); 639 prevWasHyphen = false; 640 break; 641 case '\r': 642 case '\n': 643 writer.write('\n'); 644 prevWasHyphen = false; 645 break; 646 case '\uFFFE': 647 writer.write('\uFFFD'); 648 prevWasHyphen = false; 649 break; 650 case '\uFFFF': 651 writer.write('\uFFFD'); 652 prevWasHyphen = false; 653 break; 654 default: 655 if (c < ' ') { 656 writer.write('\uFFFD'); 657 } else { 658 writer.write(c); 659 } 660 prevWasHyphen = false; 661 break; 662 } 663 } 664 if (prevWasHyphen) { 665 writer.write(' '); 666 } 667 writer.write("-->"); 668 } catch (IOException e) { 669 throw new SAXException(e); 670 } 671 } 672 673 public final void endCDATA() throws SAXException { 674 } 675 676 public final void endDTD() throws SAXException { 677 } 678 679 public final void endEntity(String name) throws SAXException { 680 } 681 682 public final void startCDATA() throws SAXException { 683 } 684 685 public final void startDTD(String name, String publicId, String systemId) 686 throws SAXException { 687 } 688 689 public final void startEntity(String name) throws SAXException { 690 } 691 692 public final void startPrefixMapping(String prefix, String uri) 693 throws SAXException { 694 if (prefix.length() == 0 || uri.equals(lookupUri(prefix))) { 695 return; 696 } 697 if (uri.equals(lookupUri(prefix))) { 698 return; 699 } 700 if ("http://www.w3.org/XML/1998/namespace".equals(uri)) { 701 if ("xml".equals(prefix)) { 702 return; 703 } else { 704 throw new SAXException("Attempt to declare a reserved NS uri."); 705 } 706 } 707 if ("http://www.w3.org/2000/xmlns/".equals(uri)) { 708 throw new SAXException("Attempt to declare a reserved NS uri."); 709 } 710 if (uri.length() == 0 && prefix.length() != 0) { 711 throw new SAXException("Can bind a prefix to no namespace."); 712 } 713 checkNCName(prefix); 714 Set<PrefixMapping> theSet = stack.getFirst().mappings; 715 PrefixMapping mapping = new PrefixMapping(uri, prefix); 716 if (theSet.contains(mapping)) { 717 throw new SAXException( 718 "Attempt to map one prefix to two URIs on one element."); 719 } 720 theSet.add(mapping); 721 } 722 723 public final void startPrefixMappingPrivate(String prefix, String uri) 724 throws SAXException { 725 if (uri.equals(lookupUri(prefix))) { 726 return; 727 } 728 stack.getFirst().mappings.add(new PrefixMapping(uri, prefix)); 729 } 730 731 public final void endPrefixMapping(String prefix) throws SAXException { 732 } 733 734 public final void skippedEntity(String name) throws SAXException { 735 } 736 737 }