| /* |
| * Copyright (c) 1998, 1999, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /* |
| * 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)); |
| } |
| |
| //### Used in StackTraceTool. |
| 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) { // don't recurse |
| 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 |
| //### current location. |
| 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 |
| // before the thread created its Java stack. |
| 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; |
| } |
| } |
| |
| } |