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<Element> 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 163 : return "£" ;
187 case 192 : return "À" ;
188 case 193 : return "Á" ;
189 case 194 : return "Â" ;
190 case 199 : return "Ç" ;
191 case 200 : return "È" ;
192 case 201 : return "É" ;
193 case 202 : return "Ê" ;
194 case 204 : return "Ì" ;
195 case 205 : return "Í" ;
196 case 206 : return "Î" ;
197 case 210 : return "Ò" ;
198 case 211 : return "Ó" ;
199 case 212 : return "Ô" ;
200 case 217 : return "Ù" ;
201 case 218 : return "Ú" ;
202 case 219 : return "Û" ;
203 case 224 : return "à" ;
204 case 225 : return "á" ;
205 case 226 : return "â" ;
206 case 228 : return "ä" ;
207 case 231 : return "ç" ;
208 case 232 : return "è" ;
209 case 233 : return "é" ;
210 case 234 : return "ê" ;
211 case 236 : return "ì" ;
212 case 237 : return "í" ;
213 case 238 : return "î" ;
214 case 242 : return "ò" ;
215 case 243 : return "ó" ;
216 case 244 : return "ô" ;
217 case 249 : return "ù" ;
218 case 250 : return "ú" ;
219 case 251 : return "û" ;
220 case 252 : return "ü" ;
221
222
223 case '"' : return markup ? null : """;
224 case '\'': return markup ? null : "'";
225 default:
226 if (ce == null || ce.canEncode(c)) {
227 return null;
228 } else {
229 String result = "&#x" + Integer.toHexString(c) + ";";
230
231 return result;
232 }
233 }
234 }
235
236
237
238
239
240
241
242
243
244
245
246 public static String entitied(String s, boolean mapBR, String encoding, boolean markup) {
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
262 StringBuffer b = new StringBuffer(length * 2);
263 for (int j = 0; j < i; ++j)
264 b.append(s.charAt(j));
265
266 b.append(entity);
267
268 char c;
269 for (++i; i < length; ++i) {
270 c = s.charAt(i);
271 entity = entityFor(c, mapBR, ce, markup);
272 if (entity != null) {
273 b.append(entity);
274 } else
275 b.append(c);
276 }
277 return b.toString();
278 }
279
280
281
282
283
284
285
286
287
288
289
290 public static String entitied(String s) {
291 return entitied(s, true, null, false);
292 }
293
294
295
296
297
298
299
300 public static String jsEscapeFor(char c) {
301 switch (c) {
302 case '\n': return "\\012";
303 case '"': return "\\042";
304 case '\'': return "\\047";
305 default: return null;
306 }
307 }
308
309
310
311
312
313
314 public static String jsEscaped(String s) {
315 int length = s.length();
316 int i = 0;
317 String escape = null;
318 for (i = 0; i < length && (escape = jsEscapeFor(s.charAt(i))) == null; ++i);
319
320 if (escape == null) return s;
321
322 StringBuffer b = new StringBuffer(length * 2);
323 for (int j = 0; j < i; ++j)
324 b.append(s.charAt(j));
325
326 b.append(escape);
327
328 char c;
329 for (++i; i < length; ++i) {
330 c = s.charAt(i);
331 escape = jsEscapeFor(c);
332 if (escape != null)
333 b.append(escape);
334 else
335 b.append(c);
336 }
337 return b.toString();
338 }
339
340
341
342
343
344
345
346
347 public static void write(Writer w, HTML.Tag tag, AttributeSet attributes)
348 throws IOException {
349 w.write('<');
350 w.write(tag.toString());
351 for (Enumeration<?> a = attributes.getAttributeNames();
352 a.hasMoreElements();) {
353 Object n = a.nextElement();
354 if (attributes.isDefined(n)) {
355 w.write(' ');
356 w.write(n.toString());
357 w.write("=\"");
358 w.write(entitied(attributes.getAttribute(n).toString()));
359 w.write('"');
360 }
361 }
362 w.write('>');
363 }
364
365
366
367
368
369
370 public static String stringOf(HTML.Tag tag, AttributeSet attributes) {
371 StringWriter w = new StringWriter();
372
373 try {
374 write(w, tag, attributes);
375 }
376 catch (IOException e) {
377 throw new UnexpectedExceptionException(e);
378 }
379
380 return w.toString();
381 }
382
383
384
385
386 public static class TagInstance {
387
388 public final HTML.Tag tag;
389
390 public final AttributeSet attributes;
391
392
393 public TagInstance(HTML.Tag tag, AttributeSet attributes) {
394 this.tag = tag;
395 this.attributes = attributes;
396 }
397
398
399
400
401
402 public void write(Writer w) throws IOException {
403 HTMLUtils.write(w, tag, attributes);
404 }
405
406
407
408
409
410 public String toString() {
411 return HTMLUtils.stringOf(tag, attributes);
412 }
413 }
414 }