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