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 }