src/java.datatransfer/share/classes/java/awt/datatransfer/Clipboard.java
changeset 47216 71c04702a3d5
parent 45655 02c95aa8a53a
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 1996, 2017, 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 java.io.IOException;
       
    29 import java.util.Arrays;
       
    30 import java.util.HashSet;
       
    31 import java.util.Objects;
       
    32 import java.util.Set;
       
    33 
       
    34 import sun.datatransfer.DataFlavorUtil;
       
    35 
       
    36 /**
       
    37  * A class that implements a mechanism to transfer data using cut/copy/paste
       
    38  * operations.
       
    39  * <p>
       
    40  * {@link FlavorListener}s may be registered on an instance of the Clipboard
       
    41  * class to be notified about changes to the set of {@link DataFlavor}s
       
    42  * available on this clipboard (see {@link #addFlavorListener}).
       
    43  *
       
    44  * @author Amy Fowler
       
    45  * @author Alexander Gerasimov
       
    46  * @see java.awt.Toolkit#getSystemClipboard
       
    47  * @see java.awt.Toolkit#getSystemSelection
       
    48  * @since 1.1
       
    49  */
       
    50 public class Clipboard {
       
    51 
       
    52     String name;
       
    53 
       
    54     /**
       
    55      * The owner of the clipboard.
       
    56      */
       
    57     protected ClipboardOwner owner;
       
    58 
       
    59     /**
       
    60      * Contents of the clipboard.
       
    61      */
       
    62     protected Transferable contents;
       
    63 
       
    64     /**
       
    65      * An aggregate of flavor listeners registered on this local clipboard.
       
    66      *
       
    67      * @since 1.5
       
    68      */
       
    69     private Set<FlavorListener> flavorListeners;
       
    70 
       
    71     /**
       
    72      * A set of {@code DataFlavor}s that is available on this local clipboard.
       
    73      * It is used for tracking changes of {@code DataFlavor}s available on this
       
    74      * clipboard.
       
    75      *
       
    76      * @since 1.5
       
    77      */
       
    78     private Set<DataFlavor> currentDataFlavors;
       
    79 
       
    80     /**
       
    81      * Creates a clipboard object.
       
    82      *
       
    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      *
       
    93      * @return the name of this clipboard object
       
    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 transferable
       
   102      * object and registers the specified clipboard owner as the owner of the
       
   103      * new contents.
       
   104      * <p>
       
   105      * If there is an existing owner different from the argument {@code owner},
       
   106      * that owner is notified that it no longer holds ownership of the clipboard
       
   107      * contents via an invocation of {@code ClipboardOwner.lostOwnership()} on
       
   108      * that owner. An implementation of {@code setContents()} is free not to
       
   109      * invoke {@code lostOwnership()} directly from this method. For example,
       
   110      * {@code lostOwnership()} may be invoked later on a different thread. The
       
   111      * same applies to {@code FlavorListener}s registered on this clipboard.
       
   112      * <p>
       
   113      * The method throws {@code IllegalStateException} if the clipboard is
       
   114      * currently unavailable. For example, on some platforms, the system
       
   115      * clipboard is unavailable while it is accessed by another application.
       
   116      *
       
   117      * @param  contents the transferable object representing the clipboard
       
   118      *         content
       
   119      * @param  owner the object which owns the clipboard content
       
   120      * @throws IllegalStateException if the clipboard is currently unavailable
       
   121      * @see java.awt.Toolkit#getSystemClipboard
       
   122      */
       
   123     public synchronized void setContents(Transferable contents, ClipboardOwner owner) {
       
   124         final ClipboardOwner oldOwner = this.owner;
       
   125         final Transferable oldContents = this.contents;
       
   126 
       
   127         this.owner = owner;
       
   128         this.contents = contents;
       
   129 
       
   130         if (oldOwner != null && oldOwner != owner) {
       
   131             DataFlavorUtil.getDesktopService().invokeOnEventThread(() ->
       
   132                     oldOwner.lostOwnership(Clipboard.this, oldContents));
       
   133         }
       
   134         fireFlavorsChanged();
       
   135     }
       
   136 
       
   137     /**
       
   138      * Returns a transferable object representing the current contents of the
       
   139      * clipboard. If the clipboard currently has no contents, it returns
       
   140      * {@code null}. The parameter Object requestor is not currently used. The
       
   141      * method throws {@code IllegalStateException} if the clipboard is currently
       
   142      * unavailable. For example, on some platforms, the system clipboard is
       
   143      * unavailable while it is accessed by another application.
       
   144      *
       
   145      * @param  requestor the object requesting the clip data (not used)
       
   146      * @return the current transferable object on the clipboard
       
   147      * @throws IllegalStateException if the clipboard is currently unavailable
       
   148      * @see java.awt.Toolkit#getSystemClipboard
       
   149      */
       
   150     public synchronized Transferable getContents(Object requestor) {
       
   151         return contents;
       
   152     }
       
   153 
       
   154     /**
       
   155      * Returns an array of {@code DataFlavor}s in which the current contents of
       
   156      * this clipboard can be provided. If there are no {@code DataFlavor}s
       
   157      * available, this method returns a zero-length array.
       
   158      *
       
   159      * @return an array of {@code DataFlavor}s in which the current contents of
       
   160      *         this clipboard can be provided
       
   161      * @throws IllegalStateException if this clipboard is currently unavailable
       
   162      * @since 1.5
       
   163      */
       
   164     public DataFlavor[] getAvailableDataFlavors() {
       
   165         Transferable cntnts = getContents(null);
       
   166         if (cntnts == null) {
       
   167             return new DataFlavor[0];
       
   168         }
       
   169         return cntnts.getTransferDataFlavors();
       
   170     }
       
   171 
       
   172     /**
       
   173      * Returns whether or not the current contents of this clipboard can be
       
   174      * provided in the specified {@code DataFlavor}.
       
   175      *
       
   176      * @param  flavor the requested {@code DataFlavor} for the contents
       
   177      * @return {@code true} if the current contents of this clipboard can be
       
   178      *         provided in the specified {@code DataFlavor}; {@code false}
       
   179      *         otherwise
       
   180      * @throws NullPointerException if {@code flavor} is {@code null}
       
   181      * @throws IllegalStateException if this clipboard is currently unavailable
       
   182      * @since 1.5
       
   183      */
       
   184     public boolean isDataFlavorAvailable(DataFlavor flavor) {
       
   185         if (flavor == null) {
       
   186             throw new NullPointerException("flavor");
       
   187         }
       
   188 
       
   189         Transferable cntnts = getContents(null);
       
   190         if (cntnts == null) {
       
   191             return false;
       
   192         }
       
   193         return cntnts.isDataFlavorSupported(flavor);
       
   194     }
       
   195 
       
   196     /**
       
   197      * Returns an object representing the current contents of this clipboard in
       
   198      * the specified {@code DataFlavor}. The class of the object returned is
       
   199      * defined by the representation class of {@code flavor}.
       
   200      *
       
   201      * @param  flavor the requested {@code DataFlavor} for the contents
       
   202      * @return an object representing the current contents of this clipboard in
       
   203      *         the specified {@code DataFlavor}
       
   204      * @throws NullPointerException if {@code flavor} is {@code null}
       
   205      * @throws IllegalStateException if this clipboard is currently unavailable
       
   206      * @throws UnsupportedFlavorException if the requested {@code DataFlavor} is
       
   207      *         not available
       
   208      * @throws IOException if the data in the requested {@code DataFlavor} can
       
   209      *         not be retrieved
       
   210      * @see DataFlavor#getRepresentationClass
       
   211      * @since 1.5
       
   212      */
       
   213     public Object getData(DataFlavor flavor)
       
   214         throws UnsupportedFlavorException, IOException {
       
   215         if (flavor == null) {
       
   216             throw new NullPointerException("flavor");
       
   217         }
       
   218 
       
   219         Transferable cntnts = getContents(null);
       
   220         if (cntnts == null) {
       
   221             throw new UnsupportedFlavorException(flavor);
       
   222         }
       
   223         return cntnts.getTransferData(flavor);
       
   224     }
       
   225 
       
   226     /**
       
   227      * Registers the specified {@code FlavorListener} to receive
       
   228      * {@code FlavorEvent}s from this clipboard. If {@code listener} is
       
   229      * {@code null}, no exception is thrown and no action is performed.
       
   230      *
       
   231      * @param  listener the listener to be added
       
   232      * @see #removeFlavorListener
       
   233      * @see #getFlavorListeners
       
   234      * @see FlavorListener
       
   235      * @see FlavorEvent
       
   236      * @since 1.5
       
   237      */
       
   238     public synchronized void addFlavorListener(FlavorListener listener) {
       
   239         if (listener == null) {
       
   240             return;
       
   241         }
       
   242 
       
   243         if (flavorListeners == null) {
       
   244             flavorListeners = new HashSet<>();
       
   245             currentDataFlavors = getAvailableDataFlavorSet();
       
   246         }
       
   247 
       
   248         flavorListeners.add(listener);
       
   249     }
       
   250 
       
   251     /**
       
   252      * Removes the specified {@code FlavorListener} so that it no longer
       
   253      * receives {@code FlavorEvent}s from this {@code Clipboard}. This method
       
   254      * performs no function, nor does it throw an exception, if the listener
       
   255      * specified by the argument was not previously added to this
       
   256      * {@code Clipboard}. If {@code listener} is {@code null}, no exception is
       
   257      * thrown and no action is performed.
       
   258      *
       
   259      * @param  listener the listener to be removed
       
   260      * @see #addFlavorListener
       
   261      * @see #getFlavorListeners
       
   262      * @see FlavorListener
       
   263      * @see FlavorEvent
       
   264      * @since 1.5
       
   265      */
       
   266     public synchronized void removeFlavorListener(FlavorListener listener) {
       
   267         if (listener == null || flavorListeners == null) {
       
   268             return;
       
   269         }
       
   270         flavorListeners.remove(listener);
       
   271     }
       
   272 
       
   273     /**
       
   274      * Returns an array of all the {@code FlavorListener}s currently registered
       
   275      * on this {@code Clipboard}.
       
   276      *
       
   277      * @return all of this clipboard's {@code FlavorListener}s or an empty array
       
   278      *         if no listeners are currently registered
       
   279      * @see #addFlavorListener
       
   280      * @see #removeFlavorListener
       
   281      * @see FlavorListener
       
   282      * @see FlavorEvent
       
   283      * @since 1.5
       
   284      */
       
   285     public synchronized FlavorListener[] getFlavorListeners() {
       
   286         return flavorListeners == null ? new FlavorListener[0] :
       
   287             flavorListeners.toArray(new FlavorListener[flavorListeners.size()]);
       
   288     }
       
   289 
       
   290     /**
       
   291      * Checks change of the {@code DataFlavor}s and, if necessary, notifies all
       
   292      * listeners that have registered interest for notification on
       
   293      * {@code FlavorEvent}s.
       
   294      *
       
   295      * @since 1.5
       
   296      */
       
   297     private void fireFlavorsChanged() {
       
   298         if (flavorListeners == null) {
       
   299             return;
       
   300         }
       
   301 
       
   302         Set<DataFlavor> prevDataFlavors = currentDataFlavors;
       
   303         currentDataFlavors = getAvailableDataFlavorSet();
       
   304         if (Objects.equals(prevDataFlavors, currentDataFlavors)) {
       
   305             return;
       
   306         }
       
   307         flavorListeners.forEach(listener ->
       
   308                 DataFlavorUtil.getDesktopService().invokeOnEventThread(() ->
       
   309                         listener.flavorsChanged(new FlavorEvent(Clipboard.this))));
       
   310     }
       
   311 
       
   312     /**
       
   313      * Returns a set of {@code DataFlavor}s currently available on this
       
   314      * clipboard.
       
   315      *
       
   316      * @return a set of {@code DataFlavor}s currently available on this
       
   317      *         clipboard
       
   318      * @since 1.5
       
   319      */
       
   320     private Set<DataFlavor> getAvailableDataFlavorSet() {
       
   321         Set<DataFlavor> set = new HashSet<>();
       
   322         Transferable contents = getContents(null);
       
   323         if (contents != null) {
       
   324             DataFlavor[] flavors = contents.getTransferDataFlavors();
       
   325             if (flavors != null) {
       
   326                 set.addAll(Arrays.asList(flavors));
       
   327             }
       
   328         }
       
   329         return set;
       
   330     }
       
   331 }