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