1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 package org.melati.util;
44
45 import java.io.DataInputStream;
46 import java.io.FileNotFoundException;
47 import java.io.IOException;
48 import java.io.InputStream;
49 import java.io.StringWriter;
50 import java.io.Writer;
51 import java.nio.charset.Charset;
52 import java.nio.charset.CharsetEncoder;
53 import java.util.Enumeration;
54
55 import javax.swing.text.AttributeSet;
56 import javax.swing.text.html.HTML;
57 import javax.swing.text.html.parser.AttributeList;
58 import javax.swing.text.html.parser.ContentModel;
59 import javax.swing.text.html.parser.DTD;
60 import javax.swing.text.html.parser.DTDConstants;
61 import javax.swing.text.html.parser.Element;
62
63
64
65
66 public final class HTMLUtils {
67
68 private HTMLUtils() {}
69
70
71 public static final String dtdNameForHTMLParser = "html32.bdtd";
72
73 private static DTD dtdForHTMLParser = null;
74
75
76
77
78
79
80
81 public static void add(ContentModel cm, Element existing, Element alt) {
82 if (cm.content == existing) {
83 ContentModel twig =
84 new ContentModel(0, existing, new ContentModel(0, alt, null));
85 if (cm.type == 0) {
86 cm.type = '|';
87 cm.content = twig;
88 }
89 else
90 cm.content = new ContentModel('|', twig);
91 }
92 else if (cm.content instanceof ContentModel)
93 add((ContentModel)cm.content, existing, alt);
94
95 if (cm.next != null)
96 add(cm.next, existing, alt);
97 }
98
99
100
101
102
103
104
105 public static void addToContentModels(DTD dtd,
106 Element existing, Element alt) {
107 for (Enumeration els = dtd.elementHash.elements();
108 els.hasMoreElements();) {
109 ContentModel c = ((Element)els.nextElement()).content;
110 if (c != null)
111 add(c, existing, alt);
112 }
113 }
114
115
116
117
118 public static DTD dtdForHTMLParser() {
119
120
121 if (dtdForHTMLParser == null)
122 try {
123 dtdForHTMLParser = DTD.getDTD(dtdNameForHTMLParser);
124 InputStream res = dtdForHTMLParser.getClass().
125 getResourceAsStream(dtdNameForHTMLParser);
126 if (res == null)
127 throw new FileNotFoundException(
128 "Resource " + dtdNameForHTMLParser + " not found: " +
129 "but it ought to be in rt.jar?!");
130 dtdForHTMLParser.read(new DataInputStream(res));
131
132
133
134
135
136 Element div = (Element)dtdForHTMLParser.elementHash.get("div");
137 Element i = (Element)dtdForHTMLParser.elementHash.get("i");
138
139 dtdForHTMLParser.defineElement(
140 "span", DTDConstants.STARTTAG, false, false, div.content, null, null,
141 new AttributeList("class", DTDConstants.CDATA,
142 0, null, null, null));
143
144 Element span = (Element)dtdForHTMLParser.elementHash.get("span");
145
146 addToContentModels(dtdForHTMLParser, i, span);
147 }
148 catch (Exception e) {
149 throw new UnexpectedExceptionException(
150 "making the DTD for Sun's HTML parser", e);
151 }
152
153 return dtdForHTMLParser;
154 }
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178 public static String entityFor(char c, boolean mapBR, CharsetEncoder ce, boolean markup) {
179 switch (c) {
180 case '\n': return mapBR && !markup ? "<BR>\n" : null;
181 case '<' : return markup ? null : "<" ;
182 case '>' : return markup ? null : ">" ;
183 case '&' : return markup ? null : "&" ;
184
185
186 case 192 : return "À" ;
187 case 193 : return "Á" ;
188 case 194 : return "Â" ;
189 case 199 : return "Ç" ;
190 case 200 : return "È" ;
191 case 201 : return "É" ;
192 case 202 : return "Ê" ;
193 case 204 : return "Ì" ;
194 case 205 : return "Í" ;
195 case 206 : return "Î" ;
196 case 210 : return "Ò" ;
197 case 211 : return "Ó" ;
198 case 212 : return "Ô" ;
199 case 217 : return "Ù" ;
200 case 218 : return "Ú" ;
201 case 219 : return "Û" ;
202 case 224 : return "à" ;
203 case 225 : return "á" ;
204 case 226 : return "â" ;
205 case 228 : return "ä" ;
206 case 231 : return "ç" ;
207 case 232 : return "è" ;
208 case 233 : return "é" ;
209 case 234 : return "ê" ;
210 case 236 : return "ì" ;
211 case 237 : return "í" ;
212 case 238 : return "î" ;
213 case 242 : return "ò" ;
214 case 243 : return "ó" ;
215 case 244 : return "ô" ;
216 case 249 : return "ù" ;
217 case 250 : return "ú" ;
218 case 251 : return "û" ;
219 case 252 : return "ü" ;
220
221
222 case '"' : return markup ? null : """;
223 case '\'': return markup ? null : "'";
224 default:
225 if (ce == null || ce.canEncode(c)) {
226 return null;
227 } else {
228 String result = "&#x" + Integer.toHexString(c) + ";";
229
230 return result;
231 }
232 }
233 }
234
235
236
237
238
239
240
241
242
243
244
245 public static String entitied(String s, boolean mapBR, String encoding, boolean markup) {
246 System.err.println("encoding:" + encoding);
247 int length = s.length();
248 int i;
249 String entity = null;
250
251 CharsetEncoder ce = null;
252 if (encoding != null) {
253 ce = Charset.forName(encoding).newEncoder();
254 }
255
256 for (i = 0;
257 i < length && (entity = entityFor(s.charAt(i), mapBR, ce, markup)) == null;
258 ++i);
259
260 if (entity == null) return s;
261 System.err.println("entitied:" + new Integer(s.charAt(i))+ "=" + entity);
262
263 StringBuffer b = new StringBuffer(length * 2);
264 for (int j = 0; j < i; ++j)
265 b.append(s.charAt(j));
266
267 b.append(entity);
268
269 char c;
270 for (++i; i < length; ++i) {
271 c = s.charAt(i);
272 entity = entityFor(c, mapBR, ce, markup);
273 if (entity != null) {
274 b.append(entity);
275 System.err.println(new Integer(c) + "=" + entity);
276 } else
277 b.append(c);
278 }
279 return b.toString();
280 }
281
282
283
284
285
286
287
288
289
290
291
292 public static String entitied(String s) {
293 return entitied(s, true, null, false);
294 }
295
296
297
298
299
300
301
302 public static String jsEscapeFor(char c) {
303 switch (c) {
304 case '\n': return "\\012";
305 case '"': return "\\042";
306 case '\'': return "\\047";
307 default: return null;
308 }
309 }
310
311
312
313
314
315
316 public static String jsEscaped(String s) {
317 int length = s.length();
318 int i = 0;
319 String escape = null;
320 for (i = 0; i < length && (escape = jsEscapeFor(s.charAt(i))) == null; ++i);
321
322 if (escape == null) return s;
323
324 StringBuffer b = new StringBuffer(length * 2);
325 for (int j = 0; j < i; ++j)
326 b.append(s.charAt(j));
327
328 b.append(escape);
329
330 char c;
331 for (++i; i < length; ++i) {
332 c = s.charAt(i);
333 escape = jsEscapeFor(c);
334 if (escape != null)
335 b.append(escape);
336 else
337 b.append(c);
338 }
339 return b.toString();
340 }
341
342
343
344
345
346
347
348
349 public static void write(Writer w, HTML.Tag tag, AttributeSet attributes)
350 throws IOException {
351 w.write('<');
352 w.write(tag.toString());
353 for (Enumeration a = attributes.getAttributeNames();
354 a.hasMoreElements();) {
355 Object n = a.nextElement();
356 if (attributes.isDefined(n)) {
357 w.write(' ');
358 w.write(n.toString());
359 w.write("=\"");
360 w.write(entitied(attributes.getAttribute(n).toString()));
361 w.write('"');
362 }
363 }
364 w.write('>');
365 }
366
367
368
369
370
371
372 public static String stringOf(HTML.Tag tag, AttributeSet attributes) {
373 StringWriter w = new StringWriter();
374
375 try {
376 write(w, tag, attributes);
377 }
378 catch (IOException e) {
379 throw new UnexpectedExceptionException(e);
380 }
381
382 return w.toString();
383 }
384
385
386
387
388 public static class TagInstance {
389
390 public final HTML.Tag tag;
391
392 public final AttributeSet attributes;
393
394
395 public TagInstance(HTML.Tag tag, AttributeSet attributes) {
396 this.tag = tag;
397 this.attributes = attributes;
398 }
399
400
401
402
403
404 public void write(Writer w) throws IOException {
405 HTMLUtils.write(w, tag, attributes);
406 }
407
408
409
410
411
412 public String toString() {
413 return HTMLUtils.stringOf(tag, attributes);
414 }
415 }
416 }