Commit | Line | Data |
---|---|---|
79e08fd0 | 1 | /******************************************************************************* |
b544077e | 2 | * Copyright (c) 2011, 2012 Ericsson |
013a5f1c | 3 | * |
79e08fd0 BH |
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 | |
013a5f1c | 8 | * |
79e08fd0 | 9 | * Contributors: |
36033ff0 AM |
10 | * Mathieu Denis <mathieu.denis@polymtl.ca> - Implementation and Initial API |
11 | * | |
79e08fd0 BH |
12 | *******************************************************************************/ |
13 | ||
cfd22ad0 | 14 | package org.eclipse.linuxtools.tmf.ui.viewers.statistics.model; |
79e08fd0 | 15 | |
36033ff0 AM |
16 | import java.util.Arrays; |
17 | import java.util.HashMap; | |
79e08fd0 BH |
18 | import java.util.HashSet; |
19 | import java.util.LinkedList; | |
5673a177 | 20 | import java.util.List; |
79e08fd0 BH |
21 | import java.util.Map; |
22 | import java.util.Set; | |
23 | ||
79e08fd0 | 24 | /** |
36033ff0 AM |
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. | |
013a5f1c | 28 | * |
b544077e | 29 | * @author Mathieu Denis |
36033ff0 | 30 | * @version 2.0 |
cfd22ad0 | 31 | * @since 2.0 |
79e08fd0 | 32 | */ |
36033ff0 AM |
33 | public class TmfStatisticsTree { |
34 | ||
35 | /** | |
36 | * Identification of the root. | |
37 | */ | |
38 | public static final String[] ROOT = new String[] { "root" }; //$NON-NLS-1$ | |
79e08fd0 BH |
39 | |
40 | /** | |
25a042b3 | 41 | * Header for the event type categories. |
79e08fd0 | 42 | */ |
66711dc8 | 43 | public static final String HEADER_EVENT_TYPES = Messages.TmfStatisticsData_EventTypes; |
79e08fd0 BH |
44 | |
45 | /** | |
09667aa4 MD |
46 | * Indicate that it's a value. |
47 | * | |
79e08fd0 | 48 | * Used when checking the possible child node for a node. |
09667aa4 | 49 | * |
79e08fd0 | 50 | * It differentiate a category of a value by being appended to a value. |
79e08fd0 | 51 | */ |
66711dc8 | 52 | protected static final String NODE = "z"; //$NON-NLS-1$ |
09667aa4 | 53 | |
b544077e BH |
54 | /** |
55 | * Root node key. | |
56 | */ | |
5673a177 | 57 | protected static final String ROOT_NODE_KEY = mergeString(ROOT[0], NODE); |
79e08fd0 | 58 | |
36033ff0 AM |
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 | ||
b544077e BH |
70 | /** |
71 | * Default constructor. Creates base statistics tree for counting total | |
72 | * number of events and number of events per event type. | |
73 | */ | |
36033ff0 AM |
74 | public TmfStatisticsTree() { |
75 | fNodes = new HashMap<List<String>, TmfStatisticsTreeNode>(); | |
76 | fKeys = new HashMap<String, Set<String>>(); | |
77 | ||
79e08fd0 BH |
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 | |
5673a177 | 94 | keys.put(ROOT[0], new HashSet<String>(2)); // 1 trace at the time |
79e08fd0 BH |
95 | getOrCreate(ROOT); |
96 | } | |
97 | ||
36033ff0 AM |
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. | |
09667aa4 | 112 | * |
36033ff0 AM |
113 | * @param path |
114 | * Path to the node. | |
115 | * @return Collection containing the children. | |
79e08fd0 | 116 | */ |
5673a177 AM |
117 | public List<TmfStatisticsTreeNode> getChildren(String... path) { |
118 | List<TmfStatisticsTreeNode> result = new LinkedList<TmfStatisticsTreeNode>(); | |
79e08fd0 | 119 | |
5673a177 | 120 | if (path.length % 2 == 0) { // if we are at a Category |
79e08fd0 | 121 | TmfStatisticsTreeNode current = null; |
5673a177 AM |
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 | } | |
013a5f1c | 128 | } |
79e08fd0 | 129 | } |
5673a177 | 130 | } else if (path.length == 1) { // Special case. |
013a5f1c | 131 | if (path.equals(ROOT)) { |
5673a177 AM |
132 | for (String value : getKeys().get(ROOT[0])) { |
133 | result.add(getOrCreate(value)); | |
013a5f1c AM |
134 | } |
135 | } else { | |
79e08fd0 | 136 | // Get value under the root |
013a5f1c | 137 | for (String value : getKeys().get(ROOT_NODE_KEY)) { |
5673a177 | 138 | result.add(getOrCreate(addToArray(path, value))); |
013a5f1c AM |
139 | } |
140 | } | |
79e08fd0 | 141 | } else {// If we are at a value |
5673a177 | 142 | for (String value : getKeys().get(mergeString(path[path.length - 2], NODE))) { |
79e08fd0 | 143 | // Search the parent name + NODE |
5673a177 | 144 | result.add(getOrCreate(addToArray(path, value))); |
013a5f1c | 145 | } |
79e08fd0 BH |
146 | } |
147 | ||
148 | return result; | |
149 | } | |
150 | ||
36033ff0 AM |
151 | /** |
152 | * Get every children of a node, even if it doesn't have any registered | |
153 | * events, as opposed to getChildren | |
09667aa4 | 154 | * |
36033ff0 AM |
155 | * @param path |
156 | * Path to the node. | |
157 | * @return Collection containing all the children. | |
79e08fd0 | 158 | */ |
36033ff0 | 159 | public List<TmfStatisticsTreeNode> getAllChildren(String... path) { |
79e08fd0 BH |
160 | LinkedList<TmfStatisticsTreeNode> result = new LinkedList<TmfStatisticsTreeNode>(); |
161 | ||
5673a177 | 162 | if (path.length % 2 == 0) { // if we are at a Category |
79e08fd0 | 163 | TmfStatisticsTreeNode current = null; |
5673a177 AM |
164 | for (String value : getKeys().get(path[path.length - 1])) { |
165 | current = get(addToArray(path, value)); | |
013a5f1c | 166 | if (current != null) { |
79e08fd0 | 167 | result.add(current); |
013a5f1c | 168 | } |
79e08fd0 | 169 | } |
5673a177 | 170 | } else if (path.length == 1) { // Special case. |
013a5f1c | 171 | if (path.equals(ROOT)) { |
5673a177 AM |
172 | for (String value : getKeys().get(ROOT[0])) { |
173 | result.add(getOrCreate(value)); | |
013a5f1c AM |
174 | } |
175 | } else { | |
79e08fd0 | 176 | // Get value under the root |
013a5f1c | 177 | for (String value : getKeys().get(ROOT_NODE_KEY)) { |
5673a177 | 178 | result.add(getOrCreate(addToArray(path, value))); |
013a5f1c AM |
179 | } |
180 | } | |
79e08fd0 | 181 | } else {// If we are at a value |
5673a177 | 182 | for (String value : getKeys().get(mergeString(path[path.length - 2], NODE))) { |
79e08fd0 | 183 | // Search the parent name + NODE |
5673a177 | 184 | result.add(getOrCreate(addToArray(path, value))); |
013a5f1c | 185 | } |
79e08fd0 BH |
186 | } |
187 | return result; | |
188 | } | |
189 | ||
36033ff0 AM |
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 | */ | |
89c06060 | 250 | public void setTotal(String traceName, boolean isGlobal, long qty) { |
5673a177 AM |
251 | String[][] paths = getNormalPaths(traceName); |
252 | for (String path[] : paths) { | |
89c06060 | 253 | getOrCreate(path).getValues().setValue(isGlobal, qty); |
73fbf6be | 254 | } |
25a042b3 MD |
255 | } |
256 | ||
36033ff0 AM |
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 | */ | |
89c06060 | 271 | public void setTypeCount(String traceName, String type, boolean isGlobal, long qty) { |
5673a177 AM |
272 | String[][] paths = getTypePaths(traceName, type); |
273 | for (String[] path : paths) { | |
89c06060 | 274 | getOrCreate(path).getValues().setValue(isGlobal, qty); |
25a042b3 MD |
275 | } |
276 | } | |
277 | ||
79e08fd0 | 278 | /** |
09667aa4 | 279 | * Get the event types paths. |
013a5f1c | 280 | * |
79e08fd0 BH |
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 | */ | |
5673a177 AM |
287 | protected String[][] getTypePaths(String traceName, String type) { |
288 | String[][] paths = { new String[] {traceName, HEADER_EVENT_TYPES, type } }; | |
79e08fd0 BH |
289 | return paths; |
290 | } | |
291 | ||
292 | /** | |
09667aa4 | 293 | * Get the standard paths for an event. |
013a5f1c | 294 | * |
79e08fd0 BH |
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 | */ | |
5673a177 AM |
301 | protected String[][] getNormalPaths(String traceName) { |
302 | String[][] paths = { new String[] { traceName } }; | |
79e08fd0 BH |
303 | return paths; |
304 | } | |
013a5f1c | 305 | |
36033ff0 AM |
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. | |
09667aa4 | 311 | * |
36033ff0 AM |
312 | * @param path |
313 | * Path of the new node. | |
79e08fd0 | 314 | */ |
5673a177 AM |
315 | protected void registerName(String... path) { |
316 | if (path.length == 1) { | |
013a5f1c | 317 | if (!path.equals(ROOT)) { |
5673a177 | 318 | getKeys().get(ROOT[0]).add(path[0]); |
013a5f1c | 319 | } |
5673a177 AM |
320 | } else if (path.length % 2 != 0) { |
321 | getKeys().get(path[path.length - 2]).add(path[path.length - 1]); | |
013a5f1c | 322 | } |
79e08fd0 | 323 | } |
5673a177 | 324 | |
36033ff0 AM |
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 | ||
5673a177 AM |
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 | } | |
79e08fd0 | 396 | } |