tmf: Refactor TMF statistics
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / viewers / statistics / model / TmfStatisticsTree.java
1 /*******************************************************************************
2 * Copyright (c) 2011, 2012 Ericsson
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Mathieu Denis <mathieu.denis@polymtl.ca> - Implementation and Initial API
11 *
12 *******************************************************************************/
13
14 package org.eclipse.linuxtools.tmf.ui.viewers.statistics.model;
15
16 import java.util.Arrays;
17 import java.util.HashMap;
18 import java.util.HashSet;
19 import java.util.LinkedList;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Set;
23
24 /**
25 * Base class for the statistics storage. It allow to implement a tree structure
26 * while avoiding the need to run through the tree each time you need to add a
27 * node at a given place.
28 *
29 * @author Mathieu Denis
30 * @version 2.0
31 * @since 2.0
32 */
33 public class TmfStatisticsTree {
34
35 /**
36 * Identification of the root.
37 */
38 public static final String[] ROOT = new String[] { "root" }; //$NON-NLS-1$
39
40 /**
41 * Header for the event type categories.
42 */
43 public static final String HEADER_EVENT_TYPES = Messages.TmfStatisticsData_EventTypes;
44
45 /**
46 * Indicate that it's a value.
47 *
48 * Used when checking the possible child node for a node.
49 *
50 * It differentiate a category of a value by being appended to a value.
51 */
52 protected static final String NODE = "z"; //$NON-NLS-1$
53
54 /**
55 * Root node key.
56 */
57 protected static final String ROOT_NODE_KEY = mergeString(ROOT[0], NODE);
58
59 /**
60 * Define what children a node can have. The management and usage of this map
61 * is done by subclasses. HashSet are always faster than TreeSet for String keys.
62 */
63 protected Map<String, Set<String>> fKeys;
64
65 /**
66 * The nodes in the tree.
67 */
68 protected Map<List<String>, TmfStatisticsTreeNode> fNodes;
69
70 /**
71 * Default constructor. Creates base statistics tree for counting total
72 * number of events and number of events per event type.
73 */
74 public TmfStatisticsTree() {
75 fNodes = new HashMap<List<String>, TmfStatisticsTreeNode>();
76 fKeys = new HashMap<String, Set<String>>();
77
78 Map<String, Set<String>> keys = getKeys();
79
80 // //////////// Adding category sets
81 // common
82 keys.put(HEADER_EVENT_TYPES, new HashSet<String>());
83
84 // /////////// Adding value sets
85 // Under a trace
86 Set<String> temp = new HashSet<String>(8);
87 temp.add(HEADER_EVENT_TYPES);
88 keys.put(ROOT_NODE_KEY, temp);
89 // Under an event type
90 temp = new HashSet<String>(16);
91 keys.put(mergeString(HEADER_EVENT_TYPES, NODE), temp);
92
93 // //////////// CREATE root
94 keys.put(ROOT[0], new HashSet<String>(2)); // 1 trace at the time
95 getOrCreate(ROOT);
96 }
97
98 /**
99 * Get a node.
100 *
101 * @param path
102 * Path to the node.
103 * @return The node or null.
104 */
105 public TmfStatisticsTreeNode get(String... path) {
106 List<String> pathAsList = Arrays.asList(path);
107 return fNodes.get(pathAsList);
108 }
109
110 /**
111 * Get the children of a node.
112 *
113 * @param path
114 * Path to the node.
115 * @return Collection containing the children.
116 */
117 public List<TmfStatisticsTreeNode> getChildren(String... path) {
118 List<TmfStatisticsTreeNode> result = new LinkedList<TmfStatisticsTreeNode>();
119
120 if (path.length % 2 == 0) { // if we are at a Category
121 TmfStatisticsTreeNode current = null;
122 for (String value : getKeys().get(path[path.length - 1])) {
123 current = get(addToArray(path, value));
124 if (current != null) {
125 if (current.getValues().getTotal() > 0 || current.getValues().getPartial() > 0) {
126 result.add(current);
127 }
128 }
129 }
130 } else if (path.length == 1) { // Special case.
131 if (path.equals(ROOT)) {
132 for (String value : getKeys().get(ROOT[0])) {
133 result.add(getOrCreate(value));
134 }
135 } else {
136 // Get value under the root
137 for (String value : getKeys().get(ROOT_NODE_KEY)) {
138 result.add(getOrCreate(addToArray(path, value)));
139 }
140 }
141 } else {// If we are at a value
142 for (String value : getKeys().get(mergeString(path[path.length - 2], NODE))) {
143 // Search the parent name + NODE
144 result.add(getOrCreate(addToArray(path, value)));
145 }
146 }
147
148 return result;
149 }
150
151 /**
152 * Get every children of a node, even if it doesn't have any registered
153 * events, as opposed to getChildren
154 *
155 * @param path
156 * Path to the node.
157 * @return Collection containing all the children.
158 */
159 public List<TmfStatisticsTreeNode> getAllChildren(String... path) {
160 LinkedList<TmfStatisticsTreeNode> result = new LinkedList<TmfStatisticsTreeNode>();
161
162 if (path.length % 2 == 0) { // if we are at a Category
163 TmfStatisticsTreeNode current = null;
164 for (String value : getKeys().get(path[path.length - 1])) {
165 current = get(addToArray(path, value));
166 if (current != null) {
167 result.add(current);
168 }
169 }
170 } else if (path.length == 1) { // Special case.
171 if (path.equals(ROOT)) {
172 for (String value : getKeys().get(ROOT[0])) {
173 result.add(getOrCreate(value));
174 }
175 } else {
176 // Get value under the root
177 for (String value : getKeys().get(ROOT_NODE_KEY)) {
178 result.add(getOrCreate(addToArray(path, value)));
179 }
180 }
181 } else {// If we are at a value
182 for (String value : getKeys().get(mergeString(path[path.length - 2], NODE))) {
183 // Search the parent name + NODE
184 result.add(getOrCreate(addToArray(path, value)));
185 }
186 }
187 return result;
188 }
189
190 /**
191 * Get the map of existing elements of path classified by parent.
192 *
193 * @return The map.
194 */
195 public Map<String, Set<String>> getKeys() {
196 return fKeys;
197 }
198
199 /**
200 * Get or create a node.
201 *
202 * @param path
203 * Path to the node.
204 * @return The node.
205 */
206 public TmfStatisticsTreeNode getOrCreate(String... path) {
207 List<String> pathAsList = Arrays.asList(path);
208 TmfStatisticsTreeNode current = fNodes.get(pathAsList);
209
210 if (current == null) {
211 registerName(path);
212 current = new TmfStatisticsTreeNode(this, path);
213 fNodes.put(pathAsList, current);
214 }
215 return current;
216 }
217
218 /**
219 * Get the parent of a node.
220 *
221 * @param path
222 * Path to the node.
223 * @return Parent node or null.
224 */
225 public TmfStatisticsTreeNode getParent(final String... path) {
226 if (path.length == 1) {
227 if (path.equals(ROOT)) {
228 return null;
229 }
230 return get(ROOT);
231 }
232
233 String[] parentPath = new String[path.length - 1];
234 System.arraycopy(path, 0, parentPath, 0, parentPath.length);
235 return get(parentPath);
236 }
237
238 /**
239 * Set the value to display in the "total" cells. This means the row
240 * indicating the total count of events for a trace.
241 *
242 * @param traceName
243 * The name of the trace (will be used as a sub-tree in the view)
244 * @param isGlobal
245 * Is this a for a global or a time range request? Determines if
246 * this goes in the Global column or the Selected Time Range one.
247 * @param qty
248 * The value to display
249 */
250 public void setTotal(String traceName, boolean isGlobal, long qty) {
251 String[][] paths = getNormalPaths(traceName);
252 for (String path[] : paths) {
253 getOrCreate(path).getValues().setValue(isGlobal, qty);
254 }
255 }
256
257 /**
258 * Set the value to display in the "Type count" cells. These are the counts
259 * for each event types.
260 *
261 * @param traceName
262 * The name of the trace (will be used as a sub-tree in the view)
263 * @param type
264 * The event type
265 * @param isGlobal
266 * Is this a for a global or a time range request? Determines if
267 * this goes in the Global column or the Selected Time Range one.
268 * @param qty
269 * The value to display
270 */
271 public void setTypeCount(String traceName, String type, boolean isGlobal, long qty) {
272 String[][] paths = getTypePaths(traceName, type);
273 for (String[] path : paths) {
274 getOrCreate(path).getValues().setValue(isGlobal, qty);
275 }
276 }
277
278 /**
279 * Get the event types paths.
280 *
281 * @param event
282 * Event to get the path for.
283 * @param extraInfo
284 * Extra information to pass along with the event
285 * @return Array of FixedArray representing the paths.
286 */
287 protected String[][] getTypePaths(String traceName, String type) {
288 String[][] paths = { new String[] {traceName, HEADER_EVENT_TYPES, type } };
289 return paths;
290 }
291
292 /**
293 * Get the standard paths for an event.
294 *
295 * @param event
296 * Event to get the path for.
297 * @param extraInfo
298 * Extra information to pass along with the event
299 * @return Array of FixedArray representing the paths.
300 */
301 protected String[][] getNormalPaths(String traceName) {
302 String[][] paths = { new String[] { traceName } };
303 return paths;
304 }
305
306 /**
307 * Register that a new node was created.
308 *
309 * Must make sure the {@link #getChildren(TmfFixedArray)} on the parent node
310 * will return the newly created node.
311 *
312 * @param path
313 * Path of the new node.
314 */
315 protected void registerName(String... path) {
316 if (path.length == 1) {
317 if (!path.equals(ROOT)) {
318 getKeys().get(ROOT[0]).add(path[0]);
319 }
320 } else if (path.length % 2 != 0) {
321 getKeys().get(path[path.length - 2]).add(path[path.length - 1]);
322 }
323 }
324
325 /**
326 * Resets a node.
327 *
328 * Works recursively.
329 *
330 * @param path
331 * Path to the node.
332 */
333 public void reset(final String... path) {
334 for (TmfStatisticsTreeNode node : getAllChildren(path)) {
335 reset(node.getPath());
336 List<String> nodePathList = Arrays.asList(node.getPath());
337 fNodes.remove(nodePathList);
338 }
339 }
340
341 /**
342 * Reset the global value of a node.
343 *
344 * Works recursively.
345 *
346 * @param path
347 * Path to the node.
348 * @since 2.0
349 */
350 public void resetGlobalValue(final String... path) {
351 for (TmfStatisticsTreeNode node : getChildren(path)) {
352 node.resetGlobalValue();
353 }
354 }
355
356 /**
357 * Reset the time range value of a node.
358 *
359 * Works recursively.
360 *
361 * @param path
362 * Path to the node.
363 * @since 2.0
364 */
365 public void resetTimeRangeValue(final String... path) {
366 for (TmfStatisticsTreeNode node : getChildren(path)) {
367 node.resetTimeRangeValue();
368 }
369 }
370
371 /**
372 * Function to merge many string more efficiently.
373 *
374 * @param strings
375 * Strings to merge.
376 * @return A new string containing all the strings.
377 */
378 protected static String mergeString(String... strings) {
379 StringBuilder builder = new StringBuilder();
380 for (String s : strings) {
381 builder.append(s);
382 }
383 return builder.toString();
384 }
385
386 /**
387 * Return a new array that's a copy of the old one, plus 'newElem' added at
388 * the end.
389 */
390 private static String[] addToArray(String[] array, String newElem) {
391 String[] newArray = new String[array.length + 1];
392 System.arraycopy(array, 0, newArray, 0, array.length);
393 newArray[array.length] = newElem;
394 return newArray;
395 }
396 }
This page took 0.039719 seconds and 6 git commands to generate.