149
151
HTML_BOOLEAN_ATTRIBUTES.put("script", set);
154
// HTML namespace URIs
155
static final HashSet HTML_URIS = new HashSet();
157
HTML_URIS.add("http://www.w3.org/1999/xhtml");
152
160
protected final String encoding;
153
161
final Charset charset;
154
162
final CharsetEncoder encoder;
156
final Map namespaces;
164
final LinkedList namespaces;
157
165
protected String eol;
158
166
Collection cdataSectionElements = Collections.EMPTY_SET;
160
168
protected boolean discardDefaultContent;
161
169
protected boolean xmlDeclaration = true;
171
// has a META element with the encoding been added?
172
private boolean htmlEncoded;
163
174
public StreamSerializer()
233
238
(prefix != null && prefix.startsWith("xmlns:")))
235
240
String nsuri = node.getNodeValue();
236
if (isDefined(nsuri))
241
if (isDefined(nsuri, prefix))
240
243
String name = node.getLocalName();
241
244
if (name == null)
243
247
name = node.getNodeName();
248
int ci = name.indexOf(':');
250
name = name.substring(ci + 1);
245
252
define(nsuri, name);
247
else if (uri != null && !isDefined(uri))
254
else if (uri != null && !isDefined(uri, prefix))
249
256
prefix = define(uri, prefix);
250
257
String nsname = (prefix == null) ? "xmlns" : "xmlns:" + prefix;
251
258
out.write(SPACE);
252
259
out.write(encodeText(nsname));
254
String nsvalue = "'" + encode(uri, true, true) + "'";
261
String nsvalue = "\"" + encode(uri, true, true) + "\"";
255
262
out.write(nsvalue.getBytes(encoding));
258
264
out.write(SPACE);
259
265
String a_nodeName = node.getNodeName();
262
268
if (mode == Stylesheet.OUTPUT_HTML &&
263
269
a_nodeName.equals(a_nodeValue) &&
264
270
isHTMLBoolean((Attr) node, a_nodeName))
269
value = "'" + encode(a_nodeValue, true, true) + "'";
273
value = "\"" + encode(a_nodeValue, true, true) + "\"";
270
274
out.write(encodeText(value));
272
276
case Node.ELEMENT_NODE:
277
pushNamespaceContext();
273
278
value = node.getNodeName();
275
280
out.write(encodeText(value));
276
if (uri != null && !isDefined(uri))
281
prefix = node.getPrefix();
282
if (uri != null && !isDefined(uri, prefix))
278
prefix = define(uri, node.getPrefix());
284
prefix = define(uri, prefix);
279
285
String nsname = (prefix == null) ? "xmlns" : "xmlns:" + prefix;
280
286
out.write(SPACE);
281
287
out.write(encodeText(nsname));
283
String nsvalue = "'" + encode(uri, true, true) + "'";
289
String nsvalue = "\"" + encode(uri, true, true) + "\"";
284
290
out.write(encodeText(nsvalue));
287
292
NamedNodeMap attrs = node.getAttributes();
288
293
if (attrs != null)
317
320
out.write(encodeText(value));
323
popNamespaceContext();
321
325
case Node.TEXT_NODE:
322
326
value = node.getNodeValue();
323
327
if (!"yes".equals(node.getUserData("disable-output-escaping")))
325
value = encode(value, false, false);
328
value = encode(value, false, false);
327
329
out.write(encodeText(value));
329
331
case Node.CDATA_SECTION_NODE:
330
value = "<![CDATA[" + node.getNodeValue() + "]]>";
331
out.write(encodeText(value));
332
value = node.getNodeValue();
333
// Where any instanceof of ]]> occur, split into multiple CDATA
335
int bbk = value.indexOf("]]>");
338
String head = value.substring(0, bbk + 2);
339
out.write(encodeText("<![CDATA[" + head + "]]>"));
340
value = value.substring(bbk + 2);
341
bbk = value.indexOf("]]>");
343
// Write final tail value
344
out.write(encodeText("<![CDATA[" + value + "]]>"));
333
346
case Node.COMMENT_NODE:
334
347
value = "<!--" + node.getNodeValue() + "-->";
335
348
out.write(encodeText(value));
336
349
Node cp = node.getParentNode();
337
350
if (cp != null && cp.getNodeType() == Node.DOCUMENT_NODE)
339
out.write(encodeText(eol));
351
out.write(encodeText(eol));
342
353
case Node.DOCUMENT_NODE:
343
354
case Node.DOCUMENT_FRAGMENT_NODE:
355
366
(Document) node : null;
356
367
String version = (doc != null) ? doc.getXmlVersion() : null;
357
368
if (version == null)
359
version = (String) node.getUserData("version");
369
version = (String) node.getUserData("version");
361
370
if (version == null)
367
out.write("xml version='".getBytes("US-ASCII"));
374
out.write("xml version=\"".getBytes("US-ASCII"));
368
375
out.write(version.getBytes("US-ASCII"));
370
377
if (!("UTF-8".equalsIgnoreCase(encoding)))
372
out.write(" encoding='".getBytes("US-ASCII"));
379
out.write(" encoding=\"".getBytes("US-ASCII"));
373
380
out.write(encoding.getBytes("US-ASCII"));
376
383
if ((doc != null && doc.getXmlStandalone()) ||
377
384
"yes".equals(node.getUserData("standalone")))
379
out.write(" standalone='yes'".getBytes("US-ASCII"));
385
out.write(" standalone=\"yes\"".getBytes("US-ASCII"));
383
388
out.write(encodeText(eol));
403
406
for (Node ctx = node.getFirstChild(); ctx != null;
404
407
ctx = ctx.getNextSibling())
406
if (ctx.getNodeType() == Node.ELEMENT_NODE)
409
if (ctx.getNodeType() == Node.ELEMENT_NODE &&
410
isHTMLElement(ctx, "html"))
414
html = doc.createElement("html");
415
node.appendChild(html);
418
for (Node ctx = html.getFirstChild(); ctx != null;
419
ctx = ctx.getNextSibling())
421
if (ctx.getNodeType() == Node.ELEMENT_NODE)
423
String name = ctx.getLocalName();
426
name = ctx.getNodeName();
428
if ("head".equalsIgnoreCase(name))
437
head = doc.createElement("head");
439
419
for (Node ctx = html.getFirstChild(); ctx != null;
440
420
ctx = ctx.getNextSibling())
442
if (ctx.getNodeType() == Node.ELEMENT_NODE)
422
if (isHTMLElement(ctx, "head"))
450
html.insertBefore(head, c1);
454
html.appendChild(head);
458
Node metaContent = null;
459
for (Node ctx = head.getFirstChild(); ctx != null;
460
ctx = ctx.getNextSibling())
462
if (ctx.getNodeType() == Node.ELEMENT_NODE)
464
String name = ctx.getLocalName();
467
name = ctx.getNodeName();
469
if ("meta".equalsIgnoreCase(name))
471
NamedNodeMap metaAttrs = ctx.getAttributes();
472
int len = metaAttrs.getLength();
473
String httpEquiv = null;
475
for (int i = 0; i < len; i++)
477
Node attr = metaAttrs.item(i);
478
String attrName = attr.getNodeName();
479
if ("http-equiv".equalsIgnoreCase(attrName))
481
httpEquiv = attr.getNodeValue();
483
else if ("content".equalsIgnoreCase(attrName))
488
if ("Content-Type".equalsIgnoreCase(httpEquiv))
491
metaContent = content;
499
meta = doc.createElement("meta");
501
Node first = head.getFirstChild();
504
head.appendChild(meta);
508
head.insertBefore(meta, first);
510
Node metaHttpEquiv = doc.createAttribute("http-equiv");
511
meta.getAttributes().setNamedItem(metaHttpEquiv);
512
metaHttpEquiv.setNodeValue("Content-Type");
514
if (metaContent == null)
516
metaContent = doc.createAttribute("content");
517
meta.getAttributes().setNamedItem(metaContent);
519
metaContent.setNodeValue(contentType);
431
Node metaContent = null;
432
for (Node ctx = head.getFirstChild(); ctx != null;
433
ctx = ctx.getNextSibling())
435
if (isHTMLElement(ctx, "meta"))
437
NamedNodeMap metaAttrs = ctx.getAttributes();
438
int len = metaAttrs.getLength();
439
String httpEquiv = null;
441
for (int i = 0; i < len; i++)
443
Node attr = metaAttrs.item(i);
444
String attrName = attr.getNodeName();
445
if ("http-equiv".equalsIgnoreCase(attrName))
446
httpEquiv = attr.getNodeValue();
447
else if ("content".equalsIgnoreCase(attrName))
450
if ("Content-Type".equalsIgnoreCase(httpEquiv))
453
metaContent = content;
460
meta = doc.createElement("meta");
462
Node first = head.getFirstChild();
464
head.appendChild(meta);
466
head.insertBefore(meta, first);
467
Node metaHttpEquiv = doc.createAttribute("http-equiv");
468
meta.getAttributes().setNamedItem(metaHttpEquiv);
469
metaHttpEquiv.setNodeValue("Content-Type");
471
if (metaContent == null)
473
metaContent = doc.createAttribute("content");
474
meta.getAttributes().setNamedItem(metaContent);
476
metaContent.setNodeValue(contentType);
522
481
children = node.getFirstChild();
523
482
if (children != null)
525
serialize(children, out, convertToCdata);
483
serialize(children, out, convertToCdata);
528
485
case Node.DOCUMENT_TYPE_NODE:
529
486
DocumentType doctype = (DocumentType) node;
532
out.write(encodeText("DOCTYPE "));
489
out.write(encodeText("DOCTYPE "));
533
490
value = doctype.getNodeName();
534
491
out.write(encodeText(value));
535
492
String publicId = doctype.getPublicId();
569
526
out.write(encodeText(eol));
530
System.err.println("Unhandled node type: "+nt);
534
boolean isHTMLElement(Node node, String name)
536
if (node.getNodeType() != Node.ELEMENT_NODE)
538
String localName = node.getLocalName();
539
if (localName == null)
540
localName = node.getNodeName();
541
if (!name.equalsIgnoreCase(localName))
543
String uri = node.getNamespaceURI();
544
return (uri == null || HTML_URIS.contains(uri));
547
boolean isDefined(String uri, String prefix)
549
if (XMLConstants.XML_NS_URI.equals(uri))
550
return "xml".equals(prefix);
551
if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(uri))
552
return "xmlns".equals(prefix);
555
for (Iterator i = namespaces.iterator(); i.hasNext(); )
557
Map ctx = (Map) i.next();
558
String val = (String) ctx.get(uri);
559
if (val != null && val.equals(prefix))
579
boolean isDefined(String uri)
565
void pushNamespaceContext()
581
return XMLConstants.XML_NS_URI.equals(uri) ||
582
XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(uri) ||
583
namespaces.containsKey(uri);
567
namespaces.addFirst(new HashMap());
586
570
String define(String uri, String prefix)
588
while (namespaces.containsValue(prefix))
572
if (namespaces.isEmpty())
574
HashMap ctx = (HashMap) namespaces.getFirst();
575
while (ctx.containsValue(prefix))
590
577
// Fabricate new prefix
591
578
prefix = prefix + "_";
593
namespaces.put(uri, prefix);
580
ctx.put(uri, prefix);
597
void undefine(String uri)
584
void popNamespaceContext()
599
namespaces.remove(uri);
586
namespaces.removeFirst();
602
589
final byte[] encodeText(String text)
603
590
throws IOException
606
if (!encoder.canEncode(text))
593
boolean htmlNeedingEncoding =
594
(mode == Stylesheet.OUTPUT_HTML && !htmlEncoded);
595
if (!encoder.canEncode(text) || htmlNeedingEncoding)
608
597
// Check each character
609
598
StringBuffer buf = new StringBuffer();
675
670
text.charAt(i + 1) == '{')
686
buf = new StringBuffer(text.substring(0, i));
678
buf = new StringBuffer(text.substring(0, i));
688
679
buf.append("&");
691
682
else if (c == '\'' && inAttr)
695
buf = new StringBuffer(text.substring(0, i));
697
buf.append("'");
685
buf = new StringBuffer(text.substring(0, i));
686
if (mode == Stylesheet.OUTPUT_HTML)
687
// HTML does not define ', use character entity ref
688
buf.append("'");
690
buf.append("'");
699
692
else if (c == '"' && inAttr)
703
buf = new StringBuffer(text.substring(0, i));
695
buf = new StringBuffer(text.substring(0, i));
705
696
buf.append(""");
707
698
else if (encodeCtl)
759
744
return (attributes != null && attributes.contains(attrName));
747
static String getHTMLCharacterEntity(char c)
749
// Hardcode these here to avoid loading the HTML DTD
752
case 160: return "nbsp";
753
case 161: return "iexcl";
754
case 162: return "cent";
755
case 163: return "pound";
756
case 164: return "curren";
757
case 165: return "yen";
758
case 166: return "brvbar";
759
case 167: return "sect";
760
case 168: return "uml";
761
case 169: return "copy";
762
case 170: return "ordf";
763
case 171: return "laquo";
764
case 172: return "not";
765
case 173: return "shy";
766
case 174: return "reg";
767
case 175: return "macr";
768
case 176: return "deg";
769
case 177: return "plusmn";
770
case 178: return "sup2";
771
case 179: return "sup3";
772
case 180: return "acute";
773
case 181: return "micro";
774
case 182: return "para";
775
case 183: return "middot";
776
case 184: return "cedil";
777
case 185: return "sup1";
778
case 186: return "ordm";
779
case 187: return "raquo";
780
case 188: return "frac14";
781
case 189: return "frac12";
782
case 190: return "frac34";
783
case 191: return "iquest";
784
case 192: return "Agrave";
785
case 193: return "Aacute";
786
case 194: return "Acirc";
787
case 195: return "Atilde";
788
case 196: return "Auml";
789
case 197: return "Aring";
790
case 198: return "AElig";
791
case 199: return "Ccedil";
792
case 200: return "Egrave";
793
case 201: return "Eacute";
794
case 202: return "Ecirc";
795
case 203: return "Euml";
796
case 204: return "Igrave";
797
case 205: return "Iacute";
798
case 206: return "Icirc";
799
case 207: return "Iuml";
800
case 208: return "ETH";
801
case 209: return "Ntilde";
802
case 210: return "Ograve";
803
case 211: return "Oacute";
804
case 212: return "Ocirc";
805
case 213: return "Otilde";
806
case 214: return "Ouml";
807
case 215: return "times";
808
case 216: return "Oslash";
809
case 217: return "Ugrave";
810
case 218: return "Uacute";
811
case 219: return "Ucirc";
812
case 220: return "Uuml";
813
case 221: return "Yacute";
814
case 222: return "THORN";
815
case 223: return "szlig";
816
case 224: return "agrave";
817
case 225: return "aacute";
818
case 226: return "acirc";
819
case 227: return "atilde";
820
case 228: return "auml";
821
case 229: return "aring";
822
case 230: return "aelig";
823
case 231: return "ccedil";
824
case 232: return "egrave";
825
case 233: return "eacute";
826
case 234: return "ecirc";
827
case 235: return "euml";
828
case 236: return "igrave";
829
case 237: return "iacute";
830
case 238: return "icirc";
831
case 239: return "iuml";
832
case 240: return "eth";
833
case 241: return "ntilde";
834
case 242: return "ograve";
835
case 243: return "oacute";
836
case 244: return "ocirc";
837
case 245: return "otilde";
838
case 246: return "ouml";
839
case 247: return "divide";
840
case 248: return "oslash";
841
case 249: return "ugrave";
842
case 250: return "uacute";
843
case 251: return "ucirc";
844
case 252: return "uuml";
845
case 253: return "yacute";
846
case 254: return "thorn";
847
case 255: return "yuml";
848
default: return null;