org.netbeans.spi.debugger.ui.DebuggingView Maven / Gradle / Ivy
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.spi.debugger.ui;
import java.awt.Image;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.prefs.Preferences;
import javax.swing.Action;
import javax.swing.Icon;
import org.netbeans.api.debugger.Breakpoint;
import org.netbeans.api.debugger.Session;
import org.netbeans.modules.debugger.ui.views.debugging.DebuggingViewComponent;
import org.netbeans.modules.debugger.ui.views.debugging.FiltersDescriptor;
import org.netbeans.modules.debugger.ui.views.debugging.FiltersDescriptor.FilterImpl;
import org.netbeans.modules.debugger.ui.views.debugging.FiltersDescriptor.FiltersAccessor;
import static org.netbeans.modules.debugger.ui.views.debugging.FiltersDescriptor.NATURAL_SORT;
import org.openide.windows.TopComponent;
/**
* Debugging view component provider. The debugging view displays application threads.
* Implement {@link DVSupport} and register under your session name to use
* debugging view UI and register appropriate view models for path <session name>/DebuggingView.
*
* @author Martin Entlicher
* @since 2.43
*/
public final class DebuggingView {
private static final DebuggingView INSTANCE = new DebuggingView();
private Reference dvcRef = new WeakReference(null);
private DebuggingView() {
}
/**
* Get the default implementation of debugging view provider.
* @return the default instance of debugging view provider.
*/
public static DebuggingView getDefault() {
return INSTANCE;
}
private DebuggingViewComponent getDVC() {
DebuggingViewComponent dvc;
synchronized (this) {
dvc = dvcRef.get();
if (dvc == null) {
dvc = DebuggingViewComponent.getInstance();
dvcRef = new WeakReference(dvc);
}
}
return dvc;
}
/**
* Get the debugging view top component.
* @return the {@link TopComponent} of the debugging view.
*/
public TopComponent getViewTC() {
return getDVC();
}
/**
* Support for debugging view. The component tree is created from view models
* registered under path <session name>/DebuggingView. But to fully
* support the debugging view UI, additional information is necessary.
* Implement this class to provide the additional information.
* Debugging view is created for the given debugger session only when an
* implementation of this class is found in the current session engine lookup.
*/
public abstract static class DVSupport {
/** Property name constant. */
public static final String PROP_THREAD_STARTED = "threadStarted"; // NOI18N
/** Property name constant. */
public static final String PROP_THREAD_DIED = "threadDied"; // NOI18N
/** Property name constant. */
public static final String PROP_THREAD_GROUP_ADDED = "threadGroupAdded"; // NOI18N
/** Property name constant. */
public static final String PROP_THREAD_SUSPENDED = "threadSuspended"; // NOI18N
/** Property name constant. */
public static final String PROP_THREAD_RESUMED = "threadResumed"; // NOI18N
/** Property name constant. */
public static final String PROP_CURRENT_THREAD = "currentThread"; // NOI18N
/**
* Name of property which is fired when deadlock occurs.
*/
public static final String PROP_DEADLOCK = "deadlock"; // NOI18N
public static final String PROP_STATE = "state"; // NOI18N
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
/**
* The debugger state.
*/
public static enum STATE {
RUNNING,
DISCONNECTED
}
static {
FiltersDescriptor.getInstance().setFiltersAccessor(new FiltersAccessor() {
@Override
public List getFilters(DVSupport dvs) {
return dvs.getFilters();
}
@Override
public FilterImpl getImpl(DVFilter filter) {
return filter.getImpl();
}
});
}
protected DVSupport() {
}
/**
* Get the debugger state.
* @return current state of debugger
*/
public abstract STATE getState();
/**
* Get listing of all threads at this moment.
* @return list of all threads
*/
public abstract List getAllThreads();
/**
* Get a current thread, if any.
* @return a current thread, or null
.
*/
public abstract DVThread getCurrentThread();
/**
* Get the display name of the thread. It can contain more information
* than a thread name, like current session name, etc.
* @param thread the thread
* @return the thread display name
*/
public abstract String getDisplayName(DVThread thread);
/**
* Get the display name of the frame. It can contain more information
* than a frame name, like source location, etc.
* @param frame the frame
* @return the frame display name
* @since 2.65
*/
public String getDisplayName(DVFrame frame) {
return frame.getName();
}
/**
* Get the thread icon.
* @param thread the thread
* @return the thread icon
*/
public abstract Image getIcon(DVThread thread);
/**
* Get the session associated with this debugging view.
* @return
*/
public abstract Session getSession();
/**
* Resume the application (all it's threads).
*/
public abstract void resume();
/**
* Get the set of detected deadlocks, if any.
* @return The set of deadlocks, or null
when no deadlocks are detected.
*/
public abstract Set getDeadlocks();
/**
* Utility method used by the implementing class to create deadlock description instances.
* @param threads The threads in deadlock
* @return Deadlock instance
*/
protected final Deadlock createDeadlock(Collection threads) {
return new Deadlock(threads);
}
/**
* Get the list of filters applied to debugging view.
* @return list of filters
*/
protected abstract List getFilters();
/**
* Get actions created from the provided filters.
* The result can be added to actions provider view model.
* @return filter actions.
*/
public final Action[] getFilterActions() {
return FiltersDescriptor.getInstance().getFilterActions();
}
protected final void firePropertyChange(PropertyChangeEvent pce) {
pcs.firePropertyChange(pce);
}
/**
* Fire a property change event.
* @param propertyName the property name
* @param oldValue old value
* @param newValue new value
*/
protected final void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
pcs.firePropertyChange(propertyName, oldValue, newValue);
}
/**
* Add a property change listener.
* @param pcl the property change listener
*/
public final void addPropertyChangeListener(PropertyChangeListener pcl) {
pcs.addPropertyChangeListener(pcl);
}
/**
* Remove a property change listener.
* @param pcl the property change listener
*/
public final void removePropertyChangeListener(PropertyChangeListener pcl) {
pcs.removePropertyChangeListener(pcl);
}
/**
* Declarative registration of a DVSupport implementation.
* By marking the implementation class with this annotation,
* you automatically register that implementation for use by the debugging view.
* The class must be public and have a public constructor which takes
* no arguments or takes {@link org.netbeans.spi.debugger.ContextProvider} as an argument.
*
* @author Martin Entlicher
*/
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
public @interface Registration {
/**
* The path to register this implementation in.
* Usually the session ID.
*/
String path();
/**
* An optional position in which to register this service relative to others.
* Lower-numbered services are returned in the lookup result first.
* Services with no specified position are returned last.
*/
int position() default Integer.MAX_VALUE;
}
}
/**
* Representation of a thread in debugging view.
* Nodes representing a thread in debugging view model should implement this
* interface.
*/
public static interface DVThread {
/**
* Property name fired when a thread gets suspended.
*/
public static final String PROP_SUSPENDED = "suspended"; // NOI18N
/**
* Property name fired when list of locker threads change.
*/
public static final String PROP_LOCKER_THREADS = "lockerThreads"; // NOI18N
/**
* Property name fired when current breakpoint is changed.
*/
public static final String PROP_BREAKPOINT = "currentBreakpoint"; // NOI18N
/**
* Get the name of the thread.
* @return the name of the thread
*/
public String getName();
/**
* Test if this thread is currently suspended by debugger.
* @return true
when the thread is suspended, false
otherwise.
*/
public boolean isSuspended();
/**
* Resume this thread.
*/
public void resume();
/**
* Suspend this thread.
*/
public void suspend();
/**
* Make this thread current. Code evaluation and stepping should be performed
* in the current thread.
*/
public void makeCurrent();
/**
* Get frame count of this thread. The frame count is provided when the thread
* is suspended only.
* @return the frame count, 0
when the thread is running.
* @since 2.65
*/
public default int getFrameCount() {
return 0;
}
/**
* Get the stack frames of this thread. Stack frames are provided when the thread
* is suspended only.
*
* @return a list of stack frames, it's empty when the thread is running.
* @since 2.65
*/
public default List getFrames() {
return Collections.emptyList();
}
/**
* Get the stack frames of this thread. Stack frames are provided when the thread
* is suspended only.
*
* @param from a from index, inclusive
* @param to a to index, exclusive
* @return a list of stack frames, it's empty when the thread is running.
* @since 2.65
*/
public default List getFrames(int from, int to) {
return Collections.emptyList();
}
/**
* Get the debugging view support that provides this thread.
* @return the debugging view support
*/
public DVSupport getDVSupport();
/**
* Lists threads that hold monitors that this thread is waiting on.
* @return list of locker threads
*/
public List getLockerThreads();
/**
* Resume any suspended threads that block execution of this thread by holding monitors.
*/
public void resumeBlockingThreads();
/**
* Get current breakpoint, if any. This is a breakpoint that this thread is suspended on.
* @return the current breakpoint or null
*/
public Breakpoint getCurrentBreakpoint();
/**
* Test if this thread is performing a step operation right now.
* @return true
if this thread is in a step, false
otherwise.
*/
public boolean isInStep();
/**
* Add a property change listener.
* @param pcl the property change listener
*/
public void addPropertyChangeListener(PropertyChangeListener pcl);
/**
* Remove a property change listener.
* @param pcl the property change listener
*/
public void removePropertyChangeListener(PropertyChangeListener pcl);
}
/**
* Representation of a thread group in debugging view.
* Nodes representing a thread group in debugging view model should implement
* this interface.
*/
public static interface DVThreadGroup {
/**
* Get the name of the thread group.
* @return the name of the thread group
*/
public String getName();
/**
* Get the parent thread group, if exists.
* @return the parent thread group or null
if this thread group has no parent (root thread group).
*/
public DVThreadGroup getParentThreadGroup();
/**
* Get this thread group's threads.
* @return threads from this thread group
*/
public DVThread[] getThreads();
/**
* Get this thread group's thread groups.
* @return thread groups from this thread group
*/
public DVThreadGroup[] getThreadGroups();
}
/**
* Representation of a stack frame in debugging view.
* Nodes representing a stack frame in debugging view model should implement this
* interface.
* @since 2.65
*/
public static interface DVFrame {
/**
* Get the name of the frame. Usually the frame's class + method name, or function name.
* @return the name of the frame to be displayed in the debugging view.
* @since 2.65
*/
String getName();
/**
* Get the thread of this frame.
* @since 2.65
*/
DVThread getThread();
/**
* Make this frame current. Code evaluation and stepping should be performed
* in the current frame.
* @since 2.65
*/
void makeCurrent();
/**
* Get URI of the source file associated with this frame, if any.
* @return a source URI, or null
if the file is unknown.
* @since 2.65
*/
URI getSourceURI();
/**
* Get the source MIME type, if known.
* @return the source MIME type, or null
if the source, or
* its MIME type is unknown.
* @since 2.67
*/
default String getSourceMimeType() {
return null;
}
/**
* Line location of the frame in the source code at {@link #getSourceURI()}.
*
* @return the line number, or -1
if the line is unknown
* @since 2.65
*/
int getLine();
/**
* Column location of the frame in the source code at {@link #getSourceURI()}.
*
* @return the column number, or -1
if the column is unknown
* @since 2.65
*/
int getColumn();
/**
* Pop all frames up to and including this frame off the stack.
*
* @throws UnsupportedOperationException thrown when popping of stack frames is not supported.
* @throws PopException when the pop frame operation fails.
* @since 2.70
*/
default void popOff() throws UnsupportedOperationException, PopException {
throw new UnsupportedOperationException("The frame pop is not supported.");
}
}
/**
* Thrown when {@link DVFrame#popOff()} operation fails. The message of this
* exception describes the failure.
*
* @since 2.70
*/
public static final class PopException extends Exception {
/**
* Creates a new PopException with a description message.
* @param message the description
*
* @since 2.70
*/
public PopException(String message) {
super(message);
}
}
/**
* Representation of a deadlock - one set of mutually deadlocked threads.
*/
public static final class Deadlock {
private final Collection threads;
private Deadlock(Collection threads) {
this.threads = threads;
}
/**
* Get the threads in deadlock.
* @return The threads in deadlock.
*/
public Collection getThreads() {
return threads;
}
}
/**
* Boolean state filter that is applied to the debugging view.
* It's icon is made visible in the debugging view bottom panel.
*/
public static final class DVFilter {
/**
* Pre-defined default filters enumeration.
*/
public static enum DefaultFilter {
sortAlphabetic,
sortSuspend,
sortNatural,
showQualifiedNames,
showMonitors,
showSystemThreads,
showSuspendTable,
showThreadGroups,
showSuspendedThreadsOnly,
}
private static Reference sortGroupRef = new WeakReference(null);
/**
* Get an instance of a default filter.
* @param filter the default filter kind
* @return filter implementation
*/
public static DVFilter getDefault(DefaultFilter filter) {
FilterImpl fimpl = FilterImpl.createDefault(filter);
Group g;
switch (filter) {
case sortAlphabetic:
case sortNatural:
case sortSuspend:
g = getGroupFor(filter, fimpl); //sortGroup;
break;
default:
g = null;
}
fimpl.setGroup(g);
return new DVFilter(fimpl, g);
}
private static Group getGroupFor(DefaultFilter filter, FilterImpl fimpl) {
Group group = sortGroupRef.get();
if (group == null) {
group = new Group();
sortGroupRef = new WeakReference(group);
} else {
for (DVFilter df : group.getItems()) {
if (df.getImpl().getName().equals(fimpl.getName())) {
// The group already contains this item. We need to create a new group...
group = new Group();
sortGroupRef = new WeakReference(group);
}
}
}
return group;
}
/**
* Create a custom filter.
* @param name name of the filter
* @param displayName display name of the filter (visible in an action menu)
* @param tooltip tool-tip of the filter
* @param selectedIcon icon of the filter
* @param valuePrefs preferences which are asked for the filter value
* @param valuePrefKey key that is used to retrieve the filter value from preferences
* @param isSelectedDefault whether the filter should be selected by default when preferences do not contain the value
* @param group a filter group, can be null
* @return implementation of the filter
*/
public static DVFilter create(String name, String displayName,
String tooltip, Icon selectedIcon,
Preferences valuePrefs, String valuePrefKey,
boolean isSelectedDefault, Group group) {
FilterImpl fimpl = new FilterImpl(name, displayName, tooltip, selectedIcon,
valuePrefs, valuePrefKey, isSelectedDefault);
fimpl.setGroup(group);
return new DVFilter(fimpl, group);
}
private final FilterImpl fimpl;
private final Group group;
DVFilter (FilterImpl fimpl, Group group) {
this.fimpl = fimpl;
this.group = group;
if (group != null) {
group.add(this);
}
}
private FilterImpl getImpl() {
return fimpl;
}
/**
* Get the filter group.
* @return the filter group, or null
when the filter has no group
*/
public Group getGroup() {
return group;
}
/**
* Get the filter name.
* @return the filter name
*/
public String getName() {
return fimpl.getName();
}
/**
* Get the filter display name.
* @return the filter display name
*/
public String getDisplayName() {
return fimpl.getDisplayName();
}
/**
* Get the filter tooltip.
* @return the filter tooltip
*/
public String getTooltip() {
return fimpl.getTooltip();
}
/**
* Get the filter icon.
* @return the filter icon
*/
public Icon getIcon() {
return fimpl.getIcon();
}
/**
* Test if the filter is selected.
* @return whether the filter is selected right now
*/
public boolean isSelected() {
return fimpl.isSelected();
}
/**
* Set the filter as selected/unselected.
* @param state whether to select the filter
*/
public void setSelected(boolean state) {
if (!state && group != null) {
// unselecting a grouped item
boolean isSomeSelected = false;
for (DVFilter dvf : group.getItems()) {
if (dvf.getImpl() != fimpl) {
if (dvf.isSelected()) {
isSomeSelected = true;
break;
}
}
}
if (!isSomeSelected) {
// We're trying to unselect the only selected item in the group
if (NATURAL_SORT.equals(fimpl.getName())) {
// Ignore unselect
fimpl.setSelected(true);
fimpl.assureButtonSelected(true);
return ;
}
// Else force to select the natural sort
for (DVFilter dvf : group.getItems()) {
if (NATURAL_SORT.equals(dvf.getName())) {
dvf.getImpl().setSelected(true);
}
}
}
}
fimpl.setSelected(state);
}
/**
* Get the filter preferences.
* @return the preferences of this filter
*/
public Preferences getPreferences() {
return fimpl.getPreferences();
}
/**
* Get the preference key.
* @return key that is used to retrieve the filter value from preferences
*/
public String getPrefKey() {
return fimpl.getPrefKey();
}
/**
* The filter group.
*/
public static final class Group {
private final List items = new LinkedList();
/**
* Create a new empty group.
*/
public Group() {
}
void add(DVFilter filter) {
items.add(filter);
}
/**
* Get list of filters in this group.
* @return list of filters
*/
public List getItems() {
return items;
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy