/*
* 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;
}
}
}