jdk/src/java.desktop/share/classes/java/awt/datatransfer/Clipboard.java
changeset 29046 c46ad26fb278
parent 29045 991394bd2448
parent 29044 37c6512bd1df
child 29048 39a3bcf89286
equal deleted inserted replaced
29045:991394bd2448 29046:c46ad26fb278
     1 /*
       
     2  * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package java.awt.datatransfer;
       
    27 
       
    28 import sun.datatransfer.DataFlavorUtil;
       
    29 
       
    30 import java.util.Objects;
       
    31 import java.util.Set;
       
    32 import java.util.HashSet;
       
    33 import java.util.Arrays;
       
    34 
       
    35 import java.io.IOException;
       
    36 
       
    37 /**
       
    38  * A class that implements a mechanism to transfer data using
       
    39  * cut/copy/paste operations.
       
    40  * <p>
       
    41  * {@link FlavorListener}s may be registered on an instance of the
       
    42  * Clipboard class to be notified about changes to the set of
       
    43  * {@link DataFlavor}s available on this clipboard (see
       
    44  * {@link #addFlavorListener}).
       
    45  *
       
    46  * @see java.awt.Toolkit#getSystemClipboard
       
    47  * @see java.awt.Toolkit#getSystemSelection
       
    48  *
       
    49  * @author      Amy Fowler
       
    50  * @author      Alexander Gerasimov
       
    51  */
       
    52 public class Clipboard {
       
    53 
       
    54     String name;
       
    55 
       
    56     /**
       
    57      * The owner of the clipboard.
       
    58      */
       
    59     protected ClipboardOwner owner;
       
    60     /**
       
    61      * Contents of the clipboard.
       
    62      */
       
    63     protected Transferable contents;
       
    64 
       
    65     /**
       
    66      * An aggregate of flavor listeners registered on this local clipboard.
       
    67      *
       
    68      * @since 1.5
       
    69      */
       
    70     private Set<FlavorListener> flavorListeners;
       
    71 
       
    72     /**
       
    73      * A set of <code>DataFlavor</code>s that is available on
       
    74      * this local clipboard. It is used for tracking changes
       
    75      * of <code>DataFlavor</code>s available on this clipboard.
       
    76      *
       
    77      * @since 1.5
       
    78      */
       
    79     private Set<DataFlavor> currentDataFlavors;
       
    80 
       
    81     /**
       
    82      * Creates a clipboard object.
       
    83      * @param name for the clipboard
       
    84      * @see java.awt.Toolkit#getSystemClipboard
       
    85      */
       
    86     public Clipboard(String name) {
       
    87         this.name = name;
       
    88     }
       
    89 
       
    90     /**
       
    91      * Returns the name of this clipboard object.
       
    92      * @return the name of this clipboard object
       
    93      *
       
    94      * @see java.awt.Toolkit#getSystemClipboard
       
    95      */
       
    96     public String getName() {
       
    97         return name;
       
    98     }
       
    99 
       
   100     /**
       
   101      * Sets the current contents of the clipboard to the specified
       
   102      * transferable object and registers the specified clipboard owner
       
   103      * as the owner of the new contents.
       
   104      * <p>
       
   105      * If there is an existing owner different from the argument
       
   106      * <code>owner</code>, that owner is notified that it no longer
       
   107      * holds ownership of the clipboard contents via an invocation
       
   108      * of <code>ClipboardOwner.lostOwnership()</code> on that owner.
       
   109      * An implementation of <code>setContents()</code> is free not
       
   110      * to invoke <code>lostOwnership()</code> directly from this method.
       
   111      * For example, <code>lostOwnership()</code> may be invoked later on
       
   112      * a different thread. The same applies to <code>FlavorListener</code>s
       
   113      * registered on this clipboard.
       
   114      * <p>
       
   115      * The method throws <code>IllegalStateException</code> if the clipboard
       
   116      * is currently unavailable. For example, on some platforms, the system
       
   117      * clipboard is unavailable while it is accessed by another application.
       
   118      *
       
   119      * @param contents the transferable object representing the
       
   120      *                 clipboard content
       
   121      * @param owner the object which owns the clipboard content
       
   122      * @throws IllegalStateException if the clipboard is currently unavailable
       
   123      * @see java.awt.Toolkit#getSystemClipboard
       
   124      */
       
   125     public synchronized void setContents(Transferable contents, ClipboardOwner owner) {
       
   126         final ClipboardOwner oldOwner = this.owner;
       
   127         final Transferable oldContents = this.contents;
       
   128 
       
   129         this.owner = owner;
       
   130         this.contents = contents;
       
   131 
       
   132         if (oldOwner != null && oldOwner != owner) {
       
   133             DataFlavorUtil.getDesktopService().invokeOnEventThread(() ->
       
   134                     oldOwner.lostOwnership(Clipboard.this, oldContents));
       
   135         }
       
   136         fireFlavorsChanged();
       
   137     }
       
   138 
       
   139     /**
       
   140      * Returns a transferable object representing the current contents
       
   141      * of the clipboard.  If the clipboard currently has no contents,
       
   142      * it returns <code>null</code>. The parameter Object requestor is
       
   143      * not currently used.  The method throws
       
   144      * <code>IllegalStateException</code> if the clipboard is currently
       
   145      * unavailable.  For example, on some platforms, the system clipboard is
       
   146      * unavailable while it is accessed by another application.
       
   147      *
       
   148      * @param requestor the object requesting the clip data  (not used)
       
   149      * @return the current transferable object on the clipboard
       
   150      * @throws IllegalStateException if the clipboard is currently unavailable
       
   151      * @see java.awt.Toolkit#getSystemClipboard
       
   152      */
       
   153     public synchronized Transferable getContents(Object requestor) {
       
   154         return contents;
       
   155     }
       
   156 
       
   157 
       
   158     /**
       
   159      * Returns an array of <code>DataFlavor</code>s in which the current
       
   160      * contents of this clipboard can be provided. If there are no
       
   161      * <code>DataFlavor</code>s available, this method returns a zero-length
       
   162      * array.
       
   163      *
       
   164      * @return an array of <code>DataFlavor</code>s in which the current
       
   165      *         contents of this clipboard can be provided
       
   166      *
       
   167      * @throws IllegalStateException if this clipboard is currently unavailable
       
   168      *
       
   169      * @since 1.5
       
   170      */
       
   171     public DataFlavor[] getAvailableDataFlavors() {
       
   172         Transferable cntnts = getContents(null);
       
   173         if (cntnts == null) {
       
   174             return new DataFlavor[0];
       
   175         }
       
   176         return cntnts.getTransferDataFlavors();
       
   177     }
       
   178 
       
   179     /**
       
   180      * Returns whether or not the current contents of this clipboard can be
       
   181      * provided in the specified <code>DataFlavor</code>.
       
   182      *
       
   183      * @param flavor the requested <code>DataFlavor</code> for the contents
       
   184      *
       
   185      * @return <code>true</code> if the current contents of this clipboard
       
   186      *         can be provided in the specified <code>DataFlavor</code>;
       
   187      *         <code>false</code> otherwise
       
   188      *
       
   189      * @throws NullPointerException if <code>flavor</code> is <code>null</code>
       
   190      * @throws IllegalStateException if this clipboard is currently unavailable
       
   191      *
       
   192      * @since 1.5
       
   193      */
       
   194     public boolean isDataFlavorAvailable(DataFlavor flavor) {
       
   195         if (flavor == null) {
       
   196             throw new NullPointerException("flavor");
       
   197         }
       
   198 
       
   199         Transferable cntnts = getContents(null);
       
   200         if (cntnts == null) {
       
   201             return false;
       
   202         }
       
   203         return cntnts.isDataFlavorSupported(flavor);
       
   204     }
       
   205 
       
   206     /**
       
   207      * Returns an object representing the current contents of this clipboard
       
   208      * in the specified <code>DataFlavor</code>.
       
   209      * The class of the object returned is defined by the representation
       
   210      * class of <code>flavor</code>.
       
   211      *
       
   212      * @param flavor the requested <code>DataFlavor</code> for the contents
       
   213      *
       
   214      * @return an object representing the current contents of this clipboard
       
   215      *         in the specified <code>DataFlavor</code>
       
   216      *
       
   217      * @throws NullPointerException if <code>flavor</code> is <code>null</code>
       
   218      * @throws IllegalStateException if this clipboard is currently unavailable
       
   219      * @throws UnsupportedFlavorException if the requested <code>DataFlavor</code>
       
   220      *         is not available
       
   221      * @throws IOException if the data in the requested <code>DataFlavor</code>
       
   222      *         can not be retrieved
       
   223      *
       
   224      * @see DataFlavor#getRepresentationClass
       
   225      *
       
   226      * @since 1.5
       
   227      */
       
   228     public Object getData(DataFlavor flavor)
       
   229         throws UnsupportedFlavorException, IOException {
       
   230         if (flavor == null) {
       
   231             throw new NullPointerException("flavor");
       
   232         }
       
   233 
       
   234         Transferable cntnts = getContents(null);
       
   235         if (cntnts == null) {
       
   236             throw new UnsupportedFlavorException(flavor);
       
   237         }
       
   238         return cntnts.getTransferData(flavor);
       
   239     }
       
   240 
       
   241 
       
   242     /**
       
   243      * Registers the specified <code>FlavorListener</code> to receive
       
   244      * <code>FlavorEvent</code>s from this clipboard.
       
   245      * If <code>listener</code> is <code>null</code>, no exception
       
   246      * is thrown and no action is performed.
       
   247      *
       
   248      * @param listener the listener to be added
       
   249      *
       
   250      * @see #removeFlavorListener
       
   251      * @see #getFlavorListeners
       
   252      * @see FlavorListener
       
   253      * @see FlavorEvent
       
   254      * @since 1.5
       
   255      */
       
   256     public synchronized void addFlavorListener(FlavorListener listener) {
       
   257         if (listener == null) {
       
   258             return;
       
   259         }
       
   260 
       
   261         if (flavorListeners == null) {
       
   262             flavorListeners = new HashSet<>();
       
   263             currentDataFlavors = getAvailableDataFlavorSet();
       
   264         }
       
   265 
       
   266         flavorListeners.add(listener);
       
   267     }
       
   268 
       
   269     /**
       
   270      * Removes the specified <code>FlavorListener</code> so that it no longer
       
   271      * receives <code>FlavorEvent</code>s from this <code>Clipboard</code>.
       
   272      * This method performs no function, nor does it throw an exception, if
       
   273      * the listener specified by the argument was not previously added to this
       
   274      * <code>Clipboard</code>.
       
   275      * If <code>listener</code> is <code>null</code>, no exception
       
   276      * is thrown and no action is performed.
       
   277      *
       
   278      * @param listener the listener to be removed
       
   279      *
       
   280      * @see #addFlavorListener
       
   281      * @see #getFlavorListeners
       
   282      * @see FlavorListener
       
   283      * @see FlavorEvent
       
   284      * @since 1.5
       
   285      */
       
   286     public synchronized void removeFlavorListener(FlavorListener listener) {
       
   287         if (listener == null || flavorListeners == null) {
       
   288             return;
       
   289         }
       
   290         flavorListeners.remove(listener);
       
   291     }
       
   292 
       
   293     /**
       
   294      * Returns an array of all the <code>FlavorListener</code>s currently
       
   295      * registered on this <code>Clipboard</code>.
       
   296      *
       
   297      * @return all of this clipboard's <code>FlavorListener</code>s or an empty
       
   298      *         array if no listeners are currently registered
       
   299      * @see #addFlavorListener
       
   300      * @see #removeFlavorListener
       
   301      * @see FlavorListener
       
   302      * @see FlavorEvent
       
   303      * @since 1.5
       
   304      */
       
   305     public synchronized FlavorListener[] getFlavorListeners() {
       
   306         return flavorListeners == null ? new FlavorListener[0] :
       
   307             flavorListeners.toArray(new FlavorListener[flavorListeners.size()]);
       
   308     }
       
   309 
       
   310     /**
       
   311      * Checks change of the <code>DataFlavor</code>s and, if necessary,
       
   312      * notifies all listeners that have registered interest for notification
       
   313      * on <code>FlavorEvent</code>s.
       
   314      *
       
   315      * @since 1.5
       
   316      */
       
   317     private void fireFlavorsChanged() {
       
   318         if (flavorListeners == null) {
       
   319             return;
       
   320         }
       
   321 
       
   322         Set<DataFlavor> prevDataFlavors = currentDataFlavors;
       
   323         currentDataFlavors = getAvailableDataFlavorSet();
       
   324         if (Objects.equals(prevDataFlavors, currentDataFlavors)) {
       
   325             return;
       
   326         }
       
   327         flavorListeners.forEach(listener ->
       
   328                 DataFlavorUtil.getDesktopService().invokeOnEventThread(() ->
       
   329                         listener.flavorsChanged(new FlavorEvent(Clipboard.this))));
       
   330     }
       
   331 
       
   332     /**
       
   333      * Returns a set of <code>DataFlavor</code>s currently available
       
   334      * on this clipboard.
       
   335      *
       
   336      * @return a set of <code>DataFlavor</code>s currently available
       
   337      *         on this clipboard
       
   338      *
       
   339      * @since 1.5
       
   340      */
       
   341     private Set<DataFlavor> getAvailableDataFlavorSet() {
       
   342         Set<DataFlavor> set = new HashSet<>();
       
   343         Transferable contents = getContents(null);
       
   344         if (contents != null) {
       
   345             DataFlavor[] flavors = contents.getTransferDataFlavors();
       
   346             if (flavors != null) {
       
   347                 set.addAll(Arrays.asList(flavors));
       
   348             }
       
   349         }
       
   350         return set;
       
   351     }
       
   352 }