| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
/*  | 
 | 
 * This source code is provided to illustrate the usage of a given feature  | 
 | 
 * or technique and has been deliberately simplified. Additional steps  | 
 | 
 * required for a production-quality application, such as security checks,  | 
 | 
 * input validation and proper error handling, might not be present in  | 
 | 
 * this sample code.  | 
 | 
 */  | 
 | 
 | 
 | 
 | 
 | 
package com.sun.tools.example.debug.gui;  | 
 | 
 | 
 | 
import java.io.*;  | 
 | 
import java.util.*;  | 
 | 
 | 
 | 
import com.sun.jdi.*;  | 
 | 
import com.sun.tools.example.debug.event.*;  | 
 | 
import com.sun.tools.example.debug.bdi.*;  | 
 | 
 | 
 | 
public class ContextManager { | 
 | 
 | 
 | 
    private ClassManager classManager;  | 
 | 
    private ExecutionManager runtime;  | 
 | 
 | 
 | 
    private String mainClassName;  | 
 | 
    private String vmArguments;  | 
 | 
    private String commandArguments;  | 
 | 
    private String remotePort;  | 
 | 
 | 
 | 
    private ThreadReference currentThread;  | 
 | 
 | 
 | 
    private boolean verbose;  | 
 | 
 | 
 | 
    private ArrayList<ContextListener> contextListeners = new ArrayList<ContextListener>();  | 
 | 
 | 
 | 
    public ContextManager(Environment env) { | 
 | 
        classManager = env.getClassManager();  | 
 | 
        runtime = env.getExecutionManager();  | 
 | 
        mainClassName = "";  | 
 | 
        vmArguments = "";  | 
 | 
        commandArguments = "";  | 
 | 
        currentThread = null;  | 
 | 
 | 
 | 
        ContextManagerListener listener = new ContextManagerListener();  | 
 | 
        runtime.addJDIListener(listener);  | 
 | 
        runtime.addSessionListener(listener);  | 
 | 
    }  | 
 | 
 | 
 | 
    // Program execution defaults.  | 
 | 
 | 
 | 
    //### Should there be change listeners for these?  | 
 | 
    //### They would be needed if we expected a dialog to be  | 
 | 
    //### synchronized with command input while it was open.  | 
 | 
 | 
 | 
    public String getMainClassName() { | 
 | 
        return mainClassName;  | 
 | 
    }  | 
 | 
 | 
 | 
    public void setMainClassName(String mainClassName) { | 
 | 
        this.mainClassName = mainClassName;  | 
 | 
    }  | 
 | 
 | 
 | 
    public String getVmArguments() { | 
 | 
        return processClasspathDefaults(vmArguments);  | 
 | 
    }  | 
 | 
 | 
 | 
    public void setVmArguments(String vmArguments) { | 
 | 
        this.vmArguments = vmArguments;  | 
 | 
    }  | 
 | 
 | 
 | 
    public String getProgramArguments() { | 
 | 
        return commandArguments;  | 
 | 
    }  | 
 | 
 | 
 | 
    public void setProgramArguments(String commandArguments) { | 
 | 
        this.commandArguments = commandArguments;  | 
 | 
    }  | 
 | 
 | 
 | 
    public String getRemotePort() { | 
 | 
        return remotePort;  | 
 | 
    }  | 
 | 
 | 
 | 
    public void setRemotePort(String remotePort) { | 
 | 
        this.remotePort = remotePort;  | 
 | 
 | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    // Miscellaneous debugger session preferences.  | 
 | 
 | 
 | 
    public boolean getVerboseFlag() { | 
 | 
        return verbose;  | 
 | 
    }  | 
 | 
 | 
 | 
    public void setVerboseFlag(boolean verbose) { | 
 | 
        this.verbose = verbose;  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    // Thread focus.  | 
 | 
 | 
 | 
    public ThreadReference getCurrentThread() { | 
 | 
        return currentThread;  | 
 | 
    }  | 
 | 
 | 
 | 
    public void setCurrentThread(ThreadReference t) { | 
 | 
        if (t != currentThread) { | 
 | 
            currentThread = t;  | 
 | 
            notifyCurrentThreadChanged(t);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public void setCurrentThreadInvalidate(ThreadReference t) { | 
 | 
        currentThread = t;  | 
 | 
        notifyCurrentFrameChanged(runtime.threadInfo(t),  | 
 | 
                                  0, true);  | 
 | 
    }  | 
 | 
 | 
 | 
    public void invalidateCurrentThread() { | 
 | 
        notifyCurrentFrameChanged(null, 0, true);  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    // If a view is displaying the current thread, it may  | 
 | 
    // choose to indicate which frame is current in the  | 
 | 
    // sense of the command-line UI.  It may also "warp" the  | 
 | 
    // selection to that frame when changed by an 'up' or 'down'  | 
 | 
    // command. Hence, a notifier is provided.  | 
 | 
 | 
 | 
    /******  | 
 | 
    public int getCurrentFrameIndex() { | 
 | 
        return getCurrentFrameIndex(currentThreadInfo);  | 
 | 
    }  | 
 | 
    ******/  | 
 | 
 | 
 | 
    public int getCurrentFrameIndex(ThreadReference t) { | 
 | 
        return getCurrentFrameIndex(runtime.threadInfo(t));  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    public int getCurrentFrameIndex(ThreadInfo tinfo) { | 
 | 
        if (tinfo == null) { | 
 | 
            return 0;  | 
 | 
        }  | 
 | 
        Integer currentFrame = (Integer)tinfo.getUserObject();  | 
 | 
        if (currentFrame == null) { | 
 | 
            return 0;  | 
 | 
        } else { | 
 | 
            return currentFrame.intValue();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public int moveCurrentFrameIndex(ThreadReference t, int count) throws VMNotInterruptedException { | 
 | 
        return setCurrentFrameIndex(t,count, true);  | 
 | 
    }  | 
 | 
 | 
 | 
    public int setCurrentFrameIndex(ThreadReference t, int newIndex) throws VMNotInterruptedException { | 
 | 
        return setCurrentFrameIndex(t, newIndex, false);  | 
 | 
    }  | 
 | 
 | 
 | 
    public int setCurrentFrameIndex(int newIndex) throws VMNotInterruptedException { | 
 | 
        if (currentThread == null) { | 
 | 
            return 0;  | 
 | 
        } else { | 
 | 
            return setCurrentFrameIndex(currentThread, newIndex, false);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private int setCurrentFrameIndex(ThreadReference t, int x, boolean relative) throws VMNotInterruptedException { | 
 | 
        boolean sameThread = t.equals(currentThread);  | 
 | 
        ThreadInfo tinfo = runtime.threadInfo(t);  | 
 | 
        if (tinfo == null) { | 
 | 
            return 0;  | 
 | 
        }  | 
 | 
        int maxIndex = tinfo.getFrameCount()-1;  | 
 | 
        int oldIndex = getCurrentFrameIndex(tinfo);  | 
 | 
        int newIndex = relative? oldIndex + x : x;  | 
 | 
        if (newIndex > maxIndex) { | 
 | 
            newIndex = maxIndex;  | 
 | 
        } else  if (newIndex < 0) { | 
 | 
            newIndex = 0;  | 
 | 
        }  | 
 | 
        if (!sameThread || newIndex != oldIndex) {   | 
 | 
            setCurrentFrameIndex(tinfo, newIndex);  | 
 | 
        }  | 
 | 
        return newIndex - oldIndex;  | 
 | 
    }  | 
 | 
 | 
 | 
    private void setCurrentFrameIndex(ThreadInfo tinfo, int index) { | 
 | 
        tinfo.setUserObject(new Integer(index));  | 
 | 
        //### In fact, the value may not have changed at this point.  | 
 | 
        //### We need to signal that the user attempted to change it,  | 
 | 
        //### however, so that the selection can be "warped" to the  | 
 | 
          | 
 | 
        notifyCurrentFrameChanged(tinfo.thread(), index);  | 
 | 
    }  | 
 | 
 | 
 | 
    public StackFrame getCurrentFrame() throws VMNotInterruptedException { | 
 | 
        return getCurrentFrame(runtime.threadInfo(currentThread));  | 
 | 
    }  | 
 | 
 | 
 | 
    public StackFrame getCurrentFrame(ThreadReference t) throws VMNotInterruptedException { | 
 | 
        return getCurrentFrame(runtime.threadInfo(t));  | 
 | 
    }  | 
 | 
 | 
 | 
    public StackFrame getCurrentFrame(ThreadInfo tinfo) throws VMNotInterruptedException { | 
 | 
        int index = getCurrentFrameIndex(tinfo);  | 
 | 
        try { | 
 | 
            // It is possible, though unlikely, that the VM was interrupted  | 
 | 
              | 
 | 
            return tinfo.getFrame(index);  | 
 | 
        } catch (FrameIndexOutOfBoundsException e) { | 
 | 
            return null;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public void addContextListener(ContextListener cl) { | 
 | 
        contextListeners.add(cl);  | 
 | 
    }  | 
 | 
 | 
 | 
    public void removeContextListener(ContextListener cl) { | 
 | 
        contextListeners.remove(cl);  | 
 | 
    }  | 
 | 
 | 
 | 
    //### These notifiers are fired only in response to USER-INITIATED changes  | 
 | 
    //### to the current thread and current frame.  When the current thread is set automatically  | 
 | 
    //### after a breakpoint hit or step completion, no event is generated.  Instead,  | 
 | 
    //### interested parties are expected to listen for the BreakpointHit and StepCompleted  | 
 | 
    //### events.  This convention is unclean, and I believe that it reflects a defect in  | 
 | 
    //### in the current architecture.  Unfortunately, however, we cannot guarantee the  | 
 | 
    //### order in which various listeners receive a given event, and the handlers for  | 
 | 
    //### the very same events that cause automatic changes to the current thread may also  | 
 | 
    //### need to know the current thread.  | 
 | 
 | 
 | 
    private void notifyCurrentThreadChanged(ThreadReference t) { | 
 | 
        ThreadInfo tinfo = null;  | 
 | 
        int index = 0;  | 
 | 
        if (t != null) { | 
 | 
            tinfo = runtime.threadInfo(t);  | 
 | 
            index = getCurrentFrameIndex(tinfo);  | 
 | 
        }  | 
 | 
        notifyCurrentFrameChanged(tinfo, index, false);  | 
 | 
    }  | 
 | 
 | 
 | 
    private void notifyCurrentFrameChanged(ThreadReference t, int index) { | 
 | 
        notifyCurrentFrameChanged(runtime.threadInfo(t),  | 
 | 
                                  index, false);  | 
 | 
    }  | 
 | 
 | 
 | 
    private void notifyCurrentFrameChanged(ThreadInfo tinfo, int index,  | 
 | 
                                           boolean invalidate) { | 
 | 
        ArrayList<ContextListener> l =  new ArrayList<ContextListener>(contextListeners);  | 
 | 
        CurrentFrameChangedEvent evt =  | 
 | 
            new CurrentFrameChangedEvent(this, tinfo, index, invalidate);  | 
 | 
        for (int i = 0; i < l.size(); i++) { | 
 | 
            l.get(i).currentFrameChanged(evt);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private class ContextManagerListener extends JDIAdapter  | 
 | 
                       implements SessionListener, JDIListener { | 
 | 
 | 
 | 
        // SessionListener  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public void sessionStart(EventObject e) { | 
 | 
            invalidateCurrentThread();  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public void sessionInterrupt(EventObject e) { | 
 | 
            setCurrentThreadInvalidate(currentThread);  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public void sessionContinue(EventObject e) { | 
 | 
            invalidateCurrentThread();  | 
 | 
        }  | 
 | 
 | 
 | 
        // JDIListener  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public void locationTrigger(LocationTriggerEventSet e) { | 
 | 
            setCurrentThreadInvalidate(e.getThread());  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public void exception(ExceptionEventSet e) { | 
 | 
            setCurrentThreadInvalidate(e.getThread());  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public void vmDisconnect(VMDisconnectEventSet e) { | 
 | 
            invalidateCurrentThread();  | 
 | 
        }  | 
 | 
 | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    /**  | 
 | 
     * Add a -classpath argument to the arguments passed to the exec'ed  | 
 | 
     * VM with the contents of CLASSPATH environment variable,  | 
 | 
     * if -classpath was not already specified.  | 
 | 
     *  | 
 | 
     * @param javaArgs the arguments to the VM being exec'd that  | 
 | 
     *                 potentially has a user specified -classpath argument.  | 
 | 
     * @return a javaArgs whose -classpath option has been added  | 
 | 
     */  | 
 | 
 | 
 | 
    private String processClasspathDefaults(String javaArgs) { | 
 | 
        if (javaArgs.indexOf("-classpath ") == -1) { | 
 | 
            StringBuffer munged = new StringBuffer(javaArgs);  | 
 | 
            SearchPath classpath = classManager.getClassPath();  | 
 | 
            if (classpath.isEmpty()) { | 
 | 
                String envcp = System.getProperty("env.class.path"); | 
 | 
                if ((envcp != null) && (envcp.length() > 0)) { | 
 | 
                    munged.append(" -classpath " + envcp); | 
 | 
                }  | 
 | 
            } else { | 
 | 
                munged.append(" -classpath " + classpath.asString()); | 
 | 
            }  | 
 | 
            return munged.toString();  | 
 | 
        } else { | 
 | 
            return javaArgs;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private String appendPath(String path1, String path2) { | 
 | 
        if (path1 == null || path1.length() == 0) { | 
 | 
            return path2 == null ? "." : path2;  | 
 | 
        } else if (path2 == null || path2.length() == 0) { | 
 | 
            return path1;  | 
 | 
        } else { | 
 | 
            return path1  + File.pathSeparator + path2;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
}  |