KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > openfile > DefaultOpenFileImpl


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.openfile;
20
21 import java.awt.Container JavaDoc;
22 import java.awt.EventQueue JavaDoc;
23 import java.awt.event.ActionEvent JavaDoc;
24 import java.beans.PropertyChangeEvent JavaDoc;
25 import java.beans.PropertyChangeListener JavaDoc;
26 import java.io.IOException JavaDoc;
27 import javax.swing.Action JavaDoc;
28 import javax.swing.JEditorPane JavaDoc;
29 import javax.swing.SwingUtilities JavaDoc;
30 import javax.swing.text.Element JavaDoc;
31 import javax.swing.text.StyledDocument JavaDoc;
32 import org.openide.DialogDisplayer;
33 import org.openide.ErrorManager;
34 import org.openide.NotifyDescriptor;
35 import org.openide.actions.FileSystemAction;
36 import org.openide.actions.ToolsAction;
37 import org.openide.awt.StatusDisplayer;
38 import org.openide.cookies.EditCookie;
39 import org.openide.cookies.EditorCookie;
40 import org.openide.cookies.OpenCookie;
41 import org.openide.cookies.ViewCookie;
42 import org.openide.filesystems.FileObject;
43 import org.openide.loaders.DataObject;
44 import org.openide.loaders.DataObjectNotFoundException;
45 import org.openide.nodes.Node;
46 import org.openide.nodes.NodeOperation;
47 import org.openide.text.NbDocument;
48 import org.openide.util.NbBundle;
49 import org.openide.windows.TopComponent;
50
51
52 /**
53  * Opens files when requested. Main functionality.
54  *
55  * @author Jaroslav Tulach, Jesse Glick, Marian Petras, David Konecny
56  */

57 public class DefaultOpenFileImpl implements OpenFileImpl, Runnable JavaDoc {
58     
59     /** extenstion for .java files (including the dot) */
60     static final String JavaDoc JAVA_EXT = ".JAVA"; //NOI18N
61
/** extension for .txt files (including the dot) */
62     static final String JavaDoc TXT_EXT = ".TXT"; //NOI18N
63
/**
64      * if opening file using non-observable <code>EditorCookie</code>,
65      * how long should we wait (in milliseconds) between tries?
66      *
67      * @see #openDocAtLine
68      */

69     private static final int OPEN_EDITOR_WAIT_PERIOD_MS = 100;
70     /**
71      * if opening file using non-observable <code>EditorCookie</code>,
72      * how long should we wait (in milliseconds) in total before giving up?
73      *
74      * @see #openDocAtLine
75      */

76     private static final int OPEN_EDITOR_TOTAL_TIMEOUT_MS = 1000;
77
78     private static final String JavaDoc ZIP_EXT = "zip"; //NOI18N
79
private static final String JavaDoc JAR_EXT = "jar"; //NOI18N
80
private static final String JavaDoc WAR_EXT = "war"; //NOI18N
81

82     /**
83      * parameter of this <code>Runnable</code>
84      * - file to open
85      */

86     private final FileObject fileObject;
87     /**
88      * parameter of this <code>Runnable</code>
89      * - line number to open the {@link #fileObject file} at, or <code>-1</code>
90      * to ignore
91      */

92     private final int line;
93     
94     /**
95      * Creates an instance of this class.
96      * It is used only as an instance of <code>Runnable</code>
97      * used for rescheduling to the AWT thread.
98      * The arguments are stored to local variables and when the
99      * <code>run()</code> method gets executed (in the AWT thread),
100      * they are passed to the <code>open(...)</code> method.
101      *
102      * @param file file to open (must exist)
103      * @param line line number to try to open to (starting at zero),
104      * or <code>-1</code> to ignore
105      * @param waiter double-callback or <code>null</code>
106      */

107     private DefaultOpenFileImpl(FileObject fileObject,
108                                 int line) {
109         this.fileObject = fileObject;
110         this.line = line;
111     }
112
113     /** Creates a new instance of OpenFileImpl */
114     public DefaultOpenFileImpl() {
115         
116         /* These fields are not used in the default instance. */
117         this.fileObject = null;
118         this.line = -1;
119     }
120     
121     /**
122      * Sets the specified text into the status line.
123      *
124      * @param text text to be displayed
125      */

126     protected final void setStatusLine(String JavaDoc text) {
127         StatusDisplayer.getDefault().setStatusText(text);
128     }
129     
130     /**
131      * Displays a dialog that the file cannot be open.
132      * This method is to be used in cases that the file was open via
133      * the Open File Server. The message also informs that
134      * the launcher will be notified as if the file
135      * was closed immediately.
136      *
137      * @param fileName name of file that could not be opened
138      */

139     protected void notifyCannotOpen(String JavaDoc fileName) {
140         assert EventQueue.isDispatchThread();
141         
142         DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(
143                 NbBundle.getMessage(DefaultOpenFileImpl.class,
144                                     "MSG_cannotOpenWillClose", //NOI18N
145
fileName)));
146     }
147     
148     /**
149      * Opens an editor using <code>EditorCookie</code>.
150      * If non-negative line number is passed, it also places cursor at the given
151      * line.
152      *
153      * @param cookie cookie to use for opening an editor
154      * @param observable whether the cookie is
155      * <code>EditorCookie.Observable</code>
156      * @param line line number to place cursor to (starting at <code>0</code>)
157      * @return <code>true</code> if the cookie was successfully activated,
158      * <code>false</code> if some error occured
159      */

160     private boolean openEditor(final EditorCookie editorCookie,
161                                final int line) {
162         assert EventQueue.isDispatchThread();
163         
164         /* if the editor is already open, just set the cursor and activate it */
165         JEditorPane JavaDoc[] openPanes = editorCookie.getOpenedPanes();
166         if (openPanes != null) {
167             if (line >= 0) {
168                 int cursorOffset = getCursorOffset(editorCookie.getDocument(),
169                                                    line);
170                 openPanes[0].setCaretPosition(cursorOffset);
171             }
172             Container JavaDoc c = SwingUtilities.getAncestorOfClass(TopComponent.class,
173                                                             openPanes[0]);
174             assert c != null;
175             
176             final TopComponent tc = (TopComponent) c;
177             EventQueue.invokeLater(new Runnable JavaDoc() {
178                 public void run() {
179                     tc.requestActive();
180                 }
181             });
182             return true;
183         }
184         
185         /* get the document: */
186         final StyledDocument JavaDoc doc;
187         try {
188             doc = editorCookie.openDocument();
189         } catch (IOException JavaDoc ex) {
190             String JavaDoc msg = NbBundle.getMessage(
191                     DefaultOpenFileImpl.class,
192                     "MSG_cannotOpenWillClose"); //NOI18N
193
ErrorManager.getDefault().notify(
194                     ErrorManager.EXCEPTION,
195                     ErrorManager.getDefault().annotate(ex, msg));
196             return false;
197         }
198
199         if (line < 0) {
200             editorCookie.open();
201             
202             /*
203              * editorCookie.open() may return before the editor is actually
204              * open. But since the document was successfully open,
205              * the editor should be opened quite quickly and no problem
206              * should occur.
207              */

208         } else {
209             openDocAtLine(editorCookie, doc, line);
210         }
211         return true;
212     }
213     
214     /**
215      * Opens a document in the editor at a given line.
216      * This method is used in the case that the editor is not opened yet
217      * (<code>EditorCookie.getOpenedPanes()</code> returned <code>null</code>)
218      * and is to be opened at a specific line.
219      *
220      * @param editorCookie editor cookie to use for opening the document
221      * @param doc document already loaded using the editor cookie
222      * @param line line to open the document at (first line = <code>0</code>);
223      * must be non-negative
224      */

225     private void openDocAtLine(final EditorCookie editorCookie,
226                                final StyledDocument JavaDoc doc,
227                                final int line) {
228         assert EventQueue.isDispatchThread();
229         assert line >= 0;
230         assert editorCookie.getDocument() == doc;
231         
232         /* offset must be computed here so that it is available to the task: */
233         final int offset = getCursorOffset(doc, line);
234         
235         class SetCursorTask implements Runnable JavaDoc {
236             private boolean completed = false;
237             private PropertyChangeListener JavaDoc listenerToUnregister;
238             private boolean perform() {
239                 if (EventQueue.isDispatchThread()) {
240                     run();
241                 } else {
242                     try {
243                         EventQueue.invokeAndWait(this);
244                     } catch (Exception JavaDoc ex) {
245                         ErrorManager.getDefault().notify(ex);
246                         
247                         completed = true; //so that only one exception is thrown
248
}
249                 }
250                 return completed;
251             }
252             public void run() {
253                 assert EventQueue.isDispatchThread();
254
255                 if (completed) {
256                     return;
257                 }
258
259                 JEditorPane JavaDoc[] panes = editorCookie.getOpenedPanes();
260                 if (panes != null) {
261                     panes[0].setCaretPosition(offset);
262                     if (listenerToUnregister != null) {
263                         ((EditorCookie.Observable) editorCookie)
264                         .removePropertyChangeListener(listenerToUnregister);
265                     }
266                     completed = true;
267                 }
268             }
269             private void setListenerToUnregister(PropertyChangeListener JavaDoc l) {
270                 listenerToUnregister = l;
271             }
272         }
273
274         final SetCursorTask setCursorTask = new SetCursorTask();
275         
276         editorCookie.open();
277         if (setCursorTask.perform()) {
278             return;
279         }
280         if (editorCookie instanceof EditorCookie.Observable) {
281             if (!setCursorTask.perform()) {
282                 PropertyChangeListener JavaDoc openPanesListener
283                         = new PropertyChangeListener JavaDoc() {
284                             public void propertyChange(PropertyChangeEvent JavaDoc e) {
285                                 if (EditorCookie.Observable.PROP_OPENED_PANES
286                                         .equals(e.getPropertyName())) {
287                                     setCursorTask.perform();
288                                 }
289                             }
290                         };
291                 setCursorTask.setListenerToUnregister(openPanesListener);
292                 ((EditorCookie.Observable) editorCookie)
293                         .addPropertyChangeListener(openPanesListener);
294                 setCursorTask.perform();
295             }
296         } else {
297             final int numberOfTries = OPEN_EDITOR_TOTAL_TIMEOUT_MS
298                                       / OPEN_EDITOR_WAIT_PERIOD_MS;
299             for (int i = 0; i < numberOfTries; i++) {
300                 try {
301                     Thread.currentThread().sleep(OPEN_EDITOR_WAIT_PERIOD_MS);
302                 } catch (InterruptedException JavaDoc ex) {
303                     ErrorManager.getDefault().notify(ErrorManager.EXCEPTION,
304                                                      ex);
305                 }
306                 if (setCursorTask.perform()) {
307                     break;
308                 }
309             }
310             if (!setCursorTask.completed) {
311                 StatusDisplayer.getDefault().setStatusText(NbBundle.getMessage(
312                         DefaultOpenFileImpl.class,
313                         "MSG_couldNotOpenAt")); //NOI18N
314
}
315         }
316     }
317     
318     /**
319      * Computes cursor offset of a given line of a document.
320      * The line number must be non-negative.
321      * If the line number is greater than number of the last line,
322      * the returned offset corresponds to the last line of the document.
323      *
324      * @param doc document to computer offset for
325      * @param line line number (first line = <code>0</code>)
326      * @return cursor offset of the beginning of the given line
327      */

328     private int getCursorOffset(StyledDocument JavaDoc doc, int line) {
329         assert EventQueue.isDispatchThread();
330         assert line >= 0;
331         
332         try {
333             return NbDocument.findLineOffset(doc, line);
334         } catch (IndexOutOfBoundsException JavaDoc ex) {
335             /* probably line number out of bounds */
336
337             Element JavaDoc lineRootElement
338                     = NbDocument.findLineRootElement(doc);
339             int lineCount = lineRootElement.getElementCount();
340             if (line >= lineCount) {
341                 return NbDocument.findLineOffset(doc, lineCount - 1);
342             } else {
343                 throw ex;
344             }
345         }
346     }
347     
348     /**
349      * Activates the specified cookie, thus opening a file.
350      * The file is specified by the cookie, because the cookie was obtained
351      * from it. The cookie must be one of <code>EditorCookie</code>
352      * <code>OpenCookie</code>, <code>EditCookie</code>,
353      * <code>ViewCookie</code>.
354      *
355      * @param cookie cookie to activate
356      * @param cookieClass type of the cookie - specifies action to activate
357      * @param line used only by <code>EditorCookie</code>s&nbsp;-
358      * specifies initial line to open the file at
359      * @return <code>true</code> if the cookie was successfully activated,
360      * <code>false</code> if some error occured
361      * @exception java.lang.IllegalArgumentException
362      * if <code>cookieClass</code> is not any of
363      * <code>EditorCookie</code>, <code>OpenCookie</code>,
364      * <code>ViewCookie</code>
365      * @exception java.lang.ClassCastException
366      * if the <code>cookie</code> is not an instance
367      * of the specified cookie class
368      */

369     protected boolean openByCookie(Node.Cookie cookie,
370                                    Class JavaDoc cookieClass,
371                                    final int line) {
372         assert EventQueue.isDispatchThread();
373         
374         if ((cookieClass == EditorCookie.Observable.class)
375                 || (cookieClass == EditorCookie.Observable.class)) {
376             return openEditor((EditorCookie) cookie, line);
377         } else if (cookieClass == OpenCookie.class) {
378             ((OpenCookie) cookie).open();
379         } else if (cookieClass == EditCookie.class) {
380             ((EditCookie) cookie).edit();
381         } else if (cookieClass == ViewCookie.class) {
382             ((ViewCookie) cookie).view();
383         } else {
384             throw new IllegalArgumentException JavaDoc();
385         }
386         return true;
387     }
388     
389     /**
390      * Tries to open the specified file, using one of <code>EditorCookie</code>,
391      * <code>OpenCookie</code>, <code>EditCookie</code>, <code>ViewCookie</code>
392      * (in the same order).
393      * If the client of the open file server wants, waits until the file is
394      * closed and notifies the client.
395      *
396      * @param dataObject <code>DataObject</code> representing the file
397      * @param line if <code>EditorCookie</code> is used,
398      * specifies initial line to open the file at
399      * @return <code>true</code> if the file was successfully open,
400      * <code>false</code> otherwise
401      */

402     private final boolean openDataObjectByCookie(DataObject dataObject,
403                                        int line) {
404         
405         Class JavaDoc cookieClass;
406         Node.Cookie cookie;
407         if( ( cookie = dataObject.getCookie(cookieClass = OpenCookie.class)) != null
408              || (cookie = dataObject.getCookie(cookieClass = EditCookie.class)) != null
409              || (cookie = dataObject.getCookie(cookieClass = ViewCookie.class)) != null) {
410             return openByCookie(cookie, cookieClass, line);
411         }
412         return false;
413     }
414     
415     /**
416      * This method is called when it is rescheduled to the AWT thread.
417      * (from a different thread). It is always run in the AWT thread.
418      */

419     public void run() {
420         assert EventQueue.isDispatchThread();
421         
422         open(fileObject, line);
423     }
424     
425     /**
426      * Opens the <code>FileObject</code> either by calling {@link EditorCookie}
427      * (or {@link OpenCookie} or {@link ViewCookie}),
428      * or by showing it in the Explorer.
429      */

430     public boolean open(final FileObject fileObject, int line) {
431         if (!EventQueue.isDispatchThread()) {
432             EventQueue.invokeLater(
433                     new DefaultOpenFileImpl(fileObject, line));
434             return true;
435         }
436         
437         
438         assert EventQueue.isDispatchThread();
439
440         String JavaDoc fileName = fileObject.getNameExt();
441                   
442         /* Find a DataObject for the FileObject: */
443         final DataObject dataObject;
444         try {
445             dataObject = DataObject.find(fileObject);
446         } catch (DataObjectNotFoundException ex) {
447             ErrorManager.getDefault().notify(ex);
448             return false;
449         }
450
451         Class JavaDoc cookieClass;
452         Node.Cookie cookie;
453         
454         if ( (line != -1 && ((cookie = dataObject.getCookie(cookieClass = EditorCookie.Observable.class)) != null
455              || (cookie = dataObject.getCookie(cookieClass = EditorCookie.class)) != null)) ){
456             boolean ret = openByCookie(cookie,cookieClass, line);
457             return ret;
458         }
459                             
460         /* try to open the object using the default action */
461         final Node dataNode = dataObject.getNodeDelegate();
462         final Action JavaDoc action = dataNode.getPreferredAction();
463         if (action != null && !(action instanceof FileSystemAction) && !(action instanceof ToolsAction)) {
464             EventQueue.invokeLater(new Runnable JavaDoc() {
465                 public void run() {
466                     action.actionPerformed(new ActionEvent JavaDoc(dataNode, 0, null));
467                 }
468             });
469             return true;
470         }
471         
472         /* Try to grab an editor/open/edit/view cookie and open the object: */
473         StatusDisplayer.getDefault().setStatusText(NbBundle.getMessage(DefaultOpenFileImpl.class, "MSG_opening", fileName));
474         boolean success = openDataObjectByCookie(dataObject, line);
475         if (success) {
476             return true;
477         }
478         
479         String JavaDoc ext = fileObject.getExt();
480         if (
481             ZIP_EXT.equalsIgnoreCase(ext) ||
482             JAR_EXT.equalsIgnoreCase(ext) ||
483             WAR_EXT.equalsIgnoreCase(ext) ||
484             fileObject.isFolder()
485         ) {
486             // select it in explorer:
487
Node node = dataObject.getNodeDelegate();
488             if (node != null) {
489                 NodeOperation.getDefault().explore(node);
490                 return true;
491             } else {
492                 return false;
493             }
494         }
495         
496         return false;
497     }
498     
499 }
500
Popular Tags