This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 258651
Collapse All | Expand All

(-)a/api.debugger/apichanges.xml (+16 lines)
Lines 75-80 Link Here
75
<!-- ACTUAL CHANGES BEGIN HERE: -->
75
<!-- ACTUAL CHANGES BEGIN HERE: -->
76
76
77
<changes>
77
<changes>
78
    <change id="Watch_Pin">
79
        <api name="DebuggerCoreAPI"/>
80
        <summary>API for pinned Watches</summary>
81
        <version major="1" minor="54"/>
82
        <date day="12" month="4" year="2016"/>
83
        <author login="mentlicher"/>
84
        <compatibility binary="compatible" source="compatible" addition="yes" deletion="no"/>
85
        <description>
86
            A <code>Watch.Pin</code> base interface introduced as a basis
87
            for specific platform-dependent and location-dependent implementations.
88
            <code>DebuggerManager.createPinnedwatch()</code> introduced.
89
        </description>
90
        <class package="org.netbeans.api.debugger" name="DebuggerManager"/>
91
        <class package="org.netbeans.api.debugger" name="Watch"/>
92
        <issue number="258651"/>
93
    </change>
78
    <change id="Watch_disableable">
94
    <change id="Watch_disableable">
79
        <api name="DebuggerCoreAPI"/>
95
        <api name="DebuggerCoreAPI"/>
80
        <summary>API for changing enabled state of a Watch</summary>
96
        <summary>API for changing enabled state of a Watch</summary>
(-)a/api.debugger/manifest.mf (-1 / +1 lines)
Lines 1-5 Link Here
1
Manifest-Version: 1.0
1
Manifest-Version: 1.0
2
OpenIDE-Module: org.netbeans.api.debugger/1
2
OpenIDE-Module: org.netbeans.api.debugger/1
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/api/debugger/Bundle.properties
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/api/debugger/Bundle.properties
4
OpenIDE-Module-Specification-Version: 1.53
4
OpenIDE-Module-Specification-Version: 1.54
5
OpenIDE-Module-Layer: org/netbeans/api/debugger/layer.xml
5
OpenIDE-Module-Layer: org/netbeans/api/debugger/layer.xml
(-)a/api.debugger/src/org/netbeans/api/debugger/DebuggerManager.java (+21 lines)
Lines 665-670 Link Here
665
        }
665
        }
666
        return w;
666
        return w;
667
    }
667
    }
668
    
669
    /**
670
     * Create a watch pinned at the specified pin location.
671
     * @param expr expression to watch for (the format is the responsibility
672
     *    of the debugger plug-in implementation, but it is typically
673
     *    a variable name).
674
     * @param pin A pin where the watch should be pinned at.
675
     * @return the new watch
676
     * @since 1.54
677
     */
678
    public Watch createPinnedWatch(String expr, Watch.Pin pin) {
679
        Watch w = new Watch (expr, pin);
680
        if (Boolean.TRUE.equals(watchesInitializing.get())) {
681
            watches.addElement (w);
682
        } else {
683
            initWatches ();
684
            watches.addElement (w);
685
            fireWatchCreated (w);
686
        }
687
        return w;
688
    }
668
689
669
    /**
690
    /**
670
    * Gets all shared watches in the system.
691
    * Gets all shared watches in the system.
(-)a/api.debugger/src/org/netbeans/api/debugger/Watch.java (-2 / +27 lines)
Lines 47-53 Link Here
47
import java.beans.PropertyChangeListener;
47
import java.beans.PropertyChangeListener;
48
import java.beans.PropertyChangeSupport;
48
import java.beans.PropertyChangeSupport;
49
49
50
51
/**
50
/**
52
 * Abstract definition of watch. Each watch is created for
51
 * Abstract definition of watch. Each watch is created for
53
 * one String which contains the name of variable or some expression.
52
 * one String which contains the name of variable or some expression.
Lines 68-76 Link Here
68
    private boolean         enabled = true;
67
    private boolean         enabled = true;
69
    private PropertyChangeSupport pcs;
68
    private PropertyChangeSupport pcs;
70
    
69
    
70
    private final Pin pin;
71
    
71
    
72
    Watch (String expr) {
72
    Watch (String expr) {
73
        this(expr, null);
74
    }
75
    
76
    Watch (String expr, Pin pin) {
73
        this.expression = expr;
77
        this.expression = expr;
78
        this.pin = pin;
74
        pcs = new PropertyChangeSupport (this);
79
        pcs = new PropertyChangeSupport (this);
75
    }
80
    }
76
    
81
    
Lines 121-127 Link Here
121
        }
126
        }
122
        pcs.firePropertyChange (PROP_EXPRESSION, old, expression);
127
        pcs.firePropertyChange (PROP_EXPRESSION, old, expression);
123
    }
128
    }
124
    
129
130
    /**
131
     * Get a pin location, where the watch is pinned at, if any.
132
     * @return The watch pin, or <code>null</code>.
133
     * @since 1.54
134
     */
135
    public Pin getPin() {
136
        return pin;
137
    }
138
125
    /**
139
    /**
126
     * Remove the watch from the list of all watches in the system.
140
     * Remove the watch from the list of all watches in the system.
127
     */
141
     */
Lines 147-151 Link Here
147
    public void removePropertyChangeListener (PropertyChangeListener l) {
161
    public void removePropertyChangeListener (PropertyChangeListener l) {
148
        pcs.removePropertyChangeListener (l);
162
        pcs.removePropertyChangeListener (l);
149
    }
163
    }
164
    
165
    /**
166
     * A base interface for a watch pin location. Implemented by specific
167
     * platform-dependent and location-dependent implementation.
168
     * See <code>org.netbeans.spi.debugger.ui.EditorPin</code> for the NetBeans
169
     * editor pin implementation.
170
     * @since 1.54
171
     */
172
    public static interface Pin {
173
        
174
    }
150
}
175
}
151
176
(-)a/spi.debugger.ui/manifest.mf (-1 / +1 lines)
Lines 2-7 Link Here
2
OpenIDE-Module: org.netbeans.spi.debugger.ui/1
2
OpenIDE-Module: org.netbeans.spi.debugger.ui/1
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/debugger/ui/Bundle.properties
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/debugger/ui/Bundle.properties
4
OpenIDE-Module-Layer: org/netbeans/modules/debugger/resources/mf-layer.xml
4
OpenIDE-Module-Layer: org/netbeans/modules/debugger/resources/mf-layer.xml
5
OpenIDE-Module-Specification-Version: 2.52
5
OpenIDE-Module-Specification-Version: 2.53
6
OpenIDE-Module-Provides: org.netbeans.spi.debugger.ui
6
OpenIDE-Module-Provides: org.netbeans.spi.debugger.ui
7
OpenIDE-Module-Install: org/netbeans/modules/debugger/ui/DebuggerModule.class
7
OpenIDE-Module-Install: org/netbeans/modules/debugger/ui/DebuggerModule.class
(-)a/spi.debugger.ui/nbproject/project.properties (-1 / +1 lines)
Lines 42-48 Link Here
42
42
43
is.autoload=true
43
is.autoload=true
44
javac.compilerargs=-Xlint -Xlint:-serial
44
javac.compilerargs=-Xlint -Xlint:-serial
45
javac.source=1.7
45
javac.source=1.8
46
javadoc.arch=${basedir}/arch.xml
46
javadoc.arch=${basedir}/arch.xml
47
47
48
test.config.stableBTD.includes=**/*Test.class
48
test.config.stableBTD.includes=**/*Test.class
(-)a/spi.debugger.ui/nbproject/project.xml (+17 lines)
Lines 50-55 Link Here
50
            <code-name-base>org.netbeans.spi.debugger.ui</code-name-base>
50
            <code-name-base>org.netbeans.spi.debugger.ui</code-name-base>
51
            <module-dependencies>
51
            <module-dependencies>
52
                <dependency>
52
                <dependency>
53
                    <code-name-base>org.netbeans.api.annotations.common</code-name-base>
54
                    <build-prerequisite/>
55
                    <compile-dependency/>
56
                    <run-dependency>
57
                        <release-version>1</release-version>
58
                        <specification-version>1.27</specification-version>
59
                    </run-dependency>
60
                </dependency>
61
                <dependency>
53
                    <code-name-base>org.netbeans.api.debugger</code-name-base>
62
                    <code-name-base>org.netbeans.api.debugger</code-name-base>
54
                    <build-prerequisite/>
63
                    <build-prerequisite/>
55
                    <compile-dependency/>
64
                    <compile-dependency/>
Lines 68-73 Link Here
68
                    </run-dependency>
77
                    </run-dependency>
69
                </dependency>
78
                </dependency>
70
                <dependency>
79
                <dependency>
80
                    <code-name-base>org.netbeans.modules.editor.document</code-name-base>
81
                    <build-prerequisite/>
82
                    <compile-dependency/>
83
                    <run-dependency>
84
                        <specification-version>1.6</specification-version>
85
                    </run-dependency>
86
                </dependency>
87
                <dependency>
71
                    <code-name-base>org.netbeans.modules.editor.fold</code-name-base>
88
                    <code-name-base>org.netbeans.modules.editor.fold</code-name-base>
72
                    <build-prerequisite/>
89
                    <build-prerequisite/>
73
                    <compile-dependency/>
90
                    <compile-dependency/>
(-)a/spi.debugger.ui/src/org/netbeans/modules/debugger/resources/Bundle.properties (+1 lines)
Lines 118-120 Link Here
118
HINT_CURRENT_EXP_LINE_DCBP=Current Program Counter In Expression + Disabled Conditional Breakpoint
118
HINT_CURRENT_EXP_LINE_DCBP=Current Program Counter In Expression + Disabled Conditional Breakpoint
119
HINT_CURRENT_EXP=Current Expression
119
HINT_CURRENT_EXP=Current Expression
120
120
121
HINT_WATCH_PIN=Watch
(-)a/spi.debugger.ui/src/org/netbeans/modules/debugger/resources/WatchPin.xml (+55 lines)
Line 0 Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!--
3
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
5
Copyright 2016 Oracle and/or its affiliates. All rights reserved.
6
7
Oracle and Java are registered trademarks of Oracle and/or its affiliates.
8
Other names may be trademarks of their respective owners.
9
10
The contents of this file are subject to the terms of either the GNU
11
General Public License Version 2 only ("GPL") or the Common
12
Development and Distribution License("CDDL") (collectively, the
13
"License"). You may not use this file except in compliance with the
14
License. You can obtain a copy of the License at
15
http://www.netbeans.org/cddl-gplv2.html
16
or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
17
specific language governing permissions and limitations under the
18
License.  When distributing the software, include this License Header
19
Notice in each file and include the License file at
20
nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
21
particular file as subject to the "Classpath" exception as provided
22
by Oracle in the GPL Version 2 section of the License file that
23
accompanied this code. If applicable, add the following below the
24
License Header, with the fields enclosed by brackets [] replaced by
25
your own identifying information:
26
"Portions Copyrighted [year] [name of copyright owner]"
27
28
If you wish your version of this file to be governed by only the CDDL
29
or only the GPL Version 2, indicate your decision by adding
30
"[Contributor] elects to include this software in this distribution
31
under the [CDDL or GPL Version 2] license." If you do not indicate a
32
single choice of license, a recipient has the option to distribute
33
your version of this file under either the CDDL, the GPL Version 2 or
34
to extend the choice of license to its licensees as provided above.
35
However, if you add GPL Version 2 code and therefore, elected the GPL
36
Version 2 license, then the option applies only if the new code is
37
made subject to such option by the copyright holder.
38
39
Contributor(s):
40
41
Portions Copyrighted 2016 Sun Microsystems, Inc.
42
-->
43
44
<!DOCTYPE type PUBLIC "-//NetBeans//DTD annotation type 1.1//EN" "http://www.netbeans.org/dtds/annotation-type-1_1.dtd">
45
<type
46
    name='PinnedWatch'
47
    description_key='HINT_WATCH_PIN'
48
    localizing_bundle='org.netbeans.modules.debugger.resources.Bundle'
49
    visible='true'
50
    glyph='nbresloc:/org/netbeans/editor/resources/pin.png'
51
    type='line'
52
    severity='ok'
53
    priority='100'
54
    custom_sidebar_color='0xFF8FD5'
55
/>
(-)a/spi.debugger.ui/src/org/netbeans/modules/debugger/resources/mf-layer.xml (+1 lines)
Lines 542-547 Link Here
542
            <file name="mixed_BP_broken.xml" url="mixed_BP_broken.xml"/>
542
            <file name="mixed_BP_broken.xml" url="mixed_BP_broken.xml"/>
543
            <file name="PC_mixedBP.xml" url="PC_mixedBP.xml"/>
543
            <file name="PC_mixedBP.xml" url="PC_mixedBP.xml"/>
544
            <file name="PC_mixedBP_broken.xml" url="PC_mixedBP_broken.xml"/>
544
            <file name="PC_mixedBP_broken.xml" url="PC_mixedBP_broken.xml"/>
545
            <file name="WatchPin.xml" url="WatchPin.xml"/>
545
546
546
	<!-- following are invisible annotation types which are used for combining -->
547
	<!-- following are invisible annotation types which are used for combining -->
547
            <file name="_multi_BP.xml" url="_multi_BP.xml"/>
548
            <file name="_multi_BP.xml" url="_multi_BP.xml"/>
(-)a/spi.debugger.ui/src/org/netbeans/spi/debugger/ui/EditorPin.java (+177 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2016 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.spi.debugger.ui;
43
44
import java.awt.Point;
45
import java.beans.PropertyChangeListener;
46
import java.beans.PropertyChangeSupport;
47
import java.util.Objects;
48
import org.netbeans.api.debugger.Watch;
49
import org.openide.filesystems.FileObject;
50
51
/**
52
 * Implementation of watch pin in editor.
53
 * 
54
 * @author Ralph Benjamin Ruijs
55
 * @since 2.53
56
 */
57
public final class EditorPin implements Watch.Pin {
58
59
    /**
60
     * Line property, fired when a line change.
61
     */
62
    public static final String PROP_LINE = "line";
63
    /**
64
     * Location property, fired when a location change.
65
     */
66
    public static final String PROP_LOCATION = "location";
67
    /**
68
     * Comment property, fired when a comment change.
69
     */
70
    public static final String PROP_COMMENT = "comment";
71
72
    private final PropertyChangeSupport pchs = new PropertyChangeSupport(this);
73
74
    private final FileObject file;
75
    private volatile int line;
76
    private volatile Point location;
77
    private volatile String comment;
78
    private String vpId;
79
80
    /**
81
     * Create a new pin location in editor.
82
     * @param file The editor's file
83
     * @param line The line location of the pin
84
     * @param location Coordinates of the pin location in editor
85
     */
86
    public EditorPin(FileObject file, int line, Point location) {
87
        this.file = file;
88
        this.line = line;
89
        this.location = location;
90
    }
91
92
    /**
93
     * Get the line location of the pin.
94
     * @return The line.
95
     */
96
    public int getLine() {
97
        return line;
98
    }
99
100
    /**
101
     * Get the file object associated with the editor containing the pin.
102
     * @return The file object.
103
     */
104
    public FileObject getFile() {
105
        return file;
106
    }
107
108
    /**
109
     * Location of the pin in editor.
110
     * @return The location point.
111
     */
112
    public Point getLocation() {
113
        return location;
114
    }
115
116
    /**
117
     * Move the pin to a different location in the editor pane.
118
     * @param line A new line
119
     * @param location Coordinates of the new location
120
     */
121
    public void move(int line, Point location) {
122
        int oldLine = this.line;
123
        this.line = line;
124
        if (oldLine != line) {
125
            pchs.firePropertyChange(PROP_LINE, oldLine, line);
126
        }
127
        Point oldLocation = this.location;
128
        this.location = location;
129
        if (!oldLocation.equals(location)) {
130
            pchs.firePropertyChange(PROP_LOCATION, oldLocation, location);
131
        }
132
    }
133
134
    /**
135
     * Set a textual comment to this pin.
136
     * @param comment The user comment.
137
     */
138
    public void setComment(String comment) {
139
        String oldComment = this.comment;
140
        this.comment = comment;
141
        if (!Objects.equals(oldComment, comment)) {
142
            pchs.firePropertyChange(PROP_COMMENT, oldComment, comment);
143
        }
144
    }
145
146
    /**
147
     * Get the comment of this pin.
148
     * @return The comment or <code>null</code> when no comment is set.
149
     */
150
    public String getComment() {
151
        return comment;
152
    }
153
    
154
    void setVpId(String vpId) {
155
        this.vpId = vpId;
156
    }
157
158
    String getVpId() {
159
        return vpId;
160
    }
161
162
    /**
163
     * Add a listener for property change events to this editor pin.
164
     * @param listener The listener
165
     */
166
    public void addPropertyChangeListener(PropertyChangeListener listener) {
167
        pchs.addPropertyChangeListener(listener);
168
    }
169
170
    /**
171
     * Remove a listener for property change events from this editor pin.
172
     * @param listener The listener
173
     */
174
    public void removePropertyChangeListener(PropertyChangeListener listener) {
175
        pchs.removePropertyChangeListener(listener);
176
    }
177
}
(-)a/spi.debugger.ui/src/org/netbeans/spi/debugger/ui/PinWatchUISupport.java (+428 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2016 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.spi.debugger.ui;
43
44
import java.beans.Customizer;
45
import java.beans.PropertyChangeEvent;
46
import java.beans.PropertyChangeListener;
47
import java.util.HashMap;
48
import java.util.HashSet;
49
import java.util.List;
50
import java.util.Map;
51
import java.util.Set;
52
import javax.swing.Action;
53
import org.netbeans.api.debugger.DebuggerEngine;
54
import org.netbeans.api.debugger.DebuggerManager;
55
import org.netbeans.api.debugger.DebuggerManagerAdapter;
56
import org.netbeans.api.debugger.Watch;
57
import org.netbeans.api.debugger.Watch.Pin;
58
import org.netbeans.modules.debugger.ui.annotations.WatchAnnotationProvider;
59
import org.netbeans.spi.debugger.DebuggerServiceRegistration;
60
import org.openide.loaders.DataObjectNotFoundException;
61
import org.openide.util.Exceptions;
62
import org.openide.util.NbBundle;
63
64
/**
65
 * Access to the default UI implementation of pin watches in editor.
66
 * Watches that are to be pinned by this support class, need to use the
67
 * {@link EditorPin} as the {@link Watch#getPin()} and an implementation
68
 * of {@link ValueProvider} needs to be register via {@link DebuggerServiceRegistration},
69
 * which acts as a supplier of the watch value.
70
 * <p>
71
 * Use {@link #pin(org.netbeans.api.debugger.Watch, java.lang.String)} to pin
72
 * the watch into editor where the corresponding {@link EditorPin} points at
73
 * and provide <code>valueProviderId</code> of the corresponding registered
74
 * {@link ValueProvider} which handles the value updates.
75
 * 
76
 * @author Martin Entlicher
77
 * @since 2.53
78
 */
79
public final class PinWatchUISupport {
80
    
81
    private static final PinWatchUISupport INSTANCE = new PinWatchUISupport();
82
    private final Object valueProvidersLock = new Object();
83
    private Map<String, DelegatingValueProvider> valueProviders;
84
    
85
    private PinWatchUISupport() {
86
        WatchAnnotationProvider.PIN_SUPPORT_ACCESS = new WatchAnnotationProvider.PinSupportedAccessor() {
87
            @Override
88
            public ValueProvider getValueProvider(EditorPin pin) {
89
                String id = pin.getVpId();
90
                if (id == null) {
91
                    return null;
92
                }
93
                synchronized (valueProvidersLock) {
94
                    return getValueProviders().get(id);
95
                }
96
            }
97
        };
98
        DebuggerManager.getDebuggerManager().addDebuggerListener(
99
                DebuggerManager.PROP_CURRENT_ENGINE,
100
                new DebuggerManagerAdapter() {
101
                    @Override
102
                    public void propertyChange(PropertyChangeEvent evt) {
103
                        refreshValueProviders();
104
                    }
105
                }
106
        );
107
    }
108
    
109
    /**
110
     * Get the default instance of this class.
111
     * @return An instance of {@link PinWatchUISupport} class.
112
     */
113
    public static PinWatchUISupport getDefault() {
114
        return INSTANCE;
115
    }
116
    
117
    /**
118
     * Pin the watch into an editor.
119
     * The pinned watch need to return {@link EditorPin} from {@link Watch#getPin()}
120
     * and a {@link ValueProvider} needs to be registered for the provided
121
     * <code>valueProviderId</code>.
122
     * @param watch A watch to pin at the location determined by the {@link EditorPin}.
123
     * @param valueProviderId An id of a registered {@link ValueProvider}.
124
     * @throws IllegalArgumentException is thrown when {@link Watch#getPin()}
125
     *                                  does not return an {@link EditorPin}, or
126
     *                                  when we're not able to find the editor
127
     *                                  where {@link EditorPin} points at.
128
     */
129
    public void pin(Watch watch, String valueProviderId) throws IllegalArgumentException {
130
        Pin wpin = watch.getPin();
131
        if (!(wpin instanceof EditorPin)) {
132
            throw new IllegalArgumentException("Unsupported pin: "+wpin);
133
        }
134
        synchronized (valueProvidersLock) {
135
            if (!getValueProviders().containsKey(valueProviderId)) {
136
                valueProviders.put(valueProviderId, new DelegatingValueProvider(valueProviderId));
137
            }
138
        }
139
        EditorPin pin = (EditorPin) wpin;
140
        pin.setVpId(valueProviderId);
141
        try {
142
            WatchAnnotationProvider.PIN_SUPPORT_ACCESS.pin(watch);
143
        } catch (DataObjectNotFoundException ex) {
144
            throw new IllegalArgumentException("Unable to find the pin's editor.", ex);
145
        }
146
    }
147
    
148
    private Map<String, DelegatingValueProvider> getValueProviders() {
149
        synchronized (valueProvidersLock) {
150
            if (valueProviders == null) {
151
                valueProviders = new HashMap<>();
152
                refreshValueProviders();
153
            }
154
            return valueProviders;
155
        }
156
    }
157
    
158
    private void refreshValueProviders() {
159
        DebuggerManager dm = DebuggerManager.getDebuggerManager ();
160
        DebuggerEngine e = dm.getCurrentEngine ();
161
        List<? extends ValueProvider> providers;
162
        if (e == null) {
163
            providers = dm.lookup (null, ValueProvider.class);
164
        } else {
165
            providers = DebuggerManager.join(e, dm).lookup (null, ValueProvider.class);
166
        }
167
        if (!providers.isEmpty()) {
168
            synchronized (valueProvidersLock) {
169
                if (valueProviders == null) {
170
                    valueProviders = new HashMap<>();
171
                }
172
                Set<String> existingProviderIds = new HashSet<>();
173
                for (ValueProvider provider : providers) {
174
                    String id = provider.getId();
175
                    existingProviderIds.add(id);
176
                    DelegatingValueProvider dvp = valueProviders.get(id);
177
                    if (dvp == null) {
178
                        dvp = new DelegatingValueProvider(id);
179
                        valueProviders.put(id, dvp);
180
                    }
181
                    dvp.setDelegate(provider);
182
                }
183
                Set<String> staleProviderIds = new HashSet<>(valueProviders.keySet());
184
                staleProviderIds.removeAll(existingProviderIds);
185
                for (String staleId : staleProviderIds) {
186
                    valueProviders.get(staleId).setDelegate(null);
187
                }
188
            }
189
        }
190
    }
191
    
192
    private static final class DelegatingValueProvider implements ValueProvider {
193
        
194
        private final String id;
195
        private volatile ValueProvider delegate;
196
        private final Map<Watch, ValueChangeListener> listeners = new HashMap<>();
197
        
198
        DelegatingValueProvider(String id) {
199
            this.id = id;
200
        }
201
202
        @Override
203
        public String getId() {
204
            return id;
205
        }
206
207
        @Override
208
        public String getValue(Watch watch) {
209
            ValueProvider vp = delegate;
210
            if (vp != null) {
211
                return vp.getValue(watch);
212
            } else {
213
                return null;
214
            }
215
        }
216
217
        @Override
218
        public String getEvaluatingText() {
219
            ValueProvider vp = delegate;
220
            if (vp != null) {
221
                return vp.getEvaluatingText();
222
            } else {
223
                return ValueProvider.super.getEvaluatingText();
224
            }
225
        }
226
227
        @Override
228
        public String getEditableValue(Watch watch) {
229
            ValueProvider vp = delegate;
230
            if (vp != null) {
231
                return vp.getEditableValue(watch);
232
            } else {
233
                return ValueProvider.super.getEditableValue(watch);
234
            }
235
        }
236
237
        @Override
238
        public boolean setValue(Watch watch, String value) {
239
            ValueProvider vp = delegate;
240
            if (vp != null) {
241
                return vp.setValue(watch, value);
242
            } else {
243
                return ValueProvider.super.setValue(watch, value);
244
            }
245
        }
246
247
        @Override
248
        public Action[] getHeadActions(Watch watch) {
249
            ValueProvider vp = delegate;
250
            if (vp != null) {
251
                return vp.getHeadActions(watch);
252
            } else {
253
                return ValueProvider.super.getHeadActions(watch);
254
            }
255
        }
256
257
        @Override
258
        public Action[] getTailActions(Watch watch) {
259
            ValueProvider vp = delegate;
260
            if (vp != null) {
261
                return vp.getTailActions(watch);
262
            } else {
263
                return ValueProvider.super.getTailActions(watch);
264
            }
265
        }
266
267
        @Override
268
        public synchronized void setChangeListener(Watch watch, ValueChangeListener chl) {
269
            ValueProvider vp = delegate;
270
            if (vp != null) {
271
                vp.setChangeListener(watch, chl);
272
            }
273
            listeners.put(watch, chl);
274
        }
275
276
        @Override
277
        public synchronized void unsetChangeListener(Watch watch) {
278
            ValueProvider vp = delegate;
279
            if (vp != null) {
280
                vp.unsetChangeListener(watch);
281
            }
282
            listeners.remove(watch);
283
        }
284
        
285
        synchronized void setDelegate(ValueProvider delegate) {
286
            this.delegate = delegate;
287
            if (delegate == null) {
288
                for (Map.Entry<Watch, ValueChangeListener> wvl : listeners.entrySet()) {
289
                    wvl.getValue().valueChanged(wvl.getKey());
290
                }
291
            } else {
292
                for (Map.Entry<Watch, ValueChangeListener> wvl : listeners.entrySet()) {
293
                    delegate.setChangeListener(wvl.getKey(), wvl.getValue());
294
                }
295
            }
296
        }
297
        
298
    }
299
    
300
    /**
301
     * Provider of pinned watch value.
302
     * Register an implementation of this class via {@link DebuggerServiceRegistration}
303
     * for the corresponding debugger session ID path.
304
     */
305
    public static interface ValueProvider {
306
        
307
        /**
308
         * Get a unique ID of this value provider.
309
         * Use this ID when pinning a watch via {@link #pin(org.netbeans.api.debugger.Watch, java.lang.String)}.
310
         * @return An ID of this value provider.
311
         */
312
        String getId();
313
        
314
        /**
315
         * Get current value of pinned watch. This method must not block,
316
         * it's called synchronously in the EQ thread. This method should return
317
         * most recent value of the watch, or the same instance which
318
         * {@link #getEvaluatingText()} returns when the watch value is being computed,
319
         * or <code>null</code> when the watch can not be resolved.
320
         * @param watch the watch whose value is to be returned.
321
         * @return The current value of the watch, or {@link #getEvaluatingText()},
322
         *         or <code>null</code>.
323
         */
324
        String getValue(Watch watch);
325
        
326
        /**
327
         * Get a localized text to be displayed when the watch is being evaluated.
328
         * The pin watch UI highlights changed values. It uses this string to
329
         * distinguish real watch values. Return the same instance from
330
         * {@link #getValue(org.netbeans.api.debugger.Watch)} when the watch is
331
         * being computed.
332
         * @return A localized text displayed while the watch is evaluating.
333
         */
334
        @NbBundle.Messages("WATCH_EVALUATING=Evaluating...")
335
        default String getEvaluatingText() {
336
            return Bundle.WATCH_EVALUATING();
337
        }
338
339
        /**
340
         * Determine whether the current watch value is editable and if yes,
341
         * return the value to edit.
342
         * The value returned by {@link #getValue(org.netbeans.api.debugger.Watch)}
343
         * might not be the proper value to edit. It may contain type description
344
         * or more descriptive information about the value. This method should
345
         * provide a raw textual representation of the value to edit,
346
         * or <code>null</code> when the value is not editable.<br>
347
         * The default implementation return <code>null</code>.
348
         * @param watch The watch to return the editable value for
349
         * @return The string representation of the editable value,
350
         * or <code>null</code> when the value is not editable.
351
         */
352
        default String getEditableValue(Watch watch) {
353
            return null;
354
        }
355
356
        /**
357
         * Set a watch value as a response to finished editing.
358
         * This method is called only when a prior call to
359
         * {@link #getEditableValue(org.netbeans.api.debugger.Watch)} returns
360
         * a non-null value.<br>
361
         * The default implementation throws {@link UnsupportedOperationException}.
362
         * @param watch The watch to set the value for.
363
         * @param value The new watch value.
364
         * @return <code>true</code> when the value was set successfully and
365
         *         the UI should get updated from {@link #getValue(org.netbeans.api.debugger.Watch)},
366
         *         <code>false</code> when the set fails and the last watch value
367
         *         should stay. The implementation is responsible for any error
368
         *         reporting when the set fails.
369
         */
370
        default boolean setValue(Watch watch, String value) {
371
            throw new UnsupportedOperationException("Watch not editable.");
372
        }
373
374
        /**
375
         * Get actions to be displayed at the head of pin watch component.
376
         * Typically, you put an expansion action there.
377
         * The default implementation returns <code>null</code>.
378
         * @param watch The watch to get the actions for
379
         * @return An array of actions or <code>null</code> elements (for action separators),
380
         *         or <code>null</code> when no actions should be displayed.
381
         */
382
        default Action[] getHeadActions(Watch watch) {
383
            return null;
384
        }
385
386
        /**
387
         * Get actions to be displayed at the tail of pin watch component.
388
         * Typically, you put a details action there, which displays a detailed
389
         * view of the watch value. These actions, if any, are followed by comment
390
         * and close actions, which are always present.
391
         * The default implementation returns <code>null</code>.
392
         * @param watch The watch to get the actions for
393
         * @return An array of actions or <code>null</code> elements (for action separators),
394
         *         or <code>null</code> when no actions should be displayed.
395
         */
396
        default Action[] getTailActions(Watch watch) {
397
            return null;
398
        }
399
400
        /**
401
         * Allows to set a value change listener for a specific watch.
402
         * @param watch The watch to listen for changes
403
         * @param chl The value change listener.
404
         */
405
        void setChangeListener(Watch watch, ValueChangeListener chl);
406
        
407
        /**
408
         * Unset a value change listener for a specific watch.
409
         * Use this method to unregister the listeners and free up associated resources.
410
         * @param watch The watch to unset the listener from.
411
         */
412
        void unsetChangeListener(Watch watch);
413
        
414
        /**
415
         * Listener for watch value changes.
416
         */
417
        public static interface ValueChangeListener {
418
            
419
            /**
420
             * Notify that a watch value has changed.
421
             * {@link ValueProvider#getValue(org.netbeans.api.debugger.Watch)}
422
             * then returns the new value.
423
             * @param watch The watch whose value has changed.
424
             */
425
            void valueChanged(Watch watch);
426
        }
427
    }
428
}

Return to bug 258651