KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > db > sql > loader > SQLEditorSupport


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 package org.netbeans.modules.db.sql.loader;
21
22 import java.awt.BorderLayout JavaDoc;
23 import java.awt.Component JavaDoc;
24 import java.beans.PropertyChangeListener JavaDoc;
25 import java.beans.PropertyChangeSupport JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.io.InputStream JavaDoc;
28 import java.io.InputStreamReader JavaDoc;
29 import java.io.OutputStreamWriter JavaDoc;
30 import java.sql.Connection JavaDoc;
31 import java.sql.SQLException JavaDoc;
32 import java.util.Enumeration JavaDoc;
33 import javax.swing.JPanel JavaDoc;
34 import javax.swing.text.BadLocationException JavaDoc;
35 import javax.swing.text.Document JavaDoc;
36 import javax.swing.text.EditorKit JavaDoc;
37 import javax.swing.text.StyledDocument JavaDoc;
38 import org.netbeans.api.progress.ProgressHandle;
39 import org.netbeans.api.progress.ProgressHandleFactory;
40 import org.netbeans.api.db.explorer.ConnectionManager;
41 import org.netbeans.api.db.explorer.DatabaseConnection;
42 import org.netbeans.modules.db.api.sql.execute.SQLExecuteCookie;
43 import org.netbeans.modules.db.api.sql.execute.SQLExecution;
44 import org.netbeans.modules.db.sql.execute.ui.SQLResultPanelModel;
45 import org.openide.ErrorManager;
46 import org.openide.awt.StatusDisplayer;
47 import org.openide.cookies.EditCookie;
48 import org.openide.cookies.EditorCookie;
49 import org.openide.cookies.OpenCookie;
50 import org.openide.cookies.PrintCookie;
51 import org.openide.cookies.SaveCookie;
52 import org.openide.filesystems.FileObject;
53 import org.openide.filesystems.FileStateInvalidException;
54 import org.openide.nodes.Node.Cookie;
55 import org.openide.text.DataEditorSupport;
56 import org.openide.util.Cancellable;
57 import org.openide.util.MutexException;
58 import org.openide.util.NbBundle;
59 import org.openide.util.RequestProcessor;
60 import org.netbeans.modules.db.sql.execute.ui.SQLResultPanel;
61 import org.netbeans.modules.db.sql.execute.SQLExecuteHelper;
62 import org.netbeans.modules.db.sql.execute.SQLExecutionResults;
63 import org.openide.filesystems.FileLock;
64 import org.openide.loaders.MultiDataObject;
65 import org.openide.text.CloneableEditor;
66 import org.openide.util.Mutex;
67 import org.openide.windows.CloneableOpenSupport;
68
69 /**
70  * Editor support for SQL data objects. There can be two "kinds" of SQL editors: one for normal
71  * DataObjects and one for "console" DataObjects. In the latter case the editor doesn't allow its
72  * contents to be saved explicitly, its name doesn't contain a "*" when it is modified, the respective
73  * DataObject is deleted when the editor is closed, and the contents is saved when the editor is
74  * deactivated or upon exiting NetBeans.
75  *
76  * @author Jesse Beaumont, Andrei Badea
77  */

78 public class SQLEditorSupport extends DataEditorSupport implements OpenCookie, EditCookie, EditorCookie.Observable, PrintCookie, SQLExecuteCookie {
79     
80     private static final ErrorManager LOGGER = ErrorManager.getDefault().getInstance(SQLEditorSupport.class.getName());
81     private static final boolean LOG = LOGGER.isLoggable(ErrorManager.INFORMATIONAL);
82     
83     static final String JavaDoc EDITOR_CONTAINER = "sqlEditorContainer"; // NOI18N
84

85     private static final String JavaDoc MIME_TYPE = "text/x-sql"; // NOI18N
86

87     private final PropertyChangeSupport JavaDoc sqlPropChangeSupport = new PropertyChangeSupport JavaDoc(this);
88     
89     // the RequestProcessor used for executing statements.
90
private final RequestProcessor rp = new RequestProcessor("SQLExecution", 1, true); // NOI18N
91

92     // the encoding of the current document
93
private String JavaDoc encoding;
94     
95     // the database connection to execute against
96
private DatabaseConnection dbconn;
97     
98     // whether we are executing statements
99
private boolean executing;
100     
101     // execution results. Not synchronized since accessed only from rp of throughput 1.
102
private SQLExecutionResults executionResults;
103     
104     // execution logger
105
private SQLExecutionLoggerImpl logger;
106     
107     /**
108      * SaveCookie for this support instance. The cookie is adding/removing
109      * data object's cookie set depending on if modification flag was set/unset.
110      */

111     private final SaveCookie saveCookie = new SaveCookie() {
112         public void save() throws IOException JavaDoc {
113             saveDocument();
114         }
115     };
116     
117     public SQLEditorSupport(SQLDataObject obj) {
118         super(obj, new Environment(obj));
119         setMIMEType(MIME_TYPE);
120     }
121     
122     protected boolean notifyModified () {
123         if (!super.notifyModified())
124             return false;
125         
126         if (!isConsole()) {
127             FileObject fo = getDataObject().getPrimaryFile();
128             // Add the save cookie to the data object
129
SQLDataObject obj = (SQLDataObject)getDataObject();
130             if (obj.getCookie(SaveCookie.class) == null) {
131                 obj.addCookie(saveCookie);
132                 obj.setModified(true);
133             }
134         }
135
136         return true;
137     }
138
139     protected void notifyUnmodified () {
140         super.notifyUnmodified();
141
142         // Remove the save cookie from the data object
143
SQLDataObject obj = (SQLDataObject)getDataObject();
144         Cookie cookie = obj.getCookie(SaveCookie.class);
145         if (cookie != null && cookie.equals(saveCookie)) {
146             obj.removeCookie(saveCookie);
147             obj.setModified(false);
148         }
149     }
150     
151     protected String JavaDoc messageToolTip() {
152         if (isConsole()) {
153             return getDataObject().getPrimaryFile().getName();
154         } else {
155             return super.messageToolTip();
156         }
157     }
158     
159     protected String JavaDoc messageName() {
160         if (!getDataObject().isValid()) return ""; // NOI18N
161

162         if (isConsole()) {
163             // just the name, no modified or r/o flags
164
return getDataObject().getName();
165         } else {
166             return super.messageName();
167         }
168     }
169     
170     protected String JavaDoc messageHtmlName() {
171         if (!getDataObject().isValid()) return ""; // NOI18N
172

173         if (isConsole()) {
174             // just the name, no modified or r/o flags
175
String JavaDoc name = getDataObject().getName();
176             if (name != null) {
177                 if (!name.startsWith("<html>")) { // NOI18N
178
name = "<html>" + name; // NOI18N
179
}
180             }
181             return name;
182         } else {
183             return super.messageHtmlName();
184         }
185     }
186     
187     protected void notifyClosed() {
188         super.notifyClosed();
189         
190         closeExecutionResult();
191         closeLogger();
192         
193         if (isConsole() && getDataObject().isValid()) {
194             try {
195                 getDataObject().delete();
196             } catch (IOException JavaDoc e) {
197                 ErrorManager.getDefault().notify(e);
198             }
199         }
200     }
201     
202     protected boolean canClose() {
203         if (isConsole()) {
204             return true;
205         } else {
206             return super.canClose();
207         }
208     }
209     
210     boolean isConsole() {
211         try {
212             // the "console" files are stored in the SFS
213
return "nbfs".equals(getDataObject().getPrimaryFile().getURL().getProtocol()); // NOI18N
214
} catch (FileStateInvalidException e) {
215             return false;
216         }
217     }
218     
219     protected CloneableEditor createCloneableEditor() {
220         return new SQLCloneableEditor(this);
221     }
222     
223     protected Component JavaDoc wrapEditorComponent(Component JavaDoc editor) {
224         JPanel JavaDoc container = new JPanel JavaDoc(new BorderLayout JavaDoc());
225         container.setName(EDITOR_CONTAINER); // NOI18N
226
container.add(editor, BorderLayout.CENTER);
227         return container;
228     }
229     
230     void addSQLPropertyChangeListener(PropertyChangeListener JavaDoc listener) {
231         sqlPropChangeSupport.addPropertyChangeListener(listener);
232     }
233     
234     void removeSQLPropertyChangeListener(PropertyChangeListener JavaDoc listener) {
235         sqlPropChangeSupport.removePropertyChangeListener(listener);
236     }
237     
238     synchronized DatabaseConnection getDatabaseConnection() {
239         return dbconn;
240     }
241     
242     public synchronized void setDatabaseConnection(DatabaseConnection dbconn) {
243         this.dbconn = dbconn;
244         sqlPropChangeSupport.firePropertyChange(SQLExecution.PROP_DATABASE_CONNECTION, null, null);
245     }
246     
247     public void execute() {
248         Document JavaDoc doc = getDocument();
249         if (doc == null) {
250             return;
251         }
252         String JavaDoc sql = null;
253         try {
254             sql = doc.getText(0, doc.getLength());
255         } catch (BadLocationException JavaDoc e) {
256             // should not happen
257
ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
258             sql = ""; // NOI18N
259
}
260         execute(sql, 0, sql.length());
261     }
262     
263     /**
264      * Executes either all or a part of the given sql string (which can contain
265      * zero or more SQL statements). If startOffset < endOffset, the part of
266      * sql specified is executed. If startOffset == endOffset, the statement
267      * containing the character at startOffset, if any, is executed.
268      *
269      * @param sql the SQL string to execute. If it contains multiple lines they
270      * have to be delimited by \n.
271      */

272     void execute(String JavaDoc sql, int startOffset, int endOffset) {
273         DatabaseConnection dbconn;
274         synchronized (this) {
275             dbconn = this.dbconn;
276         }
277         if (dbconn == null) {
278             return;
279         }
280         SQLExecutor executor = new SQLExecutor(this, dbconn, sql, startOffset, endOffset);
281         RequestProcessor.Task task = rp.create(executor);
282         executor.setTask(task);
283         task.schedule(0);
284     }
285     
286     synchronized boolean isExecuting() {
287         return executing;
288     }
289     
290     private synchronized void setExecuting(boolean executing) {
291         this.executing = executing;
292         sqlPropChangeSupport.firePropertyChange(SQLExecution.PROP_EXECUTING, null, null);
293     }
294     
295     private void setResultModelToEditors(final SQLResultPanelModel model) {
296         Mutex.EVENT.writeAccess(new Runnable JavaDoc() {
297             public void run() {
298                 Enumeration JavaDoc editors = allEditors.getComponents();
299                 while (editors.hasMoreElements()) {
300                     SQLCloneableEditor editor = (SQLCloneableEditor)editors.nextElement();
301                     if (model == null && !editor.hasResultComponent()) {
302                         // if hasResultComponent() is false, setting a null model
303
// would unnecessarily create the result component, so...
304
continue;
305                     }
306                     
307                     SQLResultPanel resultComponent = editor.getResultComponent();
308                     
309                     // resultComponent will be null for a deserialized
310
// and not initialized CloneableEditor
311
if (resultComponent != null) {
312                         resultComponent.setModel(model);
313                     }
314                 }
315             }
316         });
317     }
318     
319     private void setExecutionResults(SQLExecutionResults executionResults) {
320         this.executionResults = executionResults;
321     }
322     
323     private void closeExecutionResult() {
324         setResultModelToEditors(null);
325         
326         Runnable JavaDoc run = new Runnable JavaDoc() {
327             public void run() {
328                 if (executionResults != null) {
329                     executionResults.close();
330                     executionResults = null;
331                 }
332             }
333         };
334         
335         // need to run the Runnable in the request processor
336
// since it makes JDBC calls, possibly blocking
337
// the calling thread
338

339         // closeExceptionResult is sometimes called in the RP,
340
// e.g. while executing statements
341
if (rp.isRequestProcessorThread()) {
342             run.run();
343         } else {
344             rp.post(run);
345         }
346     }
347     
348     private SQLExecutionLoggerImpl createLogger() {
349         closeLogger();
350         
351         String JavaDoc loggerDisplayName = null;
352         if (isConsole()) {
353             loggerDisplayName = getDataObject().getName();
354         } else {
355             loggerDisplayName = getDataObject().getNodeDelegate().getDisplayName();
356         }
357         
358         synchronized (this) {
359             logger = new SQLExecutionLoggerImpl(loggerDisplayName, this);
360         }
361         return logger;
362     }
363     
364     private synchronized void closeLogger() {
365         if (logger != null) {
366             logger.close();
367         }
368     }
369     
370     protected void loadFromStreamToKit(StyledDocument JavaDoc doc, InputStream JavaDoc stream, EditorKit JavaDoc kit) throws IOException JavaDoc, javax.swing.text.BadLocationException JavaDoc {
371         encoding = getEncoding(stream);
372         if (LOG) {
373             LOGGER.log(ErrorManager.INFORMATIONAL, "Encoding: " + encoding); // NOI18N
374
}
375         if (encoding != null) {
376             InputStreamReader JavaDoc reader = new InputStreamReader JavaDoc(stream, encoding);
377
378             try {
379                 kit.read(reader, doc, 0);
380             } finally {
381                 stream.close();
382             }
383         } else {
384             super.loadFromStreamToKit(doc, stream, kit);
385         }
386     }
387
388     protected void saveFromKitToStream(StyledDocument JavaDoc doc, EditorKit JavaDoc kit, java.io.OutputStream JavaDoc stream) throws IOException JavaDoc, javax.swing.text.BadLocationException JavaDoc {
389         if (encoding != null) {
390             if ("utf-8".equals(encoding)) { // NOI18N
391
// write an utf-8 byte order mark
392
stream.write(0xef);
393                 stream.write(0xbb);
394                 stream.write(0xbf);
395             }
396             OutputStreamWriter JavaDoc writer = new OutputStreamWriter JavaDoc(stream, encoding);
397             try {
398                 kit.write(writer, doc, 0, doc.getLength());
399             } finally {
400                 writer.close();
401             }
402         } else {
403             super.saveFromKitToStream(doc, kit, stream);
404         }
405     }
406     
407     private static String JavaDoc getEncoding(InputStream JavaDoc stream) throws IOException JavaDoc {
408         if (!stream.markSupported()) {
409             return null;
410         }
411         stream.mark(3);
412         // test a utf-8 byte order mark
413
boolean isUTF8 = (stream.read() == 0xef && stream.read() == 0xbb && stream.read() == 0xbf);
414         if (isUTF8) {
415             return "utf-8"; // NOI18N
416
} else {
417             stream.reset();
418             return null;
419         }
420     }
421     
422     private final static class SQLExecutor implements Runnable JavaDoc, Cancellable {
423         
424         private final SQLEditorSupport parent;
425
426         // the connections which the statements are executed against
427
private final DatabaseConnection dbconn;
428         
429         // the currently executed statement(s)
430
private final String JavaDoc sql;
431         
432         private final int startOffset, endOffset;
433         
434         // the task representing the execution of statements
435
private RequestProcessor.Task task;
436         
437         public SQLExecutor(SQLEditorSupport parent, DatabaseConnection dbconn, String JavaDoc sql, int startOffset, int endOffset) {
438             assert parent != null;
439             assert dbconn != null;
440             assert sql != null;
441             
442             this.parent = parent;
443             this.dbconn = dbconn;
444             this.sql = sql;
445             this.startOffset = startOffset;
446             this.endOffset = endOffset;
447         }
448         
449         public void setTask(RequestProcessor.Task task) {
450             this.task = task;
451         }
452         
453         public void run() {
454             assert task != null : "Should have called setTask()"; // NOI18N
455

456             parent.setExecuting(true);
457             try {
458                 if (LOG) {
459                     LOGGER.log("Started the SQL execution task"); // NOI18N
460
LOGGER.log(ErrorManager.INFORMATIONAL, "Executing against " + dbconn); // NOI18N
461
}
462
463                 Mutex.EVENT.readAccess(new Mutex.Action<Void JavaDoc>() {
464                     public Void JavaDoc run() {
465                         ConnectionManager.getDefault().showConnectionDialog(dbconn);
466                         return null;
467                     }
468                 });
469
470                 Connection JavaDoc conn = dbconn.getJDBCConnection();
471                 if (LOG) {
472                     LOGGER.log(ErrorManager.INFORMATIONAL, "SQL connection: " + conn); // NOI18N
473
}
474                 if (conn == null) {
475                     return;
476                 }
477
478                 // need to save the document, otherwise the Line.Set.getOriginal mechanism does not work
479
try {
480                     Mutex.EVENT.readAccess(new Mutex.ExceptionAction<Void JavaDoc>() {
481                         public Void JavaDoc run() throws Exception JavaDoc {
482                             parent.saveDocument();
483                             return null;
484                         }
485                     });
486                 } catch (MutexException e) {
487                     ErrorManager.getDefault().notify(e.getException());
488                     return;
489                 }
490
491                 ProgressHandle handle = ProgressHandleFactory.createHandle(NbBundle.getMessage(SQLEditorSupport.class, "LBL_ExecutingStatements"), this);
492                 handle.start();
493                 try {
494                     handle.switchToIndeterminate();
495
496                     setStatusText(""); // NOI18N
497

498                     if (LOG) {
499                         LOGGER.log(ErrorManager.INFORMATIONAL, "Closing the old execution result" ); // NOI18N
500
}
501                     parent.closeExecutionResult();
502
503                     SQLExecutionLoggerImpl logger = parent.createLogger();
504                     SQLExecutionResults executionResults = SQLExecuteHelper.execute(sql, startOffset, endOffset, conn, handle, logger);
505                     handleExecutionResults(executionResults, logger);
506                 } finally {
507                     handle.finish();
508                 }
509             } finally {
510                 parent.setExecuting(false);
511             }
512         }
513         
514         private void handleExecutionResults(SQLExecutionResults executionResults, SQLExecutionLoggerImpl logger) {
515             if (executionResults == null) {
516                 // execution cancelled
517
setStatusText(NbBundle.getMessage(SQLEditorSupport.class, "LBL_ExecutionCancelled"));
518                 return;
519             }
520             
521             parent.setExecutionResults(executionResults);
522             
523             if (executionResults.hasExceptions()) {
524                 // there was at least one exception
525
setStatusText(NbBundle.getMessage(SQLEditorSupport.class, "LBL_ExecutionFinishedWithErrors"));
526                 // just that, the exceptions are in the Output window
527
return;
528             }
529             
530             if (executionResults.size() <= 0) {
531                 // no results, but successfull
532
setStatusText(NbBundle.getMessage(SQLEditorSupport.class, "LBL_ExecutedSuccessfully"));
533                 return;
534             }
535             
536             SQLResultPanelModel model;
537             try {
538                 model = SQLResultPanelModel.create(executionResults);
539             } catch (SQLException JavaDoc e) {
540                 logger.logResultSetException(e);
541                 setStatusText(NbBundle.getMessage(SQLEditorSupport.class, "LBL_ResultSetError"));
542                 return;
543             } catch (IOException JavaDoc e) {
544                 logger.logResultSetException(e);
545                 setStatusText(NbBundle.getMessage(SQLEditorSupport.class, "LBL_ResultSetError"));
546                 return;
547             }
548             
549             if (model == null) {
550                 // execution cancelled
551
setStatusText(NbBundle.getMessage(SQLEditorSupport.class, "LBL_ExecutionCancelled"));
552                 return;
553             }
554                 
555             if (!model.isEmpty()) {
556                 parent.setResultModelToEditors(model);
557             }
558             
559             setStatusText(NbBundle.getMessage(SQLEditorSupport.class, "LBL_ExecutedSuccessfully"));
560         }
561         
562         private void setStatusText(String JavaDoc statusText) {
563             StatusDisplayer.getDefault().setStatusText(statusText);
564         }
565         
566         public boolean cancel() {
567             return task.cancel();
568         }
569     }
570
571     /**
572      * Environment for this support. Ensures that getDataObject().setModified(true)
573      * is not called if this support's editor was opened as a console.
574      */

575     static final class Environment extends DataEditorSupport.Env {
576
577         public static final long serialVersionUID = 7968926994844480435L;
578
579         private transient boolean modified = false;
580
581         private transient FileLock fileLock;
582
583         public Environment(SQLDataObject obj) {
584             super(obj);
585         }
586
587         protected FileObject getFile() {
588             return getDataObject().getPrimaryFile();
589         }
590
591         protected FileLock takeLock() throws IOException JavaDoc {
592             MultiDataObject obj = (MultiDataObject)getDataObject();
593             fileLock = obj.getPrimaryEntry().takeLock();
594             return fileLock;
595         }
596
597         public void markModified() throws IOException JavaDoc {
598             if (findSQLEditorSupport().isConsole()) {
599                 modified = true;
600             } else {
601                 super.markModified();
602             }
603         }
604
605         public void unmarkModified() {
606             if (findSQLEditorSupport().isConsole()) {
607                 modified = false;
608                 if (fileLock != null && fileLock.isValid()) {
609                     fileLock.releaseLock();
610                 }
611             } else {
612                 super.unmarkModified();
613             }
614         }
615
616         public boolean isModified() {
617             if (findSQLEditorSupport().isConsole()) {
618                 return modified;
619             } else {
620                 return super.isModified();
621             }
622         }
623
624         public CloneableOpenSupport findCloneableOpenSupport() {
625             return findSQLEditorSupport();
626         }
627
628         private SQLEditorSupport findSQLEditorSupport() {
629             return (SQLEditorSupport)getDataObject().getCookie(SQLEditorSupport.class);
630         }
631     }
632 }
633
Popular Tags