1:
37:
38: package ;
39:
40: import ;
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47:
48: import ;
49: import ;
50: import ;
51:
52:
53:
108: public class XMLWriter
109: implements ContentHandler, LexicalHandler, DTDHandler, DeclHandler
110: {
111:
112:
113:
114:
115:
116:
117: private static final int CTX_ENTITY = 1;
118: private static final int CTX_ATTRIBUTE = 2;
119: private static final int CTX_CONTENT = 3;
120: private static final int CTX_UNPARSED = 4;
121: private static final int CTX_NAME = 5;
122:
123:
124:
125:
126:
127: private static String sysEOL;
128:
129: static {
130: try {
131: sysEOL = System.getProperty ("line.separator", "\n");
132:
133:
134: if (!isLineEnd (sysEOL))
135: sysEOL = "\n";
136:
137: } catch (SecurityException e) {
138: sysEOL = "\n";
139: }
140: }
141:
142: private static boolean isLineEnd (String eol)
143: {
144: return "\n".equals (eol)
145: || "\r".equals (eol)
146: || "\r\n".equals (eol);
147: }
148:
149: private Writer out;
150: private boolean inCDATA;
151: private int elementNestLevel;
152: private String eol = sysEOL;
153:
154: private short dangerMask;
155: private StringBuffer stringBuf;
156: private Locator locator;
157: private ErrorHandler errHandler;
158:
159: private boolean expandingEntities = false;
160: private int entityNestLevel;
161: private boolean xhtml;
162: private boolean startedDoctype;
163: private String encoding;
164:
165: private boolean canonical;
166: private boolean inDoctype;
167: private boolean inEpilogue;
168:
169:
170: private boolean prettyPrinting;
171: private int column;
172: private boolean noWrap;
173: private Stack space = new Stack ();
174:
175:
176:
177:
178:
179: private static final int lineLength = 75;
180:
181:
182:
187: public XMLWriter () throws IOException
188: { this (System.out); }
189:
190:
197: public XMLWriter (OutputStream out) throws IOException
198: {
199: this (new OutputStreamWriter (out, "UTF8"));
200: }
201:
202:
214: public XMLWriter (Writer writer)
215: {
216: this (writer, null);
217: }
218:
219:
251: public XMLWriter (Writer writer, String encoding)
252: {
253: setWriter (writer, encoding);
254: }
255:
256: private void setEncoding (String encoding)
257: {
258: if (encoding == null && out instanceof OutputStreamWriter)
259: encoding = ((OutputStreamWriter)out).getEncoding ();
260:
261: if (encoding != null) {
262: encoding = encoding.toUpperCase ();
263:
264:
265:
266:
267:
268:
269:
270:
271: if ("UTF8".equals (encoding)) {
272: encoding = "UTF-8";
273: } else if ("US-ASCII".equals (encoding)
274: || "ASCII".equals (encoding)) {
275: dangerMask = (short) 0xff80;
276: encoding = "US-ASCII";
277: } else if ("ISO-8859-1".equals (encoding)
278: || "8859_1".equals (encoding)
279: || "ISO8859_1".equals (encoding)) {
280: dangerMask = (short) 0xff00;
281: encoding = "ISO-8859-1";
282: } else if ("UNICODE".equals (encoding)
283: || "UNICODE-BIG".equals (encoding)
284: || "UNICODE-LITTLE".equals (encoding)) {
285: encoding = "UTF-16";
286:
287:
288:
289: }
290:
291: if (dangerMask != 0)
292: stringBuf = new StringBuffer ();
293: }
294:
295: this.encoding = encoding;
296: }
297:
298:
299:
309: final public void setWriter (Writer writer, String encoding)
310: {
311: if (out != null)
312: throw new IllegalStateException (
313: "can't change stream in mid course");
314: out = writer;
315: if (out != null)
316: setEncoding (encoding);
317: if (!(out instanceof BufferedWriter))
318: out = new BufferedWriter (out);
319: space.push ("default");
320: }
321:
322:
327: final public void setEOL (String eolString)
328: {
329: if (eolString == null)
330: eol = sysEOL;
331: else if (!isLineEnd (eolString))
332: eol = eolString;
333: else
334: throw new IllegalArgumentException (eolString);
335: }
336:
337:
341: public void setErrorHandler (ErrorHandler handler)
342: {
343: errHandler = handler;
344: }
345:
346:
353: protected void fatal (String message, Exception e)
354: throws SAXException
355: {
356: SAXParseException x;
357:
358: if (locator == null)
359: x = new SAXParseException (message, null, null, -1, -1, e);
360: else
361: x = new SAXParseException (message, locator, e);
362: if (errHandler != null)
363: errHandler.fatalError (x);
364: throw x;
365: }
366:
367:
368:
369:
370:
436: final public void setXhtml (boolean value)
437: {
438: if (locator != null)
439: throw new IllegalStateException ("started parsing");
440: xhtml = value;
441: if (xhtml)
442: canonical = false;
443: }
444:
445:
451: final public boolean isXhtml ()
452: {
453: return xhtml;
454: }
455:
456:
461: final public void setExpandingEntities (boolean value)
462: {
463: if (locator != null)
464: throw new IllegalStateException ("started parsing");
465: expandingEntities = value;
466: if (!expandingEntities)
467: canonical = false;
468: }
469:
470:
474: final public boolean isExpandingEntities ()
475: {
476: return expandingEntities;
477: }
478:
479:
502: final public void setPrettyPrinting (boolean value)
503: {
504: if (locator != null)
505: throw new IllegalStateException ("started parsing");
506: prettyPrinting = value;
507: if (prettyPrinting)
508: canonical = false;
509: }
510:
511:
514: final public boolean isPrettyPrinting ()
515: {
516: return prettyPrinting;
517: }
518:
519:
520:
559: final public void setCanonical (boolean value)
560: {
561: if (value && !"UTF-8".equals (encoding))
562: throw new IllegalArgumentException ("encoding != UTF-8");
563: canonical = value;
564: if (canonical) {
565: prettyPrinting = xhtml = false;
566: expandingEntities = true;
567: eol = "\n";
568: }
569: }
570:
571:
572:
575: final public boolean isCanonical ()
576: {
577: return canonical;
578: }
579:
580:
581:
586: final public void flush ()
587: throws IOException
588: {
589: if (out != null)
590: out.flush ();
591: }
592:
593:
594:
595:
596:
597:
598:
599:
604: final public void write (String data)
605: throws SAXException
606: {
607: char buf [] = data.toCharArray ();
608: characters (buf, 0, buf.length);
609: }
610:
611:
612:
617: public void writeElement (
618: String uri,
619: String localName,
620: String qName,
621: Attributes atts,
622: String content
623: ) throws SAXException
624: {
625: if (content == null || content.length () == 0) {
626: writeEmptyElement (uri, localName, qName, atts);
627: return;
628: }
629: startElement (uri, localName, qName, atts);
630: char chars [] = content.toCharArray ();
631: characters (chars, 0, chars.length);
632: endElement (uri, localName, qName);
633: }
634:
635:
636:
642: public void writeElement (
643: String uri,
644: String localName,
645: String qName,
646: Attributes atts,
647: int content
648: ) throws SAXException
649: {
650: writeElement (uri, localName, qName, atts, Integer.toString (content));
651: }
652:
653:
654:
655:
656: final public void setDocumentLocator (Locator l)
657: {
658: locator = l;
659: }
660:
661:
662:
663: private static final String xhtmlFullDTD =
664: "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd";
665:
666:
667:
672:
673: public void startDocument ()
674: throws SAXException
675: {
676: try {
677: if (out == null)
678: throw new IllegalStateException (
679: "null Writer given to XMLWriter");
680:
681:
682:
683:
684:
685:
686:
687: if (locator == null)
688: locator = new LocatorImpl ();
689:
690:
691:
692:
693:
694:
695:
696:
697: if (!canonical
698: && dangerMask != (short) 0xff80
699: && encoding != null) {
700: rawWrite ("<?xml version='1.0'");
701: rawWrite (" encoding='" + encoding + "'");
702: rawWrite ("?>");
703: newline ();
704: }
705:
706: if (xhtml) {
707:
708: rawWrite ("<!DOCTYPE html PUBLIC");
709: newline ();
710: rawWrite (" '-//W3C//DTD XHTML 1.0 Transitional//EN'");
711: newline ();
712: rawWrite (" '");
713:
714: rawWrite (xhtmlFullDTD);
715: rawWrite ("'>");
716: newline ();
717: newline ();
718:
719:
720:
721:
722: startedDoctype = true;
723: }
724:
725: entityNestLevel = 0;
726:
727: } catch (IOException e) {
728: fatal ("can't write", e);
729: }
730: }
731:
732:
737:
738: public void endDocument ()
739: throws SAXException
740: {
741: try {
742: if (!canonical) {
743: newline ();
744: newline ();
745: }
746: out.close ();
747: out = null;
748: locator = null;
749: } catch (IOException e) {
750: fatal ("can't write", e);
751: }
752: }
753:
754:
755: final private static boolean isEmptyElementTag (String tag)
756: {
757: switch (tag.charAt (0)) {
758: case 'a': return "area".equals (tag);
759: case 'b': return "base".equals (tag)
760: || "basefont".equals (tag)
761: || "br".equals (tag);
762: case 'c': return "col".equals (tag);
763: case 'f': return "frame".equals (tag);
764: case 'h': return "hr".equals (tag);
765: case 'i': return "img".equals (tag)
766: || "input".equals (tag)
767: || "isindex".equals (tag);
768: case 'l': return "link".equals (tag);
769: case 'm': return "meta".equals (tag);
770: case 'p': return "param".equals (tag);
771: }
772: return false;
773: }
774:
775: private static boolean indentBefore (String tag)
776: {
777:
778:
779: switch (tag.charAt (0)) {
780: case 'a': return "applet".equals (tag);
781: case 'b': return "body".equals (tag)
782: || "blockquote".equals (tag);
783: case 'c': return "center".equals (tag);
784: case 'f': return "frame".equals (tag)
785: || "frameset".equals (tag);
786: case 'h': return "head".equals (tag);
787: case 'm': return "meta".equals (tag);
788: case 'o': return "object".equals (tag);
789: case 'p': return "param".equals (tag)
790: || "pre".equals (tag);
791: case 's': return "style".equals (tag);
792: case 't': return "title".equals (tag)
793: || "td".equals (tag)
794: || "th".equals (tag);
795: }
796:
797: return false;
798: }
799:
800: private static boolean spaceBefore (String tag)
801: {
802:
803: switch (tag.charAt (0)) {
804: case 'h': return "h1".equals (tag)
805: || "h2".equals (tag)
806: || "h3".equals (tag)
807: || "h4".equals (tag)
808: || "h5".equals (tag)
809: || "h6".equals (tag)
810: || "hr".equals (tag);
811: case 'l': return "li".equals (tag);
812: case 'o': return "ol".equals (tag);
813: case 'p': return "p".equals (tag);
814: case 't': return "table".equals (tag)
815: || "tr".equals (tag);
816: case 'u': return "ul".equals (tag);
817: }
818: return false;
819: }
820:
821:
822: private static boolean spacePreserve (String tag)
823: {
824: return "pre".equals (tag)
825: || "style".equals (tag)
826: || "script".equals (tag);
827: }
828:
829:
832: final public void startPrefixMapping (String prefix, String uri)
833: {}
834:
835:
838: final public void endPrefixMapping (String prefix)
839: {}
840:
841: private void writeStartTag (
842: String name,
843: Attributes atts,
844: boolean isEmpty
845: ) throws SAXException, IOException
846: {
847: rawWrite ('<');
848: rawWrite (name);
849:
850:
851:
852: if (atts != null && atts.getLength () != 0) {
853:
854:
855: int indices [] = new int [atts.getLength ()];
856:
857: for (int i= 0; i < indices.length; i++)
858: indices [i] = i;
859:
860:
861:
862:
863:
864:
865:
866: if (canonical || prettyPrinting) {
867:
868:
869: for (int i = 1; i < indices.length; i++) {
870: int n = indices [i], j;
871: String s = atts.getQName (n);
872:
873: for (j = i - 1; j >= 0; j--) {
874: if (s.compareTo (atts.getQName (indices [j]))
875: >= 0)
876: break;
877: indices [j + 1] = indices [j];
878: }
879: indices [j + 1] = n;
880: }
881: }
882:
883:
884: for (int i= 0; i < indices.length; i++) {
885: String s = atts.getQName (indices [i]);
886:
887: if (s == null || "".equals (s))
888: throw new IllegalArgumentException ("no XML name");
889: rawWrite (" ");
890: rawWrite (s);
891: rawWrite ("=");
892: writeQuotedValue (atts.getValue (indices [i]),
893: CTX_ATTRIBUTE);
894: }
895: }
896: if (isEmpty)
897: rawWrite (" /");
898: rawWrite ('>');
899: }
900:
901:
907: final public void startElement (
908: String uri,
909: String localName,
910: String qName,
911: Attributes atts
912: ) throws SAXException
913: {
914: startedDoctype = false;
915:
916: if (locator == null)
917: locator = new LocatorImpl ();
918:
919: if (qName == null || "".equals (qName))
920: throw new IllegalArgumentException ("no XML name");
921:
922: try {
923: if (entityNestLevel != 0)
924: return;
925: if (prettyPrinting) {
926: String whitespace = null;
927:
928: if (xhtml && spacePreserve (qName))
929: whitespace = "preserve";
930: else if (atts != null)
931: whitespace = atts.getValue ("xml:space");
932: if (whitespace == null)
933: whitespace = (String) space.peek ();
934: space.push (whitespace);
935:
936: if ("default".equals (whitespace)) {
937: if (xhtml) {
938: if (spaceBefore (qName)) {
939: newline ();
940: doIndent ();
941: } else if (indentBefore (qName))
942: doIndent ();
943:
944:
945:
946: } else
947: doIndent ();
948: }
949: }
950: elementNestLevel++;
951: writeStartTag (qName, atts, xhtml && isEmptyElementTag (qName));
952:
953: if (xhtml) {
954:
955:
956: }
957:
958: } catch (IOException e) {
959: fatal ("can't write", e);
960: }
961: }
962:
963:
967: public void writeEmptyElement (
968: String uri,
969: String localName,
970: String qName,
971: Attributes atts
972: ) throws SAXException
973: {
974: if (canonical) {
975: startElement (uri, localName, qName, atts);
976: endElement (uri, localName, qName);
977: } else {
978: try {
979: writeStartTag (qName, atts, true);
980: } catch (IOException e) {
981: fatal ("can't write", e);
982: }
983: }
984: }
985:
986:
987:
988: final public void endElement (String uri, String localName, String qName)
989: throws SAXException
990: {
991: if (qName == null || "".equals (qName))
992: throw new IllegalArgumentException ("no XML name");
993:
994: try {
995: elementNestLevel--;
996: if (entityNestLevel != 0)
997: return;
998: if (xhtml && isEmptyElementTag (qName))
999: return;
1000: rawWrite ("</");
1001: rawWrite (qName);
1002: rawWrite ('>');
1003:
1004: if (prettyPrinting) {
1005: if (!space.empty ())
1006: space.pop ();
1007: else
1008: fatal ("stack discipline", null);
1009: }
1010: if (elementNestLevel == 0)
1011: inEpilogue = true;
1012:
1013: } catch (IOException e) {
1014: fatal ("can't write", e);
1015: }
1016: }
1017:
1018:
1019: final public void characters (char ch [], int start, int length)
1020: throws SAXException
1021: {
1022: if (locator == null)
1023: locator = new LocatorImpl ();
1024:
1025: try {
1026: if (entityNestLevel != 0)
1027: return;
1028: if (inCDATA) {
1029: escapeChars (ch, start, length, CTX_UNPARSED);
1030: } else {
1031: escapeChars (ch, start, length, CTX_CONTENT);
1032: }
1033: } catch (IOException e) {
1034: fatal ("can't write", e);
1035: }
1036: }
1037:
1038:
1039: final public void ignorableWhitespace (char ch [], int start, int length)
1040: throws SAXException
1041: {
1042: if (locator == null)
1043: locator = new LocatorImpl ();
1044:
1045: try {
1046: if (entityNestLevel != 0)
1047: return;
1048:
1049: escapeChars (ch, start, length, CTX_CONTENT);
1050: } catch (IOException e) {
1051: fatal ("can't write", e);
1052: }
1053: }
1054:
1055:
1061: final public void processingInstruction (String target, String data)
1062: throws SAXException
1063: {
1064: if (locator == null)
1065: locator = new LocatorImpl ();
1066:
1067:
1068: if (xhtml && startedDoctype)
1069: return;
1070:
1071:
1072:
1073:
1074: try {
1075: if (entityNestLevel != 0)
1076: return;
1077: if (canonical && inEpilogue)
1078: newline ();
1079: rawWrite ("<?");
1080: rawWrite (target);
1081: rawWrite (' ');
1082: escapeChars (data.toCharArray (), -1, -1, CTX_UNPARSED);
1083: rawWrite ("?>");
1084: if (elementNestLevel == 0 && !(canonical && inEpilogue))
1085: newline ();
1086: } catch (IOException e) {
1087: fatal ("can't write", e);
1088: }
1089: }
1090:
1091:
1092: public void skippedEntity (String name)
1093: throws SAXException
1094: {
1095: try {
1096: rawWrite ("&");
1097: rawWrite (name);
1098: rawWrite (";");
1099: } catch (IOException e) {
1100: fatal ("can't write", e);
1101: }
1102: }
1103:
1104:
1105:
1106:
1107: final public void startCDATA ()
1108: throws SAXException
1109: {
1110: if (locator == null)
1111: locator = new LocatorImpl ();
1112:
1113: if (canonical)
1114: return;
1115:
1116: try {
1117: inCDATA = true;
1118: if (entityNestLevel == 0)
1119: rawWrite ("<![CDATA[");
1120: } catch (IOException e) {
1121: fatal ("can't write", e);
1122: }
1123: }
1124:
1125:
1126: final public void endCDATA ()
1127: throws SAXException
1128: {
1129: if (canonical)
1130: return;
1131:
1132: try {
1133: inCDATA = false;
1134: if (entityNestLevel == 0)
1135: rawWrite ("]]>");
1136: } catch (IOException e) {
1137: fatal ("can't write", e);
1138: }
1139: }
1140:
1141:
1146: final public void startDTD (String name, String publicId, String systemId)
1147: throws SAXException
1148: {
1149: if (locator == null)
1150: locator = new LocatorImpl ();
1151: if (xhtml)
1152: return;
1153: try {
1154: inDoctype = startedDoctype = true;
1155: if (canonical)
1156: return;
1157: rawWrite ("<!DOCTYPE ");
1158: rawWrite (name);
1159: rawWrite (' ');
1160:
1161: if (!expandingEntities) {
1162: if (publicId != null)
1163: rawWrite ("PUBLIC '" + publicId + "' '" + systemId + "' ");
1164: else if (systemId != null)
1165: rawWrite ("SYSTEM '" + systemId + "' ");
1166: }
1167:
1168: rawWrite ('[');
1169: newline ();
1170: } catch (IOException e) {
1171: fatal ("can't write", e);
1172: }
1173: }
1174:
1175:
1176: final public void endDTD ()
1177: throws SAXException
1178: {
1179: inDoctype = false;
1180: if (canonical || xhtml)
1181: return;
1182: try {
1183: rawWrite ("]>");
1184: newline ();
1185: } catch (IOException e) {
1186: fatal ("can't write", e);
1187: }
1188: }
1189:
1190:
1193: final public void startEntity (String name)
1194: throws SAXException
1195: {
1196: try {
1197: boolean writeEOL = true;
1198:
1199:
1200:
1201: if (xhtml || expandingEntities)
1202: return;
1203:
1204: entityNestLevel++;
1205: if (name.equals ("[dtd]"))
1206: return;
1207: if (entityNestLevel != 1)
1208: return;
1209: if (!name.startsWith ("%")) {
1210: writeEOL = false;
1211: rawWrite ('&');
1212: }
1213: rawWrite (name);
1214: rawWrite (';');
1215: if (writeEOL)
1216: newline ();
1217: } catch (IOException e) {
1218: fatal ("can't write", e);
1219: }
1220: }
1221:
1222:
1225: final public void endEntity (String name)
1226: throws SAXException
1227: {
1228: if (xhtml || expandingEntities)
1229: return;
1230: entityNestLevel--;
1231: }
1232:
1233:
1241: final public void comment (char ch [], int start, int length)
1242: throws SAXException
1243: {
1244: if (locator == null)
1245: locator = new LocatorImpl ();
1246:
1247:
1248: if (xhtml && startedDoctype)
1249: return;
1250:
1251: if (canonical && inDoctype)
1252: return;
1253:
1254: try {
1255: boolean indent;
1256:
1257: if (prettyPrinting && space.empty ())
1258: fatal ("stack discipline", null);
1259: indent = prettyPrinting && "default".equals (space.peek ());
1260: if (entityNestLevel != 0)
1261: return;
1262: if (indent)
1263: doIndent ();
1264: if (canonical && inEpilogue)
1265: newline ();
1266: rawWrite ("<!--");
1267: escapeChars (ch, start, length, CTX_UNPARSED);
1268: rawWrite ("-->");
1269: if (indent)
1270: doIndent ();
1271: if (elementNestLevel == 0 && !(canonical && inEpilogue))
1272: newline ();
1273: } catch (IOException e) {
1274: fatal ("can't write", e);
1275: }
1276: }
1277:
1278:
1279:
1280:
1281: final public void notationDecl (String name,
1282: String publicId, String systemId)
1283: throws SAXException
1284: {
1285: if (xhtml)
1286: return;
1287: try {
1288:
1289: if (!startedDoctype)
1290: return;
1291:
1292: if (entityNestLevel != 0)
1293: return;
1294: rawWrite ("<!NOTATION " + name + " ");
1295: if (publicId != null)
1296: rawWrite ("PUBLIC \"" + publicId + '"');
1297: else
1298: rawWrite ("SYSTEM ");
1299: if (systemId != null)
1300: rawWrite ('"' + systemId + '"');
1301: rawWrite (">");
1302: newline ();
1303: } catch (IOException e) {
1304: fatal ("can't write", e);
1305: }
1306: }
1307:
1308:
1309: final public void unparsedEntityDecl (String name,
1310: String publicId, String systemId,
1311: String notationName)
1312: throws SAXException
1313: {
1314: if (xhtml)
1315: return;
1316: try {
1317:
1318: if (!startedDoctype) {
1319:
1320:
1321: return;
1322: }
1323:
1324: if (entityNestLevel != 0)
1325: return;
1326: rawWrite ("<!ENTITY " + name + " ");
1327: if (publicId != null)
1328: rawWrite ("PUBLIC \"" + publicId + '"');
1329: else
1330: rawWrite ("SYSTEM ");
1331: rawWrite ('"' + systemId + '"');
1332: rawWrite (" NDATA " + notationName + ">");
1333: newline ();
1334: } catch (IOException e) {
1335: fatal ("can't write", e);
1336: }
1337: }
1338:
1339:
1340:
1341:
1342: final public void attributeDecl (String eName, String aName,
1343: String type, String mode, String value)
1344: throws SAXException
1345: {
1346: if (xhtml)
1347: return;
1348: try {
1349:
1350: if (!startedDoctype)
1351: return;
1352: if (entityNestLevel != 0)
1353: return;
1354: rawWrite ("<!ATTLIST " + eName + ' ' + aName + ' ');
1355: rawWrite (type);
1356: rawWrite (' ');
1357: if (mode != null)
1358: rawWrite (mode + ' ');
1359: if (value != null)
1360: writeQuotedValue (value, CTX_ATTRIBUTE);
1361: rawWrite ('>');
1362: newline ();
1363: } catch (IOException e) {
1364: fatal ("can't write", e);
1365: }
1366: }
1367:
1368:
1369: final public void elementDecl (String name, String model)
1370: throws SAXException
1371: {
1372: if (xhtml)
1373: return;
1374: try {
1375:
1376: if (!startedDoctype)
1377: return;
1378: if (entityNestLevel != 0)
1379: return;
1380: rawWrite ("<!ELEMENT " + name + ' ' + model + '>');
1381: newline ();
1382: } catch (IOException e) {
1383: fatal ("can't write", e);
1384: }
1385: }
1386:
1387:
1388: final public void externalEntityDecl (
1389: String name,
1390: String publicId,
1391: String systemId)
1392: throws SAXException
1393: {
1394: if (xhtml)
1395: return;
1396: try {
1397:
1398: if (!startedDoctype)
1399: return;
1400: if (entityNestLevel != 0)
1401: return;
1402: rawWrite ("<!ENTITY ");
1403: if (name.startsWith ("%")) {
1404: rawWrite ("% ");
1405: rawWrite (name.substring (1));
1406: } else
1407: rawWrite (name);
1408: if (publicId != null)
1409: rawWrite (" PUBLIC \"" + publicId + '"');
1410: else
1411: rawWrite (" SYSTEM ");
1412: rawWrite ('"' + systemId + "\">");
1413: newline ();
1414: } catch (IOException e) {
1415: fatal ("can't write", e);
1416: }
1417: }
1418:
1419:
1420: final public void internalEntityDecl (String name, String value)
1421: throws SAXException
1422: {
1423: if (xhtml)
1424: return;
1425: try {
1426:
1427: if (!startedDoctype)
1428: return;
1429: if (entityNestLevel != 0)
1430: return;
1431: rawWrite ("<!ENTITY ");
1432: if (name.startsWith ("%")) {
1433: rawWrite ("% ");
1434: rawWrite (name.substring (1));
1435: } else
1436: rawWrite (name);
1437: rawWrite (' ');
1438: writeQuotedValue (value, CTX_ENTITY);
1439: rawWrite ('>');
1440: newline ();
1441: } catch (IOException e) {
1442: fatal ("can't write", e);
1443: }
1444: }
1445:
1446: private void writeQuotedValue (String value, int code)
1447: throws SAXException, IOException
1448: {
1449: char buf [] = value.toCharArray ();
1450: int off = 0, len = buf.length;
1451:
1452:
1453: noWrap = true;
1454: rawWrite ('"');
1455: escapeChars (buf, off, len, code);
1456: rawWrite ('"');
1457: noWrap = false;
1458: }
1459:
1460:
1461:
1462:
1463: private static final String HTMLlat1x [] = {
1464:
1465: "nbsp", "iexcl", "cent", "pound", "curren",
1466: "yen", "brvbar", "sect", "uml", "copy",
1467:
1468:
1469: "ordf", "laquo", "not", "shy", "reg",
1470: "macr", "deg", "plusmn", "sup2", "sup3",
1471:
1472:
1473: "acute", "micro", "para", "middot", "cedil",
1474: "sup1", "ordm", "raquo", "frac14", "frac12",
1475:
1476:
1477: "frac34", "iquest", "Agrave", "Aacute", "Acirc",
1478: "Atilde", "Auml", "Aring", "AElig", "Ccedil",
1479:
1480:
1481: "Egrave", "Eacute", "Ecirc", "Euml", "Igrave",
1482: "Iacute", "Icirc", "Iuml", "ETH", "Ntilde",
1483:
1484:
1485: "Ograve", "Oacute", "Ocirc", "Otilde", "Ouml",
1486: "times", "Oslash", "Ugrave", "Uacute", "Ucirc",
1487:
1488:
1489: "Uuml", "Yacute", "THORN", "szlig", "agrave",
1490: "aacute", "acirc", "atilde", "auml", "aring",
1491:
1492:
1493: "aelig", "ccedil", "egrave", "eacute", "ecirc",
1494: "euml", "igrave", "iacute", "icirc", "iuml",
1495:
1496:
1497: "eth", "ntilde", "ograve", "oacute", "ocirc",
1498: "otilde", "ouml", "divide", "oslash", "ugrave",
1499:
1500:
1501: "uacute", "ucirc", "uuml", "yacute", "thorn",
1502: "yuml"
1503: };
1504:
1505:
1506:
1507:
1508: private static final String HTMLsymbolx_GR [] = {
1509:
1510: "Alpha", "Beta", "Gamma", "Delta", "Epsilon",
1511: "Zeta", "Eta", "Theta", "Iota", "Kappa",
1512:
1513:
1514: "Lambda", "Mu", "Nu", "Xi", "Omicron",
1515: "Pi", "Rho", null, "Sigma", "Tau",
1516:
1517:
1518: "Upsilon", "Phi", "Chi", "Psi", "Omega"
1519: };
1520:
1521: private static final String HTMLsymbolx_gr [] = {
1522:
1523: "alpha", "beta", "gamma", "delta", "epsilon",
1524: "zeta", "eta", "theta", "iota", "kappa",
1525:
1526:
1527: "lambda", "mu", "nu", "xi", "omicron",
1528: "pi", "rho", "sigmaf", "sigma", "tau",
1529:
1530:
1531: "upsilon", "phi", "chi", "psi", "omega"
1532: };
1533:
1534:
1535:
1536:
1537: private void escapeChars (char buf [], int off, int len, int code)
1538: throws SAXException, IOException
1539: {
1540: int first = 0;
1541:
1542: if (off < 0) {
1543: off = 0;
1544: len = buf.length;
1545: }
1546: for (int i = 0; i < len; i++) {
1547: String esc;
1548: char c = buf [off + i];
1549:
1550: switch (c) {
1551:
1552:
1553:
1554:
1555:
1556:
1557: case '&':
1558: if (code == CTX_ENTITY || code == CTX_UNPARSED)
1559: continue;
1560: esc = "amp";
1561: break;
1562:
1563:
1564:
1565: case '<':
1566: if (code == CTX_ENTITY || code == CTX_UNPARSED)
1567: continue;
1568: esc = "lt";
1569: break;
1570:
1571:
1572:
1573: case '>':
1574: if (code == CTX_ENTITY || code == CTX_UNPARSED)
1575: continue;
1576: esc = "gt";
1577: break;
1578: case '\'':
1579: if (code == CTX_CONTENT || code == CTX_UNPARSED)
1580: continue;
1581: if (canonical)
1582: continue;
1583: esc = "apos";
1584: break;
1585:
1586:
1587: case '"':
1588: if (code == CTX_CONTENT || code == CTX_UNPARSED)
1589: continue;
1590: esc = "quot";
1591: break;
1592:
1593:
1594: case '\n':
1595: esc = eol;
1596: break;
1597:
1598:
1599:
1600:
1601:
1602:
1603: default:
1604:
1605:
1606:
1607:
1608:
1609:
1610:
1611:
1612:
1613:
1614:
1615:
1616:
1617:
1618:
1619:
1620:
1621:
1622:
1623:
1624:
1625:
1626:
1627:
1628: if ((c > 0xfffd)
1629: || ((c < 0x0020) && !((c == 0x0009)
1630: || (c == 0x000A) || (c == 0x000D)))
1631: || (((c & dangerMask) != 0)
1632: && (code == CTX_UNPARSED))) {
1633:
1634:
1635:
1636:
1637:
1638: throw new CharConversionException (
1639: "Illegal or non-writable character: U+"
1640: + Integer.toHexString (c));
1641: }
1642:
1643:
1644:
1645:
1646:
1647: if ((c & dangerMask) == 0)
1648: continue;
1649: esc = null;
1650:
1651:
1652:
1653: if (xhtml) {
1654:
1655:
1656: if (c >= 160 && c <= 255)
1657: esc = HTMLlat1x [c - 160];
1658:
1659:
1660: else if (c >= 913 && c <= 937)
1661: esc = HTMLsymbolx_GR [c - 913];
1662: else if (c >= 945 && c <= 969)
1663: esc = HTMLsymbolx_gr [c - 945];
1664:
1665: else switch (c) {
1666:
1667: case 338: esc = "OElig"; break;
1668: case 339: esc = "oelig"; break;
1669: case 352: esc = "Scaron"; break;
1670: case 353: esc = "scaron"; break;
1671: case 376: esc = "Yuml"; break;
1672: case 710: esc = "circ"; break;
1673: case 732: esc = "tilde"; break;
1674: case 8194: esc = "ensp"; break;
1675: case 8195: esc = "emsp"; break;
1676: case 8201: esc = "thinsp"; break;
1677: case 8204: esc = "zwnj"; break;
1678: case 8205: esc = "zwj"; break;
1679: case 8206: esc = "lrm"; break;
1680: case 8207: esc = "rlm"; break;
1681: case 8211: esc = "ndash"; break;
1682: case 8212: esc = "mdash"; break;
1683: case 8216: esc = "lsquo"; break;
1684: case 8217: esc = "rsquo"; break;
1685: case 8218: esc = "sbquo"; break;
1686: case 8220: esc = "ldquo"; break;
1687: case 8221: esc = "rdquo"; break;
1688: case 8222: esc = "bdquo"; break;
1689: case 8224: esc = "dagger"; break;
1690: case 8225: esc = "Dagger"; break;
1691: case 8240: esc = "permil"; break;
1692: case 8249: esc = "lsaquo"; break;
1693: case 8250: esc = "rsaquo"; break;
1694: case 8364: esc = "euro"; break;
1695:
1696:
1697: case 402: esc = "fnof"; break;
1698: case 977: esc = "thetasym"; break;
1699: case 978: esc = "upsih"; break;
1700: case 982: esc = "piv"; break;
1701: case 8226: esc = "bull"; break;
1702: case 8230: esc = "hellip"; break;
1703: case 8242: esc = "prime"; break;
1704: case 8243: esc = "Prime"; break;
1705: case 8254: esc = "oline"; break;
1706: case 8260: esc = "frasl"; break;
1707: case 8472: esc = "weierp"; break;
1708: case 8465: esc = "image"; break;
1709: case 8476: esc = "real"; break;
1710: case 8482: esc = "trade"; break;
1711: case 8501: esc = "alefsym"; break;
1712: case 8592: esc = "larr"; break;
1713: case 8593: esc = "uarr"; break;
1714: case 8594: esc = "rarr"; break;
1715: case 8595: esc = "darr"; break;
1716: case 8596: esc = "harr"; break;
1717: case 8629: esc = "crarr"; break;
1718: case 8656: esc = "lArr"; break;
1719: case 8657: esc = "uArr"; break;
1720: case 8658: esc = "rArr"; break;
1721: case 8659: esc = "dArr"; break;
1722: case 8660: esc = "hArr"; break;
1723: case 8704: esc = "forall"; break;
1724: case 8706: esc = "part"; break;
1725: case 8707: esc = "exist"; break;
1726: case 8709: esc = "empty"; break;
1727: case 8711: esc = "nabla"; break;
1728: case 8712: esc = "isin"; break;
1729: case 8713: esc = "notin"; break;
1730: case 8715: esc = "ni"; break;
1731: case 8719: esc = "prod"; break;
1732: case 8721: esc = "sum"; break;
1733: case 8722: esc = "minus"; break;
1734: case 8727: esc = "lowast"; break;
1735: case 8730: esc = "radic"; break;
1736: case 8733: esc = "prop"; break;
1737: case 8734: esc = "infin"; break;
1738: case 8736: esc = "ang"; break;
1739: case 8743: esc = "and"; break;
1740: case 8744: esc = "or"; break;
1741: case 8745: esc = "cap"; break;
1742: case 8746: esc = "cup"; break;
1743: case 8747: esc = "int"; break;
1744: case 8756: esc = "there4"; break;
1745: case 8764: esc = "sim"; break;
1746: case 8773: esc = "cong"; break;
1747: case 8776: esc = "asymp"; break;
1748: case 8800: esc = "ne"; break;
1749: case 8801: esc = "equiv"; break;
1750: case 8804: esc = "le"; break;
1751: case 8805: esc = "ge"; break;
1752: case 8834: esc = "sub"; break;
1753: case 8835: esc = "sup"; break;
1754: case 8836: esc = "nsub"; break;
1755: case 8838: esc = "sube"; break;
1756: case 8839: esc = "supe"; break;
1757: case 8853: esc = "oplus"; break;
1758: case 8855: esc = "otimes"; break;
1759: case 8869: esc = "perp"; break;
1760: case 8901: esc = "sdot"; break;
1761: case 8968: esc = "lceil"; break;
1762: case 8969: esc = "rceil"; break;
1763: case 8970: esc = "lfloor"; break;
1764: case 8971: esc = "rfloor"; break;
1765: case 9001: esc = "lang"; break;
1766: case 9002: esc = "rang"; break;
1767: case 9674: esc = "loz"; break;
1768: case 9824: esc = "spades"; break;
1769: case 9827: esc = "clubs"; break;
1770: case 9829: esc = "hearts"; break;
1771: case 9830: esc = "diams"; break;
1772: }
1773: }
1774:
1775:
1776: if (esc == null) {
1777: stringBuf.setLength (0);
1778: stringBuf.append ("#x");
1779: stringBuf.append (Integer.toHexString (c).toUpperCase ());
1780: esc = stringBuf.toString ();
1781:
1782:
1783:
1784:
1785:
1786: }
1787: break;
1788: }
1789: if (i != first)
1790: rawWrite (buf, off + first, i - first);
1791: first = i + 1;
1792: if (esc == eol)
1793: newline ();
1794: else {
1795: rawWrite ('&');
1796: rawWrite (esc);
1797: rawWrite (';');
1798: }
1799: }
1800: if (first < len)
1801: rawWrite (buf, off + first, len - first);
1802: }
1803:
1804:
1805:
1806: private void newline ()
1807: throws SAXException, IOException
1808: {
1809: out.write (eol);
1810: column = 0;
1811: }
1812:
1813: private void doIndent ()
1814: throws SAXException, IOException
1815: {
1816: int space = elementNestLevel * 2;
1817:
1818: newline ();
1819: column = space;
1820:
1821: while (space > 8) {
1822: out.write ("\t");
1823: space -= 8;
1824: }
1825: while (space > 0) {
1826: out.write (" ");
1827: space -= 2;
1828: }
1829: }
1830:
1831: private void rawWrite (char c)
1832: throws IOException
1833: {
1834: out.write (c);
1835: column++;
1836: }
1837:
1838: private void rawWrite (String s)
1839: throws SAXException, IOException
1840: {
1841: if (prettyPrinting && "default".equals (space.peek ())) {
1842: char data [] = s.toCharArray ();
1843: rawWrite (data, 0, data.length);
1844: } else {
1845: out.write (s);
1846: column += s.length ();
1847: }
1848: }
1849:
1850:
1851:
1852:
1853:
1854:
1855:
1856:
1857:
1858:
1859:
1860: private void rawWrite (char buf [], int offset, int length)
1861: throws SAXException, IOException
1862: {
1863: boolean wrap;
1864:
1865: if (prettyPrinting && space.empty ())
1866: fatal ("stack discipline", null);
1867:
1868: wrap = prettyPrinting && "default".equals (space.peek ());
1869: if (!wrap) {
1870: out.write (buf, offset, length);
1871: column += length;
1872: return;
1873: }
1874:
1875:
1876:
1877: while (length > 0) {
1878: int target = lineLength - column;
1879: boolean wrote = false;
1880:
1881:
1882: if (target > length || noWrap) {
1883: out.write (buf, offset, length);
1884: column += length;
1885: return;
1886: }
1887:
1888:
1889:
1890: char c;
1891:
1892: for (int i = target - 1; i >= 0; i--) {
1893: if ((c = buf [offset + i]) == ' ' || c == '\t') {
1894: i++;
1895: out.write (buf, offset, i);
1896: doIndent ();
1897: offset += i;
1898: length -= i;
1899: wrote = true;
1900: break;
1901: }
1902: }
1903: if (wrote)
1904: continue;
1905:
1906:
1907:
1908: if (target < 0)
1909: target = 0;
1910: for (int i = target; i < length; i++)
1911: if ((c = buf [offset + i]) == ' ' || c == '\t') {
1912: i++;
1913: out.write (buf, offset, i);
1914: doIndent ();
1915: offset += i;
1916: length -= i;
1917: wrote = true;
1918: break;
1919: }
1920: if (wrote)
1921: continue;
1922:
1923:
1924: out.write (buf, offset, length);
1925: column += length;
1926: break;
1927: }
1928: }
1929: }