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
26package sun.awt.dnd;
27
28import java.awt.Component;
29import java.awt.Point;
30
31import java.awt.datatransfer.DataFlavor;
32import java.awt.datatransfer.Transferable;
33import java.awt.datatransfer.UnsupportedFlavorException;
34
35import java.awt.dnd.DnDConstants;
36
37import java.awt.dnd.DropTarget;
38import java.awt.dnd.DropTargetContext;
39import java.awt.dnd.DropTargetListener;
40import java.awt.dnd.DropTargetEvent;
41import java.awt.dnd.DropTargetDragEvent;
42import java.awt.dnd.DropTargetDropEvent;
43import java.awt.dnd.InvalidDnDOperationException;
44
45import java.awt.dnd.peer.DropTargetContextPeer;
46
47import java.util.HashSet;
48import java.util.Map;
49import java.util.Arrays;
50
51import sun.awt.AWTAccessor;
52import sun.awt.AWTAccessor.DropTargetContextAccessor;
53import sun.util.logging.PlatformLogger;
54
55import java.io.IOException;
56import java.io.InputStream;
57
58import sun.awt.AppContext;
59import sun.awt.AWTPermissions;
60import sun.awt.SunToolkit;
61import sun.awt.datatransfer.DataTransferer;
62import 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
74public 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
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
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
149    public SunDropTargetContextPeer() {
150        super();
151    }
152
153    /**
154     * @return the DropTarget associated with this peer
155     */
156
157    public DropTarget getDropTarget() { return currentDT; }
158
159    /**
160     * @param actions set the current actions
161     */
162
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
172    public int getTargetActions() {
173        return currentA;
174    }
175
176    /**
177     * get the Transferable associated with the drop
178     */
179
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
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
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
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
287    protected abstract Object getNativeData(long format)
288      throws IOException;
289
290    /**
291     * @return if the transfer is a local one
292     */
293    public boolean isTransferableJVMLocal() {
294        return local != null || getJVMLocalSourceTransferable() != null;
295    }
296
297    private int handleEnterMessage(final Component component,
298                                   final int x, final int y,
299                                   final int dropAction,
300                                   final int actions, final long[] formats,
301                                   final long nativeCtxt) {
302        return postDropTargetEvent(component, x, y, dropAction, actions,
303                                   formats, nativeCtxt,
304                                   SunDropTargetEvent.MOUSE_ENTERED,
305                                   SunDropTargetContextPeer.DISPATCH_SYNC);
306    }
307
308    /**
309     * actual processing on EventQueue Thread
310     */
311
312    protected void processEnterMessage(SunDropTargetEvent event) {
313        Component  c    = (Component)event.getSource();
314        DropTarget dt   = c.getDropTarget();
315        Point      hots = event.getPoint();
316
317        local = getJVMLocalSourceTransferable();
318        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
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
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
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
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
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
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
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
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
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
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
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
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
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
725    protected abstract void doDropDone(boolean success,
726                                       int dropAction, boolean isLocal);
727
728    protected synchronized long getNativeDragContext() {
729        return nativeDragContext;
730    }
731
732    protected void eventPosted(SunDropTargetEvent e) {}
733
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
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
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
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
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
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
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
867        void setReturnValue(int ret) {
868            returnValue = ret;
869        }
870
871        int getReturnValue() {
872            return returnValue;
873        }
874
875        boolean isDone() {
876            return eventSet.isEmpty();
877        }
878
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
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
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