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