src/share/classes/sun/awt/datatransfer/SunClipboard.java

Print this page




  23  * questions.
  24  */
  25 
  26 package sun.awt.datatransfer;
  27 
  28 import java.awt.EventQueue;
  29 
  30 import java.awt.datatransfer.Clipboard;
  31 import java.awt.datatransfer.FlavorTable;
  32 import java.awt.datatransfer.SystemFlavorMap;
  33 import java.awt.datatransfer.Transferable;
  34 import java.awt.datatransfer.ClipboardOwner;
  35 import java.awt.datatransfer.DataFlavor;
  36 import java.awt.datatransfer.FlavorListener;
  37 import java.awt.datatransfer.FlavorEvent;
  38 import java.awt.datatransfer.UnsupportedFlavorException;
  39 
  40 import java.beans.PropertyChangeEvent;
  41 import java.beans.PropertyChangeListener;
  42 
  43 import java.util.Iterator;
  44 import java.util.Set;
  45 import java.util.HashSet;
  46 
  47 import java.io.IOException;
  48 
  49 import sun.awt.AppContext;
  50 import sun.awt.PeerEvent;
  51 import sun.awt.SunToolkit;
  52 import sun.awt.EventListenerAggregate;
  53 
  54 
  55 /**
  56  * Serves as a common, helper superclass for the Win32 and X11 system
  57  * Clipboards.
  58  *
  59  * @author Danila Sinopalnikov
  60  * @author Alexander Gerasimov
  61  *
  62  * @since 1.3
  63  */
  64 public abstract class SunClipboard extends Clipboard
  65     implements PropertyChangeListener {
  66 
  67     public static final FlavorTable flavorMap =
  68         (FlavorTable)SystemFlavorMap.getDefaultFlavorMap();
  69 
  70     private AppContext contentsContext = null;
  71 
  72     private final Object CLIPBOARD_FLAVOR_LISTENER_KEY;


  93     public synchronized void setContents(Transferable contents,
  94                                          ClipboardOwner owner) {
  95         // 4378007 : Toolkit.getSystemClipboard().setContents(null, null)
  96         // should throw NPE
  97         if (contents == null) {
  98             throw new NullPointerException("contents");
  99         }
 100 
 101         initContext();
 102 
 103         final ClipboardOwner oldOwner = this.owner;
 104         final Transferable oldContents = this.contents;
 105 
 106         try {
 107             this.owner = owner;
 108             this.contents = new TransferableProxy(contents, true);
 109 
 110             setContentsNative(contents);
 111         } finally {
 112             if (oldOwner != null && oldOwner != owner) {
 113                 EventQueue.invokeLater(new Runnable() {
 114                     public void run() {
 115                         oldOwner.lostOwnership(SunClipboard.this, oldContents);
 116                     }
 117                 });
 118             }
 119         }
 120     }
 121 
 122     private synchronized void initContext() {
 123         final AppContext context = AppContext.getAppContext();
 124 
 125         if (contentsContext != context) {
 126             // Need to synchronize on the AppContext to guarantee that it cannot
 127             // be disposed after the check, but before the listener is added.
 128             synchronized (context) {
 129                 if (context.isDisposed()) {
 130                     throw new IllegalStateException("Can't set contents from disposed AppContext");
 131                 }
 132                 context.addPropertyChangeListener
 133                     (AppContext.DISPOSED_PROPERTY_NAME, this);
 134             }
 135             if (contentsContext != null) {
 136                 contentsContext.removePropertyChangeListener
 137                     (AppContext.DISPOSED_PROPERTY_NAME, this);


 341      *
 342      * @throws IllegalStateException if formats could not be retrieved
 343      */
 344     protected abstract long[] getClipboardFormats();
 345 
 346     protected abstract byte[] getClipboardData(long format) throws IOException;
 347 
 348 
 349     private static Set formatArrayAsDataFlavorSet(long[] formats) {
 350         return (formats == null) ? null :
 351                 DataTransferer.getInstance().
 352                 getFlavorsForFormatsAsSet(formats, flavorMap);
 353     }
 354 
 355 
 356     public synchronized void addFlavorListener(FlavorListener listener) {
 357         if (listener == null) {
 358             return;
 359         }
 360         AppContext appContext = AppContext.getAppContext();
 361         EventListenerAggregate contextFlavorListeners = (EventListenerAggregate)
 362                 appContext.get(CLIPBOARD_FLAVOR_LISTENER_KEY);
 363         if (contextFlavorListeners == null) {
 364             contextFlavorListeners = new EventListenerAggregate(FlavorListener.class);
 365             appContext.put(CLIPBOARD_FLAVOR_LISTENER_KEY, contextFlavorListeners);
 366         }
 367         contextFlavorListeners.add(listener);
 368 
 369         if (numberOfFlavorListeners++ == 0) {
 370             long[] currentFormats = null;
 371             try {
 372                 openClipboard(null);
 373                 currentFormats = getClipboardFormats();
 374             } catch (IllegalStateException exc) {
 375             } finally {
 376                 closeClipboard();
 377             }
 378             currentDataFlavors = formatArrayAsDataFlavorSet(currentFormats);
 379 
 380             registerClipboardViewerChecked();
 381         }
 382     }
 383 
 384     public synchronized void removeFlavorListener(FlavorListener listener) {
 385         if (listener == null) {
 386             return;
 387         }
 388         AppContext appContext = AppContext.getAppContext();
 389         EventListenerAggregate contextFlavorListeners = (EventListenerAggregate)
 390                 appContext.get(CLIPBOARD_FLAVOR_LISTENER_KEY);
 391         if (contextFlavorListeners == null){
 392             //else we throw NullPointerException, but it is forbidden
 393             return;
 394         }
 395         if (contextFlavorListeners.remove(listener) &&
 396                 --numberOfFlavorListeners == 0) {
 397             unregisterClipboardViewerChecked();
 398             currentDataFlavors = null;
 399         }
 400     }
 401 





 402     public synchronized FlavorListener[] getFlavorListeners() {
 403         EventListenerAggregate contextFlavorListeners = (EventListenerAggregate)
 404                 AppContext.getAppContext().get(CLIPBOARD_FLAVOR_LISTENER_KEY);
 405         return contextFlavorListeners == null ? new FlavorListener[0] :
 406                 (FlavorListener[])contextFlavorListeners.getListenersCopy();
 407     }
 408 
 409     public boolean areFlavorListenersRegistered() {
 410         return (numberOfFlavorListeners > 0);
 411     }
 412 
 413     protected abstract void registerClipboardViewerChecked();
 414 
 415     protected abstract void unregisterClipboardViewerChecked();
 416 
 417     /**
 418      * Checks change of the <code>DataFlavor</code>s and, if necessary,
 419      * posts notifications on <code>FlavorEvent</code>s to the
 420      * AppContexts' EDTs.
 421      * The parameter <code>formats</code> is null iff we have just
 422      * failed to get formats available on the clipboard.
 423      *
 424      * @param formats data formats that have just been retrieved from
 425      *        this clipboard
 426      */
 427     public void checkChange(long[] formats) {
 428         Set prevDataFlavors = currentDataFlavors;
 429         currentDataFlavors = formatArrayAsDataFlavorSet(formats);
 430 
 431         if ((prevDataFlavors != null) && (currentDataFlavors != null) &&
 432                 prevDataFlavors.equals(currentDataFlavors)) {
 433             // we've been able to successfully get available on the clipboard
 434             // DataFlavors this and previous time and they are coincident;
 435             // don't notify
 436             return;
 437         }
 438 
 439         class SunFlavorChangeNotifier implements Runnable {
 440             private final FlavorListener flavorListener;
 441 
 442             SunFlavorChangeNotifier(FlavorListener flavorListener) {
 443                 this.flavorListener = flavorListener;
 444             }
 445 
 446             public void run() {
 447                 if (flavorListener != null) {
 448                     flavorListener.flavorsChanged(new FlavorEvent(SunClipboard.this));
 449                 }
 450             }
 451         };
 452 
 453         for (Iterator it = AppContext.getAppContexts().iterator(); it.hasNext();) {
 454             AppContext appContext = (AppContext)it.next();
 455             if (appContext == null || appContext.isDisposed()) {
 456                 continue;
 457             }
 458             EventListenerAggregate flavorListeners = (EventListenerAggregate)
 459                     appContext.get(CLIPBOARD_FLAVOR_LISTENER_KEY);
 460             if (flavorListeners != null) {
 461                 FlavorListener[] flavorListenerArray =
 462                         (FlavorListener[])flavorListeners.getListenersInternal();
 463                 for (int i = 0; i < flavorListenerArray.length; i++) {
 464                     SunToolkit.postEvent(appContext, new PeerEvent(this,
 465                             new SunFlavorChangeNotifier(flavorListenerArray[i]),
 466                             PeerEvent.PRIORITY_EVENT));
 467                 }
 468             }
 469         }
 470     }
 471 
 472 }


  23  * questions.
  24  */
  25 
  26 package sun.awt.datatransfer;
  27 
  28 import java.awt.EventQueue;
  29 
  30 import java.awt.datatransfer.Clipboard;
  31 import java.awt.datatransfer.FlavorTable;
  32 import java.awt.datatransfer.SystemFlavorMap;
  33 import java.awt.datatransfer.Transferable;
  34 import java.awt.datatransfer.ClipboardOwner;
  35 import java.awt.datatransfer.DataFlavor;
  36 import java.awt.datatransfer.FlavorListener;
  37 import java.awt.datatransfer.FlavorEvent;
  38 import java.awt.datatransfer.UnsupportedFlavorException;
  39 
  40 import java.beans.PropertyChangeEvent;
  41 import java.beans.PropertyChangeListener;
  42 
  43 import java.util.Objects;
  44 import java.util.Set;
  45 import java.util.HashSet;
  46 
  47 import java.io.IOException;
  48 
  49 import sun.awt.AppContext;
  50 import sun.awt.PeerEvent;
  51 import sun.awt.SunToolkit;

  52 
  53 
  54 /**
  55  * Serves as a common, helper superclass for the Win32 and X11 system
  56  * Clipboards.
  57  *
  58  * @author Danila Sinopalnikov
  59  * @author Alexander Gerasimov
  60  *
  61  * @since 1.3
  62  */
  63 public abstract class SunClipboard extends Clipboard
  64     implements PropertyChangeListener {
  65 
  66     public static final FlavorTable flavorMap =
  67         (FlavorTable)SystemFlavorMap.getDefaultFlavorMap();
  68 
  69     private AppContext contentsContext = null;
  70 
  71     private final Object CLIPBOARD_FLAVOR_LISTENER_KEY;


  92     public synchronized void setContents(Transferable contents,
  93                                          ClipboardOwner owner) {
  94         // 4378007 : Toolkit.getSystemClipboard().setContents(null, null)
  95         // should throw NPE
  96         if (contents == null) {
  97             throw new NullPointerException("contents");
  98         }
  99 
 100         initContext();
 101 
 102         final ClipboardOwner oldOwner = this.owner;
 103         final Transferable oldContents = this.contents;
 104 
 105         try {
 106             this.owner = owner;
 107             this.contents = new TransferableProxy(contents, true);
 108 
 109             setContentsNative(contents);
 110         } finally {
 111             if (oldOwner != null && oldOwner != owner) {
 112                 EventQueue.invokeLater(() -> oldOwner.lostOwnership(SunClipboard.this, oldContents));




 113             }
 114         }
 115     }
 116 
 117     private synchronized void initContext() {
 118         final AppContext context = AppContext.getAppContext();
 119 
 120         if (contentsContext != context) {
 121             // Need to synchronize on the AppContext to guarantee that it cannot
 122             // be disposed after the check, but before the listener is added.
 123             synchronized (context) {
 124                 if (context.isDisposed()) {
 125                     throw new IllegalStateException("Can't set contents from disposed AppContext");
 126                 }
 127                 context.addPropertyChangeListener
 128                     (AppContext.DISPOSED_PROPERTY_NAME, this);
 129             }
 130             if (contentsContext != null) {
 131                 contentsContext.removePropertyChangeListener
 132                     (AppContext.DISPOSED_PROPERTY_NAME, this);


 336      *
 337      * @throws IllegalStateException if formats could not be retrieved
 338      */
 339     protected abstract long[] getClipboardFormats();
 340 
 341     protected abstract byte[] getClipboardData(long format) throws IOException;
 342 
 343 
 344     private static Set formatArrayAsDataFlavorSet(long[] formats) {
 345         return (formats == null) ? null :
 346                 DataTransferer.getInstance().
 347                 getFlavorsForFormatsAsSet(formats, flavorMap);
 348     }
 349 
 350 
 351     public synchronized void addFlavorListener(FlavorListener listener) {
 352         if (listener == null) {
 353             return;
 354         }
 355         AppContext appContext = AppContext.getAppContext();
 356         Set<FlavorListener> flavorListeners = getFlavorListeners(appContext);
 357         if (flavorListeners == null) {
 358             flavorListeners = new HashSet<>();
 359             appContext.put(CLIPBOARD_FLAVOR_LISTENER_KEY, flavorListeners);

 360         }
 361         flavorListeners.add(listener);
 362 
 363         if (numberOfFlavorListeners++ == 0) {
 364             long[] currentFormats = null;
 365             try {
 366                 openClipboard(null);
 367                 currentFormats = getClipboardFormats();
 368             } catch (IllegalStateException exc) {
 369             } finally {
 370                 closeClipboard();
 371             }
 372             currentDataFlavors = formatArrayAsDataFlavorSet(currentFormats);
 373 
 374             registerClipboardViewerChecked();
 375         }
 376     }
 377 
 378     public synchronized void removeFlavorListener(FlavorListener listener) {
 379         if (listener == null) {
 380             return;
 381         }
 382         Set<FlavorListener> flavorListeners = getFlavorListeners(AppContext.getAppContext());
 383         if (flavorListeners == null){


 384             //else we throw NullPointerException, but it is forbidden
 385             return;
 386         }
 387         if (flavorListeners.remove(listener) && --numberOfFlavorListeners == 0) {

 388             unregisterClipboardViewerChecked();
 389             currentDataFlavors = null;
 390         }
 391     }
 392 
 393     @SuppressWarnings("unchecked")
 394     private Set<FlavorListener> getFlavorListeners(AppContext appContext) {
 395         return (Set<FlavorListener>)appContext.get(CLIPBOARD_FLAVOR_LISTENER_KEY);
 396     }
 397 
 398     public synchronized FlavorListener[] getFlavorListeners() {
 399         Set<FlavorListener> flavorListeners = getFlavorListeners(AppContext.getAppContext());
 400         return flavorListeners == null ? new FlavorListener[0]
 401                 : flavorListeners.toArray(new FlavorListener[flavorListeners.size()]);

 402     }
 403 
 404     public boolean areFlavorListenersRegistered() {
 405         return (numberOfFlavorListeners > 0);
 406     }
 407 
 408     protected abstract void registerClipboardViewerChecked();
 409 
 410     protected abstract void unregisterClipboardViewerChecked();
 411 
 412     /**
 413      * Checks change of the <code>DataFlavor</code>s and, if necessary,
 414      * posts notifications on <code>FlavorEvent</code>s to the
 415      * AppContexts' EDTs.
 416      * The parameter <code>formats</code> is null iff we have just
 417      * failed to get formats available on the clipboard.
 418      *
 419      * @param formats data formats that have just been retrieved from
 420      *        this clipboard
 421      */
 422     public synchronized void checkChange(long[] formats) {
 423         Set prevDataFlavors = currentDataFlavors;
 424         currentDataFlavors = formatArrayAsDataFlavorSet(formats);
 425 
 426         if ((prevDataFlavors != null) && (currentDataFlavors != null) &&
 427                 prevDataFlavors.equals(currentDataFlavors)) {
 428             // we've been able to successfully get available on the clipboard
 429             // DataFlavors this and previous time and they are coincident;
 430             // don't notify
 431             return;
 432         }
 433 
 434         for (AppContext appContext : AppContext.getAppContexts()) {















 435             if (appContext == null || appContext.isDisposed()) {
 436                 continue;
 437             }
 438             Set<FlavorListener> flavorListeners = getFlavorListeners(appContext);

 439             if (flavorListeners != null) {
 440                 flavorListeners.stream()
 441                         .filter(Objects::nonNull)
 442                         .forEach(listener -> SunToolkit.postEvent(appContext,
 443                                 new PeerEvent(this,
 444                                         () -> listener.flavorsChanged(new FlavorEvent(SunClipboard.this)),
 445                                         PeerEvent.PRIORITY_EVENT)));

 446             }
 447         }
 448     }
 449 
 450 }