1 19 20 package org.netbeans.modules.xml.multiview; 21 22 import org.netbeans.core.spi.multiview.MultiViewElement; 23 import org.openide.cookies.SaveCookie; 24 import org.openide.cookies.EditorCookie; 25 import org.openide.filesystems.FileObject; 26 import org.openide.filesystems.FileLock; 27 import org.openide.filesystems.FileAlreadyLockedException; 28 import org.openide.loaders.DataObjectExistsException; 29 import org.openide.loaders.MultiDataObject; 30 import org.openide.loaders.MultiFileLoader; 31 import org.openide.nodes.CookieSet; 32 import org.openide.windows.CloneableTopComponent; 33 import org.openide.ErrorManager; 34 import org.openide.NotifyDescriptor; 35 import org.openide.DialogDisplayer; 36 import org.openide.util.io.ReaderInputStream; 37 import org.openide.util.NbBundle; 38 39 import java.io.*; 40 import java.util.Enumeration ; 41 import java.util.Date ; 42 import java.lang.ref.WeakReference ; 43 44 53 public abstract class XmlMultiViewDataObject extends MultiDataObject implements CookieSet.Factory { 54 55 public static final String PROP_DOCUMENT_VALID = "document_valid"; public static final String PROP_SAX_ERROR = "sax_error"; public static final String PROPERTY_DATA_MODIFIED = "data modified"; public static final String PROPERTY_DATA_UPDATED = "data changed"; protected XmlMultiViewEditorSupport editorSupport; 60 private org.xml.sax.SAXException saxError; 61 62 private final DataCache dataCache = new DataCache(); 63 private EncodingHelper encodingHelper = new EncodingHelper(); 64 private transient long timeStamp = 0; 65 private transient WeakReference lockReference; 66 67 68 private MultiViewElement activeMVElement; 69 70 private final SaveCookie saveCookie = new SaveCookie() { 71 72 public void save() throws java.io.IOException { 73 getEditorSupport().saveDocument(); 74 } 75 }; 76 77 78 public XmlMultiViewDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException { 79 super(pf, loader); 80 getCookieSet().add(XmlMultiViewEditorSupport.class, this); 81 } 82 83 protected EditorCookie createEditorCookie() { 84 return getEditorSupport(); 85 } 86 87 public org.openide.nodes.Node.Cookie createCookie(Class clazz) { 88 if (clazz.isAssignableFrom(XmlMultiViewEditorSupport.class)) { 89 return getEditorSupport(); 90 } else { 91 return null; 92 } 93 } 94 95 96 protected synchronized XmlMultiViewEditorSupport getEditorSupport() { 97 if(editorSupport == null) { 98 editorSupport = new XmlMultiViewEditorSupport(this); 99 editorSupport.getMultiViewDescriptions(); 100 } 101 return editorSupport; 102 } 103 104 106 public void goToXmlView() { 107 getEditorSupport().goToXmlPerspective(); 108 } 109 110 protected void setSaxError(org.xml.sax.SAXException saxError) { 111 org.xml.sax.SAXException oldError = this.saxError; 112 this.saxError=saxError; 113 if (oldError==null) { 114 if (saxError != null) { 115 firePropertyChange(PROP_DOCUMENT_VALID, Boolean.TRUE, Boolean.FALSE); 116 } 117 } else { 118 if (saxError == null) { 119 firePropertyChange(PROP_DOCUMENT_VALID, Boolean.FALSE, Boolean.TRUE); 120 } 121 } 122 123 String oldErrorMessage = getErrorMessage(oldError); 124 String newErrorMessage = getErrorMessage(saxError); 125 if (oldErrorMessage==null) { 126 if (newErrorMessage!=null) { 127 firePropertyChange(PROP_SAX_ERROR, null, newErrorMessage); 128 } 129 } else if (!oldErrorMessage.equals(newErrorMessage)) { 130 firePropertyChange(PROP_SAX_ERROR, oldErrorMessage, newErrorMessage); 131 } 132 } 133 134 private static String getErrorMessage(Exception e) { 135 return e == null ? null : e.getMessage(); 136 } 137 138 public org.xml.sax.SAXException getSaxError() { 139 return saxError; 140 } 141 142 143 protected java.awt.Image getXmlViewIcon() { 144 return org.openide.util.Utilities.loadImage("org/netbeans/modules/xml/multiview/resources/xmlObject.gif"); } 146 147 149 protected abstract DesignMultiViewDesc[] getMultiViewDesc(); 150 151 public void setLastOpenView(int index) { 152 getEditorSupport().setLastOpenView(index); 153 } 154 155 156 protected FileObject handleRename(String name) throws IOException { 157 FileObject retValue = super.handleRename(name); 158 getEditorSupport().updateDisplayName(); 159 return retValue; 160 } 161 162 169 public void setModified(boolean modif) { 170 super.setModified(modif); 171 if (modif) { 173 if (getCookie(SaveCookie.class) == null) { 175 getCookieSet().add(saveCookie); 176 } 177 } else { 178 if(saveCookie.equals(getCookie(SaveCookie.class))) { 180 getCookieSet().remove(saveCookie); 181 } 182 183 } 184 } 185 186 public boolean canClose() { 187 final CloneableTopComponent topComponent = ((CloneableTopComponent) getEditorSupport().getMVTC()); 188 if (topComponent != null){ 189 Enumeration enumeration = topComponent.getReference().getComponents(); 190 if (enumeration.hasMoreElements()) { 191 enumeration.nextElement(); 192 if (enumeration.hasMoreElements()) { 193 return true; 194 } 195 } 196 } 197 FileLock lock; 198 try { 199 lock = waitForLock(); 200 } catch (IOException e) { 201 ErrorManager.getDefault().notify(e); 202 return !isModified(); 203 } 204 try { 205 return !isModified(); 206 } finally { 207 lock.releaseLock(); 208 } 209 } 210 211 public FileLock waitForLock() throws IOException { 212 return waitForLock(10000); 213 } 214 215 public FileLock waitForLock(long timeout) throws IOException { 216 long t = System.currentTimeMillis() + timeout; 217 long sleepTime = 50; 218 for (;;) { 219 try { 220 return dataCache.lock(); 221 } catch (IOException e) { 222 if (System.currentTimeMillis() > t) { 223 throw (IOException) new IOException("Cannot wait for data lock for more than " + timeout + " ms").initCause(e); } 225 try { 226 Thread.sleep(sleepTime); 227 sleepTime = 3 * sleepTime / 2; 228 } catch (InterruptedException e1) { 229 } 231 } 232 } 233 } 234 235 public org.netbeans.core.api.multiview.MultiViewPerspective getSelectedPerspective() { 236 return getEditorSupport().getSelectedPerspective(); 237 } 238 239 242 public void showElement(Object element) { 243 getEditorSupport().edit(); 244 } 245 246 248 protected MultiViewElement getActiveMultiViewElement() { 249 return activeMVElement; 250 } 251 void setActiveMultiViewElement(MultiViewElement element) { 252 activeMVElement = element; 253 } 254 257 public void openView(int index) { 258 getEditorSupport().openView(index); 259 } 260 261 protected abstract String getPrefixMark(); 262 263 boolean acceptEncoding() throws IOException { 264 encodingHelper.resetEncoding(); 265 DataCache dataCache = getDataCache(); 266 String s = dataCache.getStringData(); 267 String encoding = encodingHelper.detectEncoding(s.getBytes()); 268 if (!encodingHelper.getEncoding().equals(encoding)) { 269 Object result = showChangeEncodingDialog(encoding); 270 if (NotifyDescriptor.YES_OPTION.equals(result)) { 271 dataCache.setData(encodingHelper.setDefaultEncoding(s)); 272 } else if (NotifyDescriptor.NO_OPTION.equals(result)) { 273 showUsingDifferentEncodingMessage(encoding); 274 } else { 275 return false; 276 } 277 } 278 return true; 279 } 280 281 private void showUsingDifferentEncodingMessage(String encoding) { 282 String message = NbBundle.getMessage(XmlMultiViewDataObject.class, "TEXT_TREAT_USING_DIFFERENT_ENCODING", encoding, 283 encodingHelper.getEncoding()); 284 NotifyDescriptor.Message descriptor = new NotifyDescriptor.Message(message); 285 descriptor.setTitle(getPrimaryFile().getPath()); 286 DialogDisplayer.getDefault().notify(descriptor); 287 } 288 289 private Object showChangeEncodingDialog(String encoding) { 290 String message = NbBundle.getMessage(Utils.class, "TEXT_CHANGE_DECLARED_ENCODING", encoding, 291 encodingHelper.getEncoding()); 292 NotifyDescriptor descriptor = new NotifyDescriptor.Confirmation(message, getPrimaryFile().getPath(), 293 NotifyDescriptor.YES_NO_CANCEL_OPTION); 294 return DialogDisplayer.getDefault().notify(descriptor); 295 } 296 297 public EncodingHelper getEncodingHelper() { 298 return encodingHelper; 299 } 300 301 public DataCache getDataCache() { 302 return dataCache; 303 } 304 305 308 public class DataCache { 309 310 private transient String buffer = null; 312 private long fileTime = 0; 313 314 public void loadData() { 315 FileObject file = getPrimaryFile(); 316 if (fileTime == file.lastModified().getTime()) { 317 return; 318 } 319 try { 320 FileLock dataLock = lock(); 321 loadData(file, dataLock); 322 } catch (IOException e) { 323 if (buffer == null) { 324 buffer = ""; } 326 } 327 } 328 329 334 public void reloadData() throws IOException{ 335 FileObject file = getPrimaryFile(); 336 if (fileTime == file.lastModified().getTime()) { 337 return; 338 } 339 FileLock lock = getLock(); 340 if (lock == null){ 341 lock = lock(); 342 } 343 loadData(file, lock); 344 345 } 346 348 public void loadData(FileObject file, FileLock dataLock) throws IOException { 349 try { 350 BufferedInputStream inputStream = new BufferedInputStream(file.getInputStream()); 351 String encoding = encodingHelper.detectEncoding(inputStream); 352 if (!encodingHelper.getEncoding().equals(encoding)) { 353 showUsingDifferentEncodingMessage(encoding); 354 } 355 Reader reader = new InputStreamReader(inputStream, encodingHelper.getEncoding()); 356 long time; 357 StringBuffer sb = new StringBuffer (2048); 358 try { 359 char[] buf = new char[1024]; 360 time = file.lastModified().getTime(); 361 int i; 362 while ((i = reader.read(buf,0,1024)) != -1) { 363 sb.append(buf,0,i); 364 } 365 } finally { 366 reader.close(); 367 } 368 buffer = null; 369 fileTime = time; 370 setData(dataLock, sb.toString(), true); 371 } finally { 372 dataLock.releaseLock(); 373 } 374 } 375 377 public void setData(FileLock lock, String s, boolean modify) throws IOException { 378 testLock(lock); 379 boolean modified = isModified() || modify; 380 long oldTimeStamp = timeStamp; 381 if (setData(s)) { 382 if (!modified) { 383 saveData(lock); 384 firePropertyChange(PROPERTY_DATA_UPDATED, new Long (oldTimeStamp), new Long (timeStamp)); 385 } else { 386 firePropertyChange(PROPERTY_DATA_MODIFIED, new Long (oldTimeStamp), new Long (timeStamp)); 387 } 388 } 389 } 390 391 private boolean setData(String s) { 392 if (s.equals(buffer)) { 394 return false; 395 } 396 buffer = s; 397 long newTimeStamp = new Date ().getTime(); 398 if (newTimeStamp <= timeStamp) { 399 newTimeStamp = timeStamp + 1; 400 } 401 timeStamp = newTimeStamp; 402 fileTime = 0; 403 return true; 404 } 405 406 public synchronized void saveData(FileLock dataLock) { 407 if (buffer == null || fileTime == getPrimaryFile().lastModified().getTime()) { 408 return; 409 } 410 411 try { 412 XmlMultiViewEditorSupport editorSupport = getEditorSupport(); 413 if (editorSupport.getDocument() == null) { 414 XmlMultiViewEditorSupport.XmlEnv xmlEnv = editorSupport.getXmlEnv(); 415 FileLock lock = xmlEnv.takeLock(); 416 OutputStream outputStream = getPrimaryFile().getOutputStream(lock); 417 Writer writer = new OutputStreamWriter(outputStream, encodingHelper.getEncoding()); 418 try { 419 writer.write(buffer); 420 } finally { 421 writer.close(); 422 lock.releaseLock(); 423 xmlEnv.unmarkModified(); 424 resetFileTime(); 425 } 426 } else { 427 editorSupport.saveDocument(dataLock); 428 } 429 } catch (IOException e) { 430 ErrorManager.getDefault().notify(e); 431 } 432 } 433 434 public FileLock lock() throws IOException { 435 FileLock current = getLock(); 436 if (current != null) { 437 throw new FileAlreadyLockedException("File is already locked by [" + current + "]."); } 439 FileLock l = new FileLock(); 440 lockReference = new WeakReference (l); 441 return l; 442 } 443 444 private FileLock getLock() { 445 FileLock l = lockReference == null ? null : (FileLock) lockReference.get(); 447 if (l != null && !l.isValid()) { 448 l = null; 449 } 450 return l; 451 } 452 453 public String getStringData() { 454 if (buffer == null) { 455 loadData(); 456 } 457 return buffer; 458 } 459 460 public byte[] getData() { 461 try { 462 return getStringData().getBytes(encodingHelper.getEncoding()); 463 } catch (UnsupportedEncodingException e) { 464 ErrorManager.getDefault().notify(e); 465 return null; } 467 } 468 469 public void setData(FileLock lock, byte[] data, boolean modify) throws IOException { 470 encodingHelper.detectEncoding(data); 471 setData(lock, new String (data, encodingHelper.getEncoding()), modify); 472 } 473 474 public long getTimeStamp() { 475 return timeStamp; 476 } 477 478 public InputStream createInputStream() { 479 try { 480 encodingHelper.detectEncoding(getStringData().getBytes()); 481 return new ReaderInputStream(new StringReader(getStringData()), encodingHelper.getEncoding()); 482 } catch (IOException e) { 483 ErrorManager.getDefault().notify(e); 484 return null; 485 } 486 } 487 488 public Reader createReader() throws IOException { 489 return new StringReader(getStringData()); 490 } 491 492 public OutputStream createOutputStream() throws IOException { 493 final FileLock dataLock = lock(); 494 return new ByteArrayOutputStream() { 495 public void close() throws IOException { 496 try { 497 super.close(); 498 setData(dataLock, toByteArray(), true); 499 } finally { 500 dataLock.releaseLock(); 501 } 502 } 503 }; 504 } 505 506 public OutputStream createOutputStream(final FileLock dataLock, final boolean modify) throws IOException { 507 testLock(dataLock); 508 return new ByteArrayOutputStream() { 509 public void close() throws IOException { 510 super.close(); 511 setData(dataLock, toByteArray(), modify); 512 if (!modify) { 513 dataCache.saveData(dataLock); 514 } 515 } 516 }; 517 } 518 519 public Writer createWriter() throws IOException { 520 final FileLock dataLock = lock(); 521 return new StringWriter() { 522 public void close() throws IOException { 523 try { 524 super.close(); 525 setData(dataLock, toString(), true); 526 } finally { 527 dataLock.releaseLock(); 528 } 529 } 530 }; 531 } 532 533 public Writer createWriter(final FileLock dataLock, final boolean modify) throws IOException { 534 testLock(dataLock); 535 return new StringWriter() { 536 public void close() throws IOException { 537 super.close(); 538 setData(dataLock, toString(), modify); 539 if (!modify) { 540 dataCache.saveData(dataLock); 541 } 542 } 543 }; 544 } 545 546 public void testLock(FileLock lock) throws IOException { 547 if (lock == null) { 548 throw new IOException("Lock is null."); } else if (lock != getLock()){ 550 throw new IOException("Invalid lock [" + lock + "]. Expected [" + getLock() + "]."); } 552 } 553 554 public void resetFileTime() { 555 fileTime = getPrimaryFile().lastModified().getTime(); 556 } 557 } 558 } 559 | Popular Tags |