8057e4ae0a062b7e19f7d6cc183f08279407da05
[deliverable/tracecompass.git] / org.eclipse.tracecompass.statesystem.core / src / org / eclipse / tracecompass / internal / statesystem / core / backend / historytree / HistoryTree.java
1 /*******************************************************************************
2 * Copyright (c) 2010, 2015 Ericsson, École Polytechnique de Montréal, and others
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 * Alexandre Montplaisir - Initial API and implementation
11 * Florian Wininger - Add Extension and Leaf Node
12 * Patrick Tasse - Add message to exceptions
13 *******************************************************************************/
14
15 package org.eclipse.tracecompass.internal.statesystem.core.backend.historytree;
16
17 import java.io.File;
18 import java.io.FileInputStream;
19 import java.io.IOException;
20 import java.io.PrintWriter;
21 import java.nio.ByteBuffer;
22 import java.nio.ByteOrder;
23 import java.nio.channels.ClosedChannelException;
24 import java.nio.channels.FileChannel;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.List;
28
29 import org.eclipse.tracecompass.internal.statesystem.core.Activator;
30 import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
31 import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
32
33 /**
34 * Meta-container for the History Tree. This structure contains all the
35 * high-level data relevant to the tree.
36 *
37 * @author Alexandre Montplaisir
38 */
39 public class HistoryTree {
40
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
48 private static final int HISTORY_FILE_MAGIC_NUMBER = 0x05FFA900;
49
50 /** File format version. Increment when breaking compatibility. */
51 private static final int FILE_VERSION = 5;
52
53 // ------------------------------------------------------------------------
54 // Tree-specific configuration
55 // ------------------------------------------------------------------------
56
57 /** Container for all the configuration constants */
58 private final HTConfig config;
59
60 /** Reader/writer object */
61 private final HT_IO treeIO;
62
63 // ------------------------------------------------------------------------
64 // Variable Fields (will change throughout the existence of the SHT)
65 // ------------------------------------------------------------------------
66
67 /** Latest timestamp found in the tree (at any given moment) */
68 private long treeEnd;
69
70 /** The total number of nodes that exists in this tree */
71 private int nodeCount;
72
73 /** "Cache" to keep the active nodes in memory */
74 private final List<HTNode> latestBranch;
75
76 // ------------------------------------------------------------------------
77 // Constructors/"Destructors"
78 // ------------------------------------------------------------------------
79
80 /**
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
89 */
90 public HistoryTree(HTConfig conf) throws IOException {
91 /*
92 * Simple check to make sure we have enough place in the 0th block for
93 * the tree configuration
94 */
95 if (conf.getBlockSize() < TREE_HEADER_SIZE) {
96 throw new IllegalArgumentException();
97 }
98
99 config = conf;
100 treeEnd = conf.getTreeStart();
101 nodeCount = 0;
102 latestBranch = Collections.synchronizedList(new ArrayList<HTNode>());
103
104 /* Prepare the IO object */
105 treeIO = new HT_IO(config, true);
106
107 /* Add the first node to the tree */
108 LeafNode firstNode = initNewLeafNode(-1, conf.getTreeStart());
109 latestBranch.add(firstNode);
110 }
111
112 /**
113 * "Reader" constructor : instantiate a SHTree from an existing tree file on
114 * disk
115 *
116 * @param existingStateFile
117 * Path/filename of the history-file we are to open
118 * @param expProviderVersion
119 * The expected version of the state provider
120 * @throws IOException
121 * If an error happens reading the file
122 */
123 public HistoryTree(File existingStateFile, int expProviderVersion) throws IOException {
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;
130 long startTime;
131
132 /* Java I/O mumbo jumbo... */
133 if (!existingStateFile.exists()) {
134 throw new IOException("Selected state file does not exist"); //$NON-NLS-1$
135 }
136 if (existingStateFile.length() <= 0) {
137 throw new IOException("Empty target file"); //$NON-NLS-1$
138 }
139
140 try (FileInputStream fis = new FileInputStream(existingStateFile);
141 FileChannel fc = fis.getChannel();) {
142
143 ByteBuffer buffer = ByteBuffer.allocate(TREE_HEADER_SIZE);
144
145 buffer.order(ByteOrder.LITTLE_ENDIAN);
146 buffer.clear();
147 fc.read(buffer);
148 buffer.flip();
149
150 /*
151 * Check the magic number to make sure we're opening the right type
152 * of file
153 */
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 &&
166 expProviderVersion != ITmfStateSystemBuilder.IGNORE_PROVIDER_VERSION) {
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 */
179
180 this.nodeCount = buffer.getInt();
181 rootNodeSeqNb = buffer.getInt();
182 startTime = buffer.getLong();
183
184 this.config = new HTConfig(existingStateFile, bs, maxc, expProviderVersion, startTime);
185 }
186
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 */
192 this.treeIO = new HT_IO(config, false);
193
194 this.latestBranch = buildLatestBranch(rootNodeSeqNb);
195 this.treeEnd = getRootNode().getNodeEnd();
196
197 /*
198 * Make sure the history start time we read previously is consistent
199 * with was is actually in the root node.
200 */
201 if (startTime != getRootNode().getNodeStart()) {
202 throw new IOException("Inconsistent start times in the" + //$NON-NLS-1$
203 "history file, it might be corrupted."); //$NON-NLS-1$
204 }
205 }
206
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 */
217 private List<HTNode> buildLatestBranch(int rootNodeSeqNb) throws ClosedChannelException {
218 List<HTNode> list = new ArrayList<>();
219
220 HTNode nextChildNode = treeIO.readNode(rootNodeSeqNb);
221 list.add(nextChildNode);
222
223 /* Follow the last branch up to the leaf */
224 while (nextChildNode.getNodeType() == HTNode.NodeType.CORE) {
225 nextChildNode = treeIO.readNode(((CoreNode) nextChildNode).getLatestChild());
226 list.add(nextChildNode);
227 }
228 return Collections.synchronizedList(list);
229 }
230
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.
236 *
237 * @param requestedEndTime
238 * The greatest timestamp present in the history tree
239 */
240 public void closeTree(long requestedEndTime) {
241 /* This is an important operation, queries can wait */
242 synchronized (latestBranch) {
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 */
251 this.treeEnd = requestedEndTime;
252
253 /* Close off the latest branch of the tree */
254 for (int i = 0; i < latestBranch.size(); i++) {
255 latestBranch.get(i).closeThisNode(treeEnd);
256 treeIO.writeNode(latestBranch.get(i));
257 }
258
259 try (FileChannel fc = treeIO.getFcOut();) {
260 ByteBuffer buffer = ByteBuffer.allocate(TREE_HEADER_SIZE);
261 buffer.order(ByteOrder.LITTLE_ENDIAN);
262 buffer.clear();
263
264 /* Save the config of the tree to the header of the file */
265 fc.position(0);
266
267 buffer.putInt(HISTORY_FILE_MAGIC_NUMBER);
268
269 buffer.putInt(FILE_VERSION);
270 buffer.putInt(config.getProviderVersion());
271
272 buffer.putInt(config.getBlockSize());
273 buffer.putInt(config.getMaxChildren());
274
275 buffer.putInt(nodeCount);
276
277 /* root node seq. nb */
278 buffer.putInt(latestBranch.get(0).getSequenceNumber());
279
280 /* start time of this history */
281 buffer.putLong(latestBranch.get(0).getNodeStart());
282
283 buffer.flip();
284 int res = fc.write(buffer);
285 assert (res <= TREE_HEADER_SIZE);
286 /* done writing the file header */
287
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 }
295 }
296 }
297
298 // ------------------------------------------------------------------------
299 // Accessors
300 // ------------------------------------------------------------------------
301
302 /**
303 * Get the start time of this tree.
304 *
305 * @return The start time
306 */
307 public long getTreeStart() {
308 return config.getTreeStart();
309 }
310
311 /**
312 * Get the current end time of this tree.
313 *
314 * @return The end time
315 */
316 public long getTreeEnd() {
317 return treeEnd;
318 }
319
320 /**
321 * Get the number of nodes in this tree.
322 *
323 * @return The number of nodes
324 */
325 public int getNodeCount() {
326 return nodeCount;
327 }
328
329 /**
330 * Get the current root node of this tree
331 *
332 * @return The root node
333 */
334 public HTNode getRootNode() {
335 return latestBranch.get(0);
336 }
337
338 // ------------------------------------------------------------------------
339 // HT_IO interface
340 // ------------------------------------------------------------------------
341
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() {
350 return treeIO.supplyATReader(getNodeCount());
351 }
352
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() {
359 return config.getStateFile();
360 }
361
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() {
369 return HistoryTree.TREE_HEADER_SIZE
370 + ((long) getNodeCount() * config.getBlockSize());
371 }
372
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 {
383 /* Try to read the node from memory */
384 synchronized (latestBranch) {
385 for (HTNode node : latestBranch) {
386 if (node.getSequenceNumber() == seqNumber) {
387 return node;
388 }
389 }
390 }
391
392 /* Read the node from disk */
393 return treeIO.readNode(seqNumber);
394 }
395
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) {
403 treeIO.writeNode(node);
404 }
405
406 /**
407 * Close the history file.
408 */
409 public void closeFile() {
410 treeIO.closeFile();
411 }
412
413 /**
414 * Delete the history file.
415 */
416 public void deleteFile() {
417 treeIO.deleteFile();
418 }
419
420 // ------------------------------------------------------------------------
421 // Operations
422 // ------------------------------------------------------------------------
423
424 /**
425 * Insert an interval in the tree.
426 *
427 * @param interval
428 * The interval to be inserted
429 * @throws TimeRangeException
430 * If the start of end time of the interval are invalid
431 */
432 public void insertInterval(HTInterval interval) throws TimeRangeException {
433 if (interval.getStartTime() < config.getTreeStart()) {
434 throw new TimeRangeException("Interval Start:" + interval.getStartTime() + ", Config Start:" + config.getTreeStart()); //$NON-NLS-1$ //$NON-NLS-2$
435 }
436 tryInsertAtNode(interval, latestBranch.size() - 1);
437 }
438
439 /**
440 * Inner method to find in which node we should add the interval.
441 *
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) {
449 HTNode targetNode = latestBranch.get(indexOfNode);
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);
455 tryInsertAtNode(interval, latestBranch.size() - 1);
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 */
477 if (interval.getEndTime() > this.treeEnd) {
478 this.treeEnd = interval.getEndTime();
479 }
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.
485 *
486 * @param indexOfNode
487 * The index in latestBranch where we start adding
488 */
489 private void addSiblingNode(int indexOfNode) {
490 synchronized (latestBranch) {
491 final long splitTime = treeEnd;
492
493 if (indexOfNode >= latestBranch.size()) {
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 }
500
501 /* Check if we need to add a new root node */
502 if (indexOfNode == 0) {
503 addNewRootNode();
504 return;
505 }
506
507 /* Check if we can indeed add a child to the target parent */
508 if (((CoreNode) latestBranch.get(indexOfNode - 1)).getNbChildren() == config.getMaxChildren()) {
509 /* If not, add a branch starting one level higher instead */
510 addSiblingNode(indexOfNode - 1);
511 return;
512 }
513
514 /* Split off the new branch from the old one */
515 for (int i = indexOfNode; i < latestBranch.size(); i++) {
516 latestBranch.get(i).closeThisNode(splitTime);
517 treeIO.writeNode(latestBranch.get(i));
518
519 CoreNode prevNode = (CoreNode) latestBranch.get(i - 1);
520 HTNode newNode;
521
522 switch (latestBranch.get(i).getNodeType()) {
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 }
532
533 prevNode.linkNewChild(newNode);
534 latestBranch.set(i, newNode);
535 }
536 }
537 }
538
539 /**
540 * Similar to the previous method, except here we rebuild a completely new
541 * latestBranch
542 */
543 private void addNewRootNode() {
544 final long splitTime = this.treeEnd;
545
546 HTNode oldRootNode = latestBranch.get(0);
547 CoreNode newRootNode = initNewCoreNode(-1, config.getTreeStart());
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 */
553
554 for (int i = 0; i < latestBranch.size(); i++) {
555 latestBranch.get(i).closeThisNode(splitTime);
556 treeIO.writeNode(latestBranch.get(i));
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 */
563 int depth = latestBranch.size();
564 latestBranch.clear();
565 latestBranch.add(newRootNode);
566
567 // Create new coreNode
568 for (int i = 1; i < depth + 1; i++) {
569 CoreNode prevNode = (CoreNode) latestBranch.get(i - 1);
570 CoreNode newNode = initNewCoreNode(prevNode.getParentSequenceNumber(),
571 splitTime + 1);
572 prevNode.linkNewChild(newNode);
573 latestBranch.add(newNode);
574 }
575
576 // Create the new leafNode
577 CoreNode prevNode = (CoreNode) latestBranch.get(depth);
578 LeafNode newNode = initNewLeafNode(prevNode.getParentSequenceNumber(), splitTime + 1);
579 prevNode.linkNewChild(newNode);
580 latestBranch.add(newNode);
581 }
582
583 /**
584 * Add a new empty core node to the tree.
585 *
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) {
593 CoreNode newNode = new CoreNode(config, this.nodeCount, parentSeqNumber,
594 startTime);
595 this.nodeCount++;
596
597 /* Update the treeEnd if needed */
598 if (startTime >= this.treeEnd) {
599 this.treeEnd = startTime + 1;
600 }
601 return newNode;
602 }
603
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) {
614 LeafNode newNode = new LeafNode(config, this.nodeCount, parentSeqNumber,
615 startTime);
616 this.nodeCount++;
617
618 /* Update the treeEnd if needed */
619 if (startTime >= this.treeEnd) {
620 this.treeEnd = startTime + 1;
621 }
622 return newNode;
623 }
624
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.
629 *
630 * @param currentNode
631 * The node on which the request is made
632 * @param t
633 * The timestamp to choose which child is the next one
634 * @return The child node intersecting t
635 * @throws ClosedChannelException
636 * If the file channel was closed while we were reading the tree
637 */
638 public HTNode selectNextChild(CoreNode currentNode, long t) throws ClosedChannelException {
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 }
649
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 */
661 if (currentNode.isOnDisk()) {
662 return treeIO.readNode(potentialNextSeqNb);
663 }
664 return readNode(potentialNextSeqNb);
665 }
666
667 /**
668 * Get the current size of the history file.
669 *
670 * @return The history file size
671 */
672 public long getFileSize() {
673 return config.getStateFile().length();
674 }
675
676 // ------------------------------------------------------------------------
677 // Test/debugging methods
678 // ------------------------------------------------------------------------
679
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 */
690 @SuppressWarnings("nls")
691 public boolean checkNodeIntegrity(HTNode zenode) {
692 /* Only used for debugging, shouldn't be externalized */
693 HTNode otherNode;
694 CoreNode node;
695 StringBuffer buf = new StringBuffer();
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
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) {
711 otherNode = treeIO.readNode(node.getChild(0));
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 #"
716 + otherNode.getSequenceNumber() + ")\n");
717 ret = false;
718 }
719 if (node.isOnDisk()) {
720 otherNode = treeIO.readNode(node.getLatestChild());
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 }
729 }
730
731 /*
732 * Test that the childStartTimes[] array matches the real nodes'
733 * start times
734 */
735 for (int i = 0; i < node.getNbChildren(); i++) {
736 otherNode = treeIO.readNode(node.getChild(i));
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 }
745 }
746
747 } catch (ClosedChannelException e) {
748 e.printStackTrace();
749 }
750
751 if (!ret) {
752 System.out.println("");
753 System.out.println("SHT: Integrity check failed for node #"
754 + node.getSequenceNumber() + ":");
755 System.out.println(buf.toString());
756 }
757 return ret;
758 }
759
760 /**
761 * Check the integrity of all the nodes in the tree. Calls
762 * {@link #checkNodeIntegrity} for every node in the tree.
763 */
764 public void checkIntegrity() {
765 try {
766 for (int i = 0; i < nodeCount; i++) {
767 checkNodeIntegrity(treeIO.readNode(i));
768 }
769 } catch (ClosedChannelException e) {
770 }
771 }
772
773 /* Only used for debugging, shouldn't be externalized */
774 @SuppressWarnings("nls")
775 @Override
776 public String toString() {
777 return "Information on the current tree:\n\n" + "Blocksize: "
778 + config.getBlockSize() + "\n" + "Max nb. of children per node: "
779 + config.getMaxChildren() + "\n" + "Number of nodes: " + nodeCount
780 + "\n" + "Depth of the tree: " + latestBranch.size() + "\n"
781 + "Size of the treefile: " + this.getFileSize() + "\n"
782 + "Root node has sequence number: "
783 + latestBranch.get(0).getSequenceNumber() + "\n"
784 + "'Latest leaf' has sequence number: "
785 + latestBranch.get(latestBranch.size() - 1).getSequenceNumber();
786 }
787
788 /**
789 * Start at currentNode and print the contents of all its children, in
790 * pre-order. Give the root node in parameter to visit the whole tree, and
791 * have a nice overview.
792 */
793 /* Only used for debugging, shouldn't be externalized */
794 @SuppressWarnings("nls")
795 private void preOrderPrint(PrintWriter writer, boolean printIntervals,
796 HTNode currentNode, int curDepth) {
797
798 writer.println(currentNode.toString());
799 if (printIntervals) {
800 currentNode.debugPrintIntervals(writer);
801 }
802
803 switch (currentNode.getNodeType()) {
804 case LEAF:
805 /* Stop if it's the leaf node */
806 return;
807
808 case CORE:
809 try {
810 final CoreNode node = (CoreNode) currentNode;
811 /* Print the extensions, if any */
812 int extension = node.getExtensionSequenceNumber();
813 while (extension != -1) {
814 HTNode nextNode = treeIO.readNode(extension);
815 preOrderPrint(writer, printIntervals, nextNode, curDepth);
816 }
817
818 /* Print the child nodes */
819 for (int i = 0; i < node.getNbChildren(); i++) {
820 HTNode nextNode = treeIO.readNode(node.getChild(i));
821 for (int j = 0; j < curDepth; j++) {
822 writer.print(" ");
823 }
824 writer.print("+-");
825 preOrderPrint(writer, printIntervals, nextNode, curDepth + 1);
826 }
827 } catch (ClosedChannelException e) {
828 Activator.getDefault().logError(e.getMessage());
829 }
830 break;
831
832 default:
833 break;
834 }
835 }
836
837 /**
838 * Print out the full tree for debugging purposes
839 *
840 * @param writer
841 * PrintWriter in which to write the output
842 * @param printIntervals
843 * Flag to enable full output of the interval information
844 */
845 public void debugPrintFullTree(PrintWriter writer, boolean printIntervals) {
846 /* Only used for debugging, shouldn't be externalized */
847
848 this.preOrderPrint(writer, false, latestBranch.get(0), 0);
849
850 if (printIntervals) {
851 writer.println("\nDetails of intervals:"); //$NON-NLS-1$
852 this.preOrderPrint(writer, true, latestBranch.get(0), 0);
853 }
854 writer.println('\n');
855 }
856
857 }
This page took 0.226974 seconds and 4 git commands to generate.