Source code: com/hexidec/ekit/component/ExtendedHTMLEditorKit.java
1 /*
2 GNU Lesser General Public License
3
4 ExtendedHTMLEditorKit
5 Copyright (C) 2001-2002 Frits Jalvingh & Howard A Kistler
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 package com.hexidec.ekit.component;
23
24 import javax.swing.text.*;
25 import java.io.*;
26 import javax.swing.text.Element;
27 import javax.swing.text.StyleConstants;
28 import javax.swing.text.View;
29 import javax.swing.text.ViewFactory;
30 import javax.swing.text.html.HTMLEditorKit;
31 import javax.swing.text.html.*;
32
33 /**
34 * This class extends HTMLEditorKit so that it can provide other renderer classes
35 * instead of the defaults. Most important is the part which renders relative
36 * image paths.
37 *
38 * @author <a href="mailto:jal@grimor.com">Frits Jalvingh</a>
39 * @version 1.0
40 */
41
42 public class ExtendedHTMLEditorKit extends HTMLEditorKit
43 {
44
45
46 // --- variables ------------------------------------------
47
48
49 private static Parser defaultParser = null;
50
51 /** Constructor
52 */
53 public ExtendedHTMLEditorKit()
54 {
55 }
56
57
58
59 public HTMLEditorKit.Parser getParser(){
60 if (defaultParser == null) {
61 try {
62 Class c = Class.forName("com.hexidec.ekit.component.parser.ParserDelegator");
63 defaultParser = (Parser) c.newInstance();
64 } catch (Throwable e) {
65 System.out.println("Error:"+e);
66 }
67 }
68
69 return defaultParser;
70 }
71
72
73 /** Method for returning a ViewFactory which handles the image rendering.
74 */
75 public ViewFactory getViewFactory()
76 {
77 return new HTMLFactoryExtended();
78 }
79
80
81 public Document createDefaultDocument() {
82 StyleSheet styles = getStyleSheet();
83 StyleSheet ss = new StyleSheet();
84
85 ss.addStyleSheet(styles);
86
87 // New Reader..
88 HTMLDocument doc = new ExtendedHTMLDocument(ss);
89 doc.setParser(getParser());
90 doc.setAsynchronousLoadPriority(4);
91 doc.setTokenThreshold(100);
92 return doc;
93 }
94
95
96
97 /**
98 * Inserts content from the given stream. If <code>doc</code> is
99 * an instance of HTMLDocument, this will read
100 * HTML 3.2 text. Inserting HTML into a non-empty document must be inside
101 * the body Element, if you do not insert into the body an exception will
102 * be thrown. When inserting into a non-empty document all tags outside
103 * of the body (head, title) will be dropped.
104 *
105 * @param in the stream to read from
106 * @param doc the destination for the insertion
107 * @param pos the location in the document to place the
108 * content
109 * @exception IOException on any I/O error
110 * @exception BadLocationException if pos represents an invalid
111 * location within the document
112 * @exception RuntimeException (will eventually be a BadLocationException)
113 * if pos is invalid
114 */
115 public void read(Reader in, Document doc, int pos) throws IOException, BadLocationException {
116
117 if (doc instanceof HTMLDocument) {
118 HTMLDocument hdoc = (HTMLDocument) doc;
119 Parser p = getParser();
120 if (p == null) {
121 throw new IOException("Can't load parser");
122 }
123 if (pos > doc.getLength()) {
124 throw new BadLocationException("Invalid location", pos);
125 }
126
127 ParserCallback receiver = hdoc.getReader(pos);
128 Boolean ignoreCharset = (Boolean)doc.getProperty("IgnoreCharsetDirective");
129 p.parse(in, receiver, (ignoreCharset == null) ? false : ignoreCharset.booleanValue());
130 receiver.flush();
131 } else {
132 super.read(in, doc, pos);
133 }
134 }
135
136 /**
137 * Inserts HTML into an existing document.
138 *
139 * @param doc the document to insert into
140 * @param offset the offset to insert HTML at
141 * @param popDepth the number of ElementSpec.EndTagTypes to generate before
142 * inserting
143 * @param pushDepth the number of ElementSpec.StartTagTypes with a direction
144 * of ElementSpec.JoinNextDirection that should be generated
145 * before inserting, but after the end tags have been generated
146 * @param insertTag the first tag to start inserting into document
147 * @exception RuntimeException (will eventually be a BadLocationException)
148 * if pos is invalid
149 */
150 /** public void insertHTML(HTMLDocument doc, int offset, String html,
151 int popDepth, int pushDepth,
152 HTML.Tag insertTag) throws
153 BadLocationException, IOException {
154 Parser p = getParser();
155 if (p == null) {
156 throw new IOException("Can't load parser");
157 }
158 if (offset > doc.getLength()) {
159 throw new BadLocationException("Invalid location", offset);
160 }
161
162 ParserCallback receiver = doc.getReader(offset, popDepth, pushDepth,
163 insertTag);
164 Boolean ignoreCharset = (Boolean)doc.getProperty
165 ("IgnoreCharsetDirective");
166 p.parse(new StringReader(html), receiver, (ignoreCharset == null) ?
167 false : ignoreCharset.booleanValue());
168 receiver.flush();
169 }
170
171 /**
172 * Write content from a document to the given stream
173 * in a format appropriate for this kind of content handler.
174 *
175 * @param out the stream to write to
176 * @param doc the source for the write
177 * @param pos the location in the document to fetch the
178 * content
179 * @param len the amount to write out
180 * @exception IOException on any I/O error
181 * @exception BadLocationException if pos represents an invalid
182 * location within the document
183 */
184
185
186 public void write(Writer out, Document doc, int pos, int len)
187 throws IOException, BadLocationException {
188
189 if (doc instanceof HTMLDocument) {
190 ExtendedHTMLWriter w = new ExtendedHTMLWriter(out, (HTMLDocument)doc, pos, len);
191 w.write();
192 } else if (doc instanceof StyledDocument) {
193 MinimalHTMLWriter w = new MinimalHTMLWriter(out, (StyledDocument)doc, pos, len);
194 w.write();
195 } else {
196 super.write(out, doc, pos, len);
197 }
198 }
199
200
201
202 /**
203 * Copies the key/values in <code>element</code>s AttributeSet into
204 * <code>set</code>. This does not copy component, icon, or element
205 * names attributes. Subclasses may wish to refine what is and what
206 * isn't copied here. But be sure to first remove all the attributes that
207 * are in <code>set</code>.<p>
208 * This is called anytime the caret moves over a different location.
209 *
210 */
211 public void createInputAttributes(Element element,
212 MutableAttributeSet set) {
213
214 set.removeAttributes(set);
215 set.addAttributes(element.getAttributes());
216 set.removeAttribute(StyleConstants.ComposedTextAttribute);
217
218 Object o = set.getAttribute(StyleConstants.NameAttribute);
219 if (o instanceof HTML.Tag) {
220 HTML.Tag tag = (HTML.Tag)o;
221 // PENDING: we need a better way to express what shouldn't be
222 // copied when editing...
223 if(tag == HTML.Tag.IMG) {
224 // Remove the related image attributes, src, width, height
225 set.removeAttribute(HTML.Attribute.SRC);
226 set.removeAttribute(HTML.Attribute.HEIGHT);
227 set.removeAttribute(HTML.Attribute.WIDTH);
228 set.addAttribute(StyleConstants.NameAttribute,
229 HTML.Tag.CONTENT);
230 }
231 else if (tag == HTML.Tag.HR || tag == HTML.Tag.BR) {
232 // Don't copy HRs or BRs either.
233 set.addAttribute(StyleConstants.NameAttribute,
234 HTML.Tag.CONTENT);
235 }
236 else if (tag == HTML.Tag.COMMENT) {
237 // Don't copy COMMENTs either
238 set.addAttribute(StyleConstants.NameAttribute,
239 HTML.Tag.CONTENT);
240 set.removeAttribute(HTML.Attribute.COMMENT);
241 }
242 else if (tag == HTML.Tag.INPUT) {
243 // or INPUT either
244 set.addAttribute(StyleConstants.NameAttribute,
245 HTML.Tag.CONTENT);
246 set.removeAttribute(HTML.Tag.INPUT);
247 }
248 else if (tag instanceof HTML.UnknownTag) {
249 // Don't copy unknowns either:(
250 set.addAttribute(StyleConstants.NameAttribute,
251 HTML.Tag.CONTENT);
252 set.removeAttribute(HTML.Attribute.ENDTAG);
253 }
254 }
255 }
256
257
258 /**
259 * Creates a copy of the editor kit.
260 *
261 * @return the copy
262 */
263 public Object clone() {
264 ExtendedHTMLEditorKit o = (ExtendedHTMLEditorKit)super.clone();
265 return o;
266 }
267
268
269
270
271 /**
272 * Interface to be supported by the parser. This enables
273 * providing a different parser while reusing some of the
274 * implementation provided by this editor kit.
275 */
276
277
278 /* public class Parser {
279
280 /**
281 * Parse the given stream and drive the given callback
282 * with the results of the parse. This method should
283 * be implemented to be thread-safe.*/
284 /**
285 public void parse(Reader r, ParserCallback cb, boolean ignoreCharSet) throws IOException;
286
287 }
288
289 */
290
291
292
293
294
295
296
297
298
299
300
301
302 /* Inner Classes --------------------------------------------- */
303
304 /** Class that replaces the default ViewFactory and supports
305 * the proper rendering of both URL-based and local images.
306 */
307 public static class HTMLFactoryExtended extends HTMLFactory implements ViewFactory
308 {
309 /** Constructor
310 */
311 public HTMLFactoryExtended()
312 {
313 }
314
315 /** Method to handle IMG tags and
316 * invoke the image loader.
317 */
318 public View create(Element elem)
319 {
320 Object obj = elem.getAttributes().getAttribute(StyleConstants.NameAttribute);
321 if(obj instanceof HTML.Tag)
322 {
323 HTML.Tag tagType = (HTML.Tag)obj;
324 if(tagType == HTML.Tag.IMG)
325 {
326 return new RelativeImageView(elem);
327 }
328 }
329 return super.create(elem);
330 }
331 }
332
333 }