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