1 19 20 package org.netbeans.modules.settings.convertors; 21 22 import java.beans.PropertyChangeEvent ; 23 import java.beans.PropertyChangeListener ; 24 import java.beans.PropertyChangeSupport ; 25 import java.io.BufferedOutputStream ; 26 import java.io.ByteArrayOutputStream ; 27 import java.io.IOException ; 28 import java.io.OutputStream ; 29 import java.io.OutputStreamWriter ; 30 import java.io.Serializable ; 31 import java.io.Writer ; 32 import java.lang.ref.SoftReference ; 33 import java.lang.ref.WeakReference ; 34 import java.lang.reflect.Method ; 35 import java.util.Arrays ; 36 import java.util.Collections ; 37 import java.util.logging.Level ; 38 import javax.swing.JComponent ; 39 import org.netbeans.modules.settings.Env; 40 import org.netbeans.modules.settings.ScheduledRequest; 41 import org.netbeans.spi.settings.Convertor; 42 import org.netbeans.spi.settings.Saver; 43 import org.openide.cookies.InstanceCookie; 44 import org.openide.cookies.SaveCookie; 45 import org.openide.filesystems.FileChangeAdapter; 46 import org.openide.filesystems.FileEvent; 47 import org.openide.filesystems.FileLock; 48 import org.openide.filesystems.FileObject; 49 import org.openide.filesystems.FileSystem; 50 import org.openide.filesystems.FileUtil; 51 import org.openide.loaders.DataObject; 52 import org.openide.loaders.Environment; 53 import org.openide.loaders.InstanceDataObject; 54 import org.openide.modules.ModuleInfo; 55 import org.openide.nodes.Node; 56 import org.openide.util.Lookup; 57 import org.openide.util.SharedClassObject; 58 import org.openide.util.lookup.AbstractLookup; 59 import org.openide.util.lookup.InstanceContent; 60 import org.openide.windows.TopComponent; 61 62 67 public final class SerialDataConvertor extends FileChangeAdapter 68 implements PropertyChangeListener , FileSystem.AtomicAction { 69 73 static final String EA_NAME = "name"; 75 final Object READWRITE_LOCK = new Object (); 76 private final InstanceContent lkpContent; 77 private final Lookup lookup; 78 private final DataObject dobj; 79 private final FileObject provider; 80 private final SerialDataConvertor.NodeConvertor node; 81 private SerialDataConvertor.SettingsInstance instance; 82 private SaveSupport saver; 83 84 85 public SerialDataConvertor(DataObject dobj, FileObject provider) { 86 this.dobj = dobj; 87 this.provider = provider; 88 lkpContent = new InstanceContent(); 89 90 FileObject fo = dobj.getPrimaryFile(); 91 fo.addFileChangeListener(FileUtil.weakFileChangeListener(this, fo)); 92 93 SerialDataConvertor.SettingsInstance si = createInstance(null); 94 if (isModuleEnabled(si)) { 95 instance = si; 96 lkpContent.add(instance); 97 } 98 lkpContent.add(this); 99 node = new SerialDataConvertor.NodeConvertor(); 100 lkpContent.add(this, node); 101 lookup = new AbstractLookup(lkpContent); 102 } 103 104 109 public void write(Writer w, Object inst) throws IOException { 110 XMLSettingsSupport.storeToXML10(inst, w, ModuleInfoManager.getDefault().getModuleInfo(inst.getClass())); 111 } 112 113 116 void handleUnfiredChange() { 117 saver.propertyChange(null); 118 } 119 120 DataObject getDataObject() { 121 return dobj; 122 } 123 124 FileObject getProvider() { 125 return provider; 126 } 127 128 129 public final Lookup getLookup() { 130 return lookup; 131 } 132 133 134 private SettingsInstance createInstance(Object inst) { 135 return new SettingsInstance(inst); 136 } 137 138 139 private SaveSupport createSaveSupport(Object inst) { 140 return new SaveSupport(inst); 141 } 142 143 145 private void attachToInstance(Object inst) { 146 SerialDataConvertor.SaveSupport _saver = null; 147 synchronized (this) { 148 if (saver != null) { 149 saver.removePropertyChangeListener(this); 150 _saver = saver; 151 } 152 } 153 154 if (_saver != null) { 155 157 _saver.flush(); 158 } 159 160 synchronized (this) { 161 saver = createSaveSupport(inst); 162 saver.addPropertyChangeListener(this); 163 } 164 } 165 166 private void provideSaveCookie() { 167 if (saver.isChanged()) { 168 lkpContent.add(saver); 169 } else { 170 lkpContent.remove(saver); 171 } 172 } 173 174 private void instanceCookieChanged(Object inst) { 175 if (XMLSettingsSupport.err.isLoggable(Level.FINE)) XMLSettingsSupport.err.fine("instanceCookieChanged: " + this.dobj); if (saver != null) { 177 if (XMLSettingsSupport.err.isLoggable(Level.FINE)) XMLSettingsSupport.err.fine("canceling saver: " + this.dobj); saver.removePropertyChangeListener(this); 179 getScheduledRequest().cancel(); 180 saver = null; 181 } 182 183 SerialDataConvertor.SettingsInstance si = createInstance(inst); 184 185 boolean recreate = false; 187 if (instance != null && instance.getCachedInstance() != null) { 188 if (isSystemOption(instance.getCachedInstance())) { 189 recreate = true; 190 } 191 } 192 if (XMLSettingsSupport.err.isLoggable(Level.FINE)) XMLSettingsSupport.err.fine("need recreate: " + recreate + " for " + this.dobj); if (isModuleEnabled(si)) { 194 instance = si; 195 lkpContent.set(Arrays.asList(new Object [] { this, si }), null); 196 if (XMLSettingsSupport.err.isLoggable(Level.FINE)) XMLSettingsSupport.err.fine("module enabled: " + this.dobj); } else { 198 lkpContent.set(Collections.singleton(this), null); 199 instance = null; 200 if (XMLSettingsSupport.err.isLoggable(Level.FINE)) XMLSettingsSupport.err.fine("module disabled: " + this.dobj); } 202 203 lkpContent.add(this, node); 204 205 if (isModuleEnabled(si) && recreate) { 208 if (XMLSettingsSupport.err.isLoggable(Level.FINE)) XMLSettingsSupport.err.fine("recreating: " + this.dobj); try { 210 instance.instanceCreate(); 211 } catch (Exception ex) { 212 XMLSettingsSupport.err.log(Level.WARNING, null, ex); 213 } 214 } 215 if (XMLSettingsSupport.err.isLoggable(Level.FINE)) XMLSettingsSupport.err.fine("done: " + this.dobj); } 217 218 private static boolean isSystemOption(final Object obj) { 219 boolean b = false; 220 if (obj != null && obj instanceof SharedClassObject){ 221 for(Class c = obj.getClass(); !b && c != null; c = c.getSuperclass()) { 222 b = "org.openide.options.SystemOption".equals(c.getName()); } 224 } 225 return b; 226 } 227 228 public void propertyChange(PropertyChangeEvent evt) { 229 if (evt == null) return; 230 231 String name = evt.getPropertyName(); 232 if (name == null) 233 return; 234 else if (name == SaveSupport.PROP_SAVE) 236 provideSaveCookie(); 237 else if (name == SaveSupport.PROP_FILE_CHANGED) { 239 miUnInitialized = true; 240 if (moduleCodeBase != null) { 241 ModuleInfo mi = ModuleInfoManager.getDefault().getModule(moduleCodeBase); 242 ModuleInfoManager.getDefault(). 243 unregisterPropertyChangeListener(this, mi); 244 } 245 instanceCookieChanged(null); 246 } else if(ModuleInfo.PROP_ENABLED.equals(evt.getPropertyName())) { 247 instanceCookieChanged(null); 248 } 249 } 250 251 252 public void fileChanged(FileEvent fe) { 253 if (saver != null && fe.firedFrom(saver)) return; 254 propertyChange(new PropertyChangeEvent (this, SaveSupport.PROP_FILE_CHANGED, null, null)); 255 } 256 257 public void fileDeleted(FileEvent fe) { 258 if (saver != null && fe.firedFrom(saver)) return; 259 if (saver != null) { 260 saver.removePropertyChangeListener(this); 261 getScheduledRequest().cancel(); 262 saver = null; 263 } 264 } 265 266 private String moduleCodeBase = null; 267 private boolean miUnInitialized = true; 268 private boolean moduleMissing; 269 270 private boolean isModuleEnabled(SerialDataConvertor.SettingsInstance si) { 271 ModuleInfo mi = null; 272 if (miUnInitialized) { 273 moduleCodeBase = getModuleCodeNameBase(si); 274 miUnInitialized = false; 275 if (moduleCodeBase != null) { 276 mi = ModuleInfoManager.getDefault().getModule(moduleCodeBase); 277 moduleMissing = (mi == null); 278 if (mi != null) { 279 ModuleInfoManager.getDefault(). 280 registerPropertyChangeListener(this, mi); 281 } else { 282 XMLSettingsSupport.err.warning( 283 "Warning: unknown module code base: " + moduleCodeBase + " in " + getDataObject().getPrimaryFile()); 286 } 287 } else { 288 moduleMissing = false; 289 } 290 } else { 291 mi = ModuleInfoManager.getDefault().getModule(moduleCodeBase); 292 } 293 294 return !moduleMissing && (mi == null || mi.isEnabled()); 295 } 296 297 private String getModuleCodeNameBase(SerialDataConvertor.SettingsInstance si) { 298 try { 299 String module = si.getSettings(true).getCodeNameBase(); 300 return module; 301 } catch (IOException ex) { 302 XMLSettingsSupport.err.log(Level.WARNING, null, ex); 303 } 304 return null; 305 } 306 307 311 static void inform(Throwable t) { 312 XMLSettingsSupport.err.log(Level.WARNING, null, t); 313 } 314 315 316 public void run() throws IOException { 317 saver.writeDown(); 318 } 319 320 321 private ScheduledRequest request; 322 323 324 private synchronized ScheduledRequest getScheduledRequest() { 325 if (request == null) { 326 request = new ScheduledRequest(this.getDataObject().getPrimaryFile(), this); 327 } 328 return request; 329 } 330 331 335 336 private final class SettingsInstance implements InstanceCookie.Of { 337 338 339 private SoftReference <Object > inst; 340 341 348 349 private XMLSettingsSupport.SettingsRecognizer settings = null; 350 351 352 public SettingsInstance(Object instance) { 353 setCachedInstance(instance); 354 } 355 356 359 private XMLSettingsSupport.SettingsRecognizer getSettings(boolean header) throws IOException { 360 synchronized (this) { 361 if (settings == null) { 362 synchronized (READWRITE_LOCK) { 363 settings = new XMLSettingsSupport.SettingsRecognizer( 364 header, getDataObject().getPrimaryFile()); 365 settings.parse(); 366 } 367 return settings; 368 } 369 if (!header) { 370 if (!settings.isAllRead()) { 371 settings.setAllRead(false); 372 settings.parse(); 373 } 374 } 375 376 return settings; 377 } 378 } 379 380 public Object instanceCreate() throws IOException , ClassNotFoundException { 381 Object inst; 382 XMLSettingsSupport.SettingsRecognizer recog; 383 384 synchronized (this) { 385 inst = getCachedInstance(); 386 if (inst != null) { 387 if (XMLSettingsSupport.err.isLoggable(Level.FINE)) XMLSettingsSupport.err.fine("Cached instance1: " + inst); return inst; 389 } 390 } 391 392 recog = getSettings(false); 393 inst = recog.instanceCreate(); 394 395 396 synchronized (this) { 397 Object existing = getCachedInstance(); 398 if (existing != null) { 399 if (XMLSettingsSupport.err.isLoggable(Level.FINER)) XMLSettingsSupport.err.finer("Cached instance2: " + existing); return existing; 401 } 402 setCachedInstance(inst); 403 } 404 if (XMLSettingsSupport.err.isLoggable(Level.FINER)) XMLSettingsSupport.err.finer("Attached to instance: " + inst); attachToInstance(inst); 406 407 return inst; 408 } 409 410 public Class instanceClass() throws IOException , ClassNotFoundException { 411 Object inst = getCachedInstance(); 413 if (inst != null) { 414 return inst.getClass(); 415 } 416 417 XMLSettingsSupport.SettingsRecognizer recog = getSettings(false); 418 return recog.instanceClass(); 419 } 420 421 public boolean instanceOf(Class <?> type) { 422 try { 423 if ( 424 moduleCodeBase != null && 425 ModuleInfoManager.getDefault().isReloaded(moduleCodeBase) && 426 type.getClassLoader () != ClassLoader.getSystemClassLoader () && 427 type.getClassLoader() != null 428 ) { 429 ModuleInfo info = ModuleInfoManager.getDefault().getModule (moduleCodeBase); 431 if (info == null || !info.isEnabled ()) { 432 return false; 434 } 435 Class <?> instanceType = instanceClass (); 437 return type.isAssignableFrom (instanceType); 438 } 439 440 Object inst = getCachedInstance(); 442 if (inst != null) { 443 return type.isInstance(inst); 444 } 445 446 return getSettings(true).getInstanceOf().contains(type.getName()); 448 } catch (ClassNotFoundException ex) { 449 XMLSettingsSupport.err.log(Level.WARNING, getDataObject().getPrimaryFile().toString()); 450 inform(ex); 451 } catch (IOException ex) { 452 XMLSettingsSupport.err.log(Level.WARNING, getDataObject().getPrimaryFile().toString()); 453 inform(ex); 454 } 455 return false; 456 } 457 458 public String instanceName() { 459 Object inst = getCachedInstance(); 461 if (inst != null) { 462 return inst.getClass().getName(); 463 } 464 465 try { 466 return getSettings(true).instanceName(); 467 } catch (IOException ex) { 468 XMLSettingsSupport.err.warning(getDataObject().getPrimaryFile().toString()); 469 inform(ex); 470 return ""; } 472 } 473 474 private Object getCachedInstance() { 475 return inst.get(); 476 } 477 478 private void setCachedInstance(Object o) { 479 inst = new SoftReference <Object >(o); 480 settings = null; } 482 public void setInstance(Object inst, boolean save) throws IOException { 484 instanceCookieChanged(inst); 485 if (inst != null) { 486 attachToInstance(inst); 487 if (save) getScheduledRequest().runAndWait(); 488 } 489 } 490 491 } 492 493 496 private final class SaveSupport implements FileSystem.AtomicAction, 497 SaveCookie, PropertyChangeListener , Saver { 498 499 public static final String PROP_SAVE = "savecookie"; 501 public static final String PROP_FILE_CHANGED = "fileChanged"; 503 504 private PropertyChangeSupport changeSupport; 505 506 private int propertyChangeListenerCount = 0; 507 508 509 private boolean isChanged = false; 510 511 private final FileObject file; 512 513 private final WeakReference <Object > instance; 514 515 private Boolean knownToBeTemplate = null; 516 519 private boolean isWriting = false; 520 521 private Convertor convertor; 522 523 524 public SaveSupport(Object inst) { 525 this.instance = new WeakReference <Object >(inst); 526 file = getDataObject().getPrimaryFile(); 527 } 528 529 530 public final boolean isChanged() { 531 return isChanged; 532 } 533 534 535 private boolean acceptSave() { 536 Object inst = instance.get(); 537 if (inst == null || !(inst instanceof Serializable ) || 538 inst instanceof TopComponent) return false; 540 541 return true; 542 } 543 544 545 private boolean ignoreChange(PropertyChangeEvent pce) { 546 if (isChanged || isWriting || !getDataObject().isValid()) return true; 547 548 if (pce != null && Boolean.FALSE.equals(pce.getPropagationId())) return true; 551 552 if (knownToBeTemplate == null) knownToBeTemplate = getDataObject().isTemplate() ? Boolean.TRUE : Boolean.FALSE; 553 return knownToBeTemplate.booleanValue(); 554 } 555 556 557 private Convertor getConvertor() { 558 return convertor; 559 } 560 561 562 private Convertor initConvertor() { 563 Object inst = instance.get(); 564 if (inst == null) { 565 throw new IllegalStateException ( 566 "setting object cannot be null: " + getDataObject()); } 568 569 try { 570 FileObject newProviderFO = Env.findProvider(inst.getClass()); 571 if (newProviderFO != null) { 572 FileObject foEntity = Env.findEntityRegistration(newProviderFO); 573 if (foEntity == null) foEntity = newProviderFO; 574 Object attrb = foEntity.getAttribute(Env.EA_PUBLICID); 575 if (attrb == null || !(attrb instanceof String )) { 576 throw new IOException ("missing or invalid attribute: " + Env.EA_PUBLICID + ", provider: " + foEntity); } 579 if (XMLSettingsSupport.INSTANCE_DTD_ID.equals(attrb)) { 580 convertor = null; 581 return convertor; 582 } 583 584 attrb = newProviderFO.getAttribute(Env.EA_CONVERTOR); 585 if (attrb == null || !(attrb instanceof Convertor)) { 586 throw new IOException ("cannot create convertor: " + attrb + ", provider: " + newProviderFO); } else { 589 convertor = (Convertor) attrb; 590 return convertor; 591 } 592 } 593 } catch (IOException ex) { 594 inform(ex); 595 } 596 return convertor; 597 } 598 599 603 public synchronized void addPropertyChangeListener(PropertyChangeListener listener) { 604 if (changeSupport == null || propertyChangeListenerCount <= 0) { 605 Object inst = instance.get(); 606 if (inst == null) return; 607 if (changeSupport == null) { 608 changeSupport = new PropertyChangeSupport (this); 609 propertyChangeListenerCount = 0; 610 } 611 Convertor conv = initConvertor(); 612 if (conv != null) { 613 conv.registerSaver(inst, this); 614 } else { 615 registerPropertyChangeListener(inst); 616 } 617 } 618 propertyChangeListenerCount++; 619 changeSupport.addPropertyChangeListener(listener); 620 } 621 622 625 public synchronized void removePropertyChangeListener(PropertyChangeListener listener) { 626 if (changeSupport == null) 627 return; 628 629 propertyChangeListenerCount--; 630 changeSupport.removePropertyChangeListener(listener); 631 632 if (propertyChangeListenerCount == 0) { 633 Object inst = instance.get(); 634 if (inst == null) return; 635 636 Convertor conv = getConvertor(); 637 if (conv != null) { 638 conv.unregisterSaver(inst, this); 639 } else { 640 unregisterPropertyChangeListener(inst); 641 } 642 } 643 } 644 645 648 private void registerPropertyChangeListener(Object inst) { 649 if (inst instanceof SharedClassObject) { 650 ((SharedClassObject)inst).addPropertyChangeListener(this); 651 } 652 else if (inst instanceof JComponent ) { 653 ((JComponent ) inst).addPropertyChangeListener(this); 654 } 655 else { 656 try { 658 Method method = inst.getClass().getMethod( 659 "addPropertyChangeListener", new Class [] {PropertyChangeListener .class}); 661 method.invoke(inst, new Object [] {this}); 662 } catch (NoSuchMethodException ex) { 663 if (XMLSettingsSupport.err.isLoggable(Level.FINE)) { 665 XMLSettingsSupport.err.warning( 666 "NoSuchMethodException: " + inst.getClass().getName() + ".addPropertyChangeListener"); } 669 } catch (IllegalAccessException ex) { 670 XMLSettingsSupport.err.warning("Instance: " + inst); XMLSettingsSupport.err.log(Level.WARNING, null, ex); 673 } catch (java.lang.reflect.InvocationTargetException ex) { 674 XMLSettingsSupport.err.log(Level.WARNING, null, ex.getTargetException()); 676 } 677 } 678 } 679 680 682 private void unregisterPropertyChangeListener(Object inst) { 683 try { 684 Method method = inst.getClass().getMethod( 685 "removePropertyChangeListener", new Class [] {PropertyChangeListener .class}); 687 method.invoke(inst, new Object [] {this}); 688 } catch (NoSuchMethodException ex) { 689 if (XMLSettingsSupport.err.isLoggable(Level.FINE)) { 691 XMLSettingsSupport.err.log(Level.FINE, 692 "NoSuchMethodException: " + inst.getClass().getName() + ".removePropertyChangeListener"); } 695 } catch (IllegalAccessException ex) { 696 XMLSettingsSupport.err.log(Level.WARNING, "Instance: " + inst); XMLSettingsSupport.err.log(Level.WARNING, null, ex); 698 } catch (java.lang.reflect.InvocationTargetException ex) { 699 XMLSettingsSupport.err.log(Level.WARNING, null, ex.getTargetException()); 700 } 701 } 702 703 708 private void firePropertyChange(String name) { 709 if (changeSupport != null) 710 changeSupport.firePropertyChange(name, null, null); 711 } 712 713 714 public void flush() { 715 getScheduledRequest().forceToFinish(); 716 } 717 718 private ByteArrayOutputStream buf; 719 720 721 public final void propertyChange(PropertyChangeEvent pce) { 722 if (ignoreChange(pce)) return ; 723 isChanged = true; 724 firePropertyChange(PROP_SAVE); 725 if (acceptSave()) { 726 getScheduledRequest().schedule(instance.get()); 727 } 728 } 729 730 public void markDirty() { 731 if (ignoreChange(null)) return; 732 isChanged = true; 733 firePropertyChange(PROP_SAVE); 734 } 735 736 public void requestSave() throws IOException { 737 if (ignoreChange(null)) return; 738 isChanged = true; 739 firePropertyChange(PROP_SAVE); 740 getScheduledRequest().schedule(instance.get()); 741 } 742 743 744 public void run() throws IOException { 745 if (!getDataObject().isValid()) { 746 if (XMLSettingsSupport.err.isLoggable(Level.FINE)) { 748 XMLSettingsSupport.err.fine("invalid data object cannot be used for storing " + getDataObject()); } 750 return; 751 } 752 753 try { 754 try2run(); 755 } catch (IOException ex) { 756 if (getDataObject().isValid()) { 759 throw ex; 760 } else { 761 return; 762 } 763 } 764 } 765 766 767 private void try2run() throws IOException { 768 FileLock lock; 769 OutputStream los; 770 synchronized (READWRITE_LOCK) { 771 if (XMLSettingsSupport.err.isLoggable(Level.FINER)) { 772 XMLSettingsSupport.err.finer("saving " + getDataObject()); } 774 lock = getScheduledRequest().getFileLock(); 775 if (lock == null) return; 776 los = file.getOutputStream(lock); 777 778 OutputStream os = new BufferedOutputStream (los, 1024); 779 try { 780 buf.writeTo(os); 781 if (XMLSettingsSupport.err.isLoggable(Level.FINER)) { 782 XMLSettingsSupport.err.finer("saved " + dobj); } 784 } finally { 785 os.close(); 786 } 787 } 788 } 789 790 791 public void save() throws IOException { 792 if (!isChanged) return; 793 getScheduledRequest().runAndWait(); 794 } 795 796 797 private void writeDown() throws IOException { 798 Object inst = instance.get(); 799 if (inst == null) return ; 800 801 ByteArrayOutputStream b = new ByteArrayOutputStream (1024); 802 Writer w = new OutputStreamWriter (b, "UTF-8"); try { 804 isWriting = true; 805 Convertor conv = getConvertor(); 806 if (conv != null) { 807 conv.write(w, inst); 808 } else { 809 write(w, inst); 810 } 811 } finally { 812 w.close(); 813 isWriting = false; 814 } 815 isChanged = false; 816 817 buf = b; 818 file.getFileSystem().runAtomicAction(this); 819 buf = null; 820 if (!isChanged) firePropertyChange(PROP_SAVE); 821 } 822 } 823 824 828 831 public final static class Provider implements Environment.Provider { 832 private final FileObject providerFO; 833 834 public static Environment.Provider create(FileObject fo) { 835 return new Provider(fo); 836 } 837 838 private Provider(FileObject fo) { 839 providerFO = fo; 840 } 841 842 public Lookup getEnvironment(DataObject dobj) { 843 if (!(dobj instanceof InstanceDataObject)) return Lookup.EMPTY; 844 return new SerialDataConvertor(dobj, providerFO).getLookup(); 845 } 846 847 } 848 849 853 854 private static final class NodeConvertor implements InstanceContent.Convertor<SerialDataConvertor, Node> { 855 NodeConvertor() {} 856 857 public Node convert(SerialDataConvertor o) { 858 return new SerialDataNode(o); 859 } 860 861 public Class <Node> type(SerialDataConvertor o) { 862 return Node.class; 863 } 864 865 public String id(SerialDataConvertor o) { 866 return o.toString(); 868 } 869 870 public String displayName(SerialDataConvertor o) { 871 return o.toString(); 873 } 874 875 } 876 877 } 878 | Popular Tags |