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 | 0 | private HTMLUtils() {} |
69 | |
|
70 | |
|
71 | |
public static final String dtdNameForHTMLParser = "html32.bdtd"; |
72 | |
|
73 | 1 | private static DTD dtdForHTMLParser = null; |
74 | |
|
75 | |
|
76 | |
|
77 | |
|
78 | |
|
79 | |
|
80 | |
|
81 | |
public static void add(ContentModel cm, Element existing, Element alt) { |
82 | 0 | if (cm.content == existing) { |
83 | 0 | ContentModel twig = |
84 | |
new ContentModel(0, existing, new ContentModel(0, alt, null)); |
85 | 0 | if (cm.type == 0) { |
86 | 0 | cm.type = '|'; |
87 | 0 | cm.content = twig; |
88 | |
} |
89 | |
else |
90 | 0 | cm.content = new ContentModel('|', twig); |
91 | 0 | } |
92 | 0 | else if (cm.content instanceof ContentModel) |
93 | 0 | add((ContentModel)cm.content, existing, alt); |
94 | |
|
95 | 0 | if (cm.next != null) |
96 | 0 | add(cm.next, existing, alt); |
97 | 0 | } |
98 | |
|
99 | |
|
100 | |
|
101 | |
|
102 | |
|
103 | |
|
104 | |
|
105 | |
public static void addToContentModels(DTD dtd, |
106 | |
Element existing, Element alt) { |
107 | 0 | for (Enumeration<Element> els = dtd.elementHash.elements(); |
108 | 0 | els.hasMoreElements();) { |
109 | 0 | ContentModel c = ((Element)els.nextElement()).content; |
110 | 0 | if (c != null) |
111 | 0 | add(c, existing, alt); |
112 | 0 | } |
113 | 0 | } |
114 | |
|
115 | |
|
116 | |
|
117 | |
|
118 | |
public static DTD dtdForHTMLParser() { |
119 | |
|
120 | |
|
121 | 0 | if (dtdForHTMLParser == null) |
122 | |
try { |
123 | 0 | dtdForHTMLParser = DTD.getDTD(dtdNameForHTMLParser); |
124 | 0 | InputStream res = dtdForHTMLParser.getClass(). |
125 | 0 | getResourceAsStream(dtdNameForHTMLParser); |
126 | 0 | if (res == null) |
127 | 0 | throw new FileNotFoundException( |
128 | |
"Resource " + dtdNameForHTMLParser + " not found: " + |
129 | |
"but it ought to be in rt.jar?!"); |
130 | 0 | dtdForHTMLParser.read(new DataInputStream(res)); |
131 | |
|
132 | |
|
133 | |
|
134 | |
|
135 | |
|
136 | 0 | Element div = (Element)dtdForHTMLParser.elementHash.get("div"); |
137 | 0 | Element i = (Element)dtdForHTMLParser.elementHash.get("i"); |
138 | |
|
139 | 0 | 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 | 0 | Element span = (Element)dtdForHTMLParser.elementHash.get("span"); |
145 | |
|
146 | 0 | addToContentModels(dtdForHTMLParser, i, span); |
147 | |
} |
148 | 0 | catch (Exception e) { |
149 | 0 | throw new UnexpectedExceptionException( |
150 | |
"making the DTD for Sun's HTML parser", e); |
151 | 0 | } |
152 | |
|
153 | 0 | 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 | 47166 | switch (c) { |
180 | 0 | case '\n': return mapBR && !markup ? "<BR>\n" : null; |
181 | 10 | case '<' : return markup ? null : "<" ; |
182 | 10 | case '>' : return markup ? null : ">" ; |
183 | 0 | case '&' : return markup ? null : "&" ; |
184 | |
|
185 | |
|
186 | 12 | case 163 : return "£" ; |
187 | 2 | case 192 : return "À" ; |
188 | 16 | case 193 : return "Á" ; |
189 | 7 | case 194 : return "Â" ; |
190 | 2 | case 199 : return "Ç" ; |
191 | 2 | case 200 : return "È" ; |
192 | 2 | case 201 : return "É" ; |
193 | 2 | case 202 : return "Ê" ; |
194 | 2 | case 204 : return "Ì" ; |
195 | 2 | case 205 : return "Í" ; |
196 | 2 | case 206 : return "Î" ; |
197 | 2 | case 210 : return "Ò" ; |
198 | 2 | case 211 : return "Ó" ; |
199 | 2 | case 212 : return "Ô" ; |
200 | 2 | case 217 : return "Ù" ; |
201 | 2 | case 218 : return "Ú" ; |
202 | 2 | case 219 : return "Û" ; |
203 | 2 | case 224 : return "à" ; |
204 | 2 | case 225 : return "á" ; |
205 | 2 | case 226 : return "â" ; |
206 | 2 | case 228 : return "ä" ; |
207 | 2 | case 231 : return "ç" ; |
208 | 2 | case 232 : return "è" ; |
209 | 2 | case 233 : return "é" ; |
210 | 2 | case 234 : return "ê" ; |
211 | 2 | case 236 : return "ì" ; |
212 | 2 | case 237 : return "í" ; |
213 | 2 | case 238 : return "î" ; |
214 | 2 | case 242 : return "ò" ; |
215 | 2 | case 243 : return "ó" ; |
216 | 2 | case 244 : return "ô" ; |
217 | 2 | case 249 : return "ù" ; |
218 | 2 | case 250 : return "ú" ; |
219 | 2 | case 251 : return "û" ; |
220 | 2 | case 252 : return "ü" ; |
221 | |
|
222 | |
|
223 | 0 | case '"' : return markup ? null : """; |
224 | 96 | case '\'': return markup ? null : "'"; |
225 | |
default: |
226 | 46951 | if (ce == null || ce.canEncode(c)) { |
227 | 45299 | return null; |
228 | |
} else { |
229 | 1652 | String result = "&#x" + Integer.toHexString(c) + ";"; |
230 | |
|
231 | 1652 | 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 | 8278 | int length = s.length(); |
248 | |
int i; |
249 | 8278 | String entity = null; |
250 | |
|
251 | 8278 | CharsetEncoder ce = null; |
252 | 8278 | if (encoding != null) { |
253 | 8278 | ce = Charset.forName(encoding).newEncoder(); |
254 | |
} |
255 | |
|
256 | 8278 | for (i = 0; |
257 | 52552 | i < length && (entity = entityFor(s.charAt(i), mapBR, ce, markup)) == null; |
258 | 44274 | ++i); |
259 | |
|
260 | 8278 | if (entity == null) return s; |
261 | |
|
262 | 1833 | StringBuffer b = new StringBuffer(length * 2); |
263 | 4476 | for (int j = 0; j < i; ++j) |
264 | 2643 | b.append(s.charAt(j)); |
265 | |
|
266 | 1833 | b.append(entity); |
267 | |
|
268 | |
char c; |
269 | 2873 | for (++i; i < length; ++i) { |
270 | 1040 | c = s.charAt(i); |
271 | 1040 | entity = entityFor(c, mapBR, ce, markup); |
272 | 1040 | if (entity != null) { |
273 | 8 | b.append(entity); |
274 | |
} else |
275 | 1032 | b.append(c); |
276 | |
} |
277 | 1833 | return b.toString(); |
278 | |
} |
279 | |
|
280 | |
|
281 | |
|
282 | |
|
283 | |
|
284 | |
|
285 | |
|
286 | |
|
287 | |
|
288 | |
|
289 | |
|
290 | |
public static String entitied(String s) { |
291 | 0 | return entitied(s, true, null, false); |
292 | |
} |
293 | |
|
294 | |
|
295 | |
|
296 | |
|
297 | |
|
298 | |
|
299 | |
|
300 | |
public static String jsEscapeFor(char c) { |
301 | 4127 | switch (c) { |
302 | 0 | case '\n': return "\\012"; |
303 | 0 | case '"': return "\\042"; |
304 | 0 | case '\'': return "\\047"; |
305 | 4127 | default: return null; |
306 | |
} |
307 | |
} |
308 | |
|
309 | |
|
310 | |
|
311 | |
|
312 | |
|
313 | |
|
314 | |
public static String jsEscaped(String s) { |
315 | 430 | int length = s.length(); |
316 | 430 | int i = 0; |
317 | 430 | String escape = null; |
318 | 430 | for (i = 0; i < length && (escape = jsEscapeFor(s.charAt(i))) == null; ++i); |
319 | |
|
320 | 430 | if (escape == null) return s; |
321 | |
|
322 | 0 | StringBuffer b = new StringBuffer(length * 2); |
323 | 0 | for (int j = 0; j < i; ++j) |
324 | 0 | b.append(s.charAt(j)); |
325 | |
|
326 | 0 | b.append(escape); |
327 | |
|
328 | |
char c; |
329 | 0 | for (++i; i < length; ++i) { |
330 | 0 | c = s.charAt(i); |
331 | 0 | escape = jsEscapeFor(c); |
332 | 0 | if (escape != null) |
333 | 0 | b.append(escape); |
334 | |
else |
335 | 0 | b.append(c); |
336 | |
} |
337 | 0 | 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 | 0 | w.write('<'); |
350 | 0 | w.write(tag.toString()); |
351 | 0 | for (Enumeration<?> a = attributes.getAttributeNames(); |
352 | 0 | a.hasMoreElements();) { |
353 | 0 | Object n = a.nextElement(); |
354 | 0 | if (attributes.isDefined(n)) { |
355 | 0 | w.write(' '); |
356 | 0 | w.write(n.toString()); |
357 | 0 | w.write("=\""); |
358 | 0 | w.write(entitied(attributes.getAttribute(n).toString())); |
359 | 0 | w.write('"'); |
360 | |
} |
361 | 0 | } |
362 | 0 | w.write('>'); |
363 | 0 | } |
364 | |
|
365 | |
|
366 | |
|
367 | |
|
368 | |
|
369 | |
|
370 | |
public static String stringOf(HTML.Tag tag, AttributeSet attributes) { |
371 | 0 | StringWriter w = new StringWriter(); |
372 | |
|
373 | |
try { |
374 | 0 | write(w, tag, attributes); |
375 | |
} |
376 | 0 | catch (IOException e) { |
377 | 0 | throw new UnexpectedExceptionException(e); |
378 | 0 | } |
379 | |
|
380 | 0 | 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 | 0 | public TagInstance(HTML.Tag tag, AttributeSet attributes) { |
394 | 0 | this.tag = tag; |
395 | 0 | this.attributes = attributes; |
396 | 0 | } |
397 | |
|
398 | |
|
399 | |
|
400 | |
|
401 | |
|
402 | |
public void write(Writer w) throws IOException { |
403 | 0 | HTMLUtils.write(w, tag, attributes); |
404 | 0 | } |
405 | |
|
406 | |
|
407 | |
|
408 | |
|
409 | |
|
410 | |
public String toString() { |
411 | 0 | return HTMLUtils.stringOf(tag, attributes); |
412 | |
} |
413 | |
} |
414 | |
} |