1 /*
   2  * Copyright (c) 2000, 2013, 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 JDK1.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         }
 243 
 244         if (dropStatus != STATUS_ACCEPT || dropComplete) {
 245             throw new InvalidDnDOperationException("No drop current");
 246         }
 247 
 248         Map flavorMap = DataTransferer.getInstance().getFlavorsForFormats
 249             (currentT, DataTransferer.adaptFlavorMap
 250                 (currentDT.getFlavorMap()));
 251 
 252         lFormat = (Long)flavorMap.get(df);
 253         if (lFormat == null) {
 254             throw new UnsupportedFlavorException(df);
 255         }
 256 
 257         if (df.isRepresentationClassRemote() &&
 258             currentDA != DnDConstants.ACTION_LINK) {
 259             throw new InvalidDnDOperationException("only ACTION_LINK is permissable for transfer of java.rmi.Remote objects");
 260         }
 261 
 262         final long format = lFormat.longValue();
 263 
 264         Object ret = getNativeData(format);
 265 
 266         if (ret instanceof byte[]) {
 267             try {
 268                 return DataTransferer.getInstance().
 269                     translateBytes((byte[])ret, df, format, this);
 270             } catch (IOException e) {
 271                 throw new InvalidDnDOperationException(e.getMessage());
 272             }
 273         } else if (ret instanceof InputStream) {
 274             try {
 275                 return DataTransferer.getInstance().
 276                     translateStream((InputStream)ret, df, format, this);
 277             } catch (IOException e) {
 278                 throw new InvalidDnDOperationException(e.getMessage());
 279             }
 280         } else {
 281             throw new IOException("no native data was transfered");
 282         }
 283     }
 284 
 285     protected abstract Object getNativeData(long format)
 286       throws IOException;
 287 
 288     /**
 289      * @return if the transfer is a local one
 290      */
 291     public boolean isTransferableJVMLocal() {
 292         return local != null || getJVMLocalSourceTransferable() != null;
 293     }
 294 
 295     private int handleEnterMessage(final Component component,
 296                                    final int x, final int y,
 297                                    final int dropAction,
 298                                    final int actions, final long[] formats,
 299                                    final long nativeCtxt) {
 300         return postDropTargetEvent(component, x, y, dropAction, actions,
 301                                    formats, nativeCtxt,
 302                                    SunDropTargetEvent.MOUSE_ENTERED,
 303                                    SunDropTargetContextPeer.DISPATCH_SYNC);
 304     }
 305 
 306     /**
 307      * actual processing on EventQueue Thread
 308      */
 309 
 310     protected void processEnterMessage(SunDropTargetEvent event) {
 311         Component  c    = (Component)event.getSource();
 312         DropTarget dt   = c.getDropTarget();
 313         Point      hots = event.getPoint();
 314 
 315         local = getJVMLocalSourceTransferable();
 316 
 317         if (currentDTC != null) { // some wreckage from last time
 318             currentDTC.removeNotify();
 319             currentDTC = null;
 320         }
 321 
 322         if (c.isShowing() && dt != null && dt.isActive()) {
 323             currentDT  = dt;
 324             currentDTC = currentDT.getDropTargetContext();
 325 
 326             currentDTC.addNotify(this);
 327 
 328             currentA   = dt.getDefaultActions();
 329 
 330             try {
 331                 ((DropTargetListener)dt).dragEnter(new DropTargetDragEvent(currentDTC,
 332                                                                            hots,
 333                                                                            currentDA,
 334                                                                            currentSA));
 335             } catch (Exception e) {
 336                 e.printStackTrace();
 337                 currentDA = DnDConstants.ACTION_NONE;
 338             }
 339         } else {
 340             currentDT  = null;
 341             currentDTC = null;
 342             currentDA   = DnDConstants.ACTION_NONE;
 343             currentSA   = DnDConstants.ACTION_NONE;
 344             currentA   = DnDConstants.ACTION_NONE;
 345         }
 346 
 347     }
 348 
 349     /**
 350      * upcall to handle exit messages
 351      */
 352 
 353     private void handleExitMessage(final Component component,
 354                                    final long nativeCtxt) {
 355         /*
 356          * Even though the return value is irrelevant for this event, it is
 357          * dispatched synchronously to fix 4393148 properly.
 358          */
 359         postDropTargetEvent(component, 0, 0, DnDConstants.ACTION_NONE,
 360                             DnDConstants.ACTION_NONE, null, nativeCtxt,
 361                             SunDropTargetEvent.MOUSE_EXITED,
 362                             SunDropTargetContextPeer.DISPATCH_SYNC);
 363     }
 364 
 365     /**
 366      *
 367      */
 368 
 369     protected void processExitMessage(SunDropTargetEvent event) {
 370         Component         c   = (Component)event.getSource();
 371         DropTarget        dt  = c.getDropTarget();
 372         DropTargetContext dtc = null;
 373 
 374         if (dt == null) {
 375             currentDT = null;
 376             currentT  = null;
 377 
 378             if (currentDTC != null) {
 379                 currentDTC.removeNotify();
 380             }
 381 
 382             currentDTC = null;
 383 
 384             return;
 385         }
 386 
 387         if (dt != currentDT) {
 388 
 389             if (currentDTC != null) {
 390                 currentDTC.removeNotify();
 391             }
 392 
 393             currentDT  = dt;
 394             currentDTC = dt.getDropTargetContext();
 395 
 396             currentDTC.addNotify(this);
 397         }
 398 
 399         dtc = currentDTC;
 400 
 401         if (dt.isActive()) try {
 402             ((DropTargetListener)dt).dragExit(new DropTargetEvent(dtc));
 403         } catch (Exception e) {
 404             e.printStackTrace();
 405         } finally {
 406             currentA  = DnDConstants.ACTION_NONE;
 407             currentSA = DnDConstants.ACTION_NONE;
 408             currentDA = DnDConstants.ACTION_NONE;
 409             currentDT = null;
 410             currentT  = null;
 411 
 412             currentDTC.removeNotify();
 413             currentDTC = null;
 414 
 415             local = null;
 416 
 417             dragRejected = false;
 418         }
 419     }
 420 
 421     private int handleMotionMessage(final Component component,
 422                                     final int x, final int y,
 423                                     final int dropAction,
 424                                     final int actions, final long[] formats,
 425                                     final long nativeCtxt) {
 426         return postDropTargetEvent(component, x, y, dropAction, actions,
 427                                    formats, nativeCtxt,
 428                                    SunDropTargetEvent.MOUSE_DRAGGED,
 429                                    SunDropTargetContextPeer.DISPATCH_SYNC);
 430     }
 431 
 432     /**
 433      *
 434      */
 435 
 436     protected void processMotionMessage(SunDropTargetEvent event,
 437                                       boolean operationChanged) {
 438         Component         c    = (Component)event.getSource();
 439         Point             hots = event.getPoint();
 440         int               id   = event.getID();
 441         DropTarget        dt   = c.getDropTarget();
 442         DropTargetContext dtc  = null;
 443 
 444         if (c.isShowing() && (dt != null) && dt.isActive()) {
 445             if (currentDT != dt) {
 446                 if (currentDTC != null) {
 447                     currentDTC.removeNotify();
 448                 }
 449 
 450                 currentDT  = dt;
 451                 currentDTC = null;
 452             }
 453 
 454             dtc = currentDT.getDropTargetContext();
 455             if (dtc != currentDTC) {
 456                 if (currentDTC != null) {
 457                     currentDTC.removeNotify();
 458                 }
 459 
 460                 currentDTC = dtc;
 461                 currentDTC.addNotify(this);
 462             }
 463 
 464             currentA = currentDT.getDefaultActions();
 465 
 466             try {
 467                 DropTargetDragEvent dtde = new DropTargetDragEvent(dtc,
 468                                                                    hots,
 469                                                                    currentDA,
 470                                                                    currentSA);
 471                 DropTargetListener dtl = (DropTargetListener)dt;
 472                 if (operationChanged) {
 473                     dtl.dropActionChanged(dtde);
 474                 } else {
 475                     dtl.dragOver(dtde);
 476                 }
 477 
 478                 if (dragRejected) {
 479                     currentDA = DnDConstants.ACTION_NONE;
 480                 }
 481             } catch (Exception e) {
 482                 e.printStackTrace();
 483                 currentDA = DnDConstants.ACTION_NONE;
 484             }
 485         } else {
 486             currentDA = DnDConstants.ACTION_NONE;
 487         }
 488     }
 489 
 490     /**
 491      * upcall to handle the Drop message
 492      */
 493 
 494     private void handleDropMessage(final Component component,
 495                                    final int x, final int y,
 496                                    final int dropAction, final int actions,
 497                                    final long[] formats,
 498                                    final long nativeCtxt) {
 499         postDropTargetEvent(component, x, y, dropAction, actions,
 500                             formats, nativeCtxt,
 501                             SunDropTargetEvent.MOUSE_DROPPED,
 502                             !SunDropTargetContextPeer.DISPATCH_SYNC);
 503     }
 504 
 505     /**
 506      *
 507      */
 508 
 509     protected void processDropMessage(SunDropTargetEvent event) {
 510         Component  c    = (Component)event.getSource();
 511         Point      hots = event.getPoint();
 512         DropTarget dt   = c.getDropTarget();
 513 
 514         dropStatus   = STATUS_WAIT; // drop pending ACK
 515         dropComplete = false;
 516 
 517         if (c.isShowing() && dt != null && dt.isActive()) {
 518             DropTargetContext dtc = dt.getDropTargetContext();
 519 
 520             currentDT = dt;
 521 
 522             if (currentDTC != null) {
 523                 currentDTC.removeNotify();
 524             }
 525 
 526             currentDTC = dtc;
 527             currentDTC.addNotify(this);
 528             currentA = dt.getDefaultActions();
 529 
 530             synchronized(_globalLock) {
 531                 if ((local = getJVMLocalSourceTransferable()) != null)
 532                     setCurrentJVMLocalSourceTransferable(null);
 533             }
 534 
 535             dropInProcess = true;
 536 
 537             try {
 538                 ((DropTargetListener)dt).drop(new DropTargetDropEvent(dtc,
 539                                                                       hots,
 540                                                                       currentDA,
 541                                                                       currentSA,
 542                                                                       local != null));
 543             } finally {
 544                 if (dropStatus == STATUS_WAIT) {
 545                     rejectDrop();
 546                 } else if (dropComplete == false) {
 547                     dropComplete(false);
 548                 }
 549                 dropInProcess = false;
 550             }
 551         } else {
 552             rejectDrop();
 553         }
 554     }
 555 
 556     protected int postDropTargetEvent(final Component component,
 557                                       final int x, final int y,
 558                                       final int dropAction,
 559                                       final int actions,
 560                                       final long[] formats,
 561                                       final long nativeCtxt,
 562                                       final int eventID,
 563                                       final boolean dispatchType) {
 564         AppContext appContext = SunToolkit.targetToAppContext(component);
 565 
 566         EventDispatcher dispatcher =
 567             new EventDispatcher(this, dropAction, actions, formats, nativeCtxt,
 568                                 dispatchType);
 569 
 570         SunDropTargetEvent event =
 571             new SunDropTargetEvent(component, eventID, x, y, dispatcher);
 572 
 573         if (dispatchType == SunDropTargetContextPeer.DISPATCH_SYNC) {
 574             DataTransferer.getInstance().getToolkitThreadBlockedHandler().lock();
 575         }
 576 
 577         // schedule callback
 578         SunToolkit.postEvent(appContext, event);
 579 
 580         eventPosted(event);
 581 
 582         if (dispatchType == SunDropTargetContextPeer.DISPATCH_SYNC) {
 583             while (!dispatcher.isDone()) {
 584                 DataTransferer.getInstance().getToolkitThreadBlockedHandler().enter();
 585             }
 586 
 587             DataTransferer.getInstance().getToolkitThreadBlockedHandler().unlock();
 588 
 589             // return target's response
 590             return dispatcher.getReturnValue();
 591         } else {
 592             return 0;
 593         }
 594     }
 595 
 596     /**
 597      * acceptDrag
 598      */
 599 
 600     public synchronized void acceptDrag(int dragOperation) {
 601         if (currentDT == null) {
 602             throw new InvalidDnDOperationException("No Drag pending");
 603         }
 604         currentDA = mapOperation(dragOperation);
 605         if (currentDA != DnDConstants.ACTION_NONE) {
 606             dragRejected = false;
 607         }
 608     }
 609 
 610     /**
 611      * rejectDrag
 612      */
 613 
 614     public synchronized void rejectDrag() {
 615         if (currentDT == null) {
 616             throw new InvalidDnDOperationException("No Drag pending");
 617         }
 618         currentDA = DnDConstants.ACTION_NONE;
 619         dragRejected = true;
 620     }
 621 
 622     /**
 623      * acceptDrop
 624      */
 625 
 626     public synchronized void acceptDrop(int dropOperation) {
 627         if (dropOperation == DnDConstants.ACTION_NONE)
 628             throw new IllegalArgumentException("invalid acceptDrop() action");
 629 
 630         if (dropStatus == STATUS_WAIT || dropStatus == STATUS_ACCEPT) {
 631             currentDA = currentA = mapOperation(dropOperation & currentSA);
 632 
 633             dropStatus   = STATUS_ACCEPT;
 634             dropComplete = false;
 635         } else {
 636             throw new InvalidDnDOperationException("invalid acceptDrop()");
 637         }
 638     }
 639 
 640     /**
 641      * reject Drop
 642      */
 643 
 644     public synchronized void rejectDrop() {
 645         if (dropStatus != STATUS_WAIT) {
 646             throw new InvalidDnDOperationException("invalid rejectDrop()");
 647         }
 648         dropStatus = STATUS_REJECT;
 649         /*
 650          * Fix for 4285634.
 651          * The target rejected the drop means that it doesn't perform any
 652          * drop action. This change is to make Solaris behavior consistent
 653          * with Win32.
 654          */
 655         currentDA = DnDConstants.ACTION_NONE;
 656         dropComplete(false);
 657     }
 658 
 659     /**
 660      * mapOperation
 661      */
 662 
 663     private int mapOperation(int operation) {
 664         int[] operations = {
 665                 DnDConstants.ACTION_MOVE,
 666                 DnDConstants.ACTION_COPY,
 667                 DnDConstants.ACTION_LINK,
 668         };
 669         int   ret = DnDConstants.ACTION_NONE;
 670 
 671         for (int i = 0; i < operations.length; i++) {
 672             if ((operation & operations[i]) == operations[i]) {
 673                     ret = operations[i];
 674                     break;
 675             }
 676         }
 677 
 678         return ret;
 679     }
 680 
 681     /**
 682      * signal drop complete
 683      */
 684 
 685     public synchronized void dropComplete(boolean success) {
 686         if (dropStatus == STATUS_NONE) {
 687             throw new InvalidDnDOperationException("No Drop pending");
 688         }
 689 
 690         if (currentDTC != null) currentDTC.removeNotify();
 691 
 692         currentDT  = null;
 693         currentDTC = null;
 694         currentT   = null;
 695         currentA   = DnDConstants.ACTION_NONE;
 696 
 697         synchronized(_globalLock) {
 698             currentJVMLocalSourceTransferable = null;
 699         }
 700 
 701         dropStatus   = STATUS_NONE;
 702         dropComplete = true;
 703 
 704         try {
 705             doDropDone(success, currentDA, local != null);
 706         } finally {
 707             currentDA = DnDConstants.ACTION_NONE;
 708             // The native context is invalid after the drop is done.
 709             // Clear the reference to prohibit access.
 710             nativeDragContext = 0;
 711         }
 712     }
 713 
 714     protected abstract void doDropDone(boolean success,
 715                                        int dropAction, boolean isLocal);
 716 
 717     protected synchronized long getNativeDragContext() {
 718         return nativeDragContext;
 719     }
 720 
 721     protected void eventPosted(SunDropTargetEvent e) {}
 722 
 723     protected void eventProcessed(SunDropTargetEvent e, int returnValue,
 724                                   boolean dispatcherDone) {}
 725 
 726     protected static class EventDispatcher {
 727 
 728         private final SunDropTargetContextPeer peer;
 729 
 730         // context fields
 731         private final int dropAction;
 732         private final int actions;
 733         private final long[] formats;
 734         private long nativeCtxt;
 735         private final boolean dispatchType;
 736         private boolean dispatcherDone = false;
 737 
 738         // dispatcher state fields
 739         private int returnValue = 0;
 740         // set of events to be dispatched by this dispatcher
 741         private final HashSet eventSet = new HashSet(3);
 742 
 743         static final ToolkitThreadBlockedHandler handler =
 744             DataTransferer.getInstance().getToolkitThreadBlockedHandler();
 745 
 746         EventDispatcher(SunDropTargetContextPeer peer,
 747                         int dropAction,
 748                         int actions,
 749                         long[] formats,
 750                         long nativeCtxt,
 751                         boolean dispatchType) {
 752 
 753             this.peer         = peer;
 754             this.nativeCtxt   = nativeCtxt;
 755             this.dropAction   = dropAction;
 756             this.actions      = actions;
 757             this.formats =
 758                      (null == formats) ? null : Arrays.copyOf(formats, formats.length);
 759             this.dispatchType = dispatchType;
 760         }
 761 
 762         void dispatchEvent(SunDropTargetEvent e) {
 763             int id = e.getID();
 764 
 765             switch (id) {
 766             case SunDropTargetEvent.MOUSE_ENTERED:
 767                 dispatchEnterEvent(e);
 768                 break;
 769             case SunDropTargetEvent.MOUSE_DRAGGED:
 770                 dispatchMotionEvent(e);
 771                 break;
 772             case SunDropTargetEvent.MOUSE_EXITED:
 773                 dispatchExitEvent(e);
 774                 break;
 775             case SunDropTargetEvent.MOUSE_DROPPED:
 776                 dispatchDropEvent(e);
 777                 break;
 778             default:
 779                 throw new InvalidDnDOperationException();
 780             }
 781         }
 782 
 783         private void dispatchEnterEvent(SunDropTargetEvent e) {
 784             synchronized (peer) {
 785 
 786                 // store the drop action here to track operation changes
 787                 peer.previousDA = dropAction;
 788 
 789                 // setup peer context
 790                 peer.nativeDragContext = nativeCtxt;
 791                 peer.currentT          = formats;
 792                 peer.currentSA         = actions;
 793                 peer.currentDA         = dropAction;
 794                 // To allow data retrieval.
 795                 peer.dropStatus        = STATUS_ACCEPT;
 796                 peer.dropComplete      = false;
 797 
 798                 try {
 799                     peer.processEnterMessage(e);
 800                 } finally {
 801                     peer.dropStatus        = STATUS_NONE;
 802                 }
 803 
 804                 setReturnValue(peer.currentDA);
 805             }
 806         }
 807 
 808         private void dispatchMotionEvent(SunDropTargetEvent e) {
 809             synchronized (peer) {
 810 
 811                 boolean operationChanged = peer.previousDA != dropAction;
 812                 peer.previousDA = dropAction;
 813 
 814                 // setup peer context
 815                 peer.nativeDragContext = nativeCtxt;
 816                 peer.currentT          = formats;
 817                 peer.currentSA         = actions;
 818                 peer.currentDA         = dropAction;
 819                 // To allow data retrieval.
 820                 peer.dropStatus        = STATUS_ACCEPT;
 821                 peer.dropComplete      = false;
 822 
 823                 try {
 824                     peer.processMotionMessage(e, operationChanged);
 825                 } finally {
 826                     peer.dropStatus        = STATUS_NONE;
 827                 }
 828 
 829                 setReturnValue(peer.currentDA);
 830             }
 831         }
 832 
 833         private void dispatchExitEvent(SunDropTargetEvent e) {
 834             synchronized (peer) {
 835 
 836                 // setup peer context
 837                 peer.nativeDragContext = nativeCtxt;
 838 
 839                 peer.processExitMessage(e);
 840             }
 841         }
 842 
 843         private void dispatchDropEvent(SunDropTargetEvent e) {
 844             synchronized (peer) {
 845 
 846                 // setup peer context
 847                 peer.nativeDragContext = nativeCtxt;
 848                 peer.currentT          = formats;
 849                 peer.currentSA         = actions;
 850                 peer.currentDA         = dropAction;
 851 
 852                 peer.processDropMessage(e);
 853             }
 854         }
 855 
 856         void setReturnValue(int ret) {
 857             returnValue = ret;
 858         }
 859 
 860         int getReturnValue() {
 861             return returnValue;
 862         }
 863 
 864         boolean isDone() {
 865             return eventSet.isEmpty();
 866         }
 867 
 868         void registerEvent(SunDropTargetEvent e) {
 869             handler.lock();
 870             if (!eventSet.add(e) && dndLog.isLoggable(PlatformLogger.Level.FINE)) {
 871                 dndLog.fine("Event is already registered: " + e);
 872             }
 873             handler.unlock();
 874         }
 875 
 876         void unregisterEvent(SunDropTargetEvent e) {
 877             handler.lock();
 878             try {
 879                 if (!eventSet.remove(e)) {
 880                     // This event has already been unregistered.
 881                     return;
 882                 }
 883                 if (eventSet.isEmpty()) {
 884                     if (!dispatcherDone && dispatchType == DISPATCH_SYNC) {
 885                         handler.exit();
 886                     }
 887                     dispatcherDone = true;
 888                 }
 889             } finally {
 890                 handler.unlock();
 891             }
 892 
 893             try {
 894                 peer.eventProcessed(e, returnValue, dispatcherDone);
 895             } finally {
 896                 /*
 897                  * Clear the reference to the native context if all copies of
 898                  * the original event are processed.
 899                  */
 900                 if (dispatcherDone) {
 901                     nativeCtxt = 0;
 902                     // Fix for 6342381
 903                     peer.nativeDragContext = 0;
 904 
 905                 }
 906             }
 907         }
 908 
 909         public void unregisterAllEvents() {
 910             Object[] events = null;
 911             handler.lock();
 912             try {
 913                 events = eventSet.toArray();
 914             } finally {
 915                 handler.unlock();
 916             }
 917 
 918             if (events != null) {
 919                 for (int i = 0; i < events.length; i++) {
 920                     unregisterEvent((SunDropTargetEvent)events[i]);
 921                 }
 922             }
 923         }
 924     }
 925 }