KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > xml > core > text > TextEditorSupport


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 package org.netbeans.modules.xml.core.text;
20
21 import java.io.*;
22 import java.net.URL JavaDoc;
23 import java.awt.event.*;
24 import java.text.*;
25 import java.util.Enumeration JavaDoc;
26 import java.lang.ref.WeakReference JavaDoc;
27 import java.beans.PropertyChangeListener JavaDoc;
28 import java.beans.PropertyChangeEvent JavaDoc;
29
30 import javax.swing.Timer JavaDoc;
31 import javax.swing.JEditorPane JavaDoc;
32 import javax.swing.event.*;
33 import javax.swing.text.*;
34 import javax.swing.JViewport JavaDoc;
35
36 import org.openide.*;
37 import org.openide.awt.StatusDisplayer;
38 import org.openide.text.*;
39 import org.openide.util.*;
40 import org.openide.windows.CloneableTopComponent;
41 import org.openide.windows.CloneableOpenSupport;
42 import org.openide.windows.Workspace;
43 import org.openide.windows.Mode;
44 import org.openide.loaders.*;
45 import org.openide.cookies.*;
46 import org.openide.nodes.*;
47 import org.openide.filesystems.FileObject;
48 import org.openide.filesystems.FileLock;
49
50 import org.netbeans.modules.xml.core.*;
51 import org.netbeans.modules.xml.core.lib.*;
52 import org.netbeans.modules.xml.core.sync.*;
53 import org.netbeans.modules.xml.core.cookies.*;
54
55 import org.netbeans.modules.xml.core.settings.CoreSettings;
56
57 /**
58  * Text editor support that handles I/O encoding and sync with tree.
59  * There are two timers a long time and a short time. The long time
60  * updates tree even in middle of writing text. The short time is restarted
61  * at every text change..
62  * <p>
63  * Listens for: text document change (edit), timers and document status change (loading).
64  */

65 public class TextEditorSupport extends DataEditorSupport implements EditorCookie.Observable, EditCookie, CloseCookie, PrintCookie {
66 // ToDo:
67
// + extend CloneableEditorSupport instead of DataEditorSupport which is associated with DataObject
68

69     /**
70      * Swings document property added by this support.
71      */

72     public static final String JavaDoc PROP_DOCUMENT_URL = "doc-url";
73     
74     
75     /** XML Settings */
76     static final CoreSettings settings = CoreSettings.getDefault();
77     
78     /** Timer which countdowns the auto-reparsing time. */
79     private Timer JavaDoc timer;
80     
81     /** Used as lock object in close and openCloneableTopComponent. */
82     private static java.awt.Container JavaDoc awtLock;
83     
84     
85     private Representation rep; //it is my representation
86

87     
88     //
89
// init
90
//
91

92     /** public jsu for backward compatibility purposes. */
93     protected TextEditorSupport(XMLDataObjectLook xmlDO, Env env, String JavaDoc mime_type) {
94         super((DataObject)xmlDO, env);
95         
96         setMIMEType(mime_type);
97         
98         initTimer();
99         
100         initListeners();
101         
102         
103         //??? why it is not under text module control?
104
// it must be more lazy, why we must open document
105
// it looks that Document's StreamDescriptionProperty fits
106
// try {
107
// if (xmlDO instanceof DataObject) {
108
// DataObject dobj = (DataObject) xmlDO; // we must cast, we cannot as for cookie in cookie <init> that is produced by factory
109
// FileObject fo = dobj.getPrimaryFile();
110
// URL url = fo.getURL();
111
// String system = url.toExternalForm();
112
// openDocument().putProperty(PROP_DOCUMENT_URL, system); //openit
113
// } else {
114
// new RuntimeException("DO expected").printStackTrace();
115
// }
116
// } catch (Exception ex) {
117
// // just let property undefined
118
// ex.printStackTrace();
119
// }
120
}
121     
122     /** public jsu for backward compatibility purposes. */
123     public TextEditorSupport(XMLDataObjectLook xmlDO, String JavaDoc mime_type) {
124         this(xmlDO, new Env(xmlDO), mime_type);
125     }
126     
127     
128     //
129
// timer
130
//
131

132     /**
133      * Initialize timers and handle their ticks.
134      */

135     private void initTimer() {
136         timer = new Timer JavaDoc(0, new java.awt.event.ActionListener JavaDoc() {
137             // we are called from the AWT thread so put itno other one
138
public void actionPerformed(java.awt.event.ActionEvent JavaDoc e) {
139                 if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("$$ TextEditorSupport::initTimer::actionPerformed: event = " + e);
140                 
141                 RequestProcessor.postRequest( new Runnable JavaDoc() {
142                     public void run() {
143                         syncDocument(false);
144                     }
145                 });
146             }
147         });
148         
149         timer.setInitialDelay(settings.getAutoParsingDelay());
150         timer.setRepeats(false);
151     }
152     
153     
154     /*
155      * Add listeners at Document and document memory status (loading).
156      */

157     private void initListeners() {
158         
159         // create document listener
160

161         final DocumentListener docListener = new DocumentListener() {
162             public void insertUpdate(DocumentEvent e) {
163                 if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("** TextEditorSupport::DocumentListener::insertUpdate: event = " + e);
164                 
165                 restartTime();
166             }
167             
168             public void changedUpdate(DocumentEvent e) {
169                 if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("** TextEditorSupport::DocumentListener::changedUpdate: event = " + e);
170                 
171                 // not interested in attribute changes
172
}
173             
174             public void removeUpdate(DocumentEvent e) {
175                 if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("** TextEditorSupport::DocumentListener::removeUpdate: event = " + e);
176                 
177                 restartTime();
178             }
179             
180             private void restartTime() {
181                 if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("** TextEditorSupport::DocumentListener::restartTime: isInSync = " +
182                         getXMLDataObjectLook().getSyncInterface().isInSync());
183                 
184                 if (getXMLDataObjectLook().getSyncInterface().isInSync()) {
185                     return;
186                 }
187                 restartTimer(false);
188             }
189         };
190         
191         // listen for document loading then register to it the docListener as weak
192

193         addChangeListener(new ChangeListener JavaDoc() {
194             public void stateChanged(ChangeEvent JavaDoc evt) {
195                 
196                 if (isDocumentLoaded()) {
197                     
198                     Document doc = getDocument();
199                     // when the document is not yet loaded, do nothing
200
if (doc == null)
201                         return;
202                     doc.addDocumentListener(WeakListeners.document(docListener, doc));
203                     
204                     if (rep == null) {
205                         XMLDataObjectLook dobj = (XMLDataObjectLook) getDataObject();
206                         Synchronizator sync = dobj.getSyncInterface();
207                         
208                         //!!! What does this hardcoding mean???
209
//[DEPENDENCY] it introduces really ugly core to it's client dependencies!!!
210
if (dobj instanceof org.netbeans.modules.xml.core.XMLDataObject) {
211                             rep = new XMLTextRepresentation(TextEditorSupport.this, sync);
212                         } else if (dobj instanceof DTDDataObject) {
213                             rep = new DTDTextRepresentation(TextEditorSupport.this, sync);
214                         } else if (dobj instanceof EntityDataObject) {
215                             rep = new EntityTextRepresentation(TextEditorSupport.this, sync);
216                         }
217                         
218                         if (rep != null) {
219                             sync.addRepresentation(rep);
220                         }
221                     }
222                     
223 // } else { // moved to notifyClosed
224

225 // XMLDataObjectLook dobj = (XMLDataObjectLook) getDataObject();
226
// Synchronizator sync = dobj.getSyncInterface();
227
// rep = null;
228
// sync.removeRepresentation(rep);
229

230                 }
231             }
232         });
233         
234     }
235     
236     
237     /**
238      * It simply calls super.notifyClosed() for all instances except
239      * TextEditorSupport.class == this.getClass().
240      */

241     protected void notifyClosed() {
242         super.notifyClosed();
243         
244         // #15756 following code handles synchronization on text editor closing only!
245
if (this.getClass() != TextEditorSupport.class) return;
246         
247         XMLDataObjectLook dobj = (XMLDataObjectLook) getDataObject();
248         Synchronizator sync = dobj.getSyncInterface();
249         Representation oldRep = rep;
250         rep = null;
251         if ( oldRep != null ) { // because of remove modified document
252
sync.removeRepresentation(oldRep);
253         }
254         
255 // if ( isModified() ) { // possible way to remove needless closeDocument followed by open
256
// Task reload = reloadDocument();
257
// reload.waitFinished();
258
// }
259
}
260     
261     /**
262      */

263     Env getEnv() {
264         return (Env) env;
265     }
266     
267     
268     /**
269      */

270     protected XMLDataObjectLook getXMLDataObjectLook() {
271         return getEnv().getXMLDataObjectLook();
272     }
273     
274     /*
275      * Update presence of SaveCookie on first keystroke.
276      */

277     protected boolean notifyModified() {
278         if (getEnv().isModified()) {
279             return true;
280         }
281         if (!!! super.notifyModified()) {
282             return false;
283         } else {
284             CookieManagerCookie manager = getEnv().getXMLDataObjectLook().getCookieManager();
285             manager.addCookie(getEnv());
286             return true;
287         }
288     }
289     
290     /*
291      * Update presence of SaveCookie after save.
292      */

293     protected void notifyUnmodified() {
294         if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("Notifing unmodified"); // NOI18N
295

296         super.notifyUnmodified();
297         CookieManagerCookie manager = getEnv().getXMLDataObjectLook().getCookieManager();
298         manager.removeCookie(getEnv());
299     }
300     
301 //~~~~~~~~~~~~~~~~~~~~~~~~~ I/O ENCODING HANDLING ~~~~~~~~~~~~~~~~~~~~~~~~~~~
302

303     //indicates than document has wrong encoding @see #edit
304
private volatile boolean encodingErr = false;
305     
306     /** Read the file from the stream, detect right encoding.
307      */

308     protected void loadFromStreamToKit(StyledDocument doc, InputStream in, EditorKit kit) throws IOException, BadLocationException {
309         // predetect it to get optimalized XmlReader if utf-8
310
String JavaDoc enc = EncodingHelper.detectEncoding(in);
311         if (enc == null) {
312             enc = "UTF8"; //!!! // NOI18N
313
}
314         try {
315             Reader reader = new InputStreamReader(in, enc);
316             kit.read(reader, doc, 0);
317         } catch (CharConversionException ex) {
318             if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("\n!!! TextEditorSupport.loadFromStreamToKit: enc = '" + enc + "'", ex);
319             
320             encodingErr = true;
321         } catch (UnsupportedEncodingException ex) {
322             if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("\n!!! TextEditorSupport.loadFromStreamToKit: enc = '" + enc + "'", ex);
323             
324             encodingErr = true;
325         }
326         
327     }
328     
329     /** Store the document in proper encoding.
330      */

331     protected void saveFromKitToStream(StyledDocument doc, EditorKit kit, OutputStream out) throws IOException, BadLocationException {
332         String JavaDoc enc = EncodingHelper.detectEncoding(doc);
333         if (enc == null) {
334             enc = "UTF8"; //!!! // NOI18N
335
}
336         try {
337             
338             if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("Saving using encoding");//, new RuntimeException (enc)); // NOI18N
339
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("!!! TextEditorSupport::saveFromKitToStream: enc = " + enc);
340             
341             //test encoding on dummy stream
342
new OutputStreamWriter(new ByteArrayOutputStream(1), enc);
343             
344             if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("!!! ::saveFromKitToStream: after first test -> OK");
345             
346             Writer writer = new OutputStreamWriter(out, enc);
347             
348             if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("!!! ::saveFromKitToStream: writer = " + writer);
349             
350             kit.write(writer, doc, 0, doc.getLength());
351         } catch (UnsupportedEncodingException ex) {
352             //!!! just write nothing //?? save say as UTF-8
353

354             ErrorManager emgr = ErrorManager.getDefault();
355             IOException ioex = new IOException("Unsupported encoding " + enc); // NOI18N
356
emgr.annotate(ioex, Util.THIS.getString("MSG_unsupported_encoding", enc));
357             throw ioex;
358         }
359     }
360     
361     /*
362      * Save document using encoding declared in XML prolog if possible otherwise
363      * at UTF-8 (in such case it updates the prolog).
364      */

365     public void saveDocument() throws IOException {
366         if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("saveDocument()..."); // NOI18N
367

368         final StyledDocument doc = getDocument();
369         
370         String JavaDoc enc = EncodingHelper.detectEncoding(doc);
371         
372         if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("!!! TextEditorSupport::saveDocument: enc = " + enc);
373         
374         if (enc == null) {
375             enc = "UTF8"; //!!! // NOI18N
376
}
377         
378         try {
379             //test encoding on dummy stream
380
new OutputStreamWriter(new ByteArrayOutputStream(1), enc);
381             if (!checkCharsetConversion(Convertors.java2iana(enc))){
382                 if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("Let unsaved."); // NOI18N
383
return;
384             }
385             super.saveDocument();
386             //moved from Env.save()
387
getDataObject().setModified(false);
388             getXMLDataObjectLook().getSyncInterface().representationChanged(Document.class);
389             
390         } catch (UnsupportedEncodingException ex) {
391             
392             // ask user what next?
393

394             NotifyDescriptor descriptor = new NotifyDescriptor.Confirmation(java.text.MessageFormat.format(Util.THIS.getString("TEXT_SAVE_AS_UTF"), new Object JavaDoc[] {enc}));
395             Object JavaDoc res = DialogDisplayer.getDefault().notify(descriptor);
396             
397             if (res.equals(NotifyDescriptor.YES_OPTION)) {
398                 
399                 // update prolog to new valid encoding
400

401                 try {
402                     final int MAX_PROLOG = 1000;
403                     int maxPrologLen = Math.min(MAX_PROLOG, doc.getLength());
404                     final char prolog[] = doc.getText(0, maxPrologLen).toCharArray();
405                     int prologLen = 0; // actual prolog length
406

407                     //parse prolog and get prolog end
408
if (prolog[0] == '<' && prolog[1] == '?' && prolog[2] == 'x') {
409                         
410                         // look for delimitting ?>
411
for (int i = 3; i<maxPrologLen; i++) {
412                             if (prolog[i] == '?' && prolog[i+1] == '>') {
413                                 prologLen = i + 1;
414                                 break;
415                             }
416                         }
417                     }
418                     
419                     final int passPrologLen = prologLen;
420                     
421                     Runnable JavaDoc edit = new Runnable JavaDoc() {
422                         public void run() {
423                             try {
424                                 
425                                 doc.remove(0, passPrologLen + 1); // +1 it removes exclusive
426
doc.insertString(0, "<?xml version='1.0' encoding='UTF-8' ?> \n<!-- was: " + new String JavaDoc(prolog, 0, passPrologLen + 1) + " -->", null); // NOI18N
427

428                             } catch (BadLocationException e) {
429                                 if (System.getProperty("netbeans.debug.exceptions") != null) // NOI18N
430
e.printStackTrace();
431                             }
432                         }
433                     };
434                     
435                     NbDocument.runAtomic(doc, edit);
436                     
437                     super.saveDocument();
438                     //moved from Env.save()
439
getDataObject().setModified(false);
440                     getXMLDataObjectLook().getSyncInterface().representationChanged(Document.class);
441                     
442                     if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("Saved."); // NOI18N
443

444                 } catch (BadLocationException lex) {
445                     
446                     ErrorManager.getDefault().notify(lex);
447                 }
448                 
449             } else { // NotifyDescriptor != YES_OPTION
450
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("Let unsaved."); // NOI18N
451

452                 return;
453             }
454         } // of catch UnsupportedEncodingException
455
}
456     
457     
458     private boolean checkCharsetConversion(String JavaDoc encoding) /*throws UnsupportedEncodingException*/{
459         boolean value = true;
460         try {
461             java.nio.charset.CharsetEncoder JavaDoc coder = java.nio.charset.Charset.forName(encoding).newEncoder();
462             if (!coder.canEncode(getDocument().getText(0, getDocument().getLength()))){
463                 NotifyDescriptor nd = new NotifyDescriptor.Confirmation(
464                         NbBundle.getMessage(TextEditorSupport.class, "MSG_BadCharConversion", //NOI18N
465
new Object JavaDoc [] { getDataObject().getPrimaryFile().getNameExt(),
466                                 encoding}),
467                                         NotifyDescriptor.YES_NO_OPTION,
468                                         NotifyDescriptor.WARNING_MESSAGE);
469                                 nd.setValue(NotifyDescriptor.NO_OPTION);
470                                 DialogDisplayer.getDefault().notify(nd);
471                                 if(nd.getValue() != NotifyDescriptor.YES_OPTION)
472                                     value = false;
473             }
474         } catch (javax.swing.text.BadLocationException JavaDoc e){
475             ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
476         }
477         /*catch (java.nio.charset.UnsupportedCharsetException e){
478             throw new UnsupportedEncodingException();
479         }*/

480         return value;
481     }
482 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SYNC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
483

484     /**
485      * TEXT changed -> update TREE.
486      */

487     protected void syncDocument(boolean fromFocus) {
488         if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("@@ TextEditorSupport::syncDocument: fromFocus = " + fromFocus);
489         if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("@@ ::syncDocument: timer.isRunning = " + timer.isRunning());
490         
491         if (fromFocus && !timer.isRunning())
492             return;
493         if (timer.isRunning())
494             timer.stop();
495         
496         XMLDataObjectLook sync = getXMLDataObjectLook();
497         if (sync != null) { // && isModified()) {
498
sync.getSyncInterface().representationChanged(Document.class);
499         }
500         
501     }
502     
503     
504     /** Restart the timer which starts the parser after the specified delay.
505      * @param onlyIfRunning Restarts the timer only if it is already running
506      */

507     void restartTimer(boolean onlyIfRunning) {
508         if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("## TextEditorSupport::restartTimer: onlyIfRunning = " + onlyIfRunning);
509         if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("## ::restartTimer: timer.isRunning = " + timer.isRunning());
510         
511         if (onlyIfRunning && !timer.isRunning())
512             return;
513         
514         int delay = settings.getAutoParsingDelay();
515         if (delay > 0) {
516             timer.setInitialDelay(delay);
517             timer.restart();
518         }
519     }
520     
521 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
522

523     /*
524      * An entry point via EditCookie.
525      * Delegate to <code>openDocument()</code>.
526      */

527     public final void edit() {
528         
529         try {
530             openDocument(); //use sync version of call - prepare encodingErr
531
if (encodingErr) {
532                 String JavaDoc pattern = Util.THIS.getString("TEXT_WRONG_ENCODING");
533                 String JavaDoc msg = MessageFormat.format(pattern, new Object JavaDoc[] { getDataObject().getPrimaryFile().toString() /*compatibleEntry.getFile().toString()*/});
534                 DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(msg, NotifyDescriptor.ERROR_MESSAGE));
535                 
536             } else {
537                 Mutex.EVENT.writeAccess(new Runnable JavaDoc() {
538                     public void run() {
539                         CloneableTopComponent editor = openCloneableEditor();
540                         editor.requestActive();
541                     }
542                 });
543             }
544         } catch (UserQuestionException e){ //this is a hack due to the issue #50701
545
open();
546             if(isDocumentLoaded()) {
547                 if (encodingErr) {
548                     String JavaDoc pattern = Util.THIS.getString("TEXT_WRONG_ENCODING");
549                     String JavaDoc msg = MessageFormat.format(pattern, new Object JavaDoc[] { getDataObject().getPrimaryFile().toString() /*compatibleEntry.getFile().toString()*/});
550                     DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(msg, NotifyDescriptor.ERROR_MESSAGE));
551                     
552                 } else {
553                     Mutex.EVENT.writeAccess(new Runnable JavaDoc() {
554                         public void run() {
555                             CloneableTopComponent editor = openCloneableEditor();
556                             editor.requestActive();
557                         }
558                     });
559                 }
560             }
561         } catch (IOException ex) {
562             String JavaDoc pattern = Util.THIS.getString("TEXT_LOADING_ERROR");
563             String JavaDoc msg = MessageFormat.format(pattern, new Object JavaDoc[] { getDataObject().getPrimaryFile().toString() /*compatibleEntry.getFile().toString()*/});
564             DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(msg, NotifyDescriptor.ERROR_MESSAGE));
565         }
566         
567     }
568     
569     
570     /*
571      * Simply open for an cloneable editor. It at first tries to locate
572      * existing component in <code>allEditors</code> then if it fails create new one
573      * and registers it with <code>allEditors>/code>.
574      */

575     protected final CloneableEditor openCloneableEditor() {
576         
577         CloneableEditor ret = null;
578         
579         synchronized (getLock()) {
580             
581             String JavaDoc msg = messageOpening();
582             if (msg != null) {
583                 StatusDisplayer.getDefault().setStatusText(msg);
584             }
585             
586             Enumeration JavaDoc en = allEditors.getComponents();
587             while ( en.hasMoreElements() ) {
588                 CloneableTopComponent editor = (CloneableTopComponent)en.nextElement();
589                 if ( editor instanceof CloneableEditor ) {
590                     editor.open();
591                     ret = (CloneableEditor) editor;
592                 }
593             }
594             
595             // no opened editor, create a new one
596

597             if (ret == null) {
598                 CloneableEditor editor = (CloneableEditor)createCloneableTopComponent(); // this is important -- see final createCloneableTopComponent
599
editor.setReference(allEditors);
600                 editor.open();
601                 ret = editor;
602             }
603             
604             msg = messageOpened();
605             if (msg == null) {
606                 msg = ""; // NOI18N
607
}
608             StatusDisplayer.getDefault().setStatusText(msg);
609             
610             return ret;
611         }
612     }
613     
614     /**
615      * Creates lock object used in close and openCloneableTopComponent.
616      * @return never null
617      */

618     protected Object JavaDoc getLock() {
619         if (awtLock == null) {
620             awtLock = new java.awt.Container JavaDoc();
621         }
622         return awtLock.getTreeLock();
623     }
624     
625     /*
626      * @return component visualizing this support.
627      */

628     protected CloneableEditor createCloneableEditor() {
629         return new TextEditorComponent(this);
630     }
631     
632     // This must call super createCloneableTopComponent because it prepare document, create cloneable editor and initialize it. See super.
633
protected final CloneableTopComponent createCloneableTopComponent() {
634         return super.createCloneableTopComponent(); // creates CloneableEditor (calling createCloneableEditor)
635
}
636     
637     /**
638      */

639     public static final TextEditorSupportFactory findEditorSupportFactory(XMLDataObjectLook xmlDO, String JavaDoc mime) {
640         return new TextEditorSupportFactory(xmlDO, mime);
641     }
642     
643     //
644
// class Env
645
//
646

647     /**
648      *
649      */

650     protected static class Env extends DataEditorSupport.Env implements SaveCookie {
651         
652         /** Serial Version UID */
653         private static final long serialVersionUID=-5285524519399090028L;
654         
655         /** */
656         public Env(XMLDataObjectLook obj) {
657             super((DataObject)obj);
658         }
659         
660         /**
661          */

662         protected XMLDataObjectLook getXMLDataObjectLook() {
663             return (XMLDataObjectLook) getDataObject();
664         }
665         
666         /**
667          */

668         protected FileObject getFile() {
669             return getDataObject().getPrimaryFile();
670         }
671         
672         /**
673          */

674         protected FileLock takeLock() throws IOException {
675             return ((MultiDataObject)getDataObject()).getPrimaryEntry().takeLock();
676         }
677         
678         
679         /**
680          */

681         public synchronized void save() throws IOException {
682             findTextEditorSupport().saveDocument();
683         }
684         
685         /**
686          */

687         public CloneableOpenSupport findCloneableOpenSupport() {
688             return findTextEditorSupport();
689         }
690         
691         /**
692          */

693         public TextEditorSupport findTextEditorSupport() {
694             return (TextEditorSupport) getDataObject().getCookie(EditCookie.class);
695         }
696         
697         // copy pasted, do not get it
698
public void propertyChange(PropertyChangeEvent JavaDoc ev) {
699             if (DataObject.PROP_PRIMARY_FILE.equals(ev.getPropertyName())) {
700                 changeFile();
701             }
702             super.propertyChange(ev);
703         }
704         
705         
706     } // end: class Env
707

708     
709     //
710
// class TextEditorSupportFactory
711
//
712

713     /**
714      *
715      */

716     public static class TextEditorSupportFactory implements CookieSet.Factory {
717         /** */
718         private WeakReference JavaDoc editorRef;
719         /** */
720         private final XMLDataObjectLook dataObject; // used while creating the editor
721
/** */
722         private final String JavaDoc mime; // used while creating the editor
723

724         //
725
// init
726
//
727

728         /** Create new TextEditorSupportFactory. */
729         public TextEditorSupportFactory(XMLDataObjectLook dobj, String JavaDoc mime) {
730             this.dataObject = dobj;
731             this.mime = mime;
732         }
733         
734         
735         /**
736          */

737         protected Class JavaDoc[] supportedCookies() {
738             return new Class JavaDoc[] { EditorCookie.class,
739                     EditorCookie.Observable.class,
740                     EditCookie.class,
741                     CloseCookie.class,
742                     PrintCookie.class,
743             };
744         }
745         
746         /**
747          */

748         public final void registerCookies(CookieSet cookieSet) {
749             Class JavaDoc[] supportedCookies = supportedCookies();
750             for (int i = 0; i < supportedCookies.length; i++) {
751                 cookieSet.add(supportedCookies[i], this);
752             }
753         }
754         
755         /** Creates a Node.Cookie of given class. The method
756          * may be called more than once.
757          */

758         public final Node.Cookie createCookie(Class JavaDoc klass) {
759             Class JavaDoc[] supportedCookies = supportedCookies();
760             for (int i = 0; i < supportedCookies.length; i++) {
761                 if ( supportedCookies[i].isAssignableFrom(klass) ) {
762                     return createEditor();
763                 }
764             }
765             return null;
766         }
767         
768         /**
769          */

770         private final synchronized TextEditorSupport createEditor() { // atomic test and set
771
TextEditorSupport editorSupport = null;
772             
773             if ( editorRef != null ) {
774                 editorSupport = (TextEditorSupport) editorRef.get();
775             }
776             if ( editorSupport == null ) {
777                 editorSupport = prepareEditor();
778                 editorRef = new WeakReference JavaDoc(editorSupport);
779             }
780             
781             return editorSupport;
782         }
783         
784         /**
785          */

786         protected TextEditorSupport prepareEditor() {
787             if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("Initializing TextEditorSupport ..."); // NOI18N
788

789             return new TextEditorSupport(getDataObject(), getMIMEType());
790         }
791         
792         /**
793          */

794         protected final XMLDataObjectLook getDataObject() {
795             return dataObject;
796         }
797         
798         /**
799          */

800         protected final String JavaDoc getMIMEType() {
801             return mime;
802         }
803         
804     } // end of class TextEditorSupportFactory
805

806 }
807
Popular Tags