1 /*
2  * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.awt.dnd;
27 
28 import java.awt.Component;
29 import java.awt.Point;
30 
31 import java.awt.datatransfer.DataFlavor;
32 import java.awt.datatransfer.Transferable;
33 import java.awt.datatransfer.UnsupportedFlavorException;
34 
35 import java.awt.dnd.DnDConstants;
36 
37 import java.awt.dnd.DropTarget;
38 import java.awt.dnd.DropTargetContext;
39 import java.awt.dnd.DropTargetListener;
40 import java.awt.dnd.DropTargetEvent;
41 import java.awt.dnd.DropTargetDragEvent;
42 import java.awt.dnd.DropTargetDropEvent;
43 import java.awt.dnd.InvalidDnDOperationException;
44 
45 import java.awt.dnd.peer.DropTargetContextPeer;
46 
47 import java.util.HashSet;
48 import java.util.Map;
49 import java.util.Arrays;
50 
51 import sun.util.logging.PlatformLogger;
52 
53 import java.io.IOException;
54 import java.io.InputStream;
55 
56 import sun.awt.AppContext;
57 import sun.awt.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 
setCurrentJVMLocalSourceTransferable(Transferable t)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 
getJVMLocalSourceTransferable()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 
SunDropTargetContextPeer()147     public SunDropTargetContextPeer() {
148         super();
149     }
150 
151     /**
152      * @return the DropTarget associated with this peer
153      */
154 
getDropTarget()155     public DropTarget getDropTarget() { return currentDT; }
156 
157     /**
158      * @param actions set the current actions
159      */
160 
setTargetActions(int actions)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 
getTargetActions()170     public int getTargetActions() {
171         return currentA;
172     }
173 
174     /**
175      * get the Transferable associated with the drop
176      */
177 
getTransferable()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 
getTransferDataFlavors()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 
isDataFlavorSupported(DataFlavor df)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 
getTransferData(DataFlavor df)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(SecurityConstants.AWT.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 
getNativeData(long format)285     protected abstract Object getNativeData(long format)
286       throws IOException;
287 
288     /**
289      * @return if the transfer is a local one
290      */
isTransferableJVMLocal()291     public boolean isTransferableJVMLocal() {
292         return local != null || getJVMLocalSourceTransferable() != null;
293     }
294 
handleEnterMessage(final Component component, final int x, final int y, final int dropAction, final int actions, final long[] formats, final long nativeCtxt)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 
processEnterMessage(SunDropTargetEvent event)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 
handleExitMessage(final Component component, final long nativeCtxt)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 
processExitMessage(SunDropTargetEvent event)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 
handleMotionMessage(final Component component, final int x, final int y, final int dropAction, final int actions, final long[] formats, final long nativeCtxt)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 
processMotionMessage(SunDropTargetEvent event, boolean operationChanged)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 
handleDropMessage(final Component component, final int x, final int y, final int dropAction, final int actions, final long[] formats, final long nativeCtxt)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 
processDropMessage(SunDropTargetEvent event)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 
postDropTargetEvent(final Component component, final int x, final int y, final int dropAction, final int actions, final long[] formats, final long nativeCtxt, final int eventID, final boolean dispatchType)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 
acceptDrag(int dragOperation)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 
rejectDrag()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 
acceptDrop(int dropOperation)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 
rejectDrop()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 
mapOperation(int operation)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 
dropComplete(boolean success)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 
doDropDone(boolean success, int dropAction, boolean isLocal)714     protected abstract void doDropDone(boolean success,
715                                        int dropAction, boolean isLocal);
716 
getNativeDragContext()717     protected synchronized long getNativeDragContext() {
718         return nativeDragContext;
719     }
720 
eventPosted(SunDropTargetEvent e)721     protected void eventPosted(SunDropTargetEvent e) {}
722 
eventProcessed(SunDropTargetEvent e, int returnValue, boolean dispatcherDone)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 
EventDispatcher(SunDropTargetContextPeer peer, int dropAction, int actions, long[] formats, long nativeCtxt, boolean dispatchType)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 
dispatchEvent(SunDropTargetEvent e)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 
dispatchEnterEvent(SunDropTargetEvent e)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 
dispatchMotionEvent(SunDropTargetEvent e)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 
dispatchExitEvent(SunDropTargetEvent e)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 
dispatchDropEvent(SunDropTargetEvent e)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 
setReturnValue(int ret)856         void setReturnValue(int ret) {
857             returnValue = ret;
858         }
859 
getReturnValue()860         int getReturnValue() {
861             return returnValue;
862         }
863 
isDone()864         boolean isDone() {
865             return eventSet.isEmpty();
866         }
867 
registerEvent(SunDropTargetEvent e)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 
unregisterEvent(SunDropTargetEvent e)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 
unregisterAllEvents()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 }
926