1 19 package org.netbeans.modules.xml.tax.cookies; 20 21 import java.io.IOException ; 22 import java.util.Enumeration ; 23 import java.lang.ref.SoftReference ; 24 import java.lang.ref.WeakReference ; 25 import java.lang.ref.ReferenceQueue ; 26 import java.beans.PropertyChangeListener ; 27 import java.beans.PropertyChangeEvent ; 28 import java.beans.PropertyChangeSupport ; 29 import javax.swing.text.Document ; 30 31 import org.xml.sax.InputSource ; 32 33 import org.openide.loaders.OpenSupport; 34 import org.openide.loaders.DataObject; 35 import org.openide.loaders.MultiDataObject; 36 import org.openide.nodes.CookieSet; 37 import org.openide.nodes.Node; 38 import org.openide.util.Task; 39 import org.openide.util.RequestProcessor; 40 import org.openide.*; 41 import org.openide.cookies.*; 42 43 import org.netbeans.tax.*; 44 import org.netbeans.tax.event.TreeEvent; 45 46 import org.netbeans.modules.xml.core.*; 47 import org.netbeans.modules.xml.core.tree.*; 48 import org.netbeans.modules.xml.core.cookies.*; 49 import org.netbeans.modules.xml.core.text.*; 50 import org.netbeans.modules.xml.core.sync.*; 51 import org.netbeans.modules.xml.tax.cookies.TreeDocumentCookie; 52 import org.netbeans.modules.xml.tax.cookies.TreeEditorCookie; 53 import org.netbeans.modules.xml.tax.*; 54 import org.netbeans.modules.xml.tax.parser.DTDParsingSupport; 55 import org.netbeans.modules.xml.tax.parser.ParsingSupport; 56 import org.netbeans.modules.xml.tax.parser.XMLParsingSupport; 57 import org.openide.util.Utilities; 58 59 62 public class TreeEditorCookieImpl implements TreeEditorCookie, UpdateDocumentCookie { 63 64 65 private Task prepareTask; 66 67 private final Exception [] prepareException = new Exception [1]; 68 69 private XMLDataObjectLook xmlDO; 70 71 private final PropertyChangeSupport pchs; 72 73 private final CookieManagerCookie cookieMgr; 74 75 private int status; 76 77 private int oldStatus; 78 79 80 private TreeReference tree; 81 82 private Object treeLock = new TreeLock(); 83 84 85 private PropertyChangeListener treeListener = null; 86 87 private TreeDocumentCookie treeDocumentCookie; 89 private Representation rep; 91 92 96 97 public TreeEditorCookieImpl (XMLDataObjectLook xmlDO) { 98 this.xmlDO = xmlDO; 99 this.cookieMgr = xmlDO.getCookieManager(); 100 this.status = TreeEditorCookie.STATUS_NOT; 101 this.oldStatus = status; 102 this.pchs = new PropertyChangeSupport (this); 103 } 104 105 106 110 112 public TreeDocumentRoot openDocumentRoot () throws IOException , TreeException { 113 if ( Util.THIS.isLoggable() ) Util.THIS.debug("TreeEditorCookieImpl.openDocumentRoot()"); 115 for (;;) { 116 117 prepareDocumentRoot().waitFinished(); 118 119 synchronized (this) { TreeDocumentRoot root = getDocumentRoot(); 121 if (root == null) { 122 if (prepareException[0] instanceof IOException ) { 123 throw (IOException ) prepareException[0]; 124 } else if (prepareException[0] instanceof TreeException) { 125 throw (TreeException) prepareException[0]; 126 } else { 127 if ( Util.THIS.isLoggable() ) Util.THIS.debug("\tTree parsing retry due to (expected null): " + prepareException[0]); 129 prepareTask = null; 130 continue; } 133 } else { 134 if ( Util.THIS.isLoggable() ) Util.THIS.debug("TreeEditorCookieImpl.openDocumentRoot() = " + root); 136 return root; 137 } 138 } 139 } 140 } 141 142 145 public Task prepareDocumentRoot () { 146 147 synchronized (this) { 149 if (prepareTask != null) return prepareTask; 150 151 prepareTask = new Task(new Runnable () { 152 public void run() { 153 try { 154 155 InputSource src = inputSource(); 156 parseTree(src, true); 157 158 prepareException[0] = null; 159 } catch (IOException ex) { 160 ErrorManager em = ErrorManager.getDefault(); 161 em.annotate(ex, Util.THIS.getString("BK0001")); 162 prepareException[0] = ex; 163 } catch (TreeException ex) { 164 ErrorManager em = ErrorManager.getDefault(); 165 em.annotate(ex, Util.THIS.getString("BK0001")); 166 prepareException[0] = ex; 167 } 168 169 fireTreeAndStatus(); 170 171 } 172 }); 173 174 } 175 176 new Thread (prepareTask, "Parsing tree...").start(); return prepareTask; 178 } 179 180 183 public TreeDocumentRoot getDocumentRoot () { 184 if (tree == null) 185 return null; 186 187 TreeDocumentRoot root = tree.getDocumentRoot(); 188 if (root == null) { 189 190 194 return null; 196 } else { 197 return root; 198 } 199 } 200 201 public int getStatus () { 202 return status; 203 } 204 205 public void addPropertyChangeListener (PropertyChangeListener l) { 206 pchs.addPropertyChangeListener (l); 207 } 208 209 public void removePropertyChangeListener (PropertyChangeListener l) { 210 pchs.removePropertyChangeListener (l); 211 } 212 213 214 218 223 private void setTree (TreeDocumentRoot doc) { 224 if ( Util.THIS.isLoggable() ) Util.THIS.debug ("TreeEditorCookieImpl::setTree: " + doc); 226 if ( (doc == getDocumentRoot()) && 227 (doc != null) ) { 228 return; 229 } 230 231 if ( Util.THIS.isLoggable() ) Util.THIS.debug ("\tdifferent doc delivered, merging"); 233 TreeDocumentRoot oldTreeDoc = getDocumentRoot(); 234 235 try { 236 237 if (doc == null) { 239 if ( Util.THIS.isLoggable() ) Util.THIS.debug ("\tbefore REMOVE : " + getDocumentRoot()); 241 removeTreeDocumentCookie(); 242 243 if (rep != null) { 244 } 247 248 if (getDocumentRoot() != null) { 249 ((TreeObject)getDocumentRoot()).removePropertyChangeListener (treeListener); 250 treeListener = null; 251 } 252 tree = null; 253 254 } else if (tree == null) { 256 if ( Util.THIS.isLoggable() ) Util.THIS.debug ("\tbefore ADD : " + getDocumentRoot()); 258 tree = new TreeReference(doc); 259 260 treeListener = new TreeListener(); 261 ((TreeObject)getDocumentRoot()).addPropertyChangeListener (treeListener); 262 263 addTreeDocumentCookie(); 264 265 } else { 267 if ( Util.THIS.isLoggable() ) Util.THIS.debug ("\tbefore MERGE : " + getDocumentRoot()); 269 271 TreeObject root = (TreeObject)getDocumentRoot(); 272 root.removePropertyChangeListener(treeListener); 273 root.merge ((TreeObject)doc); 274 root.addPropertyChangeListener(treeListener); 275 276 } 277 278 if (rep == null) { 279 280 if (xmlDO instanceof XMLDataObject) { 281 rep = new XMLTreeRepresentation(this, xmlDO.getSyncInterface()); 282 } else { 283 rep = new DTDTreeRepresentation(this, xmlDO.getSyncInterface()); 284 } 285 286 xmlDO.getSyncInterface().addRepresentation(rep); 287 } 288 289 } catch (CannotMergeException exc) { if ( Util.THIS.isLoggable() ) Util.THIS.debug ("MERGE FATAL ERROR:"); exc.printStackTrace(); 292 } finally { 293 294 } 295 } 296 297 298 302 public void updateTree (Object input) { 303 try { 304 parseTree((InputSource ) input, false); 305 306 } catch (TreeException ex) { 307 308 } catch (IOException ex) { 309 310 } 311 312 fireTreeAndStatus(); 313 } 314 315 323 private void parseTree (InputSource input, boolean force) throws TreeException, IOException { 324 325 if (input == null) throw new IOException (); 326 327 String annotation = null; 328 329 if ((status == STATUS_NOT) && (getDocumentRoot() == null) && (force == false)) 331 return; 332 333 if ( Util.THIS.isLoggable() ) Util.THIS.debug ("XMLTreeEditorCookieImpl::updateTree(force=" + force + ")"); 335 try { 336 337 ParsingSupport parser = null; 338 339 if (xmlDO instanceof XMLDataObject) { 340 parser = new XMLParsingSupport(); 341 } else if (xmlDO instanceof DTDDataObject) { 342 parser = new DTDParsingSupport(); 343 } else { 344 if ( Util.THIS.isLoggable() ) Util.THIS.debug("parseTree() Unexpected instance: " + xmlDO.getClass()); } 346 347 annotation = Util.THIS.getString ("MSG_Unexpected_exception_in_parser_or_handler"); 348 349 if ( Util.THIS.isLoggable() ) Util.THIS.debug("PARSING: " + input.getSystemId()); 351 TreeDocumentRoot doc = (TreeDocumentRoot) parser.parse(input); 352 353 annotation = Util.THIS.getString ("MSG_Unexpected_exception_in_merge"); 354 setTreeAndStatus (doc, STATUS_OK); 355 356 } catch (Exception ex) { 357 358 if ( Util.THIS.isLoggable() ) Util.THIS.debug ("XMLTreeEditorCookieImpl::updateTree", ex); 360 setTreeAndStatus (null, STATUS_ERROR); 361 362 Util.THIS.getErrorManager().annotate (ex, annotation); 364 Util.THIS.debug(ex); 365 366 if (ex instanceof TreeException || ex instanceof IOException ) { 367 371 372 375 if (ex instanceof IOException ) throw (IOException ) ex; 376 if (ex instanceof TreeException) throw (TreeException) ex; 377 378 } else if (ex instanceof RuntimeException ) { 379 380 throw new TreeException(ex); 382 } 383 } 384 } 385 386 387 private void addTreeDocumentCookie() { 388 treeDocumentCookie = new TreeDocumentCookieImpl(); 389 cookieMgr.addCookie(treeDocumentCookie); 390 } 391 392 private void removeTreeDocumentCookie() { 393 if ( treeDocumentCookie != null ) { 394 cookieMgr.removeCookie (treeDocumentCookie); 395 } 396 } 397 398 399 400 private void setTreeAndStatus(final TreeDocumentRoot doc, final int newStatus) { 401 402 synchronized (treeLock) { 403 setTree (doc); oldStatus = status; 405 status = newStatus; 406 407 if ( Util.THIS.isLoggable() ) Util.THIS.debug ("Tree status transition: " + oldStatus + "=>" + newStatus); } 409 } 410 411 412 private void fireTreeAndStatus() { 413 final int fireStatus = status; 414 final int fireOldStatus = oldStatus; 415 final Object fireTree = getDocumentRoot(); 416 417 if ( Util.THIS.isLoggable() ) Util.THIS.debug("Firing tree status transition: " + oldStatus + "=>" + status); 419 RequestProcessor.postRequest( new Runnable () { 420 public void run() { 421 pchs.firePropertyChange (PROP_STATUS, fireOldStatus, fireStatus); 422 pchs.firePropertyChange (PROP_DOCUMENT_ROOT, null, fireTree); 423 } 424 }); 425 } 426 427 430 public void updateDocumentRoot() { 433 434 TreeDocumentCookie cookie = (TreeDocumentCookie) xmlDO.getCookie(TreeDocumentCookie.class); 435 if (cookie != null && cookie.getDocumentRoot() != null) { 436 Task task = new Task( new Runnable () { 437 public void run() { 438 try { 439 parseTree(inputSource(), true); 440 } catch (TreeException ex) { 441 Util.THIS.debug (ex); 442 } catch (IOException ex) { 443 Util.THIS.debug (ex); 444 } 445 } 446 }); 447 448 xmlDO.getSyncInterface().postRequest(task); 449 task.waitFinished(); 450 fireTreeAndStatus(); 451 } 452 453 } 454 455 private InputSource inputSource() { 459 Representation primary = xmlDO.getSyncInterface().getPrimaryRepresentation(); 460 InputSource src = (InputSource ) primary.getChange(InputSource .class); 461 if (src == null) { 462 if ( Util.THIS.isLoggable() ) Util.THIS.debug("Primary representation can not provide InputSource: " + primary ); } 464 return src; 465 } 466 467 468 472 private class TreeDocumentCookieImpl implements TreeDocumentCookie { 473 474 public TreeDocumentRoot getDocumentRoot() { 475 try { 476 return TreeEditorCookieImpl.this.openDocumentRoot(); 477 } catch (IOException ex) { 478 return null; 479 } catch (TreeException ex) { 480 return null; 481 } 482 } 483 484 } 486 487 491 496 private class TreeListener implements PropertyChangeListener { 497 498 private PropertyChangeEvent last; 499 500 public void propertyChange (PropertyChangeEvent e) { 501 if ( Util.THIS.isLoggable() ) Util.THIS.debug ("\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); if ( Util.THIS.isLoggable() ) Util.THIS.debug ("!!! TreeEditorCookieImpl::TreeListener::propertyChange: propertyName = '" + e.getPropertyName() + "'"); if ( Util.THIS.isLoggable() ) Util.THIS.debug ("!!! ::TreeListener::propertyChange: last = " + last); if ( Util.THIS.isLoggable() ) Util.THIS.debug ("!!! ::TreeListener::propertyChange: e = " + e); 506 508 if ( e != last ) { 509 last = e; 510 xmlDO.getSyncInterface().representationChanged (TreeDocument.class); 511 512 if ( Util.THIS.isLoggable() ) Util.THIS.debug ("!!! ::TreeListener::propertyChange: *after* representationChanged (TreeDocument.class)"); if ( Util.THIS.isLoggable() ) Util.THIS.debug ("!!! ::TreeListener::propertyChange: ( e instanceof TreeEvent ) => " + ( e instanceof TreeEvent )); 515 boolean updateFromText = false; 516 517 if ( ( e instanceof TreeEvent ) && ( (TreeEvent)e).isBubbling() ) { 518 TreeEvent treeEvent = (TreeEvent)e; 519 if ( ( treeEvent.getOriginalSource() instanceof TreeDocumentType ) && 520 ( TreeDocumentType.PROP_PUBLIC_ID.equals (treeEvent.getOriginalPropertyName()) || 521 TreeDocumentType.PROP_SYSTEM_ID.equals (treeEvent.getOriginalPropertyName()) ) ) { 522 updateFromText = true; 523 } 524 } else if ( TreeParentNode.PROP_CHILD_LIST.equals (e.getPropertyName()) && 525 ( e.getNewValue() instanceof TreeDocumentType ) ) { 526 updateFromText = true; 527 } 528 529 if ( Util.THIS.isLoggable() ) Util.THIS.debug ("!!! ::TreeListener::propertyChange: updateFromText = " + updateFromText); 531 if ( updateFromText ) { 532 xmlDO.getSyncInterface().representationChanged (Document .class); } 534 } 535 } 536 } 538 539 543 546 private class TreeReference extends WeakReference implements Runnable { 547 548 TreeReference (TreeDocumentRoot root) { 549 super(root, Utilities.activeReferenceQueue()); 550 } 551 552 public TreeDocumentRoot getDocumentRoot() { 553 return (TreeDocumentRoot) super.get(); 554 } 555 556 public TreeEditorCookieImpl getEditor() { 557 return TreeEditorCookieImpl.this; 558 } 559 560 public String toString() { 561 return "TreeReference[" + getEditor().xmlDO.getName() + "]"; 562 } 563 564 public void run() { 566 if ( Util.THIS.isLoggable() ) Util.THIS.debug("" + this + " reclaimed."); getEditor().setTreeAndStatus(null, STATUS_NOT); 568 getEditor().fireTreeAndStatus(); 569 } 570 } 572 573 574 578 public static class CookieFactoryImpl extends CookieFactory { 579 580 private WeakReference editor; 581 582 private final XMLDataObjectLook dobj; 584 585 public CookieFactoryImpl (XMLDataObjectLook dobj) { 586 this.dobj = dobj; 587 } 588 589 592 public Node.Cookie createCookie (Class klass) { 593 if ( klass.isAssignableFrom(TreeEditorCookie.class) ) { 594 return createEditor(); 595 } else if ( klass.isAssignableFrom(UpdateDocumentCookie.class) ) { 596 return createEditor(); 597 } else { 598 return null; 599 } 600 } 601 602 private synchronized TreeEditorCookieImpl createEditor() { if (editor == null) { 605 return prepareEditor(); 606 } else { 607 TreeEditorCookieImpl cached = (TreeEditorCookieImpl) editor.get(); 608 if (cached == null) { 609 return prepareEditor(); 610 } else { 611 return cached; 612 } 613 } 614 } 615 616 private TreeEditorCookieImpl prepareEditor() { 617 if ( Util.THIS.isLoggable() ) Util.THIS.debug ("Initializing TreeEditorCookieImpl ..."); 619 TreeEditorCookieImpl cake = new TreeEditorCookieImpl (dobj); 620 editor = new WeakReference (cake); 621 return cake; 622 } 623 624 public Class [] supportedCookies() { 625 return new Class [] { 626 TreeEditorCookie.class, 627 UpdateDocumentCookie.class, 628 }; 629 } 630 631 } 633 634 638 639 private class TreeLock { 640 } 641 642 } 643 | Popular Tags |