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