ss: update some classes to current code standard
[deliverable/tracecompass.git] / statesystem / org.eclipse.tracecompass.statesystem.core / src / org / eclipse / tracecompass / internal / statesystem / core / backend / historytree / HistoryTree.java
CommitLineData
a52fde77 1/*******************************************************************************
e13bd4cd 2 * Copyright (c) 2010, 2015 Ericsson, École Polytechnique de Montréal, and others
3b7f5abe 3 *
a52fde77
AM
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
3b7f5abe 8 *
bb7f92ce
FW
9 * Contributors:
10 * Alexandre Montplaisir - Initial API and implementation
11 * Florian Wininger - Add Extension and Leaf Node
e13bd4cd 12 * Patrick Tasse - Add message to exceptions
a52fde77
AM
13 *******************************************************************************/
14
e894a508 15package org.eclipse.tracecompass.internal.statesystem.core.backend.historytree;
a52fde77
AM
16
17import java.io.File;
18import java.io.FileInputStream;
19import java.io.IOException;
20import java.io.PrintWriter;
21import java.nio.ByteBuffer;
22import java.nio.ByteOrder;
3b7f5abe 23import java.nio.channels.ClosedChannelException;
a52fde77 24import java.nio.channels.FileChannel;
cb42195c
AM
25import java.util.ArrayList;
26import java.util.Collections;
27import java.util.List;
a52fde77 28
e894a508
AM
29import org.eclipse.tracecompass.internal.statesystem.core.Activator;
30import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
31import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
a52fde77
AM
32
33/**
34 * Meta-container for the History Tree. This structure contains all the
35 * high-level data relevant to the tree.
3b7f5abe 36 *
ffd0aa67 37 * @author Alexandre Montplaisir
a52fde77 38 */
8d47cc34 39public class HistoryTree {
a52fde77 40
cb42195c
AM
41 /**
42 * Size of the "tree header" in the tree-file The nodes will use this offset
43 * to know where they should be in the file. This should always be a
44 * multiple of 4K.
45 */
46 public static final int TREE_HEADER_SIZE = 4096;
47
a52fde77
AM
48 private static final int HISTORY_FILE_MAGIC_NUMBER = 0x05FFA900;
49
a96cc6be 50 /** File format version. Increment when breaking compatibility. */
da66cc75 51 private static final int FILE_VERSION = 5;
a52fde77 52
dbdc452f
AM
53 // ------------------------------------------------------------------------
54 // Tree-specific configuration
55 // ------------------------------------------------------------------------
56
57 /** Container for all the configuration constants */
0e9b2f07 58 private final HTConfig fConfig;
a52fde77 59
dbdc452f 60 /** Reader/writer object */
0e9b2f07 61 private final HT_IO fTreeIO;
a52fde77 62
dbdc452f 63 // ------------------------------------------------------------------------
ecb12461 64 // Variable Fields (will change throughout the existence of the SHT)
dbdc452f
AM
65 // ------------------------------------------------------------------------
66
67 /** Latest timestamp found in the tree (at any given moment) */
0e9b2f07 68 private long fTreeEnd;
a52fde77 69
360f899e 70 /** The total number of nodes that exists in this tree */
0e9b2f07 71 private int fNodeCount;
a52fde77 72
dbdc452f 73 /** "Cache" to keep the active nodes in memory */
0e9b2f07 74 private final List<HTNode> fLatestBranch;
a52fde77 75
dbdc452f
AM
76 // ------------------------------------------------------------------------
77 // Constructors/"Destructors"
78 // ------------------------------------------------------------------------
79
a52fde77 80 /**
8d47cc34
AM
81 * Create a new State History from scratch, using a {@link HTConfig} object
82 * for configuration.
83 *
84 * @param conf
85 * The config to use for this History Tree.
86 * @throws IOException
87 * If an error happens trying to open/write to the file
88 * specified in the config
a52fde77 89 */
8d47cc34 90 public HistoryTree(HTConfig conf) throws IOException {
a52fde77 91 /*
bb7f92ce
FW
92 * Simple check to make sure we have enough place in the 0th block for
93 * the tree configuration
a52fde77 94 */
cb42195c 95 if (conf.getBlockSize() < TREE_HEADER_SIZE) {
dbdc452f
AM
96 throw new IllegalArgumentException();
97 }
a52fde77 98
0e9b2f07
GB
99 fConfig = conf;
100 fTreeEnd = conf.getTreeStart();
101 fNodeCount = 0;
102 fLatestBranch = Collections.synchronizedList(new ArrayList<HTNode>());
a52fde77
AM
103
104 /* Prepare the IO object */
0e9b2f07 105 fTreeIO = new HT_IO(fConfig, true);
a52fde77
AM
106
107 /* Add the first node to the tree */
bb7f92ce 108 LeafNode firstNode = initNewLeafNode(-1, conf.getTreeStart());
0e9b2f07 109 fLatestBranch.add(firstNode);
a52fde77
AM
110 }
111
a52fde77
AM
112 /**
113 * "Reader" constructor : instantiate a SHTree from an existing tree file on
114 * disk
3b7f5abe 115 *
8d47cc34 116 * @param existingStateFile
a52fde77 117 * Path/filename of the history-file we are to open
a96cc6be
AM
118 * @param expProviderVersion
119 * The expected version of the state provider
a52fde77 120 * @throws IOException
8d47cc34 121 * If an error happens reading the file
a52fde77 122 */
8d47cc34 123 public HistoryTree(File existingStateFile, int expProviderVersion) throws IOException {
a52fde77
AM
124 /*
125 * Open the file ourselves, get the tree header information we need,
126 * then pass on the descriptor to the TreeIO object.
127 */
128 int rootNodeSeqNb, res;
129 int bs, maxc;
fb12b0c2 130 long startTime;
a52fde77
AM
131
132 /* Java I/O mumbo jumbo... */
fee997a5
AM
133 if (!existingStateFile.exists()) {
134 throw new IOException("Selected state file does not exist"); //$NON-NLS-1$
135 }
fb12b0c2 136 if (existingStateFile.length() <= 0) {
a96cc6be 137 throw new IOException("Empty target file"); //$NON-NLS-1$
a52fde77
AM
138 }
139
a4524c1b
AM
140 try (FileInputStream fis = new FileInputStream(existingStateFile);
141 FileChannel fc = fis.getChannel();) {
a52fde77 142
a4524c1b 143 ByteBuffer buffer = ByteBuffer.allocate(TREE_HEADER_SIZE);
a52fde77 144
a4524c1b
AM
145 buffer.order(ByteOrder.LITTLE_ENDIAN);
146 buffer.clear();
147 fc.read(buffer);
148 buffer.flip();
a52fde77 149
a96cc6be 150 /*
a4524c1b
AM
151 * Check the magic number to make sure we're opening the right type
152 * of file
a96cc6be 153 */
a4524c1b
AM
154 res = buffer.getInt();
155 if (res != HISTORY_FILE_MAGIC_NUMBER) {
156 throw new IOException("Wrong magic number"); //$NON-NLS-1$
157 }
158
159 res = buffer.getInt(); /* File format version number */
160 if (res != FILE_VERSION) {
161 throw new IOException("Mismatching History Tree file format versions"); //$NON-NLS-1$
162 }
163
164 res = buffer.getInt(); /* Event handler's version number */
165 if (res != expProviderVersion &&
bcec0116 166 expProviderVersion != ITmfStateSystemBuilder.IGNORE_PROVIDER_VERSION) {
a4524c1b
AM
167 /*
168 * The existing history was built using an event handler that
169 * doesn't match the current one in the framework.
170 *
171 * Information could be all wrong. Instead of keeping an
172 * incorrect history file, a rebuild is done.
173 */
174 throw new IOException("Mismatching event handler versions"); //$NON-NLS-1$
175 }
176
177 bs = buffer.getInt(); /* Block Size */
178 maxc = buffer.getInt(); /* Max nb of children per node */
a52fde77 179
0e9b2f07 180 fNodeCount = buffer.getInt();
a4524c1b
AM
181 rootNodeSeqNb = buffer.getInt();
182 startTime = buffer.getLong();
a52fde77 183
0e9b2f07 184 fConfig = new HTConfig(existingStateFile, bs, maxc, expProviderVersion, startTime);
a4524c1b 185 }
a52fde77 186
a52fde77
AM
187 /*
188 * FIXME We close fis here and the TreeIO will then reopen the same
189 * file, not extremely elegant. But how to pass the information here to
190 * the SHT otherwise?
191 */
0e9b2f07 192 fTreeIO = new HT_IO(fConfig, false);
a52fde77 193
0e9b2f07
GB
194 fLatestBranch = buildLatestBranch(rootNodeSeqNb);
195 fTreeEnd = getRootNode().getNodeEnd();
fb12b0c2
AM
196
197 /*
198 * Make sure the history start time we read previously is consistent
199 * with was is actually in the root node.
200 */
412a0225 201 if (startTime != getRootNode().getNodeStart()) {
fb12b0c2
AM
202 throw new IOException("Inconsistent start times in the" + //$NON-NLS-1$
203 "history file, it might be corrupted."); //$NON-NLS-1$
204 }
a52fde77
AM
205 }
206
412a0225
AM
207 /**
208 * Rebuild the latestBranch "cache" object by reading the nodes from disk
209 * (When we are opening an existing file on disk and want to append to it,
210 * for example).
211 *
212 * @param rootNodeSeqNb
213 * The sequence number of the root node, so we know where to
214 * start
215 * @throws ClosedChannelException
216 */
bb7f92ce
FW
217 private List<HTNode> buildLatestBranch(int rootNodeSeqNb) throws ClosedChannelException {
218 List<HTNode> list = new ArrayList<>();
412a0225 219
0e9b2f07 220 HTNode nextChildNode = fTreeIO.readNode(rootNodeSeqNb);
bb7f92ce 221 list.add(nextChildNode);
412a0225 222
bb7f92ce
FW
223 /* Follow the last branch up to the leaf */
224 while (nextChildNode.getNodeType() == HTNode.NodeType.CORE) {
0e9b2f07 225 nextChildNode = fTreeIO.readNode(((CoreNode) nextChildNode).getLatestChild());
bb7f92ce 226 list.add(nextChildNode);
412a0225
AM
227 }
228 return Collections.synchronizedList(list);
229 }
230
a52fde77
AM
231 /**
232 * "Save" the tree to disk. This method will cause the treeIO object to
233 * commit all nodes to disk and then return the RandomAccessFile descriptor
234 * so the Tree object can save its configuration into the header of the
235 * file.
3b7f5abe 236 *
a52fde77 237 * @param requestedEndTime
d862bcb3 238 * The greatest timestamp present in the history tree
a52fde77 239 */
8d47cc34 240 public void closeTree(long requestedEndTime) {
412a0225 241 /* This is an important operation, queries can wait */
0e9b2f07 242 synchronized (fLatestBranch) {
412a0225
AM
243 /*
244 * Work-around the "empty branches" that get created when the root
245 * node becomes full. Overwrite the tree's end time with the
246 * original wanted end-time, to ensure no queries are sent into
247 * those empty nodes.
248 *
249 * This won't be needed once extended nodes are implemented.
250 */
0e9b2f07 251 fTreeEnd = requestedEndTime;
6a1074ce 252
412a0225 253 /* Close off the latest branch of the tree */
0e9b2f07
GB
254 for (int i = 0; i < fLatestBranch.size(); i++) {
255 fLatestBranch.get(i).closeThisNode(fTreeEnd);
256 fTreeIO.writeNode(fLatestBranch.get(i));
412a0225 257 }
a52fde77 258
0e9b2f07 259 try (FileChannel fc = fTreeIO.getFcOut();) {
412a0225
AM
260 ByteBuffer buffer = ByteBuffer.allocate(TREE_HEADER_SIZE);
261 buffer.order(ByteOrder.LITTLE_ENDIAN);
262 buffer.clear();
a52fde77 263
412a0225
AM
264 /* Save the config of the tree to the header of the file */
265 fc.position(0);
a52fde77 266
412a0225 267 buffer.putInt(HISTORY_FILE_MAGIC_NUMBER);
a52fde77 268
412a0225 269 buffer.putInt(FILE_VERSION);
0e9b2f07 270 buffer.putInt(fConfig.getProviderVersion());
a52fde77 271
0e9b2f07
GB
272 buffer.putInt(fConfig.getBlockSize());
273 buffer.putInt(fConfig.getMaxChildren());
a52fde77 274
0e9b2f07 275 buffer.putInt(fNodeCount);
a52fde77 276
412a0225 277 /* root node seq. nb */
0e9b2f07 278 buffer.putInt(fLatestBranch.get(0).getSequenceNumber());
a52fde77 279
412a0225 280 /* start time of this history */
0e9b2f07 281 buffer.putLong(fLatestBranch.get(0).getNodeStart());
fb12b0c2 282
412a0225
AM
283 buffer.flip();
284 int res = fc.write(buffer);
285 assert (res <= TREE_HEADER_SIZE);
286 /* done writing the file header */
a52fde77 287
412a0225
AM
288 } catch (IOException e) {
289 /*
290 * If we were able to write so far, there should not be any
291 * problem at this point...
292 */
293 throw new RuntimeException("State system write error"); //$NON-NLS-1$
294 }
a52fde77 295 }
a52fde77
AM
296 }
297
dbdc452f
AM
298 // ------------------------------------------------------------------------
299 // Accessors
300 // ------------------------------------------------------------------------
ab604305 301
8d47cc34
AM
302 /**
303 * Get the start time of this tree.
304 *
305 * @return The start time
306 */
307 public long getTreeStart() {
0e9b2f07 308 return fConfig.getTreeStart();
a52fde77
AM
309 }
310
8d47cc34
AM
311 /**
312 * Get the current end time of this tree.
313 *
314 * @return The end time
315 */
316 public long getTreeEnd() {
0e9b2f07 317 return fTreeEnd;
a52fde77
AM
318 }
319
8d47cc34
AM
320 /**
321 * Get the number of nodes in this tree.
322 *
323 * @return The number of nodes
324 */
325 public int getNodeCount() {
0e9b2f07 326 return fNodeCount;
a52fde77
AM
327 }
328
8d47cc34 329 /**
412a0225 330 * Get the current root node of this tree
8d47cc34 331 *
412a0225 332 * @return The root node
8d47cc34 333 */
bb7f92ce 334 public HTNode getRootNode() {
0e9b2f07 335 return fLatestBranch.get(0);
cb42195c
AM
336 }
337
360f899e
EB
338 // ------------------------------------------------------------------------
339 // HT_IO interface
340 // ------------------------------------------------------------------------
341
8d47cc34
AM
342 /**
343 * Return the FileInputStream reader with which we will read an attribute
344 * tree (it will be sought to the correct position).
345 *
346 * @return The FileInputStream indicating the file and position from which
347 * the attribute tree can be read.
348 */
349 public FileInputStream supplyATReader() {
0e9b2f07 350 return fTreeIO.supplyATReader(getNodeCount());
360f899e
EB
351 }
352
8d47cc34
AM
353 /**
354 * Return the file to which we will write the attribute tree.
355 *
356 * @return The file to which we will write the attribute tree
357 */
358 public File supplyATWriterFile() {
0e9b2f07 359 return fConfig.getStateFile();
360f899e
EB
360 }
361
8d47cc34
AM
362 /**
363 * Return the position in the file (given by {@link #supplyATWriterFile})
364 * where to start writing the attribute tree.
365 *
366 * @return The position in the file where to start writing
367 */
368 public long supplyATWriterFilePos() {
360f899e 369 return HistoryTree.TREE_HEADER_SIZE
0e9b2f07 370 + ((long) getNodeCount() * fConfig.getBlockSize());
360f899e
EB
371 }
372
8d47cc34
AM
373 /**
374 * Read a node from the tree.
375 *
376 * @param seqNumber
377 * The sequence number of the node to read
378 * @return The node
379 * @throws ClosedChannelException
380 * If the tree IO is unavailable
381 */
382 public HTNode readNode(int seqNumber) throws ClosedChannelException {
360f899e 383 /* Try to read the node from memory */
0e9b2f07
GB
384 synchronized (fLatestBranch) {
385 for (HTNode node : fLatestBranch) {
412a0225
AM
386 if (node.getSequenceNumber() == seqNumber) {
387 return node;
388 }
360f899e
EB
389 }
390 }
391
392 /* Read the node from disk */
0e9b2f07 393 return fTreeIO.readNode(seqNumber);
360f899e
EB
394 }
395
8d47cc34
AM
396 /**
397 * Write a node object to the history file.
398 *
399 * @param node
400 * The node to write to disk
401 */
402 public void writeNode(HTNode node) {
0e9b2f07 403 fTreeIO.writeNode(node);
360f899e
EB
404 }
405
8d47cc34
AM
406 /**
407 * Close the history file.
408 */
409 public void closeFile() {
0e9b2f07 410 fTreeIO.closeFile();
360f899e
EB
411 }
412
8d47cc34
AM
413 /**
414 * Delete the history file.
415 */
416 public void deleteFile() {
0e9b2f07 417 fTreeIO.deleteFile();
360f899e
EB
418 }
419
dbdc452f
AM
420 // ------------------------------------------------------------------------
421 // Operations
422 // ------------------------------------------------------------------------
423
a52fde77 424 /**
8d47cc34 425 * Insert an interval in the tree.
3b7f5abe 426 *
a52fde77 427 * @param interval
d862bcb3 428 * The interval to be inserted
8d47cc34
AM
429 * @throws TimeRangeException
430 * If the start of end time of the interval are invalid
a52fde77 431 */
8d47cc34 432 public void insertInterval(HTInterval interval) throws TimeRangeException {
0e9b2f07
GB
433 if (interval.getStartTime() < fConfig.getTreeStart()) {
434 throw new TimeRangeException("Interval Start:" + interval.getStartTime() + ", Config Start:" + fConfig.getTreeStart()); //$NON-NLS-1$ //$NON-NLS-2$
a52fde77 435 }
0e9b2f07 436 tryInsertAtNode(interval, fLatestBranch.size() - 1);
a52fde77
AM
437 }
438
439 /**
440 * Inner method to find in which node we should add the interval.
3b7f5abe 441 *
a52fde77
AM
442 * @param interval
443 * The interval to add to the tree
444 * @param indexOfNode
445 * The index *in the latestBranch* where we are trying the
446 * insertion
447 */
448 private void tryInsertAtNode(HTInterval interval, int indexOfNode) {
0e9b2f07 449 HTNode targetNode = fLatestBranch.get(indexOfNode);
a52fde77
AM
450
451 /* Verify if there is enough room in this node to store this interval */
452 if (interval.getIntervalSize() > targetNode.getNodeFreeSpace()) {
453 /* Nope, not enough room. Insert in a new sibling instead. */
454 addSiblingNode(indexOfNode);
0e9b2f07 455 tryInsertAtNode(interval, fLatestBranch.size() - 1);
a52fde77
AM
456 return;
457 }
458
459 /* Make sure the interval time range fits this node */
460 if (interval.getStartTime() < targetNode.getNodeStart()) {
461 /*
462 * No, this interval starts before the startTime of this node. We
463 * need to check recursively in parents if it can fit.
464 */
465 assert (indexOfNode >= 1);
466 tryInsertAtNode(interval, indexOfNode - 1);
467 return;
468 }
469
470 /*
471 * Ok, there is room, and the interval fits in this time slot. Let's add
472 * it.
473 */
474 targetNode.addInterval(interval);
475
476 /* Update treeEnd if needed */
0e9b2f07
GB
477 if (interval.getEndTime() > fTreeEnd) {
478 fTreeEnd = interval.getEndTime();
a52fde77 479 }
a52fde77
AM
480 }
481
482 /**
483 * Method to add a sibling to any node in the latest branch. This will add
484 * children back down to the leaf level, if needed.
3b7f5abe 485 *
a52fde77
AM
486 * @param indexOfNode
487 * The index in latestBranch where we start adding
488 */
489 private void addSiblingNode(int indexOfNode) {
0e9b2f07
GB
490 synchronized (fLatestBranch) {
491 final long splitTime = fTreeEnd;
a52fde77 492
0e9b2f07 493 if (indexOfNode >= fLatestBranch.size()) {
bb7f92ce
FW
494 /*
495 * We need to make sure (indexOfNode - 1) doesn't get the last
496 * node in the branch, because that one is a Leaf Node.
497 */
498 throw new IllegalStateException();
499 }
a52fde77 500
412a0225
AM
501 /* Check if we need to add a new root node */
502 if (indexOfNode == 0) {
503 addNewRootNode();
504 return;
505 }
a52fde77 506
412a0225 507 /* Check if we can indeed add a child to the target parent */
0e9b2f07 508 if (((CoreNode) fLatestBranch.get(indexOfNode - 1)).getNbChildren() == fConfig.getMaxChildren()) {
412a0225
AM
509 /* If not, add a branch starting one level higher instead */
510 addSiblingNode(indexOfNode - 1);
511 return;
512 }
a52fde77 513
412a0225 514 /* Split off the new branch from the old one */
0e9b2f07
GB
515 for (int i = indexOfNode; i < fLatestBranch.size(); i++) {
516 fLatestBranch.get(i).closeThisNode(splitTime);
517 fTreeIO.writeNode(fLatestBranch.get(i));
a52fde77 518
0e9b2f07 519 CoreNode prevNode = (CoreNode) fLatestBranch.get(i - 1);
bb7f92ce
FW
520 HTNode newNode;
521
0e9b2f07 522 switch (fLatestBranch.get(i).getNodeType()) {
bb7f92ce
FW
523 case CORE:
524 newNode = initNewCoreNode(prevNode.getSequenceNumber(), splitTime + 1);
525 break;
526 case LEAF:
527 newNode = initNewLeafNode(prevNode.getSequenceNumber(), splitTime + 1);
528 break;
529 default:
530 throw new IllegalStateException();
531 }
a52fde77 532
bb7f92ce 533 prevNode.linkNewChild(newNode);
0e9b2f07 534 fLatestBranch.set(i, newNode);
412a0225 535 }
a52fde77 536 }
a52fde77
AM
537 }
538
539 /**
540 * Similar to the previous method, except here we rebuild a completely new
541 * latestBranch
542 */
543 private void addNewRootNode() {
0e9b2f07 544 final long splitTime = fTreeEnd;
a52fde77 545
0e9b2f07
GB
546 HTNode oldRootNode = fLatestBranch.get(0);
547 CoreNode newRootNode = initNewCoreNode(-1, fConfig.getTreeStart());
a52fde77
AM
548
549 /* Tell the old root node that it isn't root anymore */
550 oldRootNode.setParentSequenceNumber(newRootNode.getSequenceNumber());
551
552 /* Close off the whole current latestBranch */
412a0225 553
0e9b2f07
GB
554 for (int i = 0; i < fLatestBranch.size(); i++) {
555 fLatestBranch.get(i).closeThisNode(splitTime);
556 fTreeIO.writeNode(fLatestBranch.get(i));
a52fde77
AM
557 }
558
559 /* Link the new root to its first child (the previous root node) */
560 newRootNode.linkNewChild(oldRootNode);
561
562 /* Rebuild a new latestBranch */
0e9b2f07
GB
563 int depth = fLatestBranch.size();
564 fLatestBranch.clear();
565 fLatestBranch.add(newRootNode);
bb7f92ce
FW
566
567 // Create new coreNode
412a0225 568 for (int i = 1; i < depth + 1; i++) {
0e9b2f07 569 CoreNode prevNode = (CoreNode) fLatestBranch.get(i - 1);
412a0225 570 CoreNode newNode = initNewCoreNode(prevNode.getParentSequenceNumber(),
a52fde77
AM
571 splitTime + 1);
572 prevNode.linkNewChild(newNode);
0e9b2f07 573 fLatestBranch.add(newNode);
a52fde77 574 }
bb7f92ce
FW
575
576 // Create the new leafNode
0e9b2f07 577 CoreNode prevNode = (CoreNode) fLatestBranch.get(depth);
bb7f92ce
FW
578 LeafNode newNode = initNewLeafNode(prevNode.getParentSequenceNumber(), splitTime + 1);
579 prevNode.linkNewChild(newNode);
0e9b2f07 580 fLatestBranch.add(newNode);
a52fde77
AM
581 }
582
583 /**
bb7f92ce 584 * Add a new empty core node to the tree.
3b7f5abe 585 *
a52fde77
AM
586 * @param parentSeqNumber
587 * Sequence number of this node's parent
588 * @param startTime
589 * Start time of the new node
590 * @return The newly created node
591 */
592 private CoreNode initNewCoreNode(int parentSeqNumber, long startTime) {
0e9b2f07 593 CoreNode newNode = new CoreNode(fConfig, fNodeCount, parentSeqNumber,
a52fde77 594 startTime);
0e9b2f07 595 fNodeCount++;
a52fde77
AM
596
597 /* Update the treeEnd if needed */
0e9b2f07
GB
598 if (startTime >= fTreeEnd) {
599 fTreeEnd = startTime + 1;
a52fde77
AM
600 }
601 return newNode;
602 }
603
bb7f92ce
FW
604 /**
605 * Add a new empty leaf node to the tree.
606 *
607 * @param parentSeqNumber
608 * Sequence number of this node's parent
609 * @param startTime
610 * Start time of the new node
611 * @return The newly created node
612 */
613 private LeafNode initNewLeafNode(int parentSeqNumber, long startTime) {
0e9b2f07 614 LeafNode newNode = new LeafNode(fConfig, fNodeCount, parentSeqNumber,
bb7f92ce 615 startTime);
0e9b2f07 616 fNodeCount++;
bb7f92ce
FW
617
618 /* Update the treeEnd if needed */
0e9b2f07
GB
619 if (startTime >= fTreeEnd) {
620 fTreeEnd = startTime + 1;
bb7f92ce
FW
621 }
622 return newNode;
623 }
624
a52fde77
AM
625 /**
626 * Inner method to select the next child of the current node intersecting
627 * the given timestamp. Useful for moving down the tree following one
628 * branch.
3b7f5abe 629 *
a52fde77 630 * @param currentNode
d862bcb3 631 * The node on which the request is made
a52fde77 632 * @param t
d862bcb3 633 * The timestamp to choose which child is the next one
a52fde77 634 * @return The child node intersecting t
3b7f5abe
AM
635 * @throws ClosedChannelException
636 * If the file channel was closed while we were reading the tree
a52fde77 637 */
8d47cc34 638 public HTNode selectNextChild(CoreNode currentNode, long t) throws ClosedChannelException {
a52fde77
AM
639 assert (currentNode.getNbChildren() > 0);
640 int potentialNextSeqNb = currentNode.getSequenceNumber();
641
642 for (int i = 0; i < currentNode.getNbChildren(); i++) {
643 if (t >= currentNode.getChildStart(i)) {
644 potentialNextSeqNb = currentNode.getChild(i);
645 } else {
646 break;
647 }
648 }
d862bcb3 649
a52fde77
AM
650 /*
651 * Once we exit this loop, we should have found a children to follow. If
652 * we didn't, there's a problem.
653 */
654 assert (potentialNextSeqNb != currentNode.getSequenceNumber());
655
656 /*
657 * Since this code path is quite performance-critical, avoid iterating
658 * through the whole latestBranch array if we know for sure the next
659 * node has to be on disk
660 */
045badfe 661 if (currentNode.isOnDisk()) {
0e9b2f07 662 return fTreeIO.readNode(potentialNextSeqNb);
a52fde77 663 }
83c31d28 664 return readNode(potentialNextSeqNb);
a52fde77
AM
665 }
666
8d47cc34
AM
667 /**
668 * Get the current size of the history file.
669 *
670 * @return The history file size
671 */
672 public long getFileSize() {
0e9b2f07 673 return fConfig.getStateFile().length();
a52fde77
AM
674 }
675
3b7f5abe
AM
676 // ------------------------------------------------------------------------
677 // Test/debugging methods
678 // ------------------------------------------------------------------------
a52fde77 679
8d47cc34
AM
680 /**
681 * Debugging method to make sure all intervals contained in the given node
682 * have valid start and end times.
683 *
684 * @param zenode
685 * The node to check
686 * @return True if everything is fine, false if there is at least one
687 * invalid timestamp (end time < start time, time outside of the
688 * range of the node, etc.)
689 */
a52fde77 690 @SuppressWarnings("nls")
8d47cc34
AM
691 public boolean checkNodeIntegrity(HTNode zenode) {
692 /* Only used for debugging, shouldn't be externalized */
a52fde77
AM
693 HTNode otherNode;
694 CoreNode node;
ab604305 695 StringBuffer buf = new StringBuffer();
a52fde77
AM
696 boolean ret = true;
697
698 // FIXME /* Only testing Core Nodes for now */
699 if (!(zenode instanceof CoreNode)) {
700 return true;
701 }
702
703 node = (CoreNode) zenode;
704
3b7f5abe
AM
705 try {
706 /*
707 * Test that this node's start and end times match the start of the
708 * first child and the end of the last child, respectively
709 */
710 if (node.getNbChildren() > 0) {
0e9b2f07 711 otherNode = fTreeIO.readNode(node.getChild(0));
3b7f5abe
AM
712 if (node.getNodeStart() != otherNode.getNodeStart()) {
713 buf.append("Start time of node (" + node.getNodeStart() + ") "
714 + "does not match start time of first child " + "("
715 + otherNode.getNodeStart() + "), " + "node #"
ab604305 716 + otherNode.getSequenceNumber() + ")\n");
a52fde77
AM
717 ret = false;
718 }
045badfe 719 if (node.isOnDisk()) {
0e9b2f07 720 otherNode = fTreeIO.readNode(node.getLatestChild());
3b7f5abe
AM
721 if (node.getNodeEnd() != otherNode.getNodeEnd()) {
722 buf.append("End time of node (" + node.getNodeEnd()
723 + ") does not match end time of last child ("
724 + otherNode.getNodeEnd() + ", node #"
725 + otherNode.getSequenceNumber() + ")\n");
726 ret = false;
727 }
728 }
a52fde77 729 }
a52fde77 730
3b7f5abe 731 /*
bb7f92ce
FW
732 * Test that the childStartTimes[] array matches the real nodes'
733 * start times
3b7f5abe
AM
734 */
735 for (int i = 0; i < node.getNbChildren(); i++) {
0e9b2f07 736 otherNode = fTreeIO.readNode(node.getChild(i));
3b7f5abe
AM
737 if (otherNode.getNodeStart() != node.getChildStart(i)) {
738 buf.append(" Expected start time of child node #"
739 + node.getChild(i) + ": " + node.getChildStart(i)
740 + "\n" + " Actual start time of node #"
741 + otherNode.getSequenceNumber() + ": "
742 + otherNode.getNodeStart() + "\n");
743 ret = false;
744 }
a52fde77 745 }
3b7f5abe
AM
746
747 } catch (ClosedChannelException e) {
0e9b2f07 748 Activator.getDefault().logError(e.getMessage(), e);
a52fde77
AM
749 }
750
751 if (!ret) {
0e9b2f07
GB
752 Activator.getDefault().logError("SHT: Integrity check failed for node #"
753 + node.getSequenceNumber() + ":" + buf.toString());
a52fde77
AM
754 }
755 return ret;
756 }
757
8d47cc34
AM
758 /**
759 * Check the integrity of all the nodes in the tree. Calls
760 * {@link #checkNodeIntegrity} for every node in the tree.
761 */
762 public void checkIntegrity() {
3b7f5abe 763 try {
0e9b2f07
GB
764 for (int i = 0; i < fNodeCount; i++) {
765 checkNodeIntegrity(fTreeIO.readNode(i));
3b7f5abe
AM
766 }
767 } catch (ClosedChannelException e) {
a52fde77
AM
768 }
769 }
770
771 /* Only used for debugging, shouldn't be externalized */
772 @SuppressWarnings("nls")
773 @Override
774 public String toString() {
775 return "Information on the current tree:\n\n" + "Blocksize: "
0e9b2f07
GB
776 + fConfig.getBlockSize() + "\n" + "Max nb. of children per node: "
777 + fConfig.getMaxChildren() + "\n" + "Number of nodes: " + fNodeCount
778 + "\n" + "Depth of the tree: " + fLatestBranch.size() + "\n"
779 + "Size of the treefile: " + getFileSize() + "\n"
a52fde77 780 + "Root node has sequence number: "
0e9b2f07 781 + fLatestBranch.get(0).getSequenceNumber() + "\n"
a52fde77 782 + "'Latest leaf' has sequence number: "
0e9b2f07 783 + fLatestBranch.get(fLatestBranch.size() - 1).getSequenceNumber();
a52fde77
AM
784 }
785
a52fde77
AM
786 /**
787 * Start at currentNode and print the contents of all its children, in
788 * pre-order. Give the root node in parameter to visit the whole tree, and
789 * have a nice overview.
790 */
d862bcb3 791 /* Only used for debugging, shouldn't be externalized */
a52fde77
AM
792 @SuppressWarnings("nls")
793 private void preOrderPrint(PrintWriter writer, boolean printIntervals,
bb7f92ce 794 HTNode currentNode, int curDepth) {
a52fde77
AM
795
796 writer.println(currentNode.toString());
797 if (printIntervals) {
798 currentNode.debugPrintIntervals(writer);
799 }
a52fde77 800
bb7f92ce
FW
801 switch (currentNode.getNodeType()) {
802 case LEAF:
803 /* Stop if it's the leaf node */
804 return;
805
806 case CORE:
807 try {
808 final CoreNode node = (CoreNode) currentNode;
809 /* Print the extensions, if any */
810 int extension = node.getExtensionSequenceNumber();
811 while (extension != -1) {
0e9b2f07 812 HTNode nextNode = fTreeIO.readNode(extension);
bb7f92ce
FW
813 preOrderPrint(writer, printIntervals, nextNode, curDepth);
814 }
815
816 /* Print the child nodes */
817 for (int i = 0; i < node.getNbChildren(); i++) {
0e9b2f07 818 HTNode nextNode = fTreeIO.readNode(node.getChild(i));
bb7f92ce
FW
819 for (int j = 0; j < curDepth; j++) {
820 writer.print(" ");
821 }
822 writer.print("+-");
823 preOrderPrint(writer, printIntervals, nextNode, curDepth + 1);
3b7f5abe 824 }
bb7f92ce 825 } catch (ClosedChannelException e) {
bcec0116 826 Activator.getDefault().logError(e.getMessage());
a52fde77 827 }
bb7f92ce
FW
828 break;
829
830 default:
831 break;
a52fde77 832 }
a52fde77
AM
833 }
834
835 /**
836 * Print out the full tree for debugging purposes
3b7f5abe 837 *
a52fde77
AM
838 * @param writer
839 * PrintWriter in which to write the output
840 * @param printIntervals
d862bcb3 841 * Flag to enable full output of the interval information
a52fde77 842 */
8d47cc34 843 public void debugPrintFullTree(PrintWriter writer, boolean printIntervals) {
a52fde77 844 /* Only used for debugging, shouldn't be externalized */
d862bcb3 845
0e9b2f07 846 preOrderPrint(writer, false, fLatestBranch.get(0), 0);
a52fde77
AM
847
848 if (printIntervals) {
849 writer.println("\nDetails of intervals:"); //$NON-NLS-1$
0e9b2f07 850 preOrderPrint(writer, true, fLatestBranch.get(0), 0);
a52fde77
AM
851 }
852 writer.println('\n');
853 }
854
855}
This page took 0.116222 seconds and 5 git commands to generate.