KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > html > HtmlEditorSupport


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20
21 package org.netbeans.modules.html;
22 import java.io.IOException JavaDoc;
23 import java.io.InputStream JavaDoc;
24 import java.io.ObjectInput JavaDoc;
25 import java.io.OutputStream JavaDoc;
26 import java.io.ByteArrayInputStream JavaDoc;
27 import java.io.SequenceInputStream JavaDoc;
28 import java.io.InputStreamReader JavaDoc;
29 import java.io.UnsupportedEncodingException JavaDoc;
30 import java.io.OutputStreamWriter JavaDoc;
31 import javax.swing.text.EditorKit JavaDoc;
32 import javax.swing.text.StyledDocument JavaDoc;
33 import javax.swing.text.BadLocationException JavaDoc;
34 import org.netbeans.api.html.lexer.HTMLTokenId;
35 import org.netbeans.api.lexer.Token;
36 import org.netbeans.api.lexer.TokenHierarchy;
37 import org.netbeans.api.lexer.TokenSequence;
38 import org.netbeans.modules.html.palette.HTMLPaletteFactory;
39 import org.netbeans.spi.palette.PaletteController;
40 import org.openide.ErrorManager;
41 import org.openide.cookies.EditCookie;
42 import org.openide.cookies.EditorCookie;
43 import org.openide.cookies.OpenCookie;
44 import org.openide.cookies.PrintCookie;
45 import org.openide.cookies.SaveCookie;
46 import org.openide.filesystems.FileObject;
47 import org.openide.filesystems.FileLock;
48 import org.openide.loaders.DataObject;
49 import org.openide.nodes.Node;
50 import org.openide.nodes.Node.Cookie;
51 import org.openide.text.CloneableEditor;
52 import org.openide.text.DataEditorSupport;
53 import org.openide.util.Lookup;
54 import org.openide.util.NbBundle;
55 import org.openide.util.lookup.AbstractLookup;
56 import org.openide.util.lookup.InstanceContent;
57 import org.openide.util.lookup.ProxyLookup;
58 import org.openide.windows.CloneableOpenSupport;
59
60
61
62 /**
63  * Editor support for HTML data objects.
64  *
65  * @author Radim Kubacki
66  * @see org.openide.text.DataEditorSupport
67  */

68 public final class HtmlEditorSupport extends DataEditorSupport implements OpenCookie, EditCookie, EditorCookie.Observable, PrintCookie {
69
70     //constants used when finding html document content type
71
private static final String JavaDoc CHARSET_DECL = "CHARSET="; //NOI18N
72
private static final String JavaDoc HEAD_END_TAG_NAME = "</HEAD>"; //NOI18N
73

74     
75     /** SaveCookie for this support instance. The cookie is adding/removing
76      * data object's cookie set depending on if modification flag was set/unset. */

77     private final SaveCookie saveCookie = new SaveCookie() {
78         /** Implements <code>SaveCookie</code> interface. */
79         public void save() throws IOException JavaDoc {
80             HtmlEditorSupport.this.saveDocument();
81             HtmlEditorSupport.this.getDataObject().setModified(false);
82         }
83     };
84     
85     
86     /** Constructor. */
87     HtmlEditorSupport(HtmlDataObject obj) {
88         super(obj, new Environment(obj));
89         
90         setMIMEType("text/html"); // NOI18N
91
}
92     
93     /**
94      * Overrides superclass method. Adds adding of save cookie if the document has been marked modified.
95      * @return true if the environment accepted being marked as modified
96      * or false if it has refused and the document should remain unmodified
97      */

98     protected boolean notifyModified () {
99         if (!super.notifyModified())
100             return false;
101
102         addSaveCookie();
103
104         return true;
105     }
106
107     /** Overrides superclass method. Adds removing of save cookie. */
108     protected void notifyUnmodified () {
109         super.notifyUnmodified();
110
111         removeSaveCookie();
112     }
113
114     /** Helper method. Adds save cookie to the data object. */
115     private void addSaveCookie() {
116         HtmlDataObject obj = (HtmlDataObject)getDataObject();
117
118         // Adds save cookie to the data object.
119
if(obj.getCookie(SaveCookie.class) == null) {
120             obj.getCookieSet0().add(saveCookie);
121             obj.setModified(true);
122         }
123     }
124
125     /** Helper method. Removes save cookie from the data object. */
126     private void removeSaveCookie() {
127         HtmlDataObject obj = (HtmlDataObject)getDataObject();
128         
129         // Remove save cookie from the data object.
130
Cookie cookie = obj.getCookie(SaveCookie.class);
131
132         if(cookie != null && cookie.equals(saveCookie)) {
133             obj.getCookieSet0().remove(saveCookie);
134             obj.setModified(false);
135         }
136     }
137
138     /** From the begining part of the stream tries to guess the correct encoding
139      * to use for loading the document into memory.
140      *
141      * @param doc the document to read into
142      * @param stream the open stream to read from
143      * @param kit the associated editor kit
144      * @throws IOException if there was a problem reading the file
145      * @throws BadLocationException should not normally be thrown
146      * @see #saveFromKitToStream
147      */

148     protected void loadFromStreamToKit(StyledDocument JavaDoc doc, InputStream JavaDoc stream, EditorKit JavaDoc kit) throws IOException JavaDoc, BadLocationException JavaDoc {
149         byte[] arr = new byte[4096];
150         int len = stream.read (arr, 0, arr.length);
151         String JavaDoc txt = new String JavaDoc (arr, 0, (len>=0)?len:0).toUpperCase();
152         // encoding
153
txt = findEncoding (txt);
154
155         // join the streams
156
if (len < arr.length) {
157             stream = new ByteArrayInputStream JavaDoc (arr, 0, len);
158         } else {
159             stream = new SequenceInputStream JavaDoc (
160                 new ByteArrayInputStream JavaDoc (arr), stream
161             );
162         }
163         
164         if (txt != null) {
165             try {
166                 InputStreamReader JavaDoc r = new InputStreamReader JavaDoc (stream, txt);
167                 kit.read (r, doc, 0);
168                 return;
169             } catch (UnsupportedEncodingException JavaDoc ex) {
170                 // ok unsupported encoding, lets go on
171
} catch (Exception JavaDoc ex) {
172                 // annotate and try default read method
173
ErrorManager.getDefault ().annotate (
174                     ex, NbBundle.getMessage(HtmlEditorSupport.class, "MSG_errorInReadingWithEnc",
175                     getDataObject().getPrimaryFile().getPath(),txt)
176                 );
177                 ErrorManager.getDefault ().notify (ErrorManager.INFORMATIONAL, ex);
178             }
179                 
180         }
181         
182         // no or bad encoding, just read the stream
183
kit.read (stream, doc, 0);
184     }
185     
186     /**
187      * @param doc the document to write from
188      * @param kit the associated editor kit
189      * @param stream the open stream to write to
190      * @throws IOException if there was a problem writing the file
191      * @throws BadLocationException should not normally be thrown
192      * @see #loadFromStreamToKit
193      */

194     protected void saveFromKitToStream(StyledDocument JavaDoc doc, EditorKit JavaDoc kit, OutputStream JavaDoc stream) throws IOException JavaDoc, BadLocationException JavaDoc {
195         int len = doc.getLength();
196         if (len > 4096) {
197             len = 4096;
198         }
199         String JavaDoc txt = doc.getText(0, len).toUpperCase();
200         // encoding
201
txt = findEncoding (txt);
202
203         if (txt != null) {
204             // try to save in that encoding
205
try {
206                 OutputStreamWriter JavaDoc w = new OutputStreamWriter JavaDoc (stream, txt);
207                 kit.write (w, doc, 0, doc.getLength());
208                 return;
209             } catch (UnsupportedEncodingException JavaDoc ex) {
210                 // ok unsupported encoding, lets go on
211
}
212         }
213
214         // no encoding or unsupported => save in regular way
215
super.saveFromKitToStream(doc, kit, stream);
216     }
217     
218     /** Tries to guess the mime type from given input stream. Tries to find
219      * <em>&lt;meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"&gt;</em>
220      * @param txt the string to search in (should be in upper case)
221      * @return the encoding or null if no has been found
222      */

223     private static String JavaDoc findEncoding (String JavaDoc txt) {
224         int headEndOffset = txt.indexOf (HEAD_END_TAG_NAME); // NOI18N
225
headEndOffset = headEndOffset == -1 ? txt.indexOf(HEAD_END_TAG_NAME.toLowerCase()) : headEndOffset;
226         
227         if (headEndOffset == -1){
228             return null;
229         }
230         
231         TokenHierarchy hi = TokenHierarchy.create(txt, HTMLTokenId.language());
232         TokenSequence ts = hi.tokenSequence();
233         ts.moveStart();
234         while(ts.moveNext()) {
235             Token token = ts.token();
236             
237             //test we do not overlap </head>
238
if(token.offset(hi) >= headEndOffset) {
239                 break;
240             }
241             
242             if(token.id() == HTMLTokenId.VALUE) {
243                 String JavaDoc tokenImage = token.text().toString();
244                 int charsetOffset = tokenImage.indexOf(CHARSET_DECL);
245                 charsetOffset = charsetOffset == -1 ? tokenImage.indexOf(CHARSET_DECL.toLowerCase()) : charsetOffset;
246                 
247                 int charsetEndOffset = charsetOffset + CHARSET_DECL.length();
248                 if (charsetOffset != -1){
249                     int endOffset = tokenImage.indexOf('"', charsetEndOffset);
250                     
251                     if (endOffset == -1){
252                         endOffset = tokenImage.indexOf('\'', charsetEndOffset);
253                     }
254                     
255                     if (endOffset == -1){
256                         endOffset = tokenImage.indexOf(';', charsetEndOffset);
257                     }
258                     
259                     if (endOffset == -1){
260                         return null;
261                     }
262                     
263                     String JavaDoc encoding = tokenImage.substring(charsetEndOffset, endOffset);
264                     return encoding;
265                 }
266             }
267         }
268         
269         return null; // no token in token sequence or encoding not found
270
}
271     
272     /** Nested class. Environment for this support. Extends <code>DataEditorSupport.Env</code> abstract class. */
273     private static class Environment extends DataEditorSupport.Env {
274
275         private static final long serialVersionUID = 3035543168452715818L;
276         
277         /** Constructor. */
278         public Environment(HtmlDataObject obj) {
279             super(obj);
280         }
281
282         
283         /** Implements abstract superclass method. */
284         protected FileObject getFile() {
285             return getDataObject().getPrimaryFile();
286         }
287
288         /** Implements abstract superclass method.*/
289         protected FileLock takeLock() throws IOException JavaDoc {
290             return ((HtmlDataObject)getDataObject()).getPrimaryEntry().takeLock();
291         }
292
293         /**
294          * Overrides superclass method.
295          * @return text editor support (instance of enclosing class)
296          */

297         public CloneableOpenSupport findCloneableOpenSupport() {
298             return (HtmlEditorSupport)getDataObject().getCookie(HtmlEditorSupport.class);
299         }
300     } // End of nested Environment class.
301

302     
303     /** A method to create a new component. Overridden in subclasses.
304      * @return the {@link HtmlEditor} for this support
305      */

306     protected CloneableEditor createCloneableEditor() {
307         return new HtmlEditor(this);
308     }
309     
310     public static class HtmlEditor extends CloneableEditor {
311         
312         public HtmlEditor() {
313         }
314         
315         void associatePalette(HtmlEditorSupport s) {
316             
317             Node nodes[] = { s.getDataObject().getNodeDelegate() };
318             InstanceContent instanceContent = new InstanceContent();
319             associateLookup(new ProxyLookup(new Lookup[] { new AbstractLookup(instanceContent), nodes[0].getLookup()}));
320             instanceContent.add(getActionMap());
321             
322             setActivatedNodes(nodes);
323
324             DataObject dataObject = s.getDataObject();
325             if (dataObject instanceof HtmlDataObject) {
326                 try {
327                     PaletteController pc = HTMLPaletteFactory.getPalette();
328                     instanceContent.add(pc);
329                 }
330                 catch (IOException JavaDoc ioe) {
331                     //TODO exception handling
332
ioe.printStackTrace();
333                 }
334             }
335         }
336         
337         /** Creates new editor */
338         public HtmlEditor(HtmlEditorSupport s) {
339             super(s);
340             initialize();
341         }
342
343         private void initialize() {
344             associatePalette((HtmlEditorSupport)cloneableEditorSupport());
345         }
346         
347         public void readExternal(ObjectInput JavaDoc in) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
348             super.readExternal(in);
349             initialize();
350         }
351         
352     }
353     
354 }
355
Popular Tags