Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: e796716cb1d7062eebf2376a8e4fbef78a695b23 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
/*******************************************************************************
 * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Wind River Systems - initial API and implementation
 *******************************************************************************/
package org.eclipse.tm.internal.tcf.debug.model;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IMarkerDelta;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IBreakpointListener;
import org.eclipse.debug.core.IBreakpointManager;
import org.eclipse.debug.core.IBreakpointManagerListener;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.tm.internal.tcf.debug.Activator;
import org.eclipse.tm.tcf.protocol.IChannel;
import org.eclipse.tm.tcf.protocol.IToken;
import org.eclipse.tm.tcf.protocol.Protocol;
import org.eclipse.tm.tcf.services.IBreakpoints;

/**
 * TCFBreakpointsModel class handles breakpoints for all active TCF launches.
 * It downloads initial set of breakpoint data when launch is activated,
 * listens for Eclipse breakpoint manager events and propagates breakpoint changes to TCF targets.
 */
public class TCFBreakpointsModel {

    public static final String
        CDATA_CLIENT_ID    = "ClientID",
        CDATA_TYPE         = "Type",
        CDATA_FILE         = "File",
        CDATA_MARKER       = "Marker";

    public static final String
        ATTR_ID            = ITCFConstants.ID_TCF_DEBUG_MODEL + '.' + IBreakpoints.PROP_ID,
        ATTR_STATUS        = ITCFConstants.ID_TCF_DEBUG_MODEL + '.' + "Status",
        ATTR_INSTALL_COUNT = "org.eclipse.cdt.debug.core.installCount",
        ATTR_ADDRESS       = "org.eclipse.cdt.debug.core.address",
        ATTR_FUNCTION      = "org.eclipse.cdt.debug.core.function",
        ATTR_EXPRESSION    = "org.eclipse.cdt.debug.core.expression",
        ATTR_READ          = "org.eclipse.cdt.debug.core.read",
        ATTR_WRITE         = "org.eclipse.cdt.debug.core.write",
        ATTR_SIZE          = "org.eclipse.cdt.debug.core.range",
        ATTR_FILE          = "org.eclipse.cdt.debug.core.sourceHandle",
        ATTR_CONDITION     = "org.eclipse.cdt.debug.core.condition",
        ATTR_IGNORE_COUNT  = "org.eclipse.cdt.debug.core.ignoreCount",
        ATTR_CONTEXTNAMES  = ITCFConstants.ID_TCF_DEBUG_MODEL + '.' + IBreakpoints.PROP_CONTEXTNAMES,
        ATTR_CONTEXTIDS    = ITCFConstants.ID_TCF_DEBUG_MODEL + '.' + IBreakpoints.PROP_CONTEXTIDS,
        ATTR_EXE_PATHS     = ITCFConstants.ID_TCF_DEBUG_MODEL + '.' + IBreakpoints.PROP_EXECUTABLEPATHS,
        ATTR_STOP_GROUP    = ITCFConstants.ID_TCF_DEBUG_MODEL + '.' + IBreakpoints.PROP_STOP_GROUP;

    private final IBreakpointManager bp_manager = DebugPlugin.getDefault().getBreakpointManager();
    private final HashMap<IChannel,Map<String,Object>> channels = new HashMap<IChannel,Map<String,Object>>();
    private final HashMap<String,IBreakpoint> id2bp = new HashMap<String,IBreakpoint>();
    private final String client_id = UUID.randomUUID().toString();

    private abstract class BreakpointUpdate implements Runnable {

        private final IBreakpoint breakpoint;
        private boolean removed;
        protected final Map<String,Object> marker_attrs;
        protected final boolean is_local;
        protected final String marker_id;
        private final String marker_file;
        private final String marker_type;

        IBreakpoints service;
        IBreakpoints.DoneCommand done;
        Map<String,Object> tcf_attrs;

        BreakpointUpdate(IBreakpoint breakpoint, boolean removed) throws CoreException, IOException {
            this.breakpoint = breakpoint;
            this.removed = removed;
            IMarker marker = breakpoint.getMarker();
            marker_attrs = new HashMap<String,Object>(marker.getAttributes());
            is_local = isLocal(marker);
            marker_file = getFilePath(breakpoint.getMarker().getResource());
            marker_type = breakpoint.getMarker().getType();
            marker_id = getBreakpointID(breakpoint);
        }

        synchronized void exec() throws InterruptedException {
            assert !Protocol.isDispatchThread();
            if (marker_id != null) {
                Protocol.invokeLater(this);
                wait();
            }
        }

        public void run() {
            if (disposed) return;
            if (removed) id2bp.remove(marker_id);
            else id2bp.put(marker_id, breakpoint);
            if (is_local) {
                for (final IChannel channel : channels.keySet()) {
                    tcf_attrs = toBreakpointAttributes(channel, marker_id, marker_file, marker_type, marker_attrs);
                    service = channel.getRemoteService(IBreakpoints.class);
                    if (!isSupported(channel, breakpoint)) continue;
                    done = new IBreakpoints.DoneCommand() {
                        public void doneCommand(IToken token, Exception error) {
                            if (error != null) channel.terminate(error);
                        }
                    };
                    update();
                }
            }
            Protocol.sync(new Runnable() {
                public void run() {
                    synchronized (BreakpointUpdate.this) {
                        BreakpointUpdate.this.notify();
                    }
                }
            });
        };

        abstract void update();
    }

    private final IBreakpointListener breakpoint_listener = new IBreakpointListener() {

        public void breakpointAdded(final IBreakpoint breakpoint) {
            try {
                new BreakpointUpdate(breakpoint, false) {
                    @Override
                    void update() {
                        service.add(tcf_attrs, done);
                    }
                }.exec();
            }
            catch (Throwable x) {
                Activator.log("Unhandled exception in breakpoint listener", x);
            }
        }

        private Set<String> calcMarkerDeltaKeys(IMarker marker, IMarkerDelta delta) throws CoreException {
            Set<String> keys = new HashSet<String>();
            if (delta == null) return keys;
            Map<String,Object> m0 = delta.getAttributes();
            Map<String,Object> m1 = marker.getAttributes();
            if (m0 != null) keys.addAll(m0.keySet());
            if (m1 != null) keys.addAll(m1.keySet());
            for (Iterator<String> i = keys.iterator(); i.hasNext();) {
                String key = i.next();
                Object v0 = m0 != null ? m0.get(key) : null;
                Object v1 = m1 != null ? m1.get(key) : null;
                if (v0 instanceof String && ((String)v0).length() == 0) v0 = null;
                if (v1 instanceof String && ((String)v1).length() == 0) v1 = null;
                if (v0 instanceof Boolean && !((Boolean)v0).booleanValue()) v0 = null;
                if (v1 instanceof Boolean && !((Boolean)v1).booleanValue()) v1 = null;
                if ((v0 == null) != (v1 == null)) continue;
                if (v0 != null && !v0.equals(v1)) continue;
                i.remove();
            }
            keys.remove(ATTR_INSTALL_COUNT);
            keys.remove(ATTR_STATUS);
            return keys;
        }

        public void breakpointChanged(final IBreakpoint breakpoint, IMarkerDelta delta) {
            try {
                final Set<String> s = calcMarkerDeltaKeys(breakpoint.getMarker(), delta);
                if (s.isEmpty()) return;
                new BreakpointUpdate(breakpoint, false) {
                    @Override
                    void update() {
                        if (s.size() == 1 && s.contains(IBreakpoint.ENABLED)) {
                            Boolean enabled = (Boolean)tcf_attrs.get(IBreakpoints.PROP_ENABLED);
                            if (enabled == null || !enabled.booleanValue()) {
                                service.disable(new String[]{ marker_id }, done);
                            }
                            else {
                                service.enable(new String[]{ marker_id }, done);
                            }
                        }
                        else {
                            service.change(tcf_attrs, done);
                        }
                    }
                }.exec();
            }
            catch (Throwable x) {
                Activator.log("Unhandled exception in breakpoint listener", x);
            }
        }

        public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
            try {
                new BreakpointUpdate(breakpoint, true) {
                    @Override
                    void update() {
                        service.remove(new String[]{ marker_id }, done);
                    }
                }.exec();
            }
            catch (Throwable x) {
                Activator.log("Unhandled exception in breakpoint listener", x);
            }
        }
    };

    private final IBreakpointManagerListener manager_listener = new IBreakpointManagerListener() {

        public void breakpointManagerEnablementChanged(final boolean enabled) {
            try {
                IBreakpoint[] arr = bp_manager.getBreakpoints();
                if (arr == null || arr.length == 0) return;
                final Map<String,IBreakpoint> map = new HashMap<String,IBreakpoint>();
                for (int i = 0; i < arr.length; i++) {
                    IMarker marker = arr[i].getMarker();
                    Boolean b = marker.getAttribute(IBreakpoint.ENABLED, Boolean.FALSE);
                    if (!b.booleanValue()) continue;
                    String id = getBreakpointID(arr[i]);
                    if (id == null) continue;
                    map.put(id, arr[i]);
                }
                if (map.isEmpty()) return;
                Runnable r = new Runnable() {
                    public void run() {
                        if (disposed) return;
                        for (final IChannel channel : channels.keySet()) {
                            IBreakpoints service = channel.getRemoteService(IBreakpoints.class);
                            Set<String> ids = new HashSet<String>();
                            for (String id : map.keySet()) {
                                IBreakpoint bp = map.get(id);
                                if (isSupported(channel, bp)) ids.add(id);
                            }
                            IBreakpoints.DoneCommand done = new IBreakpoints.DoneCommand() {
                                public void doneCommand(IToken token, Exception error) {
                                    if (error != null) channel.terminate(error);
                                }
                            };
                            if (enabled) {
                                service.enable(ids.toArray(new String[ids.size()]), done);
                            }
                            else {
                                service.disable(ids.toArray(new String[ids.size()]), done);
                            }
                        }
                        Protocol.sync(new Runnable() {
                            public void run() {
                                synchronized (map) {
                                    map.notify();
                                }
                            }
                        });
                    }
                };
                synchronized (map) {
                    assert !Protocol.isDispatchThread();
                    Protocol.invokeLater(r);
                    map.wait();
                }
            }
            catch (Throwable x) {
                Activator.log("Unhandled exception in breakpoint listener", x);
            }
        }
    };

    private boolean disposed;

    public static TCFBreakpointsModel getBreakpointsModel() {
        return Activator.getBreakpointsModel();
    }

    public void dispose() {
        bp_manager.removeBreakpointListener(breakpoint_listener);
        bp_manager.removeBreakpointManagerListener(manager_listener);
        channels.clear();
        disposed = true;
    }

    public boolean isSupported(IChannel channel, IBreakpoint bp) {
        // TODO: implement per-channel breakpoint filtering
        return true;
    }

    /**
     * Get TCF ID of a breakpoint.
     * @param bp - IBreakpoint object.
     * @return TCF ID of the breakpoint.
     * @throws CoreException
     */
    public static String getBreakpointID(IBreakpoint bp) throws CoreException {
        IMarker marker = bp.getMarker();
        String id = (String)marker.getAttributes().get(ATTR_ID);
        if (id != null) return id;
        id = marker.getResource().getLocationURI().toString();
        if (id == null) return null;
        return id + ':' + marker.getId();
    }

    /**
     * Get IBreakpoint for given TCF breakpoint ID.
     * The mapping works only for breakpoints that were sent to (or received from) a debug target.
     * It can be used to map target responses to IBreakpoint objects.
     * @param id - TCF breakpoint ID.
     * @return IBreakpoint object associated with the ID, or null.
     */
    public IBreakpoint getBreakpoint(String id) {
        assert Protocol.isDispatchThread();
        return id2bp.get(id);
    }

    /**
     * Check breakpoint ownership.
     * @param properties - breakpoint properties as reported by TCF Breakpoints service.
     * @return true if the breakpoint is owned by local instance of Eclipse.
     * Return false if the properties represent a breakpoint created by some other TCF client.
     */
    @SuppressWarnings("unchecked")
    public boolean isLocal(Map<String,Object> properties) {
        if (properties == null) return false;
        Map<String,Object> client_data = (Map<String,Object>)properties.get(IBreakpoints.PROP_CLIENT_DATA);
        if (client_data == null) return false;
        String id = (String)client_data.get(TCFBreakpointsModel.CDATA_CLIENT_ID);
        return client_id.equals(id);
    }

    /**
     * Check breakpoint marker ownership.
     * @param marker - breakpoint marker.
     * @return true if the marker is owned by local instance of Eclipse.
     * Return false if the marker represents a breakpoint created by some other TCF client.
     */
    public boolean isLocal(IMarker marker) {
        try {
            Map<String,Object> marker_attrs = marker.getAttributes();
            if (marker_attrs == null) return false;
            return !Boolean.FALSE.equals(marker_attrs.get(IBreakpoint.PERSISTED));
        }
        catch (CoreException e) {
            return false;
        }
    }

    /**
     * Download breakpoint info to a target.
     * This is supposed to be done once after a channel is opened.
     * @param channel - TCF communication channel.
     * @param done - client callback.
     * @throws IOException
     * @throws CoreException
     */
    @SuppressWarnings("unchecked")
    public void downloadBreakpoints(final IChannel channel, final Runnable done) throws IOException, CoreException {
        assert !disposed;
        assert Protocol.isDispatchThread();
        final IBreakpoints service = channel.getRemoteService(IBreakpoints.class);
        if (service != null) {
            service.getCapabilities(null, new IBreakpoints.DoneGetCapabilities() {
                public void doneGetCapabilities(IToken token, Exception error, Map<String,Object> capabilities) {
                    if (channel.getState() != IChannel.STATE_OPEN) {
                        Protocol.invokeLater(done);
                        return;
                    }
                    if (channels.isEmpty()) {
                        bp_manager.addBreakpointListener(breakpoint_listener);
                        bp_manager.addBreakpointManagerListener(manager_listener);
                    }
                    channel.addChannelListener(new IChannel.IChannelListener() {
                        public void congestionLevel(int level) {
                        }
                        public void onChannelClosed(Throwable error) {
                            if (disposed) return;
                            channels.remove(channel);
                            if (channels.isEmpty()) {
                                bp_manager.removeBreakpointListener(breakpoint_listener);
                                bp_manager.removeBreakpointManagerListener(manager_listener);
                                id2bp.clear();
                            }
                        }
                        public void onChannelOpened() {
                        }
                    });
                    channels.put(channel, capabilities);
                    IBreakpoint[] arr = bp_manager.getBreakpoints();
                    if (arr != null && arr.length > 0) {
                        List<Map<String,Object>> bps = new ArrayList<Map<String,Object>>(arr.length);
                        for (int i = 0; i < arr.length; i++) {
                            try {
                                if (!isSupported(channel, arr[i])) continue;
                                String id = getBreakpointID(arr[i]);
                                if (id == null) continue;
                                if (!arr[i].isPersisted()) continue;
                                IMarker marker = arr[i].getMarker();
                                String file = getFilePath(marker.getResource());
                                bps.add(toBreakpointAttributes(channel, id, file, marker.getType(), marker.getAttributes()));
                                id2bp.put(id, arr[i]);
                            }
                            catch (Exception x) {
                                Activator.log("Cannot get breakpoint attributes", x);
                            }
                        }
                        if (!bps.isEmpty()) {
                            Map<String, Object>[] bp_arr = (Map<String,Object>[])bps.toArray(new Map[bps.size()]);
                            service.set(bp_arr, new IBreakpoints.DoneCommand() {
                                public void doneCommand(IToken token, Exception error) {
                                    if (error == null) done.run();
                                    else channel.terminate(error);
                                }
                            });
                            return;
                        }
                    }
                    Protocol.invokeLater(done);
                }
            });
        }
    }

    private String getFilePath(IResource resource) throws IOException {
        if (resource == ResourcesPlugin.getWorkspace().getRoot()) return null;
        IPath p = resource.getRawLocation();
        if (p == null) return null;
        return p.toFile().getCanonicalPath();
    }

    /**
     * Translate TCF breakpoint properties to Eclipse breakpoint marker attributes.
     * @param p - TCF breakpoint properties.
     * @return Eclipse marker attributes.
     */
    @SuppressWarnings("unchecked")
    public Map<String,Object> toMarkerAttributes(Map<String,Object> p) {
        assert !disposed;
        assert Protocol.isDispatchThread();
        Map<String,Object> client_data = (Map<String,Object>)p.get(IBreakpoints.PROP_CLIENT_DATA);
        if (client_data != null) {
            Map<String,Object> m = (Map<String,Object>)client_data.get(CDATA_MARKER);
            if (m != null) return m;
        }
        Map<String,Object> m = new HashMap<String,Object>();
        for (Map.Entry<String,Object> e : p.entrySet()) {
            String key = e.getKey();
            Object val = e.getValue();
            if (key.equals(IBreakpoints.PROP_ENABLED)) continue;
            if (key.equals(IBreakpoints.PROP_FILE)) continue;
            if (key.equals(IBreakpoints.PROP_LINE)) continue;
            if (key.equals(IBreakpoints.PROP_COLUMN)) continue;
            if (key.equals(IBreakpoints.PROP_LOCATION)) continue;
            if (key.equals(IBreakpoints.PROP_ACCESSMODE)) continue;
            if (key.equals(IBreakpoints.PROP_SIZE)) continue;
            if (key.equals(IBreakpoints.PROP_CONDITION)) continue;
            if (val instanceof String[]) {
                StringBuffer bf = new StringBuffer();
                for (String s : (String[])val) {
                    if (bf.length() > 0) bf.append(',');
                    bf.append(s);
                }
                if (bf.length() == 0) continue;
                val = bf.toString();
            }
            else if (val instanceof Collection) {
                StringBuffer bf = new StringBuffer();
                for (String s : (Collection<String>)val) {
                    if (bf.length() > 0) bf.append(',');
                    bf.append(s);
                }
                if (bf.length() == 0) continue;
                val = bf.toString();
            }
            m.put(ITCFConstants.ID_TCF_DEBUG_MODEL + '.' + key, val);
        }
        Boolean enabled = (Boolean)p.get(IBreakpoints.PROP_ENABLED);
        if (enabled == null) m.put(IBreakpoint.ENABLED, Boolean.FALSE);
        else m.put(IBreakpoint.ENABLED, enabled);
        String location = (String)p.get(IBreakpoints.PROP_LOCATION);
        if (location != null && location.length() > 0) {
            int access_mode = IBreakpoints.ACCESSMODE_EXECUTE;
            Number access_mode_num = (Number)p.get(IBreakpoints.PROP_ACCESSMODE);
            if (access_mode_num != null) access_mode = access_mode_num.intValue();
            if ((access_mode & IBreakpoints.ACCESSMODE_EXECUTE) != 0) {
                if (Character.isDigit(location.charAt(0))) {
                    m.put(ATTR_ADDRESS, location);
                }
                else {
                    m.put(ATTR_FUNCTION, location);
                }
            }
            else {
                m.put(ATTR_EXPRESSION, location.replaceFirst("^&\\((.+)\\)$", "$1"));
                m.put(ATTR_READ, (access_mode & IBreakpoints.ACCESSMODE_READ) != 0);
                m.put(ATTR_WRITE, (access_mode & IBreakpoints.ACCESSMODE_WRITE) != 0);
            }
            Number size_num = (Number)p.get(IBreakpoints.PROP_SIZE);
            if (size_num != null) m.put(ATTR_SIZE, size_num.toString());
        }
        m.put(IBreakpoint.REGISTERED, Boolean.TRUE);
        m.put(IBreakpoint.PERSISTED, Boolean.TRUE);
        m.put(IBreakpoint.ID, ITCFConstants.ID_TCF_DEBUG_MODEL);
        String msg = "";
        if (location != null) msg += location;
        m.put(IMarker.MESSAGE, "Breakpoint: " + msg);
        String file = (String)p.get(IBreakpoints.PROP_FILE);
        if (file != null && file.length() > 0) {
            m.put(ATTR_FILE, file);
        }
        Number line = (Number)p.get(IBreakpoints.PROP_LINE);
        if (line != null) {
            m.put(IMarker.LINE_NUMBER, new Integer(line.intValue()));
            Number column = (Number)p.get(IBreakpoints.PROP_COLUMN);
            if (column != null) {
                m.put(IMarker.CHAR_START, new Integer(column.intValue()));
                m.put(IMarker.CHAR_END, new Integer(column.intValue() + 1));
            }
        }
        String condition = (String)p.get(IBreakpoints.PROP_CONDITION);
        if (condition != null && condition.length() > 0) m.put(ATTR_CONDITION, condition);
        Number ignore_count = (Number)p.get(IBreakpoints.PROP_IGNORECOUNT);
        if (ignore_count != null) m.put(ATTR_IGNORE_COUNT, ignore_count);
        return m;
    }

    /**
     * Translate Eclipse breakpoint marker attributes to TCF breakpoint properties.
     * @param channel - TCF communication channel.
     * @param id - breakpoint ID.
     * @param file - the maker file or null.
     * @param type - the marker type.
     * @param p - the marker attributes.
     * @return TCF breakpoint properties.
     */
    public Map<String,Object> toBreakpointAttributes(IChannel channel, String id, String file, String type, Map<String,Object> p) {
        assert !disposed;
        assert Protocol.isDispatchThread();
        Map<String,Object> m = new HashMap<String,Object>();
        Map<String,Object> capabilities = channels.get(channel);
        Map<String,Object> client_data = null;
        if (capabilities != null) {
            Object obj = capabilities.get(IBreakpoints.CAPABILITY_CLIENT_DATA);
            if (obj instanceof Boolean && ((Boolean)obj).booleanValue()) client_data = new HashMap<String,Object>();
        }
        m.put(IBreakpoints.PROP_ID, id);
        if (client_data != null) {
            m.put(IBreakpoints.PROP_CLIENT_DATA, client_data);
            client_data.put(CDATA_CLIENT_ID, client_id);
            if (type != null) client_data.put(CDATA_TYPE, type);
            if (file != null) client_data.put(CDATA_FILE, file);
            client_data.put(CDATA_MARKER, p);
        }
        for (Map.Entry<String,Object> e : p.entrySet()) {
            String key = e.getKey();
            Object val = e.getValue();
            if (key.startsWith(ITCFConstants.ID_TCF_DEBUG_MODEL)) {
                String tcf_key = key.substring(ITCFConstants.ID_TCF_DEBUG_MODEL.length() + 1);
                if (IBreakpoints.PROP_CONTEXTIDS.equals(tcf_key)) {
                    val = filterContextIds(channel, ((String)val).split(",\\s*"));
                }
                else if (IBreakpoints.PROP_CONTEXTNAMES.equals(tcf_key) ||
                        IBreakpoints.PROP_STOP_GROUP.equals(tcf_key) ||
                        IBreakpoints.PROP_EXECUTABLEPATHS.equals(tcf_key)) {
                    val = ((String)val).split(",\\s*");
                }
                m.put(tcf_key, val);
            }
        }
        Boolean enabled = (Boolean)p.get(IBreakpoint.ENABLED);
        if (enabled != null && enabled.booleanValue() && bp_manager.isEnabled()) {
            m.put(IBreakpoints.PROP_ENABLED, enabled);
        }
        if (file == null) file = (String)p.get(ATTR_FILE);
        if (file != null && file.length() > 0) {
            String name = file;
            boolean file_mapping = false;
            if (capabilities != null) {
                Object obj = capabilities.get(IBreakpoints.CAPABILITY_FILE_MAPPING);
                if (obj instanceof Boolean) file_mapping = ((Boolean)obj).booleanValue();
            }
            if (!file_mapping) {
                int i = file.lastIndexOf('/');
                int j = file.lastIndexOf('\\');
                if (i > j) name = file.substring(i + 1);
                else if (i < j) name = file.substring(j + 1);
            }
            m.put(IBreakpoints.PROP_FILE, name);
            Integer line = (Integer)p.get(IMarker.LINE_NUMBER);
            if (line != null) {
                m.put(IBreakpoints.PROP_LINE, new Integer(line.intValue()));
                Integer column = (Integer)p.get(IMarker.CHAR_START);
                if (column != null) m.put(IBreakpoints.PROP_COLUMN, column);
            }
        }
        if (p.get(ATTR_EXPRESSION) != null) {
            String expr = (String)p.get(ATTR_EXPRESSION);
            if (expr != null && expr.length() != 0) {
                boolean writeAccess = Boolean.TRUE.equals(p.get(ATTR_WRITE));
                boolean readAccess = Boolean.TRUE.equals(p.get(ATTR_READ));
                int accessMode = 0;
                if (readAccess) accessMode |= IBreakpoints.ACCESSMODE_READ;
                if (writeAccess) accessMode |= IBreakpoints.ACCESSMODE_WRITE;
                m.put(IBreakpoints.PROP_ACCESSMODE, Integer.valueOf(accessMode));
                Object range = p.get(ATTR_SIZE);
                if (range != null) {
                    int size = Integer.parseInt(range.toString());
                    if (size > 0) m.put(IBreakpoints.PROP_SIZE, size);
                }
                if (!Character.isDigit(expr.charAt(0))) {
                    expr = "&(" + expr + ')';
                }
                m.put(IBreakpoints.PROP_LOCATION, expr);
            }
        }
        else if (p.get(ATTR_FUNCTION) != null) {
            String expr = (String)p.get(ATTR_FUNCTION);
            if (expr != null && expr.length() != 0) m.put(IBreakpoints.PROP_LOCATION, expr);
        }
        else if (file == null) {
            String address = (String)p.get(ATTR_ADDRESS);
            if (address != null && address.length() > 0) m.put(IBreakpoints.PROP_LOCATION, address);
        }
        String condition = (String)p.get(ATTR_CONDITION);
        if (condition != null && condition.length() > 0) m.put(IBreakpoints.PROP_CONDITION, condition);
        Number ignore_count = (Number)p.get(ATTR_IGNORE_COUNT);
        if (ignore_count != null && ignore_count.intValue() > 0) m.put(IBreakpoints.PROP_IGNORECOUNT, ignore_count);
        return m;
    }

    /**
     * Filter given array of scope IDs of the form sessionId/contextId
     * to those applicable to the given channel.
     */
    private String[] filterContextIds(IChannel channel, String[] scopeIds) {
        String sessionId = getSessionId(channel);
        List<String> contextIds = new ArrayList<String>();
        for (String scopeId : scopeIds) {
            if (scopeId.length() == 0) continue;
            int slash = scopeId.indexOf('/');
            if (slash < 0) {
                contextIds.add(scopeId);
            }
            else if (sessionId != null && sessionId.equals(scopeId.substring(0, slash))) {
                contextIds.add(scopeId.substring(slash+1));
            }
        }
        return (String[]) contextIds.toArray(new String[contextIds.size()]);
    }

    /**
     * @return launch config name for given channel or <code>null</code>
     */
    private String getSessionId(IChannel channel) {
        ILaunch[] launches = DebugPlugin.getDefault().getLaunchManager().getLaunches();
        for (ILaunch launch : launches) {
            if (launch instanceof TCFLaunch) {
                if (channel == ((TCFLaunch) launch).getChannel()) {
                    ILaunchConfiguration lc = launch.getLaunchConfiguration();
                    return lc != null ? lc.getName() : null;
                }
            }
        }
        return null;
    }
}

Back to the top