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 218933
Collapse All | Expand All

(-)a/debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/resources/mf-layer.xml (+8 lines)
Lines 85-90 Link Here
85
                        <attr name="originalFile" stringvalue="Actions/Debug/org-netbeans-modules-debugger-ui-actions-ToggleBreakpointAction.instance"/>
85
                        <attr name="originalFile" stringvalue="Actions/Debug/org-netbeans-modules-debugger-ui-actions-ToggleBreakpointAction.instance"/>
86
                        <attr name="position" intvalue="1880"/>
86
                        <attr name="position" intvalue="1880"/>
87
                    </file>
87
                    </file>
88
                    <file name="org-netbeans-modules-debugger-ui-actions-DisableBreakpointAction.shadow">
89
                        <attr name="originalFile" stringvalue="Actions/Debug/org-netbeans-modules-debugger-ui-actions-DisableBreakpointAction.instance"/>
90
                        <attr name="position" intvalue="1920"/>
91
                    </file>
88
                    </folder>
92
                    </folder>
89
            </folder>
93
            </folder>
90
            <folder name="x-java-nbdebug-class">
94
            <folder name="x-java-nbdebug-class">
Lines 273-278 Link Here
273
                <attr name="originalFile" stringvalue="Actions/Debug/org-netbeans-modules-debugger-ui-actions-ToggleBreakpointAction.instance"/>
277
                <attr name="originalFile" stringvalue="Actions/Debug/org-netbeans-modules-debugger-ui-actions-ToggleBreakpointAction.instance"/>
274
                <attr name="position" intvalue="2300"/>
278
                <attr name="position" intvalue="2300"/>
275
            </file>
279
            </file>
280
            <file name="org-netbeans-modules-debugger-ui-actions-DisableBreakpointAction.shadow">
281
                <attr name="originalFile" stringvalue="Actions/Debug/org-netbeans-modules-debugger-ui-actions-DisableBreakpointAction.instance"/>
282
                <attr name="position" intvalue="2350"/>
283
            </file>
276
            <file name="org-netbeans-modules-debugger-ui-actions-AddBreakpointAction.shadow">
284
            <file name="org-netbeans-modules-debugger-ui-actions-AddBreakpointAction.shadow">
277
                <attr name="originalFile" stringvalue="Actions/Debug/org-netbeans-modules-debugger-ui-actions-AddBreakpointAction.instance"/>
285
                <attr name="originalFile" stringvalue="Actions/Debug/org-netbeans-modules-debugger-ui-actions-AddBreakpointAction.instance"/>
278
                <attr name="position" intvalue="2400"/>
286
                <attr name="position" intvalue="2400"/>
(-)a/debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/ui/actions/ToggleBreakpointActionProvider.java (-286 / +29 lines)
Lines 44-117 Link Here
44
44
45
package org.netbeans.modules.debugger.jpda.ui.actions;
45
package org.netbeans.modules.debugger.jpda.ui.actions;
46
46
47
import com.sun.source.tree.BlockTree;
48
import com.sun.source.tree.CompilationUnitTree;
49
import com.sun.source.tree.MethodTree;
50
import com.sun.source.tree.Tree;
51
import com.sun.source.tree.VariableTree;
52
import com.sun.source.util.SourcePositions;
53
import com.sun.source.util.TreePath;
54
import java.beans.PropertyChangeEvent;
47
import java.beans.PropertyChangeEvent;
55
import java.beans.PropertyChangeListener;
48
import java.beans.PropertyChangeListener;
56
import java.io.IOException;
57
import java.lang.reflect.InvocationTargetException;
58
import java.net.MalformedURLException;
49
import java.net.MalformedURLException;
59
import java.net.URL;
50
import java.net.URL;
60
import java.util.Collections;
51
import java.util.Collections;
61
import java.util.Set;
52
import java.util.Set;
62
import java.util.concurrent.Future;
63
import javax.swing.JEditorPane;
64
import javax.swing.SwingUtilities;
53
import javax.swing.SwingUtilities;
65
import javax.swing.text.BadLocationException;
66
import org.netbeans.api.debugger.ActionsManager;
54
import org.netbeans.api.debugger.ActionsManager;
67
55
68
56
69
import org.netbeans.api.debugger.Breakpoint;
70
import org.netbeans.api.debugger.DebuggerManager;
71
import org.netbeans.spi.debugger.ContextProvider;
57
import org.netbeans.spi.debugger.ContextProvider;
72
import org.netbeans.api.debugger.jpda.JPDADebugger;
58
import org.netbeans.api.debugger.jpda.JPDADebugger;
73
import org.netbeans.api.debugger.jpda.LineBreakpoint;
59
import org.netbeans.api.debugger.jpda.LineBreakpoint;
74
import org.netbeans.api.java.source.CancellableTask;
75
import org.netbeans.api.java.source.CompilationController;
76
import org.netbeans.api.java.source.JavaSource;
77
import org.netbeans.api.java.source.JavaSource.Phase;
78
import org.netbeans.api.java.source.TreeUtilities;
79
import org.netbeans.editor.BaseDocument;
80
import org.netbeans.editor.Utilities;
81
import org.netbeans.modules.debugger.jpda.ui.EditorContextBridge;
60
import org.netbeans.modules.debugger.jpda.ui.EditorContextBridge;
82
import org.netbeans.modules.debugger.jpda.ui.JavaUtils;
83
import org.netbeans.spi.debugger.ActionsProvider;
61
import org.netbeans.spi.debugger.ActionsProvider;
84
import org.netbeans.spi.debugger.ActionsProviderSupport;
62
import org.netbeans.spi.debugger.ActionsProviderSupport;
85
import org.openide.ErrorManager;
86
import org.openide.cookies.EditorCookie;
87
import org.openide.filesystems.FileObject;
63
import org.openide.filesystems.FileObject;
88
import org.openide.filesystems.URLMapper;
64
import org.openide.filesystems.URLMapper;
89
65
90
import org.openide.loaders.DataObject;
91
import org.openide.loaders.DataObjectNotFoundException;
92
import org.openide.util.Exceptions;
93
import org.openide.util.NbBundle;
94
66
95
67
96
/** 
68
/** 
97
 *
69
 * Toggles the enabled state of a {@link LineBreakpoint}.
70
 * If the breakpoint is enabled, then it will be disabled.
71
 * If the breakpoint is disabled, then it will be enabled.
72
 * If the breakpoint does not exist, then it be created.
73
 * 
74
 * Code is based on {@link ToggleBreakpointActionProvider} from Jan Jancura.
98
 * @author   Jan Jancura
75
 * @author   Jan Jancura
76
 * @author   markiewb
99
 */
77
 */
100
@ActionsProvider.Registrations({
78
@ActionsProvider.Registrations({
101
    @ActionsProvider.Registration(path="",                     actions={ "toggleBreakpoint" }, activateForMIMETypes={ "text/x-java" }),
79
    @ActionsProvider.Registration(path="",                     actions={ "disableBreakpoint" }, activateForMIMETypes={ "text/x-java" }),
102
    @ActionsProvider.Registration(path="netbeans-JPDASession", actions={ "toggleBreakpoint" }, activateForMIMETypes={ "text/x-java" })
80
    @ActionsProvider.Registration(path="netbeans-JPDASession", actions={ "disableBreakpoint" }, activateForMIMETypes={ "text/x-java" })
103
})
81
})
104
public class ToggleBreakpointActionProvider extends ActionsProviderSupport 
82
public class DisableBreakpointActionProvider extends ActionsProviderSupport 
105
implements PropertyChangeListener {
83
implements PropertyChangeListener {
106
    
84
    
107
    private JPDADebugger debugger;
85
    private JPDADebugger debugger;
108
86
109
    
87
    
110
    public ToggleBreakpointActionProvider () {
88
    public DisableBreakpointActionProvider () {
111
        EditorContextBridge.getContext().addPropertyChangeListener (this);
89
        EditorContextBridge.getContext().addPropertyChangeListener (this);
112
    }
90
    }
113
    
91
    
114
    public ToggleBreakpointActionProvider (ContextProvider lookupProvider) {
92
    public DisableBreakpointActionProvider (ContextProvider lookupProvider) {
115
        debugger = lookupProvider.lookupFirst(null, JPDADebugger.class);
93
        debugger = lookupProvider.lookupFirst(null, JPDADebugger.class);
116
        debugger.addPropertyChangeListener (JPDADebugger.PROP_STATE, this);
94
        debugger.addPropertyChangeListener (JPDADebugger.PROP_STATE, this);
117
        EditorContextBridge.getContext().addPropertyChangeListener (this);
95
        EditorContextBridge.getContext().addPropertyChangeListener (this);
Lines 122-127 Link Here
122
        EditorContextBridge.getContext().removePropertyChangeListener (this);
100
        EditorContextBridge.getContext().removePropertyChangeListener (this);
123
    }
101
    }
124
    
102
    
103
    @Override
125
    public void propertyChange (PropertyChangeEvent evt) {
104
    public void propertyChange (PropertyChangeEvent evt) {
126
        String url = EditorContextBridge.getContext().getCurrentURL();
105
        String url = EditorContextBridge.getContext().getCurrentURL();
127
        FileObject fo;
106
        FileObject fo;
Lines 131-209 Link Here
131
            fo = null;
110
            fo = null;
132
        }
111
        }
133
        setEnabled (
112
        setEnabled (
134
            ActionsManager.ACTION_TOGGLE_BREAKPOINT,
113
            ActionsManager.ACTION_DISABLE_BREAKPOINT,
135
            (EditorContextBridge.getContext().getCurrentLineNumber () >= 0) && 
114
            (EditorContextBridge.getContext().getCurrentLineNumber () >= 0) && 
136
            (fo != null && "text/x-java".equals(fo.getMIMEType()))  // NOI18N
115
            (fo != null && "text/x-java".equals(fo.getMIMEType()))  // NOI18N
137
            //(EditorContextBridge.getCurrentURL ().endsWith (".java"))
116
            //(EditorContextBridge.getCurrentURL ().endsWith (".java"))
138
        );
117
        );
139
        if ( debugger != null && 
118
        if ( debugger != null && 
140
             debugger.getState () == JPDADebugger.STATE_DISCONNECTED
119
             debugger.getState () == JPDADebugger.STATE_DISCONNECTED
141
        ) 
120
        ) {
142
            destroy ();
121
            destroy ();
122
        }
143
    }
123
    }
144
    
124
    
125
    @Override
145
    public Set getActions () {
126
    public Set getActions () {
146
        return Collections.singleton (ActionsManager.ACTION_TOGGLE_BREAKPOINT);
127
        return Collections.singleton (ActionsManager.ACTION_DISABLE_BREAKPOINT);
147
    }
128
    }
148
    
129
    
130
    @Override
149
    public void doAction (Object action) {
131
    public void doAction (Object action) {
150
        DebuggerManager d = DebuggerManager.getDebuggerManager ();
151
        
152
        // 1) get source name & line number
132
        // 1) get source name & line number
153
        int lineNumber = EditorContextBridge.getContext().getCurrentLineNumber ();
133
        int lineNumber = EditorContextBridge.getContext().getCurrentLineNumber ();
154
        String url = EditorContextBridge.getContext().getCurrentURL ();
134
        String url = EditorContextBridge.getContext().getCurrentURL ();
155
        if ("".equals (url.trim ())) return;
135
        if ("".equals (url.trim ())) return;
156
136
157
        // 2) find and remove existing line breakpoint
137
        // 2) find and enable/disable existing line breakpoint
158
        LineBreakpoint lb = findBreakpoint(url, lineNumber);
138
        LineBreakpoint lb = ToggleBreakpointActionProvider.findBreakpoint(url, lineNumber);
159
        if (lb != null) {
139
        if (lb != null) {
160
            d.removeBreakpoint (lb);
140
            if (lb.isEnabled()) {
161
            return;
141
                lb.disable();
142
            } else {
143
                lb.enable();
144
            }
145
        }else{
146
            //try to add method breakpoint
147
            new ToggleBreakpointActionProvider().doAction(action);
162
        }
148
        }
163
//        Breakpoint[] bs = d.getBreakpoints ();
164
//        int i, k = bs.length;
165
//        for (i = 0; i < k; i++) {
166
//            if (!(bs [i] instanceof LineBreakpoint)) continue;
167
//            LineBreakpoint lb = (LineBreakpoint) bs [i];
168
//            if (ln != lb.getLineNumber ()) continue;
169
//            if (!url.equals (lb.getURL ())) continue;
170
//            d.removeBreakpoint (lb);
171
//            return;
172
//        }
173
174
        // 3) check if a line breakpoint could be addded at the selected location
175
        //    if not, try to adjust position or cancel the action
176
        JEditorPane[] editorPane = new JEditorPane[1];
177
        int adjustedLineNumber = checkLineBreakability(url, lineNumber, editorPane);
178
        if (adjustedLineNumber != lineNumber) {
179
            if (adjustedLineNumber == -1 || findBreakpoint(url, adjustedLineNumber) != null) {
180
                java.awt.Toolkit.getDefaultToolkit().beep();
181
                if (editorPane[0] != null) {
182
                    Utilities.setStatusText(editorPane[0], ""); // workaroud, no status text is displayed when the same text has been already set before
183
                    String msg = NbBundle.getMessage(ToggleBreakpointActionProvider.class, "CTL_Cannot_Toggle_Breakpoint");
184
                    Utilities.setStatusText(editorPane[0], msg);
185
                }
186
                return;
187
            } else {
188
                if (editorPane[0] != null) {
189
                    Utilities.setStatusText(editorPane[0], ""); // workaroud, no status text is displayed when the same text has been already set before
190
                    String msg = NbBundle.getMessage(ToggleBreakpointActionProvider.class, "CTL_Breakpoint_Position_Adjusted");
191
                    Utilities.setStatusText(editorPane[0], msg);
192
                }
193
                lineNumber = adjustedLineNumber;                
194
            }
195
        }
196
197
        // 4) create a new line breakpoint
198
        lb = LineBreakpoint.create (
199
            url,
200
            lineNumber
201
        );
202
        lb.setPrintText (
203
            NbBundle.getBundle (ToggleBreakpointActionProvider.class).getString 
204
                ("CTL_Line_Breakpoint_Print_Text")
205
        );
206
        d.addBreakpoint (lb);
207
    }
149
    }
208
150
209
    @Override
151
    @Override
Lines 215-417 Link Here
215
            }
157
            }
216
        });
158
        });
217
    }
159
    }
218
219
    private int checkLineBreakability(String url, final int lineNumber, final JEditorPane[] editorPane) {
220
        FileObject fileObj = null;
221
        try {
222
            fileObj = URLMapper.findFileObject(new URL(url));
223
        } catch (MalformedURLException e) {
224
        }
225
        if (fileObj == null) return lineNumber;
226
        DataObject dobj = null;
227
        try {
228
            dobj = DataObject.find(fileObj);
229
        } catch (DataObjectNotFoundException ex) {
230
        }
231
        if (dobj == null) return lineNumber;
232
        final EditorCookie ec = (EditorCookie)dobj.getCookie(EditorCookie.class);
233
        if (ec == null) return lineNumber;
234
        final BaseDocument doc = (BaseDocument)ec.getDocument();
235
        if (doc == null) return lineNumber;
236
        final int rowStartOffset = Utilities.getRowStartFromLineOffset(doc, lineNumber - 1);
237
        final int rowEndOffset;
238
        try {
239
            rowEndOffset = Utilities.getRowEnd(doc, rowStartOffset);
240
        } catch (BadLocationException ex) {
241
            return lineNumber;
242
        }
243
        JavaSource js = JavaSource.forFileObject(fileObj);
244
        if (js == null) return lineNumber;
245
246
        if (SwingUtilities.isEventDispatchThread()) {
247
            JEditorPane[] openedPanes = ec.getOpenedPanes();
248
            if (openedPanes != null && openedPanes.length > 0) {
249
                editorPane[0] = openedPanes[0];
250
            }
251
        } else {
252
            try {
253
                SwingUtilities.invokeAndWait(new Runnable() {
254
                    public void run() {
255
                        JEditorPane[] openedPanes = ec.getOpenedPanes();
256
                        if (openedPanes != null && openedPanes.length > 0) {
257
                            editorPane[0] = openedPanes[0];
258
                        }
259
                    }
260
                });
261
            } catch (InvocationTargetException ex) {
262
                Exceptions.printStackTrace(ex);
263
            } catch (InterruptedException ex) {
264
                Exceptions.printStackTrace(ex);
265
            }
266
        }
267
268
        final int[] result = new int[] {lineNumber};
269
        final Future<Void> scanFinished;
270
        try {
271
            scanFinished = JavaUtils.runWhenScanFinishedReallyLazy(js, new CancellableTask<CompilationController>() {
272
                public void cancel() {
273
                }
274
                public void run(CompilationController ci) throws Exception {
275
                    if (ci.toPhase(Phase.RESOLVED).compareTo(Phase.RESOLVED) < 0) {
276
                        ErrorManager.getDefault().log(ErrorManager.WARNING,
277
                                "Unable to resolve "+ci.getFileObject()+" to phase "+Phase.RESOLVED+", current phase = "+ci.getPhase()+
278
                                "\nDiagnostics = "+ci.getDiagnostics()+
279
                                "\nFree memory = "+Runtime.getRuntime().freeMemory());
280
                        return;
281
                    }
282
                    SourcePositions positions = ci.getTrees().getSourcePositions();
283
                    CompilationUnitTree compUnit = ci.getCompilationUnit();
284
                    TreeUtilities treeUtils = ci.getTreeUtilities();
285
286
                    Tree outerTree = null;
287
                    Tree execTree = null;
288
                    TreePath outerTreePath = null;
289
                    TreePath execTreePath = null;
290
                    Tree lastTree = null;
291
                    long execTreeStartOffs = 0, execTreeEndOffs = 0;
292
                    for (int index = rowStartOffset; index <= rowEndOffset; index++) {
293
                        TreePath path = treeUtils.pathFor(index);
294
                        Tree tree = path.getLeaf();
295
                        if (tree.equals(lastTree)) continue;
296
297
                        long startOffs = positions.getStartPosition(compUnit, tree);
298
                        long endOffs = positions.getEndPosition(compUnit, tree);
299
                        if (outerTree == null && startOffs < rowStartOffset) {
300
                            outerTree = tree;
301
                            outerTreePath = path;
302
                            lastTree = tree;
303
                            continue;
304
                        }
305
306
                        if (startOffs >= rowStartOffset) {
307
                            if (execTree == null ||
308
                                    (execTreeStartOffs >= startOffs && execTreeEndOffs <= endOffs)) {
309
                                execTree = tree;
310
                                execTreePath = path;
311
                                execTreeStartOffs = startOffs;
312
                                execTreeEndOffs = endOffs;
313
                                    if (execTree instanceof VariableTree && isBreakable(execTreePath)) {
314
                                    break;
315
                                }
316
                            } else if (startOffs > execTreeEndOffs) {
317
                                if (isBreakable(execTreePath)) {
318
                                    break;
319
                                } else {
320
                                    execTree = tree;
321
                                    execTreePath = path;
322
                                    execTreeStartOffs = startOffs;
323
                                    execTreeEndOffs = endOffs;
324
                                    if (execTree instanceof VariableTree && isBreakable(execTreePath)) {
325
                                        break;
326
                                    }
327
                                }
328
                            }
329
                        } // if
330
                        lastTree = tree;
331
                    }
332
333
                    if (execTree == null || !isBreakable(execTreePath)) {
334
                        if (outerTree != null && isBreakable(outerTreePath)) {
335
                            long offs = positions.getStartPosition(compUnit, outerTree);
336
                            result[0] = Utilities.getLineOffset(doc, (int)offs) + 1;
337
                        } else {
338
                            if (outerTree != null && outerTree instanceof BlockTree) {
339
                                Tree pTree = outerTreePath.getParentPath().getLeaf();
340
                                if (pTree instanceof MethodTree) {
341
                                    long endOffs = positions.getEndPosition(compUnit, pTree);
342
                                    if (endOffs <= rowEndOffset) {
343
                                        return; // i.e. result[0] is original lineNumber - allow toggle breakpoint at method end
344
                                    }
345
                                }
346
                            }
347
                            result[0] = -1; // do not allow to add a breakpoint
348
                        }
349
                    }
350
                }
351
            }, true);
352
            if (!scanFinished.isDone()) {
353
                if (java.awt.EventQueue.isDispatchThread()) {
354
                    return lineNumber;
355
                } else {
356
                    try {
357
                        scanFinished.get();
358
                    } catch (InterruptedException iex) {
359
                        return lineNumber;
360
                    } catch (java.util.concurrent.ExecutionException eex) {
361
                        ErrorManager.getDefault().notify(eex);
362
                        return lineNumber;
363
                    }
364
                }
365
            }
366
        } catch (IOException ioex) {
367
            ErrorManager.getDefault().notify(ioex);
368
            return lineNumber;
369
        }
370
        return result[0];
371
    }
372
373
    private static boolean isBreakable(TreePath path) {
374
        Tree tree = path.getLeaf();
375
        switch (tree.getKind()) {
376
            case BLOCK:
377
            case ANNOTATION_TYPE:
378
            case CLASS:
379
            case ENUM:
380
            case INTERFACE:
381
            case COMPILATION_UNIT:
382
            case IMPORT:
383
            case MODIFIERS:
384
            case EMPTY_STATEMENT:
385
                return false;
386
        }
387
        while (path != null) {
388
            tree = path.getLeaf();
389
            Tree.Kind kind = tree.getKind();
390
            if (kind == Tree.Kind.IMPORT) return false;
391
            if (kind == Tree.Kind.VARIABLE) {
392
                VariableTree varTree = (VariableTree)tree;
393
                if (varTree.getInitializer() == null) {
394
                    return false;
395
                }
396
            }
397
            path = path.getParentPath();
398
        }
399
        return true;
400
    }
401
402
    static LineBreakpoint findBreakpoint (String url, int lineNumber) {
403
        Breakpoint[] breakpoints = DebuggerManager.getDebuggerManager().getBreakpoints();
404
        for (int i = 0; i < breakpoints.length; i++) {
405
            if (!(breakpoints[i] instanceof LineBreakpoint)) {
406
                continue;
407
            }
408
            LineBreakpoint lb = (LineBreakpoint) breakpoints[i];
409
            if (!lb.getURL ().equals (url)) continue;
410
            if (lb.getLineNumber() == lineNumber) {
411
                return lb;
412
            }
413
        }
414
        return null;
415
    }
416
    
417
}
160
}

Return to bug 218933