1 19 20 package org.openide.text; 21 22 23 import java.beans.*; 24 import java.io.*; 25 import java.lang.ref.Reference ; 26 import java.util.*; 27 import java.util.logging.Level ; 28 import java.util.logging.Logger ; 29 import javax.swing.text.*; 30 import org.netbeans.modules.openide.loaders.UIException; 31 import org.openide.*; 32 import org.openide.filesystems.*; 33 import org.openide.loaders.*; 34 import org.openide.nodes.*; 35 import org.openide.util.*; 36 import org.openide.util.lookup.*; 37 import org.openide.windows.CloneableOpenSupport; 38 39 43 public class DataEditorSupport extends CloneableEditorSupport { 44 45 static final Logger ERR = Logger.getLogger("org.openide.text.DataEditorSupport"); 47 48 private final DataObject obj; 49 50 private NodeListener nodeL; 51 52 57 public DataEditorSupport (DataObject obj, CloneableEditorSupport.Env env) { 58 super (env, new DOEnvLookup (obj)); 59 this.obj = obj; 60 } 61 62 65 final CloneableEditorSupport.Env desEnv() { 66 return (CloneableEditorSupport.Env) env; 67 } 68 69 87 public static CloneableEditorSupport create (DataObject obj, MultiDataObject.Entry entry, org.openide.nodes.CookieSet set) { 88 return new SimpleES (obj, entry, set); 89 } 90 91 94 public final DataObject getDataObject () { 95 return obj; 96 } 97 98 101 protected String messageOpening () { 102 return NbBundle.getMessage (DataObject.class , "CTL_ObjectOpen", obj.getPrimaryFile().getNameExt(), 104 FileUtil.getFileDisplayName(obj.getPrimaryFile()) 105 ); 106 } 107 108 109 112 protected String messageOpened () { 113 return NbBundle.getMessage (DataObject.class, "CTL_ObjectOpened", obj.getPrimaryFile().getNameExt(), 115 FileUtil.getFileDisplayName(obj.getPrimaryFile()) 116 ); 117 } 118 119 124 protected String messageSave () { 125 return NbBundle.getMessage ( 126 DataObject.class, 127 "MSG_SaveFile", obj.getPrimaryFile().getNameExt() 129 ); 130 } 131 132 136 protected String messageName () { 137 if (! obj.isValid()) return ""; 139 return addFlagsToName(obj.getNodeDelegate().getDisplayName()); 140 } 141 142 protected String messageHtmlName() { 143 if (! obj.isValid()) return null; 144 145 String name = obj.getNodeDelegate().getHtmlDisplayName(); 146 if (name != null) { 147 if (!name.startsWith("<html>")) { 148 name = "<html>" + name; 149 } 150 name = addFlagsToName(name); 151 } 152 return name; 153 } 154 155 156 private String addFlagsToName(String name) { 157 int version = 3; 158 if (isModified ()) { 159 if (!obj.getPrimaryFile().canWrite()) { 160 version = 2; 161 } else { 162 version = 1; 163 } 164 } else { 165 if (!obj.getPrimaryFile().canWrite()) { 166 version = 0; 167 } 168 } 169 170 return NbBundle.getMessage (DataObject.class, "LAB_EditorName", 171 new Integer (version), name ); 172 } 173 174 protected String documentID() { 175 if (! obj.isValid()) return ""; return obj.getPrimaryFile().getName(); 177 } 178 179 183 protected String messageToolTip () { 184 return FileUtil.getFileDisplayName(obj.getPrimaryFile()); 186 } 187 188 196 protected String messageLine (Line line) { 197 return NbBundle.getMessage(DataObject.class, "FMT_LineDisplayName2", 198 obj.getPrimaryFile().getNameExt(), 199 FileUtil.getFileDisplayName(obj.getPrimaryFile()), 200 new Integer (line.getLineNumber() + 1)); 201 } 202 203 204 213 protected void initializeCloneableEditor (CloneableEditor editor) { 214 if(obj.isValid()) { 217 Node ourNode = obj.getNodeDelegate(); 218 editor.setActivatedNodes (new Node[] { ourNode }); 219 editor.setIcon(ourNode.getIcon (java.beans.BeanInfo.ICON_COLOR_16x16)); 220 NodeListener nl = new DataNodeListener(editor); 221 ourNode.addNodeListener(org.openide.nodes.NodeOp.weakNodeListener (nl, ourNode)); 222 nodeL = nl; 223 } 224 } 225 226 228 protected void notifyClosed() { 229 nodeL = null; 231 232 super.notifyClosed(); 233 } 234 235 241 protected StyledDocument createStyledDocument (EditorKit kit) { 242 StyledDocument doc = super.createStyledDocument (kit); 243 244 doc.putProperty(javax.swing.text.Document.TitleProperty, 246 FileUtil.getFileDisplayName(obj.getPrimaryFile()) 247 ); 248 doc.putProperty(javax.swing.text.Document.StreamDescriptionProperty, 250 obj 251 ); 252 253 return doc; 254 } 255 256 259 protected boolean canClose() { 260 if(desEnv().isModified() && isEnvReadOnly()) { 261 Object result = DialogDisplayer.getDefault().notify( 262 new NotifyDescriptor.Confirmation( 263 NbBundle.getMessage(DataObject.class, 264 "MSG_FileReadOnlyClosing", 265 new Object [] {((Env)env).getFileImpl().getNameExt()}), 266 NotifyDescriptor.OK_CANCEL_OPTION, 267 NotifyDescriptor.WARNING_MESSAGE 268 )); 269 270 return result == NotifyDescriptor.OK_OPTION; 271 } 272 273 return super.canClose(); 274 } 275 276 278 public void saveDocument() throws IOException { 279 if(desEnv().isModified() && isEnvReadOnly()) { 280 IOException e = new IOException("File is read-only: " + ((Env)env).getFileImpl()); UIException.annotateUser(e, null, 282 org.openide.util.NbBundle.getMessage(org.openide.loaders.DataObject.class, 283 "MSG_FileReadOnlySaving", 284 new java.lang.Object []{((org.openide.text.DataEditorSupport.Env) env).getFileImpl().getNameExt()}), 285 null, null); 286 throw e; 287 } 288 super.saveDocument(); 289 } 290 291 292 boolean isEnvReadOnly() { 293 CloneableEditorSupport.Env env = desEnv(); 294 return env instanceof Env && !((Env) env).getFileImpl().canWrite(); 295 } 296 297 298 final DataObject getDataObjectHack2 () { 299 return obj; 300 } 301 302 304 final void callUpdateTitles () { 305 updateTitles (); 306 } 307 308 317 public static DataObject findDataObject (Line l) { 318 if (l == null) throw new NullPointerException (); 319 return (DataObject)l.getLookup ().lookup (DataObject.class); 320 } 321 322 324 public static abstract class Env extends OpenSupport.Env implements CloneableEditorSupport.Env { 325 326 static final long serialVersionUID = -2945098431098324441L; 327 328 331 private transient FileObject fileObject; 332 333 337 transient FileLock fileLock; 338 340 private transient boolean warned; 341 342 345 public Env (DataObject obj) { 346 super (obj); 347 } 348 349 352 private FileObject getFileImpl () { 353 changeFile(); 355 return fileObject; 356 } 357 358 361 protected abstract FileObject getFile (); 362 363 367 protected abstract FileLock takeLock () throws IOException; 368 369 374 protected final void changeFile () { 375 376 FileObject newFile = getFile (); 377 378 if (newFile.equals (fileObject)) { 379 return; 381 } 382 383 boolean lockAgain; 384 if (fileLock != null) { 385 if(fileLock.isValid()) { 391 ERR.fine("changeFile releaseLock: " + fileLock + " for " + fileObject); fileLock.releaseLock (); 393 lockAgain = true; 394 } else { 395 fileLock = null; 396 lockAgain = false; 397 } 398 } else { 400 lockAgain = false; 401 } 402 403 fileObject = newFile; 404 ERR.fine("changeFile: " + newFile + " for " + fileObject); fileObject.addFileChangeListener (new EnvListener (this)); 406 407 if (lockAgain) { try { 409 fileLock = takeLock (); 410 ERR.fine("changeFile takeLock: " + fileLock + " for " + fileObject); } catch (IOException e) { 412 Logger.getLogger(DataEditorSupport.class.getName()).log(Level.WARNING, null, e); 413 } 414 } 415 416 } 417 418 419 422 public InputStream inputStream() throws IOException { 423 final FileObject fo = getFileImpl (); 424 if (!warned && fo.getSize () > 1024 * 1024) { 425 class ME extends org.openide.util.UserQuestionException { 426 private long size; 427 428 public ME (long size) { 429 super ("The file is too big. " + size + " bytes."); 430 this.size = size; 431 } 432 433 public String getLocalizedMessage () { 434 Object [] arr = { 435 fo.getPath (), 436 fo.getNameExt (), 437 new Long (size), new Long (size / 1024 + 1), new Long (size / (1024 * 1024)), new Long (size / (1024 * 1024 * 1024)), }; 442 return NbBundle.getMessage(DataObject.class, "MSG_ObjectIsTooBig", arr); 443 } 444 445 public void confirmed () { 446 warned = true; 447 } 448 } 449 throw new ME (fo.getSize ()); 450 } 451 InputStream is = getFileImpl ().getInputStream (); 452 return is; 453 } 454 455 458 public OutputStream outputStream() throws IOException { 459 ERR.fine("outputStream: " + fileLock + " for " + fileObject); if (fileLock == null || !fileLock.isValid()) { 461 fileLock = takeLock (); 462 } 463 ERR.fine("outputStream after takeLock: " + fileLock + " for " + fileObject); try { 465 return getFileImpl ().getOutputStream (fileLock); 466 } catch (IOException fse) { 467 if (fileLock == null || !fileLock.isValid()) { 470 fileLock = takeLock (); 471 } 472 ERR.fine("ugly workaround for #40552: " + fileLock + " for " + fileObject); return getFileImpl ().getOutputStream (fileLock); 474 } 475 } 476 477 479 public Date getTime() { 480 getFileImpl().refresh(false); 482 return getFileImpl ().lastModified (); 483 } 484 485 488 public String getMimeType() { 489 return getFileImpl ().getMIMEType (); 490 } 491 492 504 public void markModified() throws java.io.IOException { 505 if (fileLock == null || !fileLock.isValid()) { 508 fileLock = takeLock (); 509 } 510 ERR.fine("markModified: " + fileLock + " for " + fileObject); 512 if (!getFileImpl().canWrite()) { 513 if(fileLock != null && fileLock.isValid()) { 514 fileLock.releaseLock(); 515 } 516 throw new IOException("File " + getFileImpl().getNameExt() + " is read-only!"); } 519 520 this.getDataObject ().setModified (true); 521 } 522 523 526 public void unmarkModified() { 527 ERR.fine("unmarkModified: " + fileLock + " for " + fileObject); if (fileLock != null && fileLock.isValid()) { 529 fileLock.releaseLock(); 530 ERR.fine("releaseLock: " + fileLock + " for " + fileObject); } 532 533 this.getDataObject ().setModified (false); 534 } 535 536 540 final void fileChanged (boolean expected, long time) { 541 ERR.fine("fileChanged: " + expected + " for " + fileObject); if (expected) { 543 firePropertyChange (PROP_TIME, null, null); 545 } else { 546 firePropertyChange (PROP_TIME, null, new Date (time)); 547 } 548 } 549 550 553 final void fileRemoved(boolean canBeVetoed) { 554 570 } 571 572 public CloneableOpenSupport findCloneableOpenSupport() { 573 CloneableOpenSupport cos = super.findCloneableOpenSupport (); 574 if (cos instanceof DataEditorSupport) { 575 Object o = ((DataEditorSupport)cos).env; 576 if (o != this && o instanceof Env) { 577 ((Env)o).warned = this.warned; 578 } 579 } 580 return cos; 581 } 582 583 private void readObject (ObjectInputStream ois) throws ClassNotFoundException , IOException { 584 ois.defaultReadObject (); 585 warned = true; 586 } 587 } 589 592 private static final class EnvListener extends FileChangeAdapter { 593 594 private Reference <Env> env; 595 596 598 public EnvListener (Env env) { 599 this.env = new java.lang.ref.WeakReference <Env> (env); 600 } 601 602 603 604 public void fileDeleted(FileEvent fe) { 605 Env env = this.env.get(); 606 FileObject fo = fe.getFile(); 607 if(env == null || env.getFileImpl() != fo) { 608 fo.removeFileChangeListener(this); 611 return; 612 } 613 614 fo.removeFileChangeListener(this); 615 616 env.fileRemoved(true); 617 fo.addFileChangeListener(this); 618 } 619 620 623 public void fileChanged(FileEvent fe) { 624 Env env = this.env.get (); 625 if (env == null || env.getFileImpl () != fe.getFile ()) { 626 fe.getFile ().removeFileChangeListener (this); 629 return; 630 } 631 632 if(fe.getFile().isVirtual()) { 634 fe.getFile().removeFileChangeListener(this); 636 env.fileRemoved(true); 639 fe.getFile().addFileChangeListener(this); 640 } else { 641 env.fileChanged (fe.isExpected (), fe.getTime ()); 642 } 643 } 644 645 } 646 647 650 private final class DataNodeListener extends NodeAdapter { 651 652 CloneableEditor editor; 653 654 DataNodeListener (CloneableEditor editor) { 655 this.editor = editor; 656 } 657 658 public void propertyChange(final PropertyChangeEvent ev) { 659 Mutex.EVENT.writeAccess(new Runnable () { 660 public void run() { 661 if (Node.PROP_DISPLAY_NAME.equals(ev.getPropertyName())) { 662 callUpdateTitles(); 663 } 664 if (Node.PROP_ICON.equals(ev.getPropertyName())) { 665 if (obj.isValid()) { 666 editor.setIcon(obj.getNodeDelegate().getIcon (java.beans.BeanInfo.ICON_COLOR_16x16)); 667 } 668 } 669 } 670 }); 671 } 672 673 } 675 678 private static class DOEnvLookup extends AbstractLookup 679 implements PropertyChangeListener { 680 private DataObject dobj; 681 private InstanceContent ic; 682 683 public DOEnvLookup (DataObject dobj) { 684 this (dobj, new InstanceContent ()); 685 } 686 687 private DOEnvLookup (DataObject dobj, InstanceContent ic) { 688 super (ic); 689 this.ic = ic; 690 this.dobj = dobj; 691 dobj.addPropertyChangeListener(WeakListeners.propertyChange(this, dobj)); 692 693 updateLookup (); 694 } 695 696 private void updateLookup() { 697 ic.set(Arrays.asList(new Object [] { dobj, dobj.getPrimaryFile() }), null); 698 } 699 700 public void propertyChange(PropertyChangeEvent ev) { 701 String propName = ev.getPropertyName(); 702 if (propName == null || propName == DataObject.PROP_PRIMARY_FILE) { 703 updateLookup(); 704 } 705 } 706 } 707 708 } 709 | Popular Tags |