~ubuntu-branches/ubuntu/trusty/eclipse-linuxtools/trusty

« back to all changes in this revision

Viewing changes to systemtap/org.eclipse.linuxtools.callgraph/src/org/eclipse/linuxtools/internal/callgraph/StapGraph.java

  • Committer: Package Import Robot
  • Author(s): Jakub Adam
  • Date: 2012-06-29 12:07:30 UTC
  • Revision ID: package-import@ubuntu.com-20120629120730-bfri1xys1i71dpn6
Tags: upstream-1.0.0
ImportĀ upstreamĀ versionĀ 1.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*******************************************************************************
 
2
 * Copyright (c) 2009 Red Hat, Inc.
 
3
 * All rights reserved. This program and the accompanying materials
 
4
 * are made available under the terms of the Eclipse Public License v1.0
 
5
 * which accompanies this distribution, and is available at
 
6
 * http://www.eclipse.org/legal/epl-v10.html
 
7
 * 
 
8
 * Contributors:
 
9
 *     Red Hat - initial API and implementation
 
10
 *******************************************************************************/
 
11
package org.eclipse.linuxtools.internal.callgraph;
 
12
 
 
13
import java.text.NumberFormat;
 
14
import java.util.ArrayList;
 
15
import java.util.Comparator;
 
16
import java.util.HashMap;
 
17
import java.util.List;
 
18
import java.util.Locale;
 
19
import java.util.TreeSet;
 
20
import java.util.Map.Entry;
 
21
 
 
22
import org.eclipse.cdt.core.model.ICProject;
 
23
import org.eclipse.core.runtime.Status;
 
24
import org.eclipse.draw2d.Animation;
 
25
import org.eclipse.draw2d.Label;
 
26
import org.eclipse.jface.viewers.IDoubleClickListener;
 
27
import org.eclipse.jface.viewers.StructuredSelection;
 
28
import org.eclipse.jface.viewers.TreeViewer;
 
29
import org.eclipse.linuxtools.internal.callgraph.core.MP;
 
30
import org.eclipse.linuxtools.internal.callgraph.graphlisteners.Projectionist;
 
31
import org.eclipse.linuxtools.internal.callgraph.graphlisteners.StapGraphKeyListener;
 
32
import org.eclipse.linuxtools.internal.callgraph.graphlisteners.StapGraphMouseListener;
 
33
import org.eclipse.linuxtools.internal.callgraph.graphlisteners.StapGraphMouseWheelListener;
 
34
import org.eclipse.linuxtools.internal.callgraph.treeviewer.StapTreeContentProvider;
 
35
import org.eclipse.linuxtools.internal.callgraph.treeviewer.StapTreeDoubleClickListener;
 
36
import org.eclipse.linuxtools.internal.callgraph.treeviewer.StapTreeLabelProvider;
 
37
import org.eclipse.linuxtools.internal.callgraph.treeviewer.StapTreeListener;
 
38
import org.eclipse.swt.SWT;
 
39
import org.eclipse.swt.graphics.Color;
 
40
import org.eclipse.swt.layout.GridData;
 
41
import org.eclipse.swt.widgets.Canvas;
 
42
import org.eclipse.swt.widgets.Composite;
 
43
import org.eclipse.swt.widgets.Display;
 
44
import org.eclipse.zest.core.widgets.Graph;
 
45
import org.eclipse.zest.core.widgets.GraphNode;
 
46
import org.eclipse.zest.layouts.LayoutStyles;
 
47
 
 
48
 
 
49
public class StapGraph extends Graph {
 
50
 
 
51
        public static final String CONSTANT_TOP_NODE_NAME = Messages.getString("StapGraph.StartNode"); //$NON-NLS-1$
 
52
        public static final int CONSTANT_HORIZONTAL_SPACING = 50; 
 
53
        public static final int CONSTANT_DRAWMODE_LEVEL = 0;
 
54
        public static final int CONSTANT_DRAWMODE_RADIAL = 1;
 
55
        public static final int CONSTANT_DRAWMODE_TREE = 2;
 
56
        public static final int CONSTANT_DRAWMODE_AGGREGATE = 3;
 
57
        public static final int CONSTANT_ANIMATION_SLOW = 1;
 
58
        public static final int CONSTANT_ANIMATION_FASTER = 2;
 
59
        public static final int CONSTANT_ANIMATION_FASTEST = 3;
 
60
        public static final int CONSTANT_MAX_NUMBER_OF_SIBLINGS = 3;
 
61
        public static final int CONSTANT_MAX_NUMBER_OF_RADIAL_SIBLINGS = 15;
 
62
        public static final int CONSTANT_VERTICAL_INCREMENT = 50;
 
63
        public static final int CONSTANT_HORIZONTAL_SPACING_FOR_LEVEL = 150;
 
64
        public static final Color CONSTANT_HAS_PARENT = new Color(Display.getCurrent(), 240, 200,
 
65
                        200);
 
66
        public static final Color CONSTANT_HAS_CHILDREN = new Color(Display.getCurrent(), 200,
 
67
                        250, 200);
 
68
        public static final Color CONSTANT_MARKED = new Color(Display.getCurrent(), 210, 112, 214);
 
69
        private int ANIMATION_TIME = 500;
 
70
        //Draw level management
 
71
        private int topLevelToDraw;
 
72
        private int bottomLevelToDraw;
 
73
        private int topLevelOnScreen;
 
74
        public int levelBuffer = 30;
 
75
        private int maxNodes = 150;
 
76
        private Projectionist proj;
 
77
 
 
78
 
 
79
 
 
80
        private int lowestLevelOfNodesAdded;
 
81
        public HashMap<Integer, List<Integer>> levels;                  //Level number, list of node ids
 
82
        
 
83
 
 
84
        //Node management
 
85
        private int idOfLastNode;       
 
86
        private int idOfLastCollapsedNode;
 
87
        public HashMap<Integer, StapNode> nodeMap;                              // HashMap of current nodes
 
88
        public HashMap<Integer, StapData> nodeDataMap;                  // HashMap of all data
 
89
        //The negative side of nodeDataMap is collapsed, the positive side is uncollapsed
 
90
        
 
91
        public List<GraphNode> aggregateNodes;
 
92
        public HashMap<String, Long> aggregateTime;
 
93
        public HashMap<String, Integer> aggregateCount;
 
94
        private HashMap <Integer, Integer> collapsedLevelSize;
 
95
        public List<Integer> markedNodes;
 
96
        public List<Integer> markedCollapsedNodes;
 
97
        
 
98
        //Modes
 
99
        private boolean collapse_mode;
 
100
        private int draw_mode;
 
101
        private int animation_mode;
 
102
 
 
103
        //Time
 
104
        private long totalTime;
 
105
        private long endTime;
 
106
        private long startTime;
 
107
 
 
108
        //The current center/top of the nodes list
 
109
        private int rootVisibleNodeNumber;
 
110
        
 
111
        //Special cases
 
112
        private boolean killInvalidFunctions;                                   //Toggle hiding of invalid functions                                    
 
113
        
 
114
        //Tree viewer
 
115
        private static TreeViewer treeViewer;
 
116
        private Composite treeComp;
 
117
        private static IDoubleClickListener treeDoubleListener;
 
118
        public HashMap<Integer, Integer> currentPositionInLevel;
 
119
        //(level, next horizontal position to place a node)
 
120
        
 
121
        //For cycling through marked nodes
 
122
        private int nextMarkedNode;
 
123
 
 
124
        //Zooming factor
 
125
        public double scale;
 
126
        
 
127
 
 
128
        
 
129
        private ArrayList<Integer> callOrderList;
 
130
        private int lastFunctionCalled;
 
131
        private int treeLevelFromRoot;
 
132
        private Canvas thumbCanvas;
 
133
        private ICProject project;
 
134
        private boolean threaded;
 
135
        private int counter;            //All purpose counting variable
 
136
        
 
137
        
 
138
        
 
139
        public StapGraphMouseListener getMouseListener() {
 
140
                return mListener;
 
141
        }
 
142
 
 
143
 
 
144
 
 
145
        public StapGraphMouseWheelListener getMouseWheelListener() {
 
146
                return mwListener;
 
147
        }
 
148
 
 
149
 
 
150
 
 
151
        public StapGraphKeyListener getKeyListener() {
 
152
                return kListener;
 
153
        }
 
154
 
 
155
 
 
156
 
 
157
        private StapGraphMouseListener mListener;
 
158
        private StapGraphMouseWheelListener mwListener;
 
159
        private StapGraphKeyListener kListener;
 
160
        
 
161
        private CallgraphView callgraphView;
 
162
        
 
163
        public StapGraph(Composite parent, int style, Composite treeComp, Canvas tCanvas,
 
164
                        CallgraphView callgraphView) {
 
165
                super(parent, style);
 
166
 
 
167
                //-------------Initialize variables
 
168
                thumbCanvas = tCanvas;
 
169
                nodeMap = new HashMap<Integer, StapNode>();
 
170
                levels = new HashMap<Integer, List<Integer>>();
 
171
                nodeDataMap = new HashMap<Integer, StapData>();
 
172
                aggregateTime = new HashMap<String, Long>();
 
173
                aggregateCount = new HashMap<String, Integer>();
 
174
                currentPositionInLevel = new HashMap<Integer, Integer>();
 
175
                collapsedLevelSize = new HashMap<Integer, Integer>();
 
176
                markedNodes = new ArrayList<Integer>();
 
177
                markedCollapsedNodes = new ArrayList<Integer>();
 
178
                animation_mode = 1;
 
179
                idOfLastNode = 0;
 
180
                rootVisibleNodeNumber=0;
 
181
                totalTime = 0;
 
182
                collapse_mode = false;
 
183
                killInvalidFunctions = true;
 
184
                nextMarkedNode = -1;
 
185
                scale = 1;
 
186
                treeLevelFromRoot = 0;
 
187
                idOfLastCollapsedNode = 0;
 
188
                this.callgraphView = callgraphView;
 
189
                
 
190
                this.treeComp = treeComp;
 
191
                if (treeViewer == null || treeViewer.getControl().isDisposed()) {
 
192
                        //Only create once
 
193
                        treeViewer = new TreeViewer(this.treeComp);
 
194
                        StapTreeListener stl = new StapTreeListener(treeViewer.getTree().getHorizontalBar());
 
195
                        treeViewer.addTreeListener(stl);
 
196
                }
 
197
                
 
198
                                
 
199
                //-------------Add listeners
 
200
                mListener = new StapGraphMouseListener(this);
 
201
                kListener = new StapGraphKeyListener(this);
 
202
                mwListener = new StapGraphMouseWheelListener(this);
 
203
                this.addMouseListener(mListener);               
 
204
                this.addKeyListener(kListener);
 
205
                this.addMouseWheelListener(mwListener);
 
206
        
 
207
        }
 
208
 
 
209
        
 
210
        
 
211
        
 
212
        /**
 
213
         * Initialize the treeviewer with data from the graph. If the treeviewer
 
214
         * has already been initialized (i.e. if it already has a content provider
 
215
         * set), we merely call treeViewer.refresh();
 
216
         */
 
217
        public void initializeTree() {
 
218
                if (treeViewer.getContentProvider() == null) {
 
219
                        StapTreeContentProvider scp = new StapTreeContentProvider();
 
220
                        treeViewer.setContentProvider(scp);
 
221
                } else {
 
222
                        ((StapTreeContentProvider) treeViewer.getContentProvider())
 
223
                                        .setGraph(this);
 
224
                        treeViewer.refresh();
 
225
                        return;
 
226
                }
 
227
                
 
228
                ((StapTreeContentProvider) treeViewer.getContentProvider()).setGraph(this);
 
229
                
 
230
                if (treeViewer.getLabelProvider() != null)
 
231
                        treeViewer.getLabelProvider().dispose();
 
232
                StapTreeLabelProvider prov = new StapTreeLabelProvider();
 
233
                treeViewer.setLabelProvider(prov);
 
234
 
 
235
                if (treeDoubleListener != null) {
 
236
                        treeViewer.removeDoubleClickListener(treeDoubleListener);
 
237
                }
 
238
                treeDoubleListener = new StapTreeDoubleClickListener(treeViewer, this);
 
239
                treeViewer.addDoubleClickListener(treeDoubleListener);
 
240
                
 
241
                treeViewer.setInput(getNodeData(getTopNode()));
 
242
                treeViewer.refresh();
 
243
        }
 
244
        
 
245
        
 
246
        
 
247
 
 
248
        /**
 
249
         * Convenience method to loadData with a message preset.
 
250
         * 
 
251
         * @param style
 
252
         * @param id
 
253
         * @param txt
 
254
         * @param time
 
255
         * @param called
 
256
         * @param caller
 
257
         * @return
 
258
         */
 
259
        public int loadData(int style, int id, String txt, long time, int called,
 
260
                        int caller, boolean isMarked, String message) {
 
261
                //-------------Invalid function catching
 
262
                // Catches some random C/C++ directive functions
 
263
                if (id < 10 && killInvalidFunctions) {
 
264
                        if (txt.contains(")")) { //$NON-NLS-1$
 
265
                                return -1;
 
266
                        } else if (txt.contains(".")) { //$NON-NLS-1$
 
267
                                return -1;
 
268
                        } else if (txt.contains("\"")) { //$NON-NLS-1$
 
269
                                return -1;
 
270
                        }
 
271
                } 
 
272
                
 
273
                //-------------Add node to appropriate map/list
 
274
                StapData n = new StapData(this, style, txt, time, called, 
 
275
                                id, caller, isMarked);
 
276
                if (isMarked) {
 
277
                        n.setMessage(message);
 
278
                        markedNodes.add(id);
 
279
                }
 
280
                nodeDataMap.put(id, n);
 
281
 
 
282
                // Make no assumptions about the order that data is input
 
283
                if (id > idOfLastNode)
 
284
                        idOfLastNode = id;
 
285
                return id;
 
286
        }
 
287
                
 
288
        public void insertMessage(int id, String message) {
 
289
                StapData temp = nodeDataMap.get(id);
 
290
                if (temp == null) return;
 
291
                temp.insertMessage(message);
 
292
                nodeDataMap.put(id, temp);
 
293
        }
 
294
        
 
295
        /*
 
296
         * Fully functional draw functions
 
297
         * 
 
298
         * -Radial
 
299
         * -Tree
 
300
         */
 
301
 
 
302
        /**
 
303
         * Draws a 2-node-layer circle
 
304
         * Draws all nodes in  place.
 
305
         * @param centerNode
 
306
         */
 
307
        public void drawRadial(int centerNode) {
 
308
                int radius = Math.max(CONSTANT_VERTICAL_INCREMENT,
 
309
                                Math.min(this.getBounds().width,
 
310
                                this.getBounds().height)
 
311
                                / 2 - 2*CONSTANT_VERTICAL_INCREMENT);
 
312
 
 
313
                rootVisibleNodeNumber = centerNode;
 
314
                StapData nodeData = getNodeData(centerNode);
 
315
                int collapsed = nodeData.getPartOfCollapsedNode();
 
316
                if (!nodeData.isCollapsed && collapsed != StapData.NOT_PART_OF_COLLAPSED_NODE) {
 
317
                        nodeData = getNodeData(collapsed);
 
318
                }
 
319
                treeViewer.expandToLevel(nodeData, 0);
 
320
                treeViewer.setSelection(new StructuredSelection(nodeData));
 
321
                
 
322
                if (nodeMap.get(centerNode) == null) {
 
323
                        nodeMap.put(centerNode, getNodeData(centerNode).makeNode(this));
 
324
                }
 
325
                
 
326
 
 
327
                // Draw node in center
 
328
                StapNode n = nodeMap.get(centerNode);
 
329
                int x = this.getBounds().width / 2 - n.getSize().width/2;
 
330
                int y = this.getBounds().height / 2;
 
331
                n.setLocation(x, y);
 
332
                
 
333
                if (getNodeData(centerNode).isMarked())
 
334
                        nodeMap.get(centerNode).setBackgroundColor(CONSTANT_MARKED);
 
335
                radialHelper(centerNode, x, y, radius, 0);
 
336
        }
 
337
 
 
338
        /**
 
339
         * Helps animation of radial draw. Can be replaced by a draw and moveAll.
 
340
         * 
 
341
         * @param centerNode
 
342
         */
 
343
        public void preDrawRadial(int centerNode) {
 
344
                rootVisibleNodeNumber = centerNode;
 
345
                
 
346
                if (nodeMap.get(centerNode) == null) {
 
347
                        nodeMap.put(centerNode, getNodeData(centerNode).makeNode(this));
 
348
                        StapNode n = nodeMap.get(centerNode);
 
349
                        n.setLocation(this.getBounds().width / 2, this.getShell()
 
350
                                        .getSize().y / 2);
 
351
                }
 
352
 
 
353
                //Pass coordinates of the node to radialHelper
 
354
                StapNode n = nodeMap.get(centerNode);
 
355
                int x = n.getLocation().x;
 
356
                int y = n.getLocation().y;
 
357
                radialHelper(centerNode, x, y, 0, 0);
 
358
        }
 
359
 
 
360
        /**
 
361
         * Completes radial-mode draws 
 
362
         * 
 
363
         * @param id
 
364
         * @param x
 
365
         * @param y
 
366
         * @param radius
 
367
         * @param startFromChild
 
368
         */
 
369
        public void radialHelper(int id, int x, int y, int radius, int startFromChild) {                
 
370
                //-------------Draw parent node
 
371
                // Draw caller node right beside this one, in a different color
 
372
                int callerID = nodeDataMap.get(id).parent;
 
373
                if (callerID != -1) {
 
374
                        if (getNode(callerID) == null) {
 
375
                                nodeMap.put(callerID, getNodeData(callerID).makeNode(this));
 
376
                        }
 
377
                        getNode(callerID).setBackgroundColor(CONSTANT_HAS_PARENT);
 
378
                        getNode(callerID).setLocation(x + radius / 5, y - radius / 5);
 
379
                        if (getNode(id).connection == null) {
 
380
                                getNode(id).makeConnection(SWT.NONE, getNode(callerID), getNodeData(id).timesCalled);
 
381
                        }
 
382
                        
 
383
                        if (getNodeData(callerID).isMarked())
 
384
                                nodeMap.get(callerID).setBackgroundColor(CONSTANT_MARKED);
 
385
                }
 
386
                
 
387
 
 
388
                //-------------Draw children nodes
 
389
                List<Integer> nodeList;
 
390
                if (!collapse_mode) {
 
391
                        nodeList = nodeDataMap.get(id).children;
 
392
                }
 
393
                else {
 
394
                        nodeList = nodeDataMap.get(id).collapsedChildren;
 
395
                }
 
396
 
 
397
                int numberOfNodes;
 
398
                
 
399
                if (nodeList.size() >= CONSTANT_MAX_NUMBER_OF_RADIAL_SIBLINGS ) {
 
400
                        numberOfNodes = CONSTANT_MAX_NUMBER_OF_RADIAL_SIBLINGS;
 
401
                }
 
402
                else
 
403
                        numberOfNodes = nodeList.size();
 
404
                        
 
405
                
 
406
                double angle;
 
407
                if (numberOfNodes > 5)
 
408
                        angle = 2 * Math.PI / numberOfNodes;
 
409
                else
 
410
                        angle = 2 * Math.PI / CONSTANT_MAX_NUMBER_OF_RADIAL_SIBLINGS;
 
411
                
 
412
                int i = 0;
 
413
 
 
414
                for (i = 0; i < numberOfNodes; i++) {
 
415
                        
 
416
 
 
417
                        int subID = nodeList.get(i);
 
418
                        int yOffset = 0;
 
419
                        int xOffset = 0;
 
420
                        if (nodeMap.get(subID) == null) {
 
421
                                nodeMap.put(subID, getNodeData(subID).makeNode(this));
 
422
                        }
 
423
                        
 
424
                        StapNode subN = nodeMap.get(subID);
 
425
                        
 
426
                        if (radius != 0) {
 
427
                                yOffset = (int) (radius * Math.cos((float) angle * i));
 
428
                                xOffset = (int) (radius * Math.sin((float) angle * i)) - subN.getSize().width/2 + getNode(id).getSize().width/2;
 
429
                        }
 
430
 
 
431
                        if (hasChildren(subID))
 
432
                                subN.setBackgroundColor(CONSTANT_HAS_CHILDREN);
 
433
                        subN.setLocation(x + xOffset, y + yOffset);
 
434
                        if (subN.connection == null) {
 
435
                                subN.makeConnection(SWT.NONE, nodeMap.get(id), nodeDataMap
 
436
                                                .get(subID).timesCalled);
 
437
                        }
 
438
                        
 
439
                        StapData d = getNodeData(subID);
 
440
                        if (d.isMarked())
 
441
                                subN.setBackgroundColor(CONSTANT_MARKED);
 
442
                }
 
443
        }
 
444
        
 
445
        
 
446
        /**
 
447
         * Draws nodes according to the name of the function (not accounting for call
 
448
         * heirarchies). Uses colour to indicate the number of calls and size to indicate
 
449
         * the percentage time spent.
 
450
         */
 
451
        private void drawAggregateView(){
 
452
                
 
453
                if (aggregateNodes == null){
 
454
                        aggregateNodes = new ArrayList<GraphNode>();
 
455
                }else{
 
456
                        aggregateNodes.clear();
 
457
                }
 
458
                
 
459
                //-------------Format numbers
 
460
                float percentage_time;
 
461
                float percentage_count;
 
462
                int maxTimesCalled = 0;
 
463
                final int colorLevels = 15;
 
464
                final int colorLevelDifference = 12;
 
465
                int primary;
 
466
                int secondary;
 
467
                
 
468
                NumberFormat num = NumberFormat.getInstance(Locale.CANADA);
 
469
                num.setMinimumFractionDigits(2);
 
470
                num.setMaximumFractionDigits(2);
 
471
 
 
472
                //FIND THE MOST TIMES A FUNCTION IS CALLED
 
473
                for (int val : aggregateCount.values()){
 
474
                        if ( val > maxTimesCalled){
 
475
                                maxTimesCalled = val;
 
476
                        }
 
477
                }
 
478
                
 
479
                
 
480
                //TEMPORARY STORAGE OF THE ENTRIES
 
481
                //IMPLEMENTS A COMPARATOR TO STORE BY ORDER OF THE VALUE
 
482
                TreeSet<Entry<String, Long>> sortedValues = new TreeSet<Entry<String, Long>>(StapGraph.VALUE_ORDER);
 
483
                HashMap<String, Long> tempMap = new HashMap<String, Long>();
 
484
                tempMap.putAll(aggregateTime);
 
485
                
 
486
                for (String key : tempMap.keySet()) {
 
487
                        long time = aggregateTime.get(key);
 
488
                        //This is a stupid way to get the times right, but it is almost always guaranteed to work.
 
489
 
 
490
                        while (time < 0)
 
491
                                time += endTime;
 
492
                        tempMap.put(key, time);
 
493
                }
 
494
                
 
495
                sortedValues.addAll(tempMap.entrySet());
 
496
                
 
497
                //-------------Draw nodes
 
498
                for (Entry<String, Long> ent: sortedValues) {
 
499
                        String key = ent.getKey();
 
500
                                GraphNode n = new GraphNode(this.getGraphModel(),SWT.NONE);
 
501
                                aggregateNodes.add(n);
 
502
                                
 
503
                                percentage_count = (float)aggregateCount.get(key) / (float)maxTimesCalled;
 
504
                                percentage_time = ((float)  ent.getValue()/ this
 
505
                                                .getTotalTime() * 100);
 
506
                                
 
507
                                n.setText(key + "\n"  //$NON-NLS-1$
 
508
                                                + num.format((float)percentage_time) + "%" + "\n" //$NON-NLS-1$ //$NON-NLS-2$
 
509
                                                + aggregateCount.get(key) + "\n") ; //$NON-NLS-1$
 
510
                                n.setData("AGGREGATE_NAME", key); //$NON-NLS-1$
 
511
                                
 
512
                                
 
513
                                primary = (int)(percentage_count * colorLevels * colorLevelDifference);
 
514
                                secondary = (colorLevels * colorLevelDifference) - (int)(percentage_count * colorLevels * colorLevelDifference);
 
515
                                
 
516
                                primary = Math.max(0, primary);
 
517
                                secondary = Math.max(0, secondary);
 
518
                                
 
519
                                primary = Math.min(primary, 255);
 
520
                                secondary = Math.min(secondary, 255);
 
521
                                
 
522
                                
 
523
                                Color c = new Color(this.getDisplay(),primary,0,secondary);
 
524
                                n.setBackgroundColor(c);
 
525
                                n.setHighlightColor(c);
 
526
                                n.setForegroundColor(new Color(this.getDisplay(),255,255,255));
 
527
                                n.setTooltip(new Label(
 
528
                                                Messages.getString("StapGraph.Func")+ key + "\n" //$NON-NLS-1$ //$NON-NLS-2$
 
529
                                                + Messages.getString("StapGraph.Time") + num.format((float)percentage_time) + "%" + "\n" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
530
                                                + Messages.getString("StapGraph.NumOfCalls") + aggregateCount.get(key)           //$NON-NLS-1$
 
531
                                ));
 
532
                                n.setBorderWidth(2);
 
533
                }
 
534
 
 
535
                
 
536
                //Set layout to gridlayout
 
537
                this.setLayoutAlgorithm(new AggregateLayoutAlgorithm(LayoutStyles.NONE, sortedValues, this.getTotalTime(), this.getBounds().width), true);
 
538
        }
 
539
 
 
540
 
 
541
 
 
542
        /**
 
543
         * Draws a tree starting with node id, putting node id at location x,y
 
544
         * @param id
 
545
         * @param x
 
546
         * @param y
 
547
         */
 
548
        private void drawTree(int id, int x, int y) {
 
549
                
 
550
                //-------------Create node id
 
551
                // Create and set
 
552
                if (nodeMap.get(id) == null) {
 
553
                        nodeMap.put(id, getNodeData(id).makeNode(this));
 
554
                }
 
555
                StapNode n = getNode(id);
 
556
                n.setLocation(x,y);
 
557
                n.setSize(n.getSize().width/scale, n.getSize().height/scale);
 
558
 
 
559
                //This is the lowest level of nodes to draw, and it still has kids
 
560
                if (getLevelOfNode(id) == bottomLevelToDraw &&
 
561
                                getNodeData(id).children.size() > 0)
 
562
                        n.setBackgroundColor(CONSTANT_HAS_CHILDREN);
 
563
                
 
564
                if (getNodeData(id).isMarked())
 
565
                        n.setBackgroundColor(CONSTANT_MARKED);
 
566
                
 
567
                
 
568
                //-------------Get appropriate list of children
 
569
                List<Integer> callees = null;
 
570
                int usefulSize = 0;
 
571
                
 
572
                // Determine which list of callees to use
 
573
                if (!collapse_mode)
 
574
                        callees = getNodeData(id).children;
 
575
                else
 
576
                        callees = getNodeData(id).collapsedChildren;
 
577
                if (callees == null)
 
578
                        return;
 
579
                
 
580
                int cLevel = getLevelOfNode(id) + 1;
 
581
                
 
582
                if (!collapse_mode) {
 
583
                        if (levels.get(cLevel) != null) {
 
584
                                usefulSize = levels.get(cLevel).size() - collapsedLevelSize.get(cLevel);
 
585
                        }
 
586
                }
 
587
                else {
 
588
                if (collapsedLevelSize.get(cLevel) != null)
 
589
                        usefulSize = collapsedLevelSize.get(cLevel);
 
590
                }
 
591
                //-------------Draw all children
 
592
                for (int i = 0; i < callees.size(); i++) {
 
593
                        //Find the number of nodes on this level for spacing purposes
 
594
                        int childID = callees.get(i);
 
595
                        int childLevel = getLevelOfNode(childID);
 
596
 
 
597
                        
 
598
                        //Initialise the offset to roughly centre the nodes 
 
599
                        if (currentPositionInLevel.get(getLevelOfNode(childID)) == null) {
 
600
                                int tmp = (int) (CONSTANT_HORIZONTAL_SPACING*(usefulSize-1) * -1/scale);
 
601
                                currentPositionInLevel.put(childLevel, getNode(rootVisibleNodeNumber)
 
602
                                                .getLocation().x + tmp);
 
603
                        }
 
604
                        
 
605
                        //Recursive iteration
 
606
                        if (childLevel <= bottomLevelToDraw && 
 
607
                                        childLevel <= lowestLevelOfNodesAdded) {
 
608
                                drawTree(callees.get(i), currentPositionInLevel.get(childLevel), 
 
609
                                                y + (int)(CONSTANT_VERTICAL_INCREMENT/scale));
 
610
                                                                
 
611
                                //Do not scale newSize or nodes will no longer be adjacent
 
612
                                int newSize = currentPositionInLevel.get(getLevelOfNode(childID)) 
 
613
                                                                + getNode(childID).getSize().width;
 
614
                                
 
615
                                //Leave a small blank space between nodes for aesthetic purposes
 
616
                                if (i == callees.size() - 1)
 
617
                                        newSize += CONSTANT_HORIZONTAL_SPACING/3;
 
618
                                currentPositionInLevel.put(getLevelOfNode(childID), newSize);
 
619
                        }
 
620
                        
 
621
                }
 
622
        }
 
623
        
 
624
        /**
 
625
         * Extend the tree downwards
 
626
         */
 
627
        public void extendTree() {
 
628
                if (bottomLevelToDraw >= lowestLevelOfNodesAdded) 
 
629
                        return;
 
630
                
 
631
                
 
632
                StapData data = getNodeData(rootVisibleNodeNumber);
 
633
                if (data.children != null) {
 
634
                        if (data.children.size() < 1) {
 
635
                                return;
 
636
                        }
 
637
                }
 
638
                
 
639
                List<Integer> list = data.children;
 
640
                if (isCollapseMode())
 
641
                        list = data.collapsedChildren;
 
642
                
 
643
                if (list.size() == 1) {
 
644
                        //Special case - only one child of the root node
 
645
                        //Therefore change root node to this new root node
 
646
                        int aMode = animation_mode;
 
647
                        draw(CONSTANT_DRAWMODE_TREE, CONSTANT_ANIMATION_FASTEST, list.get(0));
 
648
                        setAnimationMode(aMode);
 
649
                        return;
 
650
                }
 
651
                
 
652
                
 
653
                List<Integer> bottomList = levels.get(bottomLevelToDraw);
 
654
                bottomLevelToDraw++;
 
655
                
 
656
                for (int i : bottomList) {
 
657
                        if (getNode(i) != null) {
 
658
                                getNode(i).setBackgroundColor(DEFAULT_NODE_COLOR);
 
659
                                getParentNode(i).setBackgroundColor(DEFAULT_NODE_COLOR);
 
660
                                drawTree(i, getNode(i).getLocation().x, getNode(i).getLocation().y);
 
661
                        }
 
662
                }
 
663
                
 
664
                treeLevelFromRoot++;            
 
665
        }
 
666
        
 
667
        /**
 
668
         * Removes nodes from the bottom of the tree
 
669
         */
 
670
        public void shrinkTree() {
 
671
                if (treeLevelFromRoot < 1) 
 
672
                        return;
 
673
                
 
674
                
 
675
                bottomLevelToDraw--;
 
676
                deleteAll(rootVisibleNodeNumber);
 
677
                
 
678
                int i = rootVisibleNodeNumber;
 
679
                currentPositionInLevel.clear();
 
680
                drawTree(i, getNode(i).getLocation().x, getNode(i).getLocation().y);
 
681
                
 
682
                treeLevelFromRoot--;            
 
683
        }
 
684
        
 
685
 
 
686
        /**
 
687
         * Draws the next node, unless the next node does not exist.
 
688
         */
 
689
        public void drawNextNode() {
 
690
                if (isCollapseMode()) {
 
691
                        setCollapseMode(false);
 
692
                }
 
693
                int toDraw = getNextCalledNode(getRootVisibleNodeNumber());
 
694
                if (toDraw != -1)
 
695
                        draw(toDraw);
 
696
                 else
 
697
                        proj.pause();
 
698
        }
 
699
 
 
700
        
 
701
        /**
 
702
         * Moves all nodes to the point x,y
 
703
         * @param x
 
704
         * @param y
 
705
         */
 
706
        public void moveAllNodesTo(int x, int y) {
 
707
                for (int i: nodeMap.keySet()) {
 
708
                        nodeMap.get(i).setLocation(x,y);
 
709
                }
 
710
        }
 
711
 
 
712
 
 
713
        /**
 
714
         * Draws a tree roughly starting from node id
 
715
         */
 
716
        public void drawBox(int id, int x, int y) {
 
717
                setLevelLimits(id);
 
718
                int MaxLevelPixelWidth = 1;
 
719
                int currPixelWidth = 1;
 
720
                
 
721
                // FIND THE LEVEL THAT WILL BE THE WIDEST
 
722
                // WILL BE A USEFUL VALUE LATER ON
 
723
                int count;
 
724
                
 
725
                
 
726
                for (int i = topLevelToDraw; i <= bottomLevelToDraw; i++) {
 
727
                        count = 0;
 
728
                        levels.get(i).add(0, count);
 
729
                        int size = levels.get(i).size();
 
730
                        for (int j = 1; j < size; j++){
 
731
                                int val = levels.get(i).get(j);
 
732
                                StapData data = nodeDataMap.get(val);
 
733
                                if (!data.isOnlyChildWithThisName()) {
 
734
                                        if (collapse_mode && data.isPartOfCollapsedNode()) {
 
735
                                                continue;
 
736
                                        }
 
737
                                        if (!collapse_mode && data.isCollapsed)
 
738
                                                continue;
 
739
                                }
 
740
                                
 
741
                                currPixelWidth += data.name.length() * 10 + StapGraph.CONSTANT_HORIZONTAL_SPACING_FOR_LEVEL;
 
742
                                if (MaxLevelPixelWidth < currPixelWidth) {
 
743
                                        MaxLevelPixelWidth = currPixelWidth;
 
744
                                }
 
745
                                count++;
 
746
                                levels.get(i).remove(0);
 
747
                                levels.get(i).add(0, count);
 
748
                        }
 
749
                        currPixelWidth = 1;
 
750
                }
 
751
 
 
752
                MaxLevelPixelWidth = (int)(MaxLevelPixelWidth/scale);
 
753
                counter = 0;
 
754
                if (id == getFirstUsefulNode())
 
755
                        nodeMap.get(id).setLocation(150 + (MaxLevelPixelWidth/2),y);
 
756
                
 
757
                drawFromBottomToTop(bottomLevelToDraw, y
 
758
                                + ((bottomLevelToDraw  - topLevelToDraw ) * 3 * (int)(CONSTANT_VERTICAL_INCREMENT/scale)),
 
759
                                MaxLevelPixelWidth);
 
760
                
 
761
                if (id == getFirstUsefulNode())
 
762
                        nodeMap.get(id).setLocation(150 + (MaxLevelPixelWidth/2),y);
 
763
        }
 
764
        
 
765
        
 
766
        public void drawFromBottomToTop(int level, int height,
 
767
                        int MaxLevelPixelWidth) {
 
768
                
 
769
                // FINISHED DRAWING THE ROOT IN THE LAST RECURSIVE CALL
 
770
                if (level == 0 || level < topLevelToDraw ) {
 
771
                        return;
 
772
                }
 
773
                
 
774
                // FIND ALL THE CHILDREN AT LEVEL 'level'
 
775
                int total = levels.get(level).remove(0);
 
776
                int count = 1;
 
777
                
 
778
                //CREATE THE NODES
 
779
                for (int i = 0; i < levels.get(level).size(); i ++) {
 
780
                        int id = levels.get(level).get(i);
 
781
                        
 
782
                        StapData data = nodeDataMap.get(id);
 
783
                        if (!data.isOnlyChildWithThisName()) {
 
784
                                if (collapse_mode && data.isPartOfCollapsedNode() ) {
 
785
                                        continue;
 
786
                                }
 
787
                                if (!collapse_mode && nodeDataMap.get(id).isCollapsed)
 
788
                                        continue;
 
789
                        }
 
790
                        
 
791
                        if (nodeMap.get(id) == null) {
 
792
                                nodeMap.put(id, getNodeData(id).makeNode(this));
 
793
                        }
 
794
                        
 
795
                        StapNode n = nodeMap.get(id);
 
796
                        
 
797
                        n.setVisible(true);
 
798
                        n.setSize(n.getSize().width/scale, n.getSize().height/scale);
 
799
                        //Placement algorithm
 
800
                        if (getAnimationMode() == CONSTANT_ANIMATION_SLOW){     
 
801
                                
 
802
                                if (counter <= ANIMATION_TIME)
 
803
                                        Animation.markBegin();
 
804
                                n.setLocation(150 + (nodeMap.get(getRootVisibleNodeNumber()).getLocation().x),nodeMap.get(getRootVisibleNodeNumber()).getLocation().y);
 
805
                                n.setLocation(150 + (MaxLevelPixelWidth / (total + 1) * count),height);
 
806
 
 
807
                                if (counter <= ANIMATION_TIME) {
 
808
                                        Animation.run(ANIMATION_TIME/nodeMap.size()/3);
 
809
                                        counter+=ANIMATION_TIME/nodeMap.size();
 
810
                                }
 
811
                                        
 
812
                        }else{
 
813
                                n.setLocation(150 + (MaxLevelPixelWidth / (total + 1) * count),height);                         
 
814
                        }
 
815
                        
 
816
                        //IF WE CANNOT DISPLAY ALL NODES COLOUR NODES ON BOTTOM THAT STILL HAVE CHILDREN
 
817
                        if (level == bottomLevelToDraw && nodeDataMap.get(id).children.size() != 0){
 
818
                                n.setBackgroundColor(CONSTANT_HAS_CHILDREN);
 
819
                        }
 
820
                        
 
821
                        
 
822
                        
 
823
                        if (getNodeData(n.id).isMarked())
 
824
                                n.setBackgroundColor(CONSTANT_MARKED);
 
825
                        
 
826
                        
 
827
                        // FIND ALL THE NODES THAT THIS NODE CALLS AND MAKE CONNECTIONS
 
828
                        List<Integer> setOfCallees = null;
 
829
                        if (collapse_mode)
 
830
                                setOfCallees = nodeDataMap.get(id).collapsedChildren;
 
831
                        else 
 
832
                                setOfCallees = nodeDataMap.get(id).children;
 
833
                        
 
834
                        for (int val : setOfCallees) {
 
835
                                if (nodeMap.get(val) != null)
 
836
                                        nodeMap.get(val).makeConnection(SWT.NONE, n, 
 
837
                                                nodeDataMap.get(val).timesCalled);
 
838
                        }
 
839
                        
 
840
                        count++;
 
841
                }
 
842
                // DRAW THE NEXT LEVEL UP
 
843
                drawFromBottomToTop(level - 1, height - (3 * (int)(CONSTANT_VERTICAL_INCREMENT/scale)),
 
844
                                MaxLevelPixelWidth);
 
845
        }
 
846
 
 
847
        
 
848
        
 
849
        /*
 
850
         * Level/node management
 
851
         */
 
852
 
 
853
        /**
 
854
         * Delete all nodes except for the node with the specified nodeID
 
855
         * 
 
856
         * @param exception
 
857
         *            - id of node NOT to delete (use -1 for 'no exceptions')
 
858
         */
 
859
        public void deleteAll(int exception) {          
 
860
                //-------------Delete aggregate nodes
 
861
                if (aggregateNodes != null){                    
 
862
                        for (GraphNode n : aggregateNodes){
 
863
                                n.dispose();
 
864
                        }
 
865
                        aggregateNodes.clear();
 
866
                }
 
867
                
 
868
                //-------------Save exception node's location
 
869
                int x = -1;
 
870
                int y = -1;
 
871
                if (exception != -1 && nodeMap.get(exception) != null) {
 
872
                        x =     nodeMap.get(exception).getLocation().x;
 
873
                        y = nodeMap.get(exception).getLocation().y;
 
874
                }
 
875
 
 
876
                //-------------Delete all nodes
 
877
                for (int i : nodeMap.keySet()) {
 
878
                        StapNode node = nodeMap.get(i);
 
879
                        if (node == null)
 
880
                                continue;
 
881
                        
 
882
                        node.unhighlight();
 
883
                        node.dispose();
 
884
                }
 
885
                nodeMap.clear();
 
886
 
 
887
                //-------------Recreate exception
 
888
                if (x != -1 && y != -1) {
 
889
                        StapNode n =getNodeData(exception).makeNode(this);
 
890
                        n.setLocation(x,y);
 
891
                        n.highlight();
 
892
                        nodeMap.put(exception, n);
 
893
                }
 
894
        }
 
895
 
 
896
        /**
 
897
         * Delete a number of levels from the top of the graph
 
898
         * 
 
899
         * @param numberOfLevelsToDelete
 
900
         */
 
901
        private void deleteLevelsFromTop(int numberOfLevelsToDelete) {
 
902
 
 
903
                if (numberOfLevelsToDelete <= 0)
 
904
                        return;
 
905
 
 
906
                for (int i = 0; i < numberOfLevelsToDelete; i++) {
 
907
                        List<Integer> level = levels.get(topLevelToDraw);
 
908
                        for (int j = 0; j < level.size(); j++) {
 
909
                                if (nodeMap.get(level.get(j)) != null)
 
910
                                        nodeMap.remove(level.get(j)).dispose();
 
911
                        }
 
912
                        topLevelToDraw++;
 
913
                }
 
914
        }
 
915
 
 
916
        /**
 
917
         * Delete a number of levels from the bottom of the graph
 
918
         * 
 
919
         * @param numberOfLevelsToDelete
 
920
         */
 
921
        private void deleteLevelsFromBottom(int numberOfLevelsToDelete) {
 
922
 
 
923
                if (numberOfLevelsToDelete <= 0)
 
924
                        return;
 
925
 
 
926
                for (int i = 0; i < numberOfLevelsToDelete; i++) {
 
927
                        List<Integer> level = levels.get(getBottomLevelToDraw());
 
928
 
 
929
                        for (int j = 0; j < level.size(); j++) {
 
930
                                if (nodeMap.get(level.get(j)) != null)
 
931
                                        nodeMap.remove(level.get(j)).dispose();
 
932
                        }
 
933
                        bottomLevelToDraw--;
 
934
                }
 
935
        }
 
936
 
 
937
        /**
 
938
         * Sets top level limit to the level of id, bottom level limit to top level
 
939
         * limit + CONSTANT_LEVEL_BUFFER.
 
940
         * Deletes extraneous levels, changes topLevelToDraw, bottomLevelToDraw
 
941
         * 
 
942
         * Convenience method: Calls setLevelLimitsToLevel(levelOfNode(id))
 
943
         * 
 
944
         * @param id - node to recenter with
 
945
         */
 
946
        public void setLevelLimits(int id) {
 
947
                setTopLevelTo(getLevelOfNode(id));
 
948
        }
 
949
        
 
950
        /**
 
951
         * Sets top level limit to the given level, bottom level limit to top level
 
952
         * limit + CONSTANT_LEVEL_BUFFER.
 
953
         * Deletes extraneous levels, changes topLevelToDraw, bottomLevelToDraw
 
954
         * 
 
955
         * @param id - node to recenter with
 
956
         */
 
957
        
 
958
        public void setTopLevelTo(int new_topLevelToDraw) {
 
959
                changeLevelLimits(new_topLevelToDraw);
 
960
                
 
961
                int new_bottomLevelToDraw = new_topLevelToDraw + levelBuffer;
 
962
                if (new_bottomLevelToDraw > lowestLevelOfNodesAdded)
 
963
                        new_bottomLevelToDraw = lowestLevelOfNodesAdded;
 
964
 
 
965
                deleteLevelsFromTop(new_topLevelToDraw - topLevelToDraw);
 
966
                deleteLevelsFromBottom(getBottomLevelToDraw() - new_bottomLevelToDraw);
 
967
 
 
968
                topLevelToDraw = new_topLevelToDraw;
 
969
                bottomLevelToDraw = new_bottomLevelToDraw;
 
970
        }
 
971
 
 
972
        
 
973
        public boolean changeLevelLimits(int lvl) {
 
974
                int numberOfNodes = 0;
 
975
                List<Integer> list;
 
976
                
 
977
                int maxLevel = min(lvl + levelBuffer, lowestLevelOfNodesAdded);
 
978
                
 
979
                for (int level = lvl; level < maxLevel; level++) {
 
980
                        for (int id : levels.get(level)) {
 
981
                                if (isCollapseMode())
 
982
                                        list = getNodeData(id).collapsedChildren;
 
983
                                else
 
984
                                        list = getNodeData(id).children;
 
985
                                
 
986
                                numberOfNodes += list.size();
 
987
                                
 
988
                                if (numberOfNodes > maxNodes) {
 
989
                                        levelBuffer = max(0,level-1);
 
990
                                        return true;
 
991
                                }
 
992
                        }
 
993
                }
 
994
                
 
995
                return false;
 
996
 
 
997
                
 
998
        }
 
999
        
 
1000
        /**
 
1001
         * Convenience method to redraw everything.
 
1002
         */
 
1003
        public void draw() {
 
1004
                draw(getRootVisibleNodeNumber());
 
1005
        }
 
1006
        
 
1007
        /**
 
1008
         * Convenience method to draw with current draw parameters. Equivalent to
 
1009
         * draw(graph.draw_mode, graph.animation_mode, id)
 
1010
         * @param id
 
1011
         */
 
1012
        public void draw(int id) {
 
1013
                draw(draw_mode, animation_mode, id);
 
1014
        }
 
1015
        
 
1016
        /**
 
1017
         * Convenience method to draw with current draw parameters. Equivalent to
 
1018
         * draw(graph.draw_mode, animation, id)
 
1019
         * @param id
 
1020
         */
 
1021
        public void draw(int animation, int id) {
 
1022
                draw(draw_mode, animation, id);
 
1023
        }
 
1024
        
 
1025
 
 
1026
        /**
 
1027
         * Draws with the given modes.
 
1028
         * @param drawMode
 
1029
         * @param animationMode
 
1030
         * @param id
 
1031
         */
 
1032
        public void draw(int drawMode, int animationMode, int id) {
 
1033
                setDrawMode(drawMode);
 
1034
                setAnimationMode(animationMode);
 
1035
                if (nodeDataMap.get(id) == null)
 
1036
                        return;
 
1037
                this.clearSelection();
 
1038
                treeLevelFromRoot = 0;
 
1039
                currentPositionInLevel.clear();
 
1040
                
 
1041
                
 
1042
                
 
1043
                this.setRedraw(false);
 
1044
                if (draw_mode == CONSTANT_DRAWMODE_RADIAL) {
 
1045
                        //Remove thumbnail
 
1046
                        GridData gd = (GridData) thumbCanvas.getLayoutData();
 
1047
                        gd.exclude = true;
 
1048
                        thumbCanvas.setLayoutData(gd);
 
1049
                        thumbCanvas.setVisible(false);
 
1050
                        callgraphView.layout();
 
1051
                        
 
1052
                        
 
1053
                        //Add treeComp
 
1054
                        gd = (GridData) treeComp.getLayoutData();
 
1055
                        gd.exclude = false;
 
1056
                        treeComp.setLayoutData(gd);
 
1057
                        treeComp.setVisible(true);
 
1058
                        treeViewer.collapseToLevel(getNodeData(id), 1);
 
1059
                        treeViewer.expandToLevel(getNodeData(id), 1);
 
1060
                        
 
1061
                        
 
1062
                } else if (draw_mode == CONSTANT_DRAWMODE_AGGREGATE){
 
1063
                        //Remove treeComp
 
1064
                        GridData gd = (GridData) treeComp.getLayoutData();
 
1065
                        gd.exclude = true;
 
1066
                        treeComp.setLayoutData(gd);
 
1067
                        treeComp.setVisible(false);
 
1068
                        
 
1069
                        callgraphView.layout();
 
1070
                        //Remove thumbnail
 
1071
                        gd = (GridData) thumbCanvas.getLayoutData();
 
1072
                        gd.exclude = true;
 
1073
                        thumbCanvas.setLayoutData(gd);
 
1074
                        thumbCanvas.setVisible(false);                                  
 
1075
                }
 
1076
                else{
 
1077
                        //Remove treeComp
 
1078
                        GridData gd = (GridData) treeComp.getLayoutData();
 
1079
                        gd.exclude = true;
 
1080
                        treeComp.setLayoutData(gd);
 
1081
                        treeComp.setVisible(false);
 
1082
 
 
1083
                        callgraphView.layout();
 
1084
 
 
1085
                        //Add thumbnail
 
1086
                        gd = (GridData) thumbCanvas.getLayoutData();
 
1087
                        gd.exclude = true;
 
1088
                        thumbCanvas.setLayoutData(gd);
 
1089
                        thumbCanvas.setVisible(true);
 
1090
                        thumbCanvas.setBackground(this.getBackground());
 
1091
                        
 
1092
                        
 
1093
                }
 
1094
                callgraphView.layout();
 
1095
                this.setRedraw(true);
 
1096
 
 
1097
                
 
1098
                //-------------Draw tree
 
1099
                if (draw_mode == CONSTANT_DRAWMODE_TREE) {                      
 
1100
                        if (animation_mode == CONSTANT_ANIMATION_SLOW) {
 
1101
                                if (nodeMap.get(id) == null)
 
1102
                                        nodeMap.put(id, getNodeData(id).makeNode(this));
 
1103
                                int tempX = nodeMap.get(id).getLocation().x;
 
1104
                                int tempY = nodeMap.get(id).getLocation().y;
 
1105
                                Animation.markBegin();
 
1106
                                moveAllNodesTo(tempX, tempY);
 
1107
                                Animation.run(ANIMATION_TIME);
 
1108
                                
 
1109
                                deleteAll(id);
 
1110
                                setLevelLimits(id);
 
1111
                                rootVisibleNodeNumber = id;
 
1112
                                drawTree(id, this.getBounds().width / 2, 20);
 
1113
                                currentPositionInLevel.clear();
 
1114
 
 
1115
                                this.update();
 
1116
                                Animation.markBegin();
 
1117
                                drawTree(id, this.getBounds().width / 2, 20);
 
1118
 
 
1119
                                Animation.run(ANIMATION_TIME);
 
1120
                                getNode(id).unhighlight();
 
1121
                        } else {
 
1122
                                deleteAll(id);
 
1123
                                setLevelLimits(id);
 
1124
                                rootVisibleNodeNumber = id;
 
1125
                                drawTree(id, this.getBounds().width / 2, 20);
 
1126
                                getNode(id).unhighlight();
 
1127
                        }
 
1128
                }
 
1129
                
 
1130
                
 
1131
                //-------------Draw radial
 
1132
                else if (draw_mode == CONSTANT_DRAWMODE_RADIAL) {
 
1133
                        
 
1134
                        if (animation_mode == CONSTANT_ANIMATION_SLOW) {
 
1135
                                rootVisibleNodeNumber = id;
 
1136
                                deleteAll(id);
 
1137
 
 
1138
                                preDrawRadial(id);
 
1139
                                this.redraw();
 
1140
                                this.getLightweightSystem().getUpdateManager()
 
1141
                                                .performUpdate();
 
1142
        
 
1143
                                Animation.markBegin();
 
1144
                                nodeMap.get(id).setLocation(this.getBounds().width / 2,
 
1145
                                                this.getBounds().height / 2);
 
1146
                                drawRadial(id); 
 
1147
                                Animation.run(ANIMATION_TIME);
 
1148
                                callgraphView.maximizeOrRefresh(false);
 
1149
                        }
 
1150
        
 
1151
                        else {  
 
1152
                                deleteAll(id);
 
1153
                                drawRadial(id);
 
1154
                        }
 
1155
                }
 
1156
                
 
1157
                //-------------Draw level
 
1158
                else if (draw_mode == CONSTANT_DRAWMODE_LEVEL) {
 
1159
                        rootVisibleNodeNumber = id;
 
1160
                        if (animation_mode == CONSTANT_ANIMATION_SLOW) {
 
1161
                                if (nodeMap.get(id) == null)
 
1162
                                        nodeMap.put(id, getNodeData(id).makeNode(this));
 
1163
                                
 
1164
                                Animation.markBegin();
 
1165
                                moveAllNodesTo(nodeMap.get(id).getLocation().x, nodeMap.get(id).getLocation().y);
 
1166
                                Animation.run(ANIMATION_TIME);
 
1167
                                
 
1168
                                deleteAll(id);
 
1169
                                
 
1170
                                drawBox(id, 0, 0);
 
1171
                                
 
1172
                        } else {
 
1173
                                if (nodeMap.get(id) == null)
 
1174
                                        nodeMap.put(id, getNodeData(id).makeNode(this));
 
1175
                                deleteAll(id);
 
1176
                                drawBox(id, 0, 0);
 
1177
 
 
1178
                        }
 
1179
                }
 
1180
                
 
1181
                
 
1182
                //-------------Draw aggregate
 
1183
                else if (draw_mode == CONSTANT_DRAWMODE_AGGREGATE) {
 
1184
                        rootVisibleNodeNumber = getFirstUsefulNode();
 
1185
                        deleteAll(-1);
 
1186
                        drawAggregateView();
 
1187
                }
 
1188
                
 
1189
                if (getNode(id) != null)
 
1190
                        getNode(id).unhighlight();
 
1191
                clearSelection();
 
1192
        
 
1193
                //AFTER FIRST LOADING LET THE GRAPH EXPAND TO FILL THE VIEW
 
1194
                this.setLayoutData(new GridData(SWT.FILL,SWT.FILL,true,true));
 
1195
        }
 
1196
        
 
1197
        
 
1198
        
 
1199
        @SuppressWarnings("unchecked")
 
1200
        /**
 
1201
         * Unhighlights all selected nodes and sets selection to null
 
1202
         */
 
1203
        public void clearSelection() {
 
1204
                List<GraphNode> list = this.getSelection();
 
1205
                
 
1206
                for (GraphNode n : list) {
 
1207
                        if (n != null) n.unhighlight();
 
1208
                }
 
1209
                this.setSelection(null);
 
1210
                
 
1211
        }
 
1212
 
 
1213
        /*
 
1214
         * THE FOLLOWING METHODS ARE NOT WELL TESTED
 
1215
         * 
 
1216
         * Some are not thoroughly tested, and some just plain don't work. Use at your peril!
 
1217
         */
 
1218
 
 
1219
        /**
 
1220
         * Shift the given node to the given location, moving all children nodes
 
1221
         * accordingly
 
1222
         * 
 
1223
         * 
 
1224
         * @param id
 
1225
         * @param x
 
1226
         * @param y
 
1227
         */
 
1228
        public void moveRecursive(int id, int xTarget, int yTarget) {
 
1229
                if (nodeMap.get(id) != null) {
 
1230
                        int x = nodeMap.get(id).getLocation().x;
 
1231
                        int y = nodeMap.get(id).getLocation().y;
 
1232
                        nodeMap.get(id).setLocation(x + xTarget, y + yTarget);
 
1233
                }
 
1234
                //If a node is null, then its children must be null
 
1235
                else
 
1236
                        return;
 
1237
 
 
1238
                List<Integer> list = null;
 
1239
                
 
1240
                if (collapse_mode)
 
1241
                        list = nodeDataMap.get(id).collapsedChildren;
 
1242
                else
 
1243
                        list = nodeDataMap.get(id).children;
 
1244
                for (int i = 0; i < list.size(); i++) {
 
1245
                        moveRecursive(list.get(i), xTarget, yTarget);
 
1246
                }
 
1247
        }
 
1248
        
 
1249
        
 
1250
        /**
 
1251
         * Moves all nodes by the given amount. Adds xDiff, yDiff to the current x,y coordinates.
 
1252
         * 
 
1253
         * Currently unused.
 
1254
         */
 
1255
        public void moveAllNodesBy(int xDiff, int yDiff) {
 
1256
                for (int id : nodeMap.keySet()) {
 
1257
                        if (nodeMap.get(id) == null) continue;
 
1258
                        
 
1259
                        int x = nodeMap.get(id).getLocation().x;
 
1260
                        int y = nodeMap.get(id).getLocation().y;
 
1261
                        getNode(id).setLocation(x + xDiff, y + yDiff);
 
1262
                }
 
1263
        }
 
1264
        
 
1265
 
 
1266
 
 
1267
        /**
 
1268
         * Recursively collapses all children of node id, and puts them in the
 
1269
         * collapsedCallees list of id.
 
1270
         * 
 
1271
         * At the end of this run, each collapsed node will have a list of other
 
1272
         * collapsed nodes AND a list of non-collapsed nodes. So will node #id.
 
1273
         * 
 
1274
         * Uncollapsed nodes, however, will not have a list of collapsed nodes attached. 
 
1275
         * 
 
1276
         * @param ID of node to start from (use getFirstUsefulNode() to collapse everything
 
1277
         * @return True if successful
 
1278
         */
 
1279
        public boolean recursivelyCollapseAllChildrenOfNode(int id) {
 
1280
                //-------------Initialize
 
1281
                //If all nodes have been collapsed, don't do anything
 
1282
                setCollapseMode(true);
 
1283
 
 
1284
                if (nodeDataMap.get(id).children.size() == 0)
 
1285
                        return true;
 
1286
                nodeDataMap.get(id).hasCollapsedChildren = true;
 
1287
 
 
1288
                
 
1289
                
 
1290
                // Name, id
 
1291
                HashMap<String, Integer> newNodeMap = new HashMap<String, Integer>();
 
1292
                
 
1293
                for (int collapsedID : nodeDataMap.get(id).collapsedChildren) {
 
1294
                        newNodeMap.put(getNodeData(collapsedID).name, collapsedID);
 
1295
                }
 
1296
                // id of 'collapsed' node, id of its uncollapsed twin
 
1297
                HashMap<Integer, Integer> collapsedNodesWithOnlyOneNodeInThem = new HashMap<Integer, Integer>();
 
1298
                int size = nodeDataMap.get(id).children.size();
 
1299
                
 
1300
                
 
1301
                
 
1302
                //-------------Iterate
 
1303
                for (int i = 0; i < size; i++) {
 
1304
 
 
1305
                        int childID = nodeDataMap.get(id).children.get(i);
 
1306
                        if (getNodeData(childID).isPartOfCollapsedNode())
 
1307
                                continue;
 
1308
                        int childLevel = getLevelOfNode(childID);
 
1309
                        if (collapsedLevelSize.get(childLevel) == null)
 
1310
                                collapsedLevelSize.put(childLevel, 0);
 
1311
                        String nodeName = nodeDataMap.get(childID).name;
 
1312
 
 
1313
                        /*
 
1314
                         * Aggregate data for the given node
 
1315
                         */
 
1316
                        if (newNodeMap.get(nodeName) != null) {
 
1317
                                int aggregateID = newNodeMap.get(nodeName);
 
1318
 
 
1319
                                if (collapsedNodesWithOnlyOneNodeInThem.get(aggregateID) != null) {
 
1320
                                        
 
1321
                                        //-------------Aggregate nodes - second node to be found
 
1322
                                        // We still think this is an only child, but now we know better.
 
1323
                                        // Create a new data node and aggregate
 
1324
                                        this.loadData(SWT.NONE, aggregateID, nodeName, nodeDataMap
 
1325
                                                        .get(childID).getTime(), nodeDataMap.get(childID).timesCalled,
 
1326
                                                        id, nodeDataMap.get(childID).isMarked(), ""); //$NON-NLS-1$
 
1327
                                        
 
1328
                                        if (getNodeData(aggregateID).isMarked()) {
 
1329
                                                markedCollapsedNodes.add(aggregateID);
 
1330
                                                markedNodes.remove((Integer) aggregateID);
 
1331
                                        }
 
1332
                                        
 
1333
                                        nodeDataMap.get(id).children.remove((Integer) aggregateID);
 
1334
                                        nodeDataMap.get(id).collapsedChildren.add(aggregateID);
 
1335
                                        nodeDataMap.get(childID).setPartOfCollapsedNode(aggregateID);
 
1336
 
 
1337
                                        nodeDataMap.get(aggregateID).collapsedParent = id;
 
1338
 
 
1339
                                        // Aggregate the first node that we found, and set it
 
1340
                                        // as the uncollapsed piece of the aggregate node
 
1341
                                        int otherChildID = collapsedNodesWithOnlyOneNodeInThem
 
1342
                                                        .get(aggregateID);
 
1343
                                        aggregateData(nodeDataMap.get(aggregateID), nodeDataMap
 
1344
                                                        .get(otherChildID));
 
1345
                                        collapsedNodesWithOnlyOneNodeInThem.remove(aggregateID);
 
1346
                                        nodeDataMap.get(aggregateID).children.addAll(nodeDataMap
 
1347
                                                        .get(otherChildID).children);
 
1348
                                        nodeDataMap.get(aggregateID).setPartOfCollapsedNode(StapData.NOT_PART_OF_COLLAPSED_NODE);
 
1349
 
 
1350
                                        nodeDataMap.get(otherChildID).setPartOfCollapsedNode(aggregateID);
 
1351
                                        nodeDataMap.get(aggregateID).uncollapsedPiece = otherChildID;
 
1352
 
 
1353
                                } else 
 
1354
                                        //-------------Aggregate - third and additional nodes
 
1355
                                        aggregateData(nodeDataMap.get(aggregateID), nodeDataMap
 
1356
                                                        .get(childID));
 
1357
 
 
1358
                                
 
1359
                                //-------------Complete aggregation
 
1360
                                nodeDataMap.get(aggregateID).children
 
1361
                                                .addAll(nodeDataMap.get(childID).children);
 
1362
                                nodeDataMap.get(aggregateID).isCollapsed = true;
 
1363
 
 
1364
                                if (nodeMap.get(childID) != null) {
 
1365
                                        nodeMap.get(childID).setLocation(
 
1366
                                                        nodeMap.get(id).getLocation().x
 
1367
                                                                        - nodeMap.get(id).getSize().width,
 
1368
                                                        nodeMap.get(id).getLocation().y);
 
1369
                                }
 
1370
 
 
1371
                                nodeDataMap.get(childID).setPartOfCollapsedNode(aggregateID);
 
1372
                        } else {
 
1373
                                //-------------First child with this name
 
1374
                                
 
1375
                                idOfLastCollapsedNode--;
 
1376
                                newNodeMap.put(nodeName, idOfLastCollapsedNode);
 
1377
                                collapsedNodesWithOnlyOneNodeInThem.put(idOfLastCollapsedNode, childID);
 
1378
                                if (nodeMap.get(childID) != null) {
 
1379
                                        nodeMap.get(childID).setLocation(
 
1380
                                                        nodeMap.get(id).getLocation().x,
 
1381
                                                        nodeMap.get(id).getLocation().y);
 
1382
                                }
 
1383
 
 
1384
                                int tmp = collapsedLevelSize.get(childLevel) + 1;
 
1385
                                collapsedLevelSize.put(childLevel, tmp);
 
1386
                        }
 
1387
                }
 
1388
 
 
1389
                //-------------Handle nodes that only appeared once
 
1390
                for (int i : collapsedNodesWithOnlyOneNodeInThem.keySet()) {
 
1391
                        int childID =collapsedNodesWithOnlyOneNodeInThem.get(i); 
 
1392
                        nodeDataMap.get(childID).onlyChildWithThisName = true;
 
1393
                        nodeDataMap.get(id).collapsedChildren.add(childID);
 
1394
                        newNodeMap.remove(nodeDataMap.get(childID).name);
 
1395
                        nodeDataMap.get(childID).collapsedParent = id;
 
1396
                        //This node is technically a part of itself
 
1397
                        nodeDataMap.get(childID).setPartOfCollapsedNode(childID);
 
1398
                        
 
1399
                        if (getNodeData(childID).isMarked())
 
1400
                                markedCollapsedNodes.add(childID);
 
1401
                }
 
1402
 
 
1403
 
 
1404
                
 
1405
                //-------------Finish iterations
 
1406
                for (int i : nodeDataMap.get(id).collapsedChildren) {
 
1407
                        recursivelyCollapseAllChildrenOfNode(i);
 
1408
                }
 
1409
 
 
1410
                collapsedNodesWithOnlyOneNodeInThem.clear();
 
1411
                newNodeMap.clear();
 
1412
 
 
1413
                nodeDataMap.get(id).hasCollapsedChildren = true;
 
1414
                return true;
 
1415
        }
 
1416
 
 
1417
        /**
 
1418
         * Add time, called values for the two given nodes, storing them inside
 
1419
         * victim. Also adds marked collapsed nodes to markedCollapsedNodes list
 
1420
         * @param target
 
1421
         * @param victim
 
1422
         */
 
1423
        public void aggregateData(StapData target, StapData victim) {
 
1424
                target.setTime(target.getTime()+ victim.getTime());
 
1425
                target.timesCalled += victim.timesCalled;
 
1426
                if (victim.isMarked() || target.isMarked()) {
 
1427
                        target.setMarked();
 
1428
                        markedCollapsedNodes.add(target.id);
 
1429
                }
 
1430
        }
 
1431
 
 
1432
        /*
 
1433
         * Convenience methods
 
1434
         */
 
1435
 
 
1436
        /**
 
1437
         * Prints the name of every node on the given level
 
1438
         * @param level
 
1439
         */
 
1440
        public void printContents(int level) {
 
1441
                if (levels.get(level) != null)
 
1442
                        return;
 
1443
                MP.println("Contents of level " + level + ":\n"); //$NON-NLS-1$ //$NON-NLS-2$
 
1444
                for (int i = 0; i < levels.get(level).size(); i++) {
 
1445
                        MP.println(nodeDataMap.get(levels.get(level).get(i)).name);
 
1446
                }
 
1447
                MP.println("---------------------------"); //$NON-NLS-1$
 
1448
        }
 
1449
 
 
1450
        /**
 
1451
         * 
 
1452
         * @param id of node
 
1453
         * @return StapNode
 
1454
         */
 
1455
        public StapNode getNode(int id) {
 
1456
                return nodeMap.get(id);
 
1457
        }
 
1458
 
 
1459
        /**
 
1460
         * 
 
1461
         * @param id of node
 
1462
         * @return StapData 
 
1463
         */
 
1464
        public StapData getNodeData(int id) {
 
1465
                return nodeDataMap.get(id);
 
1466
        }
 
1467
 
 
1468
        /**
 
1469
         * Recommend using getFirstUsefulNode instead.
 
1470
         * @return First node in level 0
 
1471
         */
 
1472
        public int getTopNode() {
 
1473
                return levels.get(topLevelToDraw).get(0);
 
1474
        }
 
1475
        
 
1476
        /**
 
1477
         * Recommend use of this function instead of getTopNode()
 
1478
         * @return First node that is not the dummy first node
 
1479
         */
 
1480
        public int getFirstUsefulNode() {
 
1481
                if (threaded)
 
1482
                        return 0;
 
1483
                int id = 0;
 
1484
                
 
1485
                if (nodeDataMap.get(id).name == CONSTANT_TOP_NODE_NAME) {
 
1486
                        id++;
 
1487
                }
 
1488
 
 
1489
                // Get first node that is not TOP_NODE_NAME
 
1490
                while (nodeDataMap.get(id) == null && id < idOfLastNode) {
 
1491
                        id++;
 
1492
                }
 
1493
 
 
1494
                return id;
 
1495
        }
 
1496
 
 
1497
        /**
 
1498
         * 
 
1499
         * @return Top level to draw - the level should be defined by draw algorithms 
 
1500
         */
 
1501
        public int getTopLevel() {
 
1502
                return topLevelToDraw;
 
1503
        }
 
1504
 
 
1505
        /**
 
1506
         * Returns the level of recursion associated with the given node.
 
1507
         * @param nodeID 
 
1508
         * @return
 
1509
         */
 
1510
        public int getLevelOfNode(int nodeID) {
 
1511
                return nodeDataMap.get(nodeID).levelOfRecursion;
 
1512
        }
 
1513
 
 
1514
        /**
 
1515
         * Returns true if the given node has any children.
 
1516
         * @param nodeID
 
1517
         * @return
 
1518
         */
 
1519
        public boolean hasChildren(int nodeID) {
 
1520
                if (nodeDataMap.get(nodeID).children.size() > 0)
 
1521
                        return true;
 
1522
                return false;
 
1523
        }
 
1524
 
 
1525
        /**
 
1526
         * Attempts to set dimensions (not used)
 
1527
         * @param width
 
1528
         * @param height
 
1529
         */
 
1530
        public void setDimensions(int width, int height) {
 
1531
                this.getBounds().width = width;
 
1532
                this.getBounds().height = height;
 
1533
        }
 
1534
        
 
1535
        /**
 
1536
         * Sets animation mode - all available modes are named 
 
1537
         * StapGraph.CONSTANT_ANIMATION_*
 
1538
         * @param mode
 
1539
         */
 
1540
        public void setAnimationMode(int mode) {
 
1541
                animation_mode = mode;
 
1542
                if (mode == CONSTANT_ANIMATION_SLOW){
 
1543
                        callgraphView.getAnimation_slow().setChecked(true);
 
1544
                        callgraphView.getAnimation_fast().setChecked(false);
 
1545
                }else if (mode == CONSTANT_ANIMATION_FASTEST){
 
1546
                        callgraphView.getAnimation_slow().setChecked(false);
 
1547
                        callgraphView.getAnimation_fast().setChecked(true);                     
 
1548
                }
 
1549
        }
 
1550
        
 
1551
        public void setCollapseMode(boolean value) {
 
1552
                if (collapse_mode == value || 
 
1553
                                draw_mode == StapGraph.CONSTANT_DRAWMODE_AGGREGATE)
 
1554
                        return;
 
1555
                
 
1556
                if (draw_mode != StapGraph.CONSTANT_DRAWMODE_LEVEL) {
 
1557
                        if (collapse_mode) {
 
1558
                                //Collapsed to noncollapsed
 
1559
                                if (!getRootData().isOnlyChildWithThisName()) {
 
1560
                                        //A collapsed node that isn't an only child must have an
 
1561
                                        //uncollapsed piece
 
1562
                                        rootVisibleNodeNumber = getRootData().uncollapsedPiece;
 
1563
                                }
 
1564
        
 
1565
                        } else {
 
1566
                                //Uncollapsed to collapsed -- set center node to collapsed node
 
1567
                                if (!getRootData().isOnlyChildWithThisName()) {
 
1568
                                        int temp = getRootData().getPartOfCollapsedNode();
 
1569
                                        if (temp != StapData.NOT_PART_OF_COLLAPSED_NODE) {
 
1570
                                                rootVisibleNodeNumber = temp;
 
1571
                                        }
 
1572
                                }
 
1573
                        }
 
1574
                }
 
1575
                collapse_mode = value;
 
1576
                callgraphView.getMode_collapsednodes().setChecked(value);
 
1577
                nextMarkedNode = -1;
 
1578
        }
 
1579
 
 
1580
        /**
 
1581
         * 
 
1582
         * @return getNodeData(getRootVisibleNodeNumber())
 
1583
         */
 
1584
        public StapData getRootData() {
 
1585
                return getNodeData(getRootVisibleNodeNumber());
 
1586
        }
 
1587
        
 
1588
        /**
 
1589
         * Gets id of root visible node
 
1590
         * @return rootVisibleNode - ID of centre node
 
1591
         */
 
1592
        public int getRootVisibleNodeNumber() {
 
1593
                return rootVisibleNodeNumber;
 
1594
        }
 
1595
 
 
1596
        /**
 
1597
         * Sets id of root visible node
 
1598
         * @param id - ID of centre node
 
1599
         */
 
1600
        public void setRootVisibleNodeNumber(int id) {
 
1601
                this.rootVisibleNodeNumber = id;
 
1602
        }
 
1603
 
 
1604
        /**
 
1605
         * Gets to the total time spent running tapped program
 
1606
         * @return Time in milliseconds
 
1607
         */
 
1608
        public long getTotalTime() {
 
1609
                if (totalTime == 0 || totalTime > 1200000000000000000l)
 
1610
                        return endTime - startTime;
 
1611
                return totalTime;
 
1612
        }
 
1613
 
 
1614
        /**
 
1615
         * Sets total time spent running tapped program
 
1616
         * @param totalTime - Time in milliseconds
 
1617
         */
 
1618
        public void setTotalTime(long val) {
 
1619
                this.totalTime = val;
 
1620
        }
 
1621
 
 
1622
        /**
 
1623
         * 
 
1624
         * @return Number of the lowest level in which nodes exist
 
1625
         */
 
1626
        public int getLowestLevelOfNodesAdded() {
 
1627
                return lowestLevelOfNodesAdded;
 
1628
        }
 
1629
 
 
1630
        /**
 
1631
         * Set lowest level to which nodes have been added
 
1632
         * 
 
1633
         * WARNING: Do not set this without adding nodes to that level first, or there
 
1634
         * may be null pointer exceptions.
 
1635
         * @param lowestLevelOfNodesAdded
 
1636
         */
 
1637
        public void setLowestLevelOfNodesAdded(int lowestLevelOfNodesAdded) {
 
1638
                this.lowestLevelOfNodesAdded = lowestLevelOfNodesAdded;
 
1639
        }
 
1640
        
 
1641
        
 
1642
        public int getDrawMode() {
 
1643
                return draw_mode;
 
1644
        }
 
1645
 
 
1646
        public void setDrawMode(int draw_mode) {
 
1647
                this.draw_mode = draw_mode;
 
1648
        }
 
1649
 
 
1650
        /**
 
1651
         * Resets the tree and graph to center on the first useful node.
 
1652
         * Does NOT change draw mode, animation mode or collapse mode.
 
1653
         */
 
1654
        public void reset() {
 
1655
                setSelection(null);
 
1656
                
 
1657
                draw(draw_mode, animation_mode, getFirstUsefulNode());
 
1658
                if (! (draw_mode == StapGraph.CONSTANT_DRAWMODE_AGGREGATE)){
 
1659
                        getNode(getFirstUsefulNode()).unhighlight();                    
 
1660
                }
 
1661
                if (treeViewer!=null) {
 
1662
                        treeViewer.collapseAll();
 
1663
                        treeViewer.expandToLevel(2);
 
1664
                }
 
1665
                scale = 1;
 
1666
                nextMarkedNode = -1;
 
1667
        }
 
1668
        
 
1669
        
 
1670
        public boolean isCollapseMode() {
 
1671
                return this.collapse_mode;
 
1672
        }
 
1673
 
 
1674
 
 
1675
        public void setBottomLevelToDraw(int bottomLevelToDraw) {
 
1676
                this.bottomLevelToDraw = bottomLevelToDraw;
 
1677
        }
 
1678
 
 
1679
 
 
1680
        public int getBottomLevelToDraw() {
 
1681
                return bottomLevelToDraw;
 
1682
        }
 
1683
 
 
1684
 
 
1685
        public void setTopLevelOnScreen(int topLevelOnScreen) {
 
1686
                this.topLevelOnScreen = topLevelOnScreen;
 
1687
        }
 
1688
 
 
1689
 
 
1690
        public int getTopLevelOnScreen() {
 
1691
                return topLevelOnScreen;
 
1692
        }
 
1693
        
 
1694
 
 
1695
        /**
 
1696
         * Returns a list of all nodes at the given level.
 
1697
         * 
 
1698
         * @param level
 
1699
         * @return List of ID's of all nodes at the given level.
 
1700
         */
 
1701
        public List<Integer> getLevel(int level) {
 
1702
                if (level < 0 || level > lowestLevelOfNodesAdded) {
 
1703
                        return null;
 
1704
                }
 
1705
                
 
1706
                return levels.get(level); 
 
1707
        }
 
1708
 
 
1709
 
 
1710
        public TreeViewer getTreeViewer() {
 
1711
                return treeViewer;
 
1712
        }
 
1713
        
 
1714
        
 
1715
        /**
 
1716
         * Returns the StapNode object for the parent of node id. May be null. 
 
1717
         * @param id
 
1718
         * @return
 
1719
         */
 
1720
        public StapNode getParentNode(int id) {
 
1721
                return nodeMap.get(nodeDataMap.get(id).parent);
 
1722
        }
 
1723
        
 
1724
        
 
1725
        /**
 
1726
         * Returns a StapData object for the parent of node id. May be null. 
 
1727
         * @param id
 
1728
         * @return
 
1729
         */
 
1730
        public StapData getParentData(int id) {
 
1731
                return nodeDataMap.get(nodeDataMap.get(id).parent);
 
1732
        }
 
1733
        
 
1734
        /**
 
1735
         * Returns the id of the next node that was called. This is necessarily either a
 
1736
         * child of the current root node or of one of its ancestors' children.
 
1737
         * 
 
1738
         * @param id
 
1739
         * @return Id of next node that was called.
 
1740
         */
 
1741
        public int getNextCalledNode(int id) {
 
1742
                int returnID = -1;
 
1743
                
 
1744
                if (isCollapseMode()) {
 
1745
                        setCollapseMode(false);
 
1746
                        //Redraw the current graph in uncollapsed mode if currently collapsed
 
1747
                        draw();
 
1748
                }
 
1749
                
 
1750
                for (int count = callOrderList.indexOf((Integer)id) + 1;
 
1751
                        count < callOrderList.size(); count++) {
 
1752
                        int next = callOrderList.get(count);
 
1753
                        if (getNodeData(id) == null)
 
1754
                                continue;
 
1755
                        if (!getNodeData(next).isCollapsed || getNodeData(next).isOnlyChildWithThisName()) {
 
1756
                                return next;
 
1757
                        }
 
1758
                }
 
1759
                
 
1760
                
 
1761
                return returnID;
 
1762
        }
 
1763
        
 
1764
        /**
 
1765
         * Returns the id of the previous node that was called.
 
1766
         * 
 
1767
         * @param id
 
1768
         * @return Id of previous node that was called.
 
1769
         */
 
1770
        public int getPreviousCalledNode(int id) {
 
1771
                int returnID = -1;
 
1772
                
 
1773
                for (int count = callOrderList.indexOf((Integer)id) - 1;
 
1774
                        count > -1; count--) {
 
1775
                        if (getNodeData(id) == null)
 
1776
                                continue;
 
1777
                        if (!getNodeData(id).isCollapsed || getNodeData(id).isOnlyChildWithThisName()) {
 
1778
                                returnID = callOrderList.get(count);
 
1779
                                return returnID;
 
1780
                        }
 
1781
                }
 
1782
                
 
1783
                
 
1784
                return returnID;
 
1785
        }
 
1786
        
 
1787
        
 
1788
 
 
1789
        /**
 
1790
         * Returns the id of the next marked node in current collapse mode. 
 
1791
         * Wraps back to the first marked node.
 
1792
         * 
 
1793
         * @return Node id of next marked node.
 
1794
         */
 
1795
        public int getNextMarkedNode() {
 
1796
                List<Integer> list = markedNodes;
 
1797
                if (collapse_mode)
 
1798
                        list = markedCollapsedNodes;
 
1799
                
 
1800
                if (list.size() == 0)
 
1801
                        return -1;
 
1802
                
 
1803
                
 
1804
                nextMarkedNode++;
 
1805
                if (nextMarkedNode >= list.size())
 
1806
                        nextMarkedNode = 0;
 
1807
                
 
1808
                
 
1809
                return list.get(nextMarkedNode);
 
1810
        }
 
1811
        
 
1812
        
 
1813
        /**
 
1814
         * Returns the id of the next marked node in current collapse mode. 
 
1815
         * Wraps back to the first marked node.
 
1816
         * 
 
1817
         * @return Node id of next marked node.
 
1818
         */
 
1819
        public int getPreviousMarkedNode() {
 
1820
                List<Integer> list = markedNodes;
 
1821
                if (collapse_mode)
 
1822
                        list = markedCollapsedNodes;
 
1823
                
 
1824
                if (list.size() == 0)
 
1825
                        return -1;
 
1826
                
 
1827
                nextMarkedNode--;
 
1828
                if (nextMarkedNode < 0)
 
1829
                        nextMarkedNode = list.size() - 1;
 
1830
                
 
1831
                
 
1832
                return list.get(nextMarkedNode);
 
1833
        }
 
1834
        
 
1835
        
 
1836
        public void play() {
 
1837
                if (proj == null || proj.getResult() == Status.OK_STATUS) {
 
1838
                        proj = new Projectionist("Projectionist", this, 2000);  //$NON-NLS-1$
 
1839
                        proj.schedule();
 
1840
                } else {
 
1841
                        proj.pause();
 
1842
                }
 
1843
        }
 
1844
 
 
1845
        
 
1846
        
 
1847
        public static final Comparator<Entry<String, Long>> VALUE_ORDER = new Comparator<Entry<String, Long>>()
 
1848
    {
 
1849
        public int compare(Entry<String, Long> a, Entry<String, Long> b){
 
1850
                return ((Long)a.getValue()).compareTo(((Long)b.getValue()));
 
1851
        }
 
1852
    };
 
1853
 
 
1854
        
 
1855
    /**
 
1856
     * Increments the scrollbars by x, y
 
1857
     * 
 
1858
     * @param x
 
1859
     * @param y
 
1860
     */
 
1861
    public void scrollBy(int x, int y) {
 
1862
        this.scrollTo(this.getHorizontalBar().getSelection() + x, 
 
1863
                        this.getVerticalBar().getSelection() + y);
 
1864
    }
 
1865
    
 
1866
    
 
1867
    /**
 
1868
     * Smoothly increments the scrollbars by x, y
 
1869
     * 
 
1870
     * @param x
 
1871
     * @param y
 
1872
     */
 
1873
    public void scrollSmoothBy(int x, int y) {
 
1874
        this.scrollSmoothTo(this.getHorizontalBar().getSelection() + x, 
 
1875
                        this.getVerticalBar().getSelection() + y);
 
1876
    }
 
1877
    
 
1878
    /**
 
1879
     * Retruns the number of StapData objects placed in the nodeDataMap.
 
1880
     * @return
 
1881
     */
 
1882
    public int getNodeDataMapSize() {
 
1883
        return nodeDataMap.size();
 
1884
    }
 
1885
    
 
1886
        
 
1887
        public int getAnimationMode() {
 
1888
                return animation_mode;
 
1889
        }
 
1890
 
 
1891
 
 
1892
        public int getLevelBuffer() {
 
1893
                return levelBuffer;
 
1894
        }
 
1895
 
 
1896
 
 
1897
        public void setLevelBuffer(int val) {
 
1898
                levelBuffer = val;
 
1899
        }
 
1900
        
 
1901
        public int min(int a, int b) {
 
1902
                if (a < b) return a;
 
1903
                return b;
 
1904
        }
 
1905
 
 
1906
        public int max(int a, int b) {
 
1907
                if (a > b) return a;
 
1908
                return b;
 
1909
        }
 
1910
 
 
1911
        public int getMaxNodes() {
 
1912
                return maxNodes;
 
1913
        }
 
1914
 
 
1915
        public void setMaxNodes(int val) {
 
1916
                maxNodes = val;
 
1917
        }
 
1918
 
 
1919
        public ArrayList<Integer> getCallOrderList() {
 
1920
                return callOrderList;
 
1921
        }
 
1922
 
 
1923
        public void setCallOrderList(ArrayList<Integer> callOrderList) {
 
1924
                this.callOrderList = callOrderList;
 
1925
        }
 
1926
 
 
1927
        public int getLastFunctionCalled() {
 
1928
                return lastFunctionCalled;
 
1929
        }
 
1930
 
 
1931
        public void setLastFunctionCalled(int lastFunctionCalled) {
 
1932
                this.lastFunctionCalled = lastFunctionCalled;
 
1933
        }
 
1934
 
 
1935
        public ICProject getProject() {
 
1936
                return project;
 
1937
        }
 
1938
 
 
1939
        public Projectionist getProjectionist() {
 
1940
                return proj;
 
1941
        }
 
1942
 
 
1943
        public void setProject(ICProject myProject) {
 
1944
                this.project = myProject;
 
1945
        }
 
1946
        
 
1947
        public CallgraphView getCallgraphView() {
 
1948
                return callgraphView;
 
1949
        }
 
1950
 
 
1951
        public void setEndTime(long val) {
 
1952
                endTime = val;
 
1953
        }
 
1954
        
 
1955
        public long getEndTime() {
 
1956
                return endTime;
 
1957
        }
 
1958
 
 
1959
        public void setStartTime(long val) {
 
1960
                startTime = val;                
 
1961
        }
 
1962
 
 
1963
        public void setThreaded() {
 
1964
                threaded = true;                
 
1965
        }
 
1966
        
 
1967
        public boolean getCollapseMode() {
 
1968
                return collapse_mode;
 
1969
        }
 
1970
 
 
1971
 
 
1972
 
 
1973
        public void addCalled(int idChild) {
 
1974
                getNodeData(idChild).timesCalled++;
 
1975
        }
 
1976
}