1 19 20 package org.netbeans.modules.db.sql.loader; 21 22 import java.awt.BorderLayout ; 23 import java.awt.Component ; 24 import java.beans.PropertyChangeListener ; 25 import java.beans.PropertyChangeSupport ; 26 import java.io.IOException ; 27 import java.io.InputStream ; 28 import java.io.InputStreamReader ; 29 import java.io.OutputStreamWriter ; 30 import java.sql.Connection ; 31 import java.sql.SQLException ; 32 import java.util.Enumeration ; 33 import javax.swing.JPanel ; 34 import javax.swing.text.BadLocationException ; 35 import javax.swing.text.Document ; 36 import javax.swing.text.EditorKit ; 37 import javax.swing.text.StyledDocument ; 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 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 EDITOR_CONTAINER = "sqlEditorContainer"; 85 private static final String MIME_TYPE = "text/x-sql"; 87 private final PropertyChangeSupport sqlPropChangeSupport = new PropertyChangeSupport (this); 88 89 private final RequestProcessor rp = new RequestProcessor("SQLExecution", 1, true); 92 private String encoding; 94 95 private DatabaseConnection dbconn; 97 98 private boolean executing; 100 101 private SQLExecutionResults executionResults; 103 104 private SQLExecutionLoggerImpl logger; 106 107 111 private final SaveCookie saveCookie = new SaveCookie() { 112 public void save() throws IOException { 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 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 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 messageToolTip() { 152 if (isConsole()) { 153 return getDataObject().getPrimaryFile().getName(); 154 } else { 155 return super.messageToolTip(); 156 } 157 } 158 159 protected String messageName() { 160 if (!getDataObject().isValid()) return ""; 162 if (isConsole()) { 163 return getDataObject().getName(); 165 } else { 166 return super.messageName(); 167 } 168 } 169 170 protected String messageHtmlName() { 171 if (!getDataObject().isValid()) return ""; 173 if (isConsole()) { 174 String name = getDataObject().getName(); 176 if (name != null) { 177 if (!name.startsWith("<html>")) { name = "<html>" + name; } 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 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 return "nbfs".equals(getDataObject().getPrimaryFile().getURL().getProtocol()); } catch (FileStateInvalidException e) { 215 return false; 216 } 217 } 218 219 protected CloneableEditor createCloneableEditor() { 220 return new SQLCloneableEditor(this); 221 } 222 223 protected Component wrapEditorComponent(Component editor) { 224 JPanel container = new JPanel (new BorderLayout ()); 225 container.setName(EDITOR_CONTAINER); container.add(editor, BorderLayout.CENTER); 227 return container; 228 } 229 230 void addSQLPropertyChangeListener(PropertyChangeListener listener) { 231 sqlPropChangeSupport.addPropertyChangeListener(listener); 232 } 233 234 void removeSQLPropertyChangeListener(PropertyChangeListener 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 doc = getDocument(); 249 if (doc == null) { 250 return; 251 } 252 String sql = null; 253 try { 254 sql = doc.getText(0, doc.getLength()); 255 } catch (BadLocationException e) { 256 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); 258 sql = ""; } 260 execute(sql, 0, sql.length()); 261 } 262 263 272 void execute(String 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 () { 297 public void run() { 298 Enumeration editors = allEditors.getComponents(); 299 while (editors.hasMoreElements()) { 300 SQLCloneableEditor editor = (SQLCloneableEditor)editors.nextElement(); 301 if (model == null && !editor.hasResultComponent()) { 302 continue; 305 } 306 307 SQLResultPanel resultComponent = editor.getResultComponent(); 308 309 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 run = new Runnable () { 327 public void run() { 328 if (executionResults != null) { 329 executionResults.close(); 330 executionResults = null; 331 } 332 } 333 }; 334 335 339 if (rp.isRequestProcessorThread()) { 342 run.run(); 343 } else { 344 rp.post(run); 345 } 346 } 347 348 private SQLExecutionLoggerImpl createLogger() { 349 closeLogger(); 350 351 String 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 doc, InputStream stream, EditorKit kit) throws IOException , javax.swing.text.BadLocationException { 371 encoding = getEncoding(stream); 372 if (LOG) { 373 LOGGER.log(ErrorManager.INFORMATIONAL, "Encoding: " + encoding); } 375 if (encoding != null) { 376 InputStreamReader reader = new InputStreamReader (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 doc, EditorKit kit, java.io.OutputStream stream) throws IOException , javax.swing.text.BadLocationException { 389 if (encoding != null) { 390 if ("utf-8".equals(encoding)) { stream.write(0xef); 393 stream.write(0xbb); 394 stream.write(0xbf); 395 } 396 OutputStreamWriter writer = new OutputStreamWriter (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 getEncoding(InputStream stream) throws IOException { 408 if (!stream.markSupported()) { 409 return null; 410 } 411 stream.mark(3); 412 boolean isUTF8 = (stream.read() == 0xef && stream.read() == 0xbb && stream.read() == 0xbf); 414 if (isUTF8) { 415 return "utf-8"; } else { 417 stream.reset(); 418 return null; 419 } 420 } 421 422 private final static class SQLExecutor implements Runnable , Cancellable { 423 424 private final SQLEditorSupport parent; 425 426 private final DatabaseConnection dbconn; 428 429 private final String sql; 431 432 private final int startOffset, endOffset; 433 434 private RequestProcessor.Task task; 436 437 public SQLExecutor(SQLEditorSupport parent, DatabaseConnection dbconn, String 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()"; 456 parent.setExecuting(true); 457 try { 458 if (LOG) { 459 LOGGER.log("Started the SQL execution task"); LOGGER.log(ErrorManager.INFORMATIONAL, "Executing against " + dbconn); } 462 463 Mutex.EVENT.readAccess(new Mutex.Action<Void >() { 464 public Void run() { 465 ConnectionManager.getDefault().showConnectionDialog(dbconn); 466 return null; 467 } 468 }); 469 470 Connection conn = dbconn.getJDBCConnection(); 471 if (LOG) { 472 LOGGER.log(ErrorManager.INFORMATIONAL, "SQL connection: " + conn); } 474 if (conn == null) { 475 return; 476 } 477 478 try { 480 Mutex.EVENT.readAccess(new Mutex.ExceptionAction<Void >() { 481 public Void run() throws Exception { 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(""); 498 if (LOG) { 499 LOGGER.log(ErrorManager.INFORMATIONAL, "Closing the old execution result" ); } 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 setStatusText(NbBundle.getMessage(SQLEditorSupport.class, "LBL_ExecutionCancelled")); 518 return; 519 } 520 521 parent.setExecutionResults(executionResults); 522 523 if (executionResults.hasExceptions()) { 524 setStatusText(NbBundle.getMessage(SQLEditorSupport.class, "LBL_ExecutionFinishedWithErrors")); 526 return; 528 } 529 530 if (executionResults.size() <= 0) { 531 setStatusText(NbBundle.getMessage(SQLEditorSupport.class, "LBL_ExecutedSuccessfully")); 533 return; 534 } 535 536 SQLResultPanelModel model; 537 try { 538 model = SQLResultPanelModel.create(executionResults); 539 } catch (SQLException e) { 540 logger.logResultSetException(e); 541 setStatusText(NbBundle.getMessage(SQLEditorSupport.class, "LBL_ResultSetError")); 542 return; 543 } catch (IOException e) { 544 logger.logResultSetException(e); 545 setStatusText(NbBundle.getMessage(SQLEditorSupport.class, "LBL_ResultSetError")); 546 return; 547 } 548 549 if (model == null) { 550 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 statusText) { 563 StatusDisplayer.getDefault().setStatusText(statusText); 564 } 565 566 public boolean cancel() { 567 return task.cancel(); 568 } 569 } 570 571 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 { 592 MultiDataObject obj = (MultiDataObject)getDataObject(); 593 fileLock = obj.getPrimaryEntry().takeLock(); 594 return fileLock; 595 } 596 597 public void markModified() throws IOException { 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 |