Login  Register

Strange ClassCastException: A puzzle for Java/ImageJ experts to help me wit?.

Posted by Bill Mohler on Apr 29, 2009; 2:50pm
URL: http://imagej.273.s1.nabble.com/Strange-ClassCastException-A-puzzle-for-Java-ImageJ-experts-to-help-me-wit-tp3692688.html

I hope that some of you with time and experience can help me with a
suggestion.  Please take some time, if you have a chance, to work
through the details I'm providing below:

I have created a new class called MultiQTVirtualStack (MQTVS for
short here), spinning off of the success of Jeffrey Woodard's
QTVirtualStack (QTVS for short here, which makes working with
QuickTime movies in a true ImageJ StackWindow a delight).

MQTVS extends VirtualStack (and therefore ImageStack) and allows the
user to open a collection of QTVSs into a single Virtual HyperStack
window, in which each QTVS plays as a separate channel, but all the
QTVS-derived channels are navigated synchronously in the Z and T
dimensions.  The window can be viewed in either Composite (color
overlay), Color (separate channel), or Grayscale (separate channel)
modes that are available for HyperStacks.  This all works nicely now
for me.  Happy to share the code, but it's not cleaned up pretty yet.

Now I am trying to create a PluginFrame with which the user can shift
the registration of the Z and T dimensions of the various QTVS
channels of the MQTVS window, so as to better synchronize movies that
were collected separately and may start at different relative times
during an event of interest that needs to be compared.

I have previously created methods MQTVS.adjustSingleMovieZ(int
channel, boolean forward) and MQTVS.adjustSingleMovieT(int channel,
boolean forward), which in turn call methods in my modified version
of QTVS to rearrange the order of elements in the ArrayList,
frameLocations, pointing to movie frames by a single position in
either Z or T.  This interaction of methods has worked well for me
when I code "for" loops that make a fixed number of calls to the
MQTVS.adjustSingleMovie...() methods from within the constructor of
MQTVS (an experimental proof of principal that works very nicely).
Here are the methods themselves, isolated from the rest of their
classes for simplicity.  The problems I'm having in calling these
methods via a PluginFrame are described below.

 From MQTVS:
   /*These methods allow viewer to shift the Z or T positions of a
given channel to better synchronize movies.*/
        public void adjustSingleMovieZ(int channel, boolean forward) {
                this.forward = forward;
                ((QTVirtualStack) stack[channel]).shiftMovieZ(forward);
                IJ.log("got to MQTVS.ASMZ");

        }

        public void adjustSingleMovieT(int channel, boolean forward) {
                this.forward = forward;
                ((QTVirtualStack) stack[channel]).shiftMovieT(forward);

        }


 From QTVS:
   /*These methods allow viewer to shift the Z or T positions of a
given channel to better synchronize movies.*/
        public void shiftMovieT(boolean forward) {
                this.forward = forward;
                if (!forward) {
                        for (int z = 0; z < maxSlicesSingleMovie ; z++) {
                                this.frameLocations.add( (z*
maxTimesSingleMovie) , this.frameLocations.get( ((z+1)*
maxTimesSingleMovie) -1) );
                                this.frameLocations.remove(  ((z+1)*
maxTimesSingleMovie) -1 );
                        }
                } else {
                        for (int z = maxSlicesSingleMovie-1 ; z >= 0  ; z--) {
                                this.frameLocations.add( ((z+1)*
maxTimesSingleMovie-1) , this.frameLocations.get( (z)*
maxTimesSingleMovie ));
                                this.frameLocations.remove( ((z)*
maxTimesSingleMovie ) );
                        }
                }
        }

        public void shiftMovieZ(boolean forward) {

                this.forward = forward;
                ArrayList copyFrames = new ArrayList();
                IJ.log("Got to the QTVS.SMZ");

                if (forward) {
                        for (int t= 0; t < maxTimesSingleMovie; t++) {
                                copyFrames.add(
this.frameLocations.get(((maxSlicesSingleMovie-1)*maxTimesSingleMovie)
) );
 
        this.frameLocations.remove(((maxSlicesSingleMovie-1)*maxTimesSingleMovie)
);
                        }
                        this.frameLocations.addAll(0, copyFrames);
                } else {
                        for (int t= 0; t < maxTimesSingleMovie; t++) {
                                copyFrames.add( this.frameLocations.get(0) );
                                this.frameLocations.remove(0);
                        }
                        this.frameLocations.addAll(copyFrames);
                }

        }



**Now to the problems I am having (apparently at the level of the JVM??).
Here I try to call the public method MQTVS.adjustSingleMovieZ(int
channel, boolean forward), sending parameters set by the user in the
slider of the PluginFrame.  I have no problems compiling, and get no
exceptions reported in the log window of ImageJ.  But (thanks to help
from Wayne), I find a ClassCastException from the event dispatch
thread in the console.  The confusion comes in the apparent
correctness of the type of a variable that I assign with the
ImagePlus.getStack() command, but the wrongness of how the event
dispatch thread deals with the variable when I try to cast it to its
true type (I think). First I show the code from the method in the
PluginFrame that reads the value of sliders and should call
MQTVS.adjustSingleMovieZ(int channel, boolean forward) to change a
specific QTVS's frame position being displayed.  Then below the code,
I show the IJ.log output from several lines in the method, and then
the ClassCastException from the console.

The key method from my PluginFrame MovieAlignFrameMOD:
     public synchronized void adjustmentValueChanged(AdjustmentEvent e) {
        ImagePlus imp = WindowManager.getCurrentImage();

        ImageStack stack = imp.getStack();
        IJ.log(""+stack.getClass().getName() + "1");

        ImageWindow win = imp.getWindow();
        int zImpPos = imp.getSlice();
        int tImpPos = imp.getFrame();
        IJ.log("beforetheloop " +  imp+" \n" + imp.getStack()) ;
        boolean forward = false;

        if (imp==null) return;
                for (int i=0; i< imp.getNChannels(); i++){
                        if (e.getSource()==sliceShifter[i]) {
                                int zShiftLive = sliceShifter[i].getValue();
                                int zShiftNet = zShiftLive - previousShiftZ[i];
                                int zShiftNetAbs = zShiftNet;
                                if (zShiftNet < 0) {
                                        zShiftNetAbs = -zShiftNet;
                                        forward = true;
                                }
                                previousShiftZ[i] = previousShiftZ[i]
+ zShiftNet;

                                for(int j = 0; j < zShiftNetAbs; j++){
                                        IJ.log("intheloop j= " + j +
" " + imp+" \n" + imp.getStack()) ;  
 
        IJ.log(""+stack.getClass().getName() + "2");

                        /** THIS DOES NOT REPORT, SO IT'S CLEAR THAT
stack IS NOT reporting as instanceof MultiQTVirtualStack,
                        even though it reports as one using
stack.getClass().getName() **/
                                        if (stack instanceof
MultiQTVirtualStack ) IJ.log("MultiQTVirtualStack? Yes!!!!");

                        /*** ...But THIS STILL READS OUT AS
MultiQTVirtualStack  ******/
 
        IJ.log(""+stack.getClass().getName() + "3");

                        /**** THIS GIVES A "SILENT"
ClassCastException ON THE EVENT DISPATCH THREAD, CAN SEE IN CONSOLE
*****/
        /* LINE 289 */ ((MultiQTVirtualStack)
stack).adjustSingleMovieZ(i, forward);

                        /***** Once the exception has been thrown,
this next log statement fails to run ***********/
 
        IJ.log(""+stack.getClass().getName() + "4");

                                }

                        }
                        else if (e.getSource()==frameShifter[i]) {
                                int tShiftLive = frameShifter[i].getValue();
                                int tShiftNet = tShiftLive - previousShiftT[i];
                                previousShiftT[i] = tShiftNet;
                        /****** THE ACTIONS OF THIS IF CONDITION WILL
BE FILLED IN LATER ONCE I FIGURE OUT OTHER PROBLEMS ********/

                        }
                        notify();
                }
        }
     }



Here's what is logged by the IJ.log() statements above during a
slider adjustment:

MultiQTVirtualStack1
beforetheloop imp[3-Movie Overlay #1 : see Log window for details
600x411x38961]
stack[600x411x38961]
intheloop j= 0 imp[3-Movie Overlay #1 : see Log window for details
600x411x38961]
stack[600x411x38961]
MultiQTVirtualStack2
MultiQTVirtualStack3


!!Note that I did not leave out the 4th log statement in copying.
The CCE apparently blocks the ability to run the 4th IJ.log statement
after line 289.  Also, the instanceof test fails (even before line
289), because I don't get the "MultiQTVirtualStack? Yes!!!" log
printed.


Finally, this is the exception from the event dispatch thread at the
critical line 289:

4/28/09 10:09:08 AM [0x0-0x4c04c].gov.nih.info.rsb.ImageJ[748]
Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException:
MultiQTVirtualStack
4/28/09 10:09:08 AM [0x0-0x4c04c].gov.nih.info.rsb.ImageJ[748]  at
MovieAlignFrameMOD.adjustmentValueChanged(MovieAlignFrameMOD.java:289)
4/28/09 10:09:08 AM [0x0-0x4c04c].gov.nih.info.rsb.ImageJ[748]  at
java.awt.Scrollbar.processAdjustmentEvent(Scrollbar.java:1094)
4/28/09 10:09:08 AM [0x0-0x4c04c].gov.nih.info.rsb.ImageJ[748]  at
java.awt.Scrollbar.processEvent(Scrollbar.java:1061)
4/28/09 10:09:08 AM [0x0-0x4c04c].gov.nih.info.rsb.ImageJ[748]  at
java.awt.Component.dispatchEventImpl(Component.java:4068)
4/28/09 10:09:08 AM [0x0-0x4c04c].gov.nih.info.rsb.ImageJ[748]  at
java.awt.Component.dispatchEvent(Component.java:3903)
4/28/09 10:09:08 AM [0x0-0x4c04c].gov.nih.info.rsb.ImageJ[748]  at
java.awt.EventQueue.dispatchEvent(EventQueue.java:463)
4/28/09 10:09:08 AM [0x0-0x4c04c].gov.nih.info.rsb.ImageJ[748]  at
java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:269)
4/28/09 10:09:08 AM [0x0-0x4c04c].gov.nih.info.rsb.ImageJ[748]  at
java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:190)
4/28/09 10:09:08 AM [0x0-0x4c04c].gov.nih.info.rsb.ImageJ[748]  at
java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:184)
4/28/09 10:09:08 AM [0x0-0x4c04c].gov.nih.info.rsb.ImageJ[748]  at
java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:176)



In summary, I seem to have an object called stack returned to me by
ImagePlus.getStack() which both is a MultiQTVirtualStack by some
tests and is NOT a MultiQTVirtualStack by others, and these other
criteria are what I really need to make my PluginFrame work.

Can anyone give me a hint of an explanation of what is going on here?