1 /*
   2  * Copyright (c) 2000, 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 sun.awt.dnd;
  27 
  28 import java.awt.Component;
  29 import java.awt.Point;
  30 
  31 import java.awt.datatransfer.DataFlavor;
  32 import java.awt.datatransfer.Transferable;
  33 import java.awt.datatransfer.UnsupportedFlavorException;
  34 
  35 import java.awt.dnd.DnDConstants;
  36 
  37 import java.awt.dnd.DropTarget;
  38 import java.awt.dnd.DropTargetContext;
  39 import java.awt.dnd.DropTargetListener;
  40 import java.awt.dnd.DropTargetEvent;
  41 import java.awt.dnd.DropTargetDragEvent;
  42 import java.awt.dnd.DropTargetDropEvent;
  43 import java.awt.dnd.InvalidDnDOperationException;
  44 
  45 import java.awt.dnd.peer.DropTargetContextPeer;
  46 
  47 import java.util.HashSet;
  48 import java.util.Map;
  49 import java.util.Arrays;
  50 
  51 import sun.util.logging.PlatformLogger;
  52 
  53 import java.io.IOException;
  54 import java.io.InputStream;
  55 
  56 import sun.awt.AppContext;
  57 import sun.awt.AWTPermissions;
  58 import sun.awt.SunToolkit;
  59 import sun.awt.datatransfer.DataTransferer;
  60 import sun.awt.datatransfer.ToolkitThreadBlockedHandler;
  61 
  62 /**
  63  * <p>
  64  * The SunDropTargetContextPeer class is the generic class responsible for handling
  65  * the interaction between a windowing systems DnD system and Java.
  66  * </p>
  67  *
  68  * @since 1.3.1
  69  *
  70  */
  71 
  72 public abstract class SunDropTargetContextPeer implements DropTargetContextPeer, Transferable {
  73 
  74     /*
  75      * A boolean constant that requires the peer to wait until the
  76      * SunDropTargetEvent is processed and return the status back
  77      * to the native code.
  78      */
  79     public static final boolean DISPATCH_SYNC = true;
  80     private   DropTarget              currentDT;
  81     private   DropTargetContext       currentDTC;
  82     private   long[]                  currentT;
  83     private   int                     currentA;   // target actions
  84     private   int                     currentSA;  // source actions
  85     private   int                     currentDA;  // current drop action
  86     private   int                     previousDA;
  87 
  88     private   long                    nativeDragContext;
  89 
  90     private   Transferable            local;
  91 
  92     private boolean                   dragRejected = false;
  93 
  94     protected int                     dropStatus   = STATUS_NONE;
  95     protected boolean                 dropComplete = false;
  96 
  97     // The flag is used to monitor whether the drop action is
  98     // handled by a user. That allows to distinct during
  99     // which operation getTransferData() method is invoked.
 100     boolean                           dropInProcess = false;
 101 
 102     /*
 103      * global lock
 104      */
 105 
 106     protected static final Object _globalLock = new Object();
 107 
 108     private static final PlatformLogger dndLog = PlatformLogger.getLogger("sun.awt.dnd.SunDropTargetContextPeer");
 109 
 110     /*
 111      * a primitive mechanism for advertising intra-JVM Transferables
 112      */
 113 
 114     protected static Transferable         currentJVMLocalSourceTransferable = null;
 115 
 116     public static void setCurrentJVMLocalSourceTransferable(Transferable t) throws InvalidDnDOperationException {
 117         synchronized(_globalLock) {
 118             if (t != null && currentJVMLocalSourceTransferable != null) {
 119                     throw new InvalidDnDOperationException();
 120             } else {
 121                 currentJVMLocalSourceTransferable = t;
 122             }
 123         }
 124     }
 125 
 126     /**
 127      * obtain the transferable iff the operation is in the same VM
 128      */
 129 
 130     private static Transferable getJVMLocalSourceTransferable() {
 131         return currentJVMLocalSourceTransferable;
 132     }
 133 
 134     /*
 135      * constants used by dropAccept() or dropReject()
 136      */
 137 
 138     protected final static int STATUS_NONE   =  0; // none pending
 139     protected final static int STATUS_WAIT   =  1; // drop pending
 140     protected final static int STATUS_ACCEPT =  2;
 141     protected final static int STATUS_REJECT = -1;
 142 
 143     /**
 144      * create the peer
 145      */
 146 
 147     public SunDropTargetContextPeer() {
 148         super();
 149     }
 150 
 151     /**
 152      * @return the DropTarget associated with this peer
 153      */
 154 
 155     public DropTarget getDropTarget() { return currentDT; }
 156 
 157     /**
 158      * @param actions set the current actions
 159      */
 160 
 161     public synchronized void setTargetActions(int actions) {
 162         currentA = actions &
 163             (DnDConstants.ACTION_COPY_OR_MOVE | DnDConstants.ACTION_LINK);
 164     }
 165 
 166     /**
 167      * @return the current target actions
 168      */
 169 
 170     public int getTargetActions() {
 171         return currentA;
 172     }
 173 
 174     /**
 175      * get the Transferable associated with the drop
 176      */
 177 
 178     public Transferable getTransferable() {
 179         return this;
 180     }
 181 
 182     /**
 183      * @return current DataFlavors available
 184      */
 185     // NOTE: This method may be called by privileged threads.
 186     //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
 187 
 188     public DataFlavor[] getTransferDataFlavors() {
 189         final Transferable    localTransferable = local;
 190 
 191         if (localTransferable != null) {
 192             return localTransferable.getTransferDataFlavors();
 193         } else {
 194             return DataTransferer.getInstance().getFlavorsForFormatsAsArray
 195                 (currentT, DataTransferer.adaptFlavorMap
 196                     (currentDT.getFlavorMap()));
 197         }
 198     }
 199 
 200     /**
 201      * @return if the flavor is supported
 202      */
 203 
 204     public boolean isDataFlavorSupported(DataFlavor df) {
 205         Transferable localTransferable = local;
 206 
 207         if (localTransferable != null) {
 208             return localTransferable.isDataFlavorSupported(df);
 209         } else {
 210             return DataTransferer.getInstance().getFlavorsForFormats
 211                 (currentT, DataTransferer.adaptFlavorMap
 212                     (currentDT.getFlavorMap())).
 213                 containsKey(df);
 214         }
 215     }
 216 
 217     /**
 218      * @return the data
 219      */
 220 
 221     public Object getTransferData(DataFlavor df)
 222       throws UnsupportedFlavorException, IOException,
 223         InvalidDnDOperationException
 224     {
 225 
 226         SecurityManager sm = System.getSecurityManager();
 227         try {
 228             if (!dropInProcess && sm != null) {
 229                 sm.checkPermission(AWTPermissions.ACCESS_CLIPBOARD_PERMISSION);
 230             }
 231         } catch (Exception e) {
 232             Thread currentThread = Thread.currentThread();
 233             currentThread.getUncaughtExceptionHandler().uncaughtException(currentThread, e);
 234             return null;
 235         }
 236 
 237         Long lFormat = null;
 238         Transferable localTransferable = local;
 239 
 240         if (localTransferable != null) {
 241             return localTransferable.getTransferData(df);
 242         } else if (df.isMimeTypeEqual(DataFlavor.javaJVMLocalObjectMimeType)) {
 243             // Workaround to JDK-8024061: Exception thrown when drag and drop
 244             //      between two components is executed quickly.
 245             // It is expected localTransferable is not null if javaJVMLocalObjectMimeType
 246             // is used. Executing further results in ClassCastException, so null is
 247             // returned here as no transfer data is available in this case.
 248             return null;
 249         }
 250 
 251         if (dropStatus != STATUS_ACCEPT || dropComplete) {
 252             throw new InvalidDnDOperationException("No drop current");
 253         }
 254 
 255         Map<DataFlavor, Long> flavorMap = DataTransferer.getInstance()
 256             .getFlavorsForFormats(currentT, DataTransferer.adaptFlavorMap
 257                 (currentDT.getFlavorMap()));
 258 
 259         lFormat = flavorMap.get(df);
 260         if (lFormat == null) {
 261             throw new UnsupportedFlavorException(df);
 262         }
 263 
 264         if (df.isRepresentationClassRemote() &&
 265             currentDA != DnDConstants.ACTION_LINK) {
 266             throw new InvalidDnDOperationException("only ACTION_LINK is permissable for transfer of java.rmi.Remote objects");
 267         }
 268 
 269         final long format = lFormat.longValue();
 270 
 271         Object ret = getNativeData(format);
 272 
 273         if (ret instanceof byte[]) {
 274             try {
 275                 return DataTransferer.getInstance().
 276                     translateBytes((byte[])ret, df, format, this);
 277             } catch (IOException e) {
 278                 throw new InvalidDnDOperationException(e.getMessage());
 279             }
 280         } else if (ret instanceof InputStream) {
 281             try {
 282                 return DataTransferer.getInstance().
 283                     translateStream((InputStream)ret, df, format, this);
 284             } catch (IOException e) {
 285                 throw new InvalidDnDOperationException(e.getMessage());
 286             }
 287         } else {
 288             throw new IOException("no native data was transfered");
 289         }
 290     }
 291 
 292     protected abstract Object getNativeData(long format)
 293       throws IOException;
 294 
 295     /**
 296      * @return if the transfer is a local one
 297      */
 298     public boolean isTransferableJVMLocal() {
 299         return local != null || getJVMLocalSourceTransferable() != null;
 300     }
 301 
 302     private int handleEnterMessage(final Component component,
 303                                    final int x, final int y,
 304                                    final int dropAction,
 305                                    final int actions, final long[] formats,
 306                                    final long nativeCtxt) {
 307         return postDropTargetEvent(component, x, y, dropAction, actions,
 308                                    formats, nativeCtxt,
 309                                    SunDropTargetEvent.MOUSE_ENTERED,
 310                                    SunDropTargetContextPeer.DISPATCH_SYNC);
 311     }
 312 
 313     /**
 314      * actual processing on EventQueue Thread
 315      */
 316 
 317     protected void processEnterMessage(SunDropTargetEvent event) {
 318         Component  c    = (Component)event.getSource();
 319         DropTarget dt   = c.getDropTarget();
 320         Point      hots = event.getPoint();
 321 
 322         local = getJVMLocalSourceTransferable();
 323 
 324         if (currentDTC != null) { // some wreckage from last time
 325             currentDTC.removeNotify();
 326             currentDTC = null;
 327         }
 328 
 329         if (c.isShowing() && dt != null && dt.isActive()) {
 330             currentDT  = dt;
 331             currentDTC = currentDT.getDropTargetContext();
 332 
 333             currentDTC.addNotify(this);
 334 
 335             currentA   = dt.getDefaultActions();
 336 
 337             try {
 338                 ((DropTargetListener)dt).dragEnter(new DropTargetDragEvent(currentDTC,
 339                                                                            hots,
 340                                                                            currentDA,
 341                                                                            currentSA));
 342             } catch (Exception e) {
 343                 e.printStackTrace();
 344                 currentDA = DnDConstants.ACTION_NONE;
 345             }
 346         } else {
 347             currentDT  = null;
 348             currentDTC = null;
 349             currentDA   = DnDConstants.ACTION_NONE;
 350             currentSA   = DnDConstants.ACTION_NONE;
 351             currentA   = DnDConstants.ACTION_NONE;
 352         }
 353 
 354     }
 355 
 356     /**
 357      * upcall to handle exit messages
 358      */
 359 
 360     private void handleExitMessage(final Component component,
 361                                    final long nativeCtxt) {
 362         /*
 363          * Even though the return value is irrelevant for this event, it is
 364          * dispatched synchronously to fix 4393148 properly.
 365          */
 366         postDropTargetEvent(component, 0, 0, DnDConstants.ACTION_NONE,
 367                             DnDConstants.ACTION_NONE, null, nativeCtxt,
 368                             SunDropTargetEvent.MOUSE_EXITED,
 369                             SunDropTargetContextPeer.DISPATCH_SYNC);
 370     }
 371 
 372     /**
 373      *
 374      */
 375 
 376     protected void processExitMessage(SunDropTargetEvent event) {
 377         Component         c   = (Component)event.getSource();
 378         DropTarget        dt  = c.getDropTarget();
 379         DropTargetContext dtc = null;
 380 
 381         if (dt == null) {
 382             currentDT = null;
 383             currentT  = null;
 384 
 385             if (currentDTC != null) {
 386                 currentDTC.removeNotify();
 387             }
 388 
 389             currentDTC = null;
 390 
 391             return;
 392         }
 393 
 394         if (dt != currentDT) {
 395 
 396             if (currentDTC != null) {
 397                 currentDTC.removeNotify();
 398             }
 399 
 400             currentDT  = dt;
 401             currentDTC = dt.getDropTargetContext();
 402 
 403             currentDTC.addNotify(this);
 404         }
 405 
 406         dtc = currentDTC;
 407 
 408         if (dt.isActive()) try {
 409             ((DropTargetListener)dt).dragExit(new DropTargetEvent(dtc));
 410         } catch (Exception e) {
 411             e.printStackTrace();
 412         } finally {
 413             currentA  = DnDConstants.ACTION_NONE;
 414             currentSA = DnDConstants.ACTION_NONE;
 415             currentDA = DnDConstants.ACTION_NONE;
 416             currentDT = null;
 417             currentT  = null;
 418 
 419             currentDTC.removeNotify();
 420             currentDTC = null;
 421 
 422             local = null;
 423 
 424             dragRejected = false;
 425         }
 426     }
 427 
 428     private int handleMotionMessage(final Component component,
 429                                     final int x, final int y,
 430                                     final int dropAction,
 431                                     final int actions, final long[] formats,
 432                                     final long nativeCtxt) {
 433         return postDropTargetEvent(component, x, y, dropAction, actions,
 434                                    formats, nativeCtxt,
 435                                    SunDropTargetEvent.MOUSE_DRAGGED,
 436                                    SunDropTargetContextPeer.DISPATCH_SYNC);
 437     }
 438 
 439     /**
 440      *
 441      */
 442 
 443     protected void processMotionMessage(SunDropTargetEvent event,
 444                                       boolean operationChanged) {
 445         Component         c    = (Component)event.getSource();
 446         Point             hots = event.getPoint();
 447         int               id   = event.getID();
 448         DropTarget        dt   = c.getDropTarget();
 449         DropTargetContext dtc  = null;
 450 
 451         if (c.isShowing() && (dt != null) && dt.isActive()) {
 452             if (currentDT != dt) {
 453                 if (currentDTC != null) {
 454                     currentDTC.removeNotify();
 455                 }
 456 
 457                 currentDT  = dt;
 458                 currentDTC = null;
 459             }
 460 
 461             dtc = currentDT.getDropTargetContext();
 462             if (dtc != currentDTC) {
 463                 if (currentDTC != null) {
 464                     currentDTC.removeNotify();
 465                 }
 466 
 467                 currentDTC = dtc;
 468                 currentDTC.addNotify(this);
 469             }
 470 
 471             currentA = currentDT.getDefaultActions();
 472 
 473             try {
 474                 DropTargetDragEvent dtde = new DropTargetDragEvent(dtc,
 475                                                                    hots,
 476                                                                    currentDA,
 477                                                                    currentSA);
 478                 DropTargetListener dtl = (DropTargetListener)dt;
 479                 if (operationChanged) {
 480                     dtl.dropActionChanged(dtde);
 481                 } else {
 482                     dtl.dragOver(dtde);
 483                 }
 484 
 485                 if (dragRejected) {
 486                     currentDA = DnDConstants.ACTION_NONE;
 487                 }
 488             } catch (Exception e) {
 489                 e.printStackTrace();
 490                 currentDA = DnDConstants.ACTION_NONE;
 491             }
 492         } else {
 493             currentDA = DnDConstants.ACTION_NONE;
 494         }
 495     }
 496 
 497     /**
 498      * upcall to handle the Drop message
 499      */
 500 
 501     private void handleDropMessage(final Component component,
 502                                    final int x, final int y,
 503                                    final int dropAction, final int actions,
 504                                    final long[] formats,
 505                                    final long nativeCtxt) {
 506         postDropTargetEvent(component, x, y, dropAction, actions,
 507                             formats, nativeCtxt,
 508                             SunDropTargetEvent.MOUSE_DROPPED,
 509                             !SunDropTargetContextPeer.DISPATCH_SYNC);
 510     }
 511 
 512     /**
 513      *
 514      */
 515 
 516     protected void processDropMessage(SunDropTargetEvent event) {
 517         Component  c    = (Component)event.getSource();
 518         Point      hots = event.getPoint();
 519         DropTarget dt   = c.getDropTarget();
 520 
 521         dropStatus   = STATUS_WAIT; // drop pending ACK
 522         dropComplete = false;
 523 
 524         if (c.isShowing() && dt != null && dt.isActive()) {
 525             DropTargetContext dtc = dt.getDropTargetContext();
 526 
 527             currentDT = dt;
 528 
 529             if (currentDTC != null) {
 530                 currentDTC.removeNotify();
 531             }
 532 
 533             currentDTC = dtc;
 534             currentDTC.addNotify(this);
 535             currentA = dt.getDefaultActions();
 536 
 537             synchronized(_globalLock) {
 538                 if ((local = getJVMLocalSourceTransferable()) != null)
 539                     setCurrentJVMLocalSourceTransferable(null);
 540             }
 541 
 542             dropInProcess = true;
 543 
 544             try {
 545                 ((DropTargetListener)dt).drop(new DropTargetDropEvent(dtc,
 546                                                                       hots,
 547                                                                       currentDA,
 548                                                                       currentSA,
 549                                                                       local != null));
 550             } finally {
 551                 if (dropStatus == STATUS_WAIT) {
 552                     rejectDrop();
 553                 } else if (dropComplete == false) {
 554                     dropComplete(false);
 555                 }
 556                 dropInProcess = false;
 557             }
 558         } else {
 559             rejectDrop();
 560         }
 561     }
 562 
 563     protected int postDropTargetEvent(final Component component,
 564                                       final int x, final int y,
 565                                       final int dropAction,
 566                                       final int actions,
 567                                       final long[] formats,
 568                                       final long nativeCtxt,
 569                                       final int eventID,
 570                                       final boolean dispatchType) {
 571         AppContext appContext = SunToolkit.targetToAppContext(component);
 572 
 573         EventDispatcher dispatcher =
 574             new EventDispatcher(this, dropAction, actions, formats, nativeCtxt,
 575                                 dispatchType);
 576 
 577         SunDropTargetEvent event =
 578             new SunDropTargetEvent(component, eventID, x, y, dispatcher);
 579 
 580         if (dispatchType == SunDropTargetContextPeer.DISPATCH_SYNC) {
 581             DataTransferer.getInstance().getToolkitThreadBlockedHandler().lock();
 582         }
 583 
 584         // schedule callback
 585         SunToolkit.postEvent(appContext, event);
 586 
 587         eventPosted(event);
 588 
 589         if (dispatchType == SunDropTargetContextPeer.DISPATCH_SYNC) {
 590             while (!dispatcher.isDone()) {
 591                 DataTransferer.getInstance().getToolkitThreadBlockedHandler().enter();
 592             }
 593 
 594             DataTransferer.getInstance().getToolkitThreadBlockedHandler().unlock();
 595 
 596             // return target's response
 597             return dispatcher.getReturnValue();
 598         } else {
 599             return 0;
 600         }
 601     }
 602 
 603     /**
 604      * acceptDrag
 605      */
 606 
 607     public synchronized void acceptDrag(int dragOperation) {
 608         if (currentDT == null) {
 609             throw new InvalidDnDOperationException("No Drag pending");
 610         }
 611         currentDA = mapOperation(dragOperation);
 612         if (currentDA != DnDConstants.ACTION_NONE) {
 613             dragRejected = false;
 614         }
 615     }
 616 
 617     /**
 618      * rejectDrag
 619      */
 620 
 621     public synchronized void rejectDrag() {
 622         if (currentDT == null) {
 623             throw new InvalidDnDOperationException("No Drag pending");
 624         }
 625         currentDA = DnDConstants.ACTION_NONE;
 626         dragRejected = true;
 627     }
 628 
 629     /**
 630      * acceptDrop
 631      */
 632 
 633     public synchronized void acceptDrop(int dropOperation) {
 634         if (dropOperation == DnDConstants.ACTION_NONE)
 635             throw new IllegalArgumentException("invalid acceptDrop() action");
 636 
 637         if (dropStatus == STATUS_WAIT || dropStatus == STATUS_ACCEPT) {
 638             currentDA = currentA = mapOperation(dropOperation & currentSA);
 639 
 640             dropStatus   = STATUS_ACCEPT;
 641             dropComplete = false;
 642         } else {
 643             throw new InvalidDnDOperationException("invalid acceptDrop()");
 644         }
 645     }
 646 
 647     /**
 648      * reject Drop
 649      */
 650 
 651     public synchronized void rejectDrop() {
 652         if (dropStatus != STATUS_WAIT) {
 653             throw new InvalidDnDOperationException("invalid rejectDrop()");
 654         }
 655         dropStatus = STATUS_REJECT;
 656         /*
 657          * Fix for 4285634.
 658          * The target rejected the drop means that it doesn't perform any
 659          * drop action. This change is to make Solaris behavior consistent
 660          * with Win32.
 661          */
 662         currentDA = DnDConstants.ACTION_NONE;
 663         dropComplete(false);
 664     }
 665 
 666     /**
 667      * mapOperation
 668      */
 669 
 670     private int mapOperation(int operation) {
 671         int[] operations = {
 672                 DnDConstants.ACTION_MOVE,
 673                 DnDConstants.ACTION_COPY,
 674                 DnDConstants.ACTION_LINK,
 675         };
 676         int   ret = DnDConstants.ACTION_NONE;
 677 
 678         for (int i = 0; i < operations.length; i++) {
 679             if ((operation & operations[i]) == operations[i]) {
 680                     ret = operations[i];
 681                     break;
 682             }
 683         }
 684 
 685         return ret;
 686     }
 687 
 688     /**
 689      * signal drop complete
 690      */
 691 
 692     public synchronized void dropComplete(boolean success) {
 693         if (dropStatus == STATUS_NONE) {
 694             throw new InvalidDnDOperationException("No Drop pending");
 695         }
 696 
 697         if (currentDTC != null) currentDTC.removeNotify();
 698 
 699         currentDT  = null;
 700         currentDTC = null;
 701         currentT   = null;
 702         currentA   = DnDConstants.ACTION_NONE;
 703 
 704         synchronized(_globalLock) {
 705             currentJVMLocalSourceTransferable = null;
 706         }
 707 
 708         dropStatus   = STATUS_NONE;
 709         dropComplete = true;
 710 
 711         try {
 712             doDropDone(success, currentDA, local != null);
 713         } finally {
 714             currentDA = DnDConstants.ACTION_NONE;
 715             // The native context is invalid after the drop is done.
 716             // Clear the reference to prohibit access.
 717             nativeDragContext = 0;
 718         }
 719     }
 720 
 721     protected abstract void doDropDone(boolean success,
 722                                        int dropAction, boolean isLocal);
 723 
 724     protected synchronized long getNativeDragContext() {
 725         return nativeDragContext;
 726     }
 727 
 728     protected void eventPosted(SunDropTargetEvent e) {}
 729 
 730     protected void eventProcessed(SunDropTargetEvent e, int returnValue,
 731                                   boolean dispatcherDone) {}
 732 
 733     protected static class EventDispatcher {
 734 
 735         private final SunDropTargetContextPeer peer;
 736 
 737         // context fields
 738         private final int dropAction;
 739         private final int actions;
 740         private final long[] formats;
 741         private long nativeCtxt;
 742         private final boolean dispatchType;
 743         private boolean dispatcherDone = false;
 744 
 745         // dispatcher state fields
 746         private int returnValue = 0;
 747         // set of events to be dispatched by this dispatcher
 748         private final HashSet<SunDropTargetEvent> eventSet = new HashSet<>(3);
 749 
 750         static final ToolkitThreadBlockedHandler handler =
 751             DataTransferer.getInstance().getToolkitThreadBlockedHandler();
 752 
 753         EventDispatcher(SunDropTargetContextPeer peer,
 754                         int dropAction,
 755                         int actions,
 756                         long[] formats,
 757                         long nativeCtxt,
 758                         boolean dispatchType) {
 759 
 760             this.peer         = peer;
 761             this.nativeCtxt   = nativeCtxt;
 762             this.dropAction   = dropAction;
 763             this.actions      = actions;
 764             this.formats =
 765                      (null == formats) ? null : Arrays.copyOf(formats, formats.length);
 766             this.dispatchType = dispatchType;
 767         }
 768 
 769         void dispatchEvent(SunDropTargetEvent e) {
 770             int id = e.getID();
 771 
 772             switch (id) {
 773             case SunDropTargetEvent.MOUSE_ENTERED:
 774                 dispatchEnterEvent(e);
 775                 break;
 776             case SunDropTargetEvent.MOUSE_DRAGGED:
 777                 dispatchMotionEvent(e);
 778                 break;
 779             case SunDropTargetEvent.MOUSE_EXITED:
 780                 dispatchExitEvent(e);
 781                 break;
 782             case SunDropTargetEvent.MOUSE_DROPPED:
 783                 dispatchDropEvent(e);
 784                 break;
 785             default:
 786                 throw new InvalidDnDOperationException();
 787             }
 788         }
 789 
 790         private void dispatchEnterEvent(SunDropTargetEvent e) {
 791             synchronized (peer) {
 792 
 793                 // store the drop action here to track operation changes
 794                 peer.previousDA = dropAction;
 795 
 796                 // setup peer context
 797                 peer.nativeDragContext = nativeCtxt;
 798                 peer.currentT          = formats;
 799                 peer.currentSA         = actions;
 800                 peer.currentDA         = dropAction;
 801                 // To allow data retrieval.
 802                 peer.dropStatus        = STATUS_ACCEPT;
 803                 peer.dropComplete      = false;
 804 
 805                 try {
 806                     peer.processEnterMessage(e);
 807                 } finally {
 808                     peer.dropStatus        = STATUS_NONE;
 809                 }
 810 
 811                 setReturnValue(peer.currentDA);
 812             }
 813         }
 814 
 815         private void dispatchMotionEvent(SunDropTargetEvent e) {
 816             synchronized (peer) {
 817 
 818                 boolean operationChanged = peer.previousDA != dropAction;
 819                 peer.previousDA = dropAction;
 820 
 821                 // setup peer context
 822                 peer.nativeDragContext = nativeCtxt;
 823                 peer.currentT          = formats;
 824                 peer.currentSA         = actions;
 825                 peer.currentDA         = dropAction;
 826                 // To allow data retrieval.
 827                 peer.dropStatus        = STATUS_ACCEPT;
 828                 peer.dropComplete      = false;
 829 
 830                 try {
 831                     peer.processMotionMessage(e, operationChanged);
 832                 } finally {
 833                     peer.dropStatus        = STATUS_NONE;
 834                 }
 835 
 836                 setReturnValue(peer.currentDA);
 837             }
 838         }
 839 
 840         private void dispatchExitEvent(SunDropTargetEvent e) {
 841             synchronized (peer) {
 842 
 843                 // setup peer context
 844                 peer.nativeDragContext = nativeCtxt;
 845 
 846                 peer.processExitMessage(e);
 847             }
 848         }
 849 
 850         private void dispatchDropEvent(SunDropTargetEvent e) {
 851             synchronized (peer) {
 852 
 853                 // setup peer context
 854                 peer.nativeDragContext = nativeCtxt;
 855                 peer.currentT          = formats;
 856                 peer.currentSA         = actions;
 857                 peer.currentDA         = dropAction;
 858 
 859                 peer.processDropMessage(e);
 860             }
 861         }
 862 
 863         void setReturnValue(int ret) {
 864             returnValue = ret;
 865         }
 866 
 867         int getReturnValue() {
 868             return returnValue;
 869         }
 870 
 871         boolean isDone() {
 872             return eventSet.isEmpty();
 873         }
 874 
 875         void registerEvent(SunDropTargetEvent e) {
 876             handler.lock();
 877             if (!eventSet.add(e) && dndLog.isLoggable(PlatformLogger.Level.FINE)) {
 878                 dndLog.fine("Event is already registered: " + e);
 879             }
 880             handler.unlock();
 881         }
 882 
 883         void unregisterEvent(SunDropTargetEvent e) {
 884             handler.lock();
 885             try {
 886                 if (!eventSet.remove(e)) {
 887                     // This event has already been unregistered.
 888                     return;
 889                 }
 890                 if (eventSet.isEmpty()) {
 891                     if (!dispatcherDone && dispatchType == DISPATCH_SYNC) {
 892                         handler.exit();
 893                     }
 894                     dispatcherDone = true;
 895                 }
 896             } finally {
 897                 handler.unlock();
 898             }
 899 
 900             try {
 901                 peer.eventProcessed(e, returnValue, dispatcherDone);
 902             } finally {
 903                 /*
 904                  * Clear the reference to the native context if all copies of
 905                  * the original event are processed.
 906                  */
 907                 if (dispatcherDone) {
 908                     nativeCtxt = 0;
 909                     // Fix for 6342381
 910                     peer.nativeDragContext = 0;
 911 
 912                 }
 913             }
 914         }
 915 
 916         public void unregisterAllEvents() {
 917             Object[] events = null;
 918             handler.lock();
 919             try {
 920                 events = eventSet.toArray();
 921             } finally {
 922                 handler.unlock();
 923             }
 924 
 925             if (events != null) {
 926                 for (int i = 0; i < events.length; i++) {
 927                     unregisterEvent((SunDropTargetEvent)events[i]);
 928                 }
 929             }
 930         }
 931     }
 932 }