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

« back to all changes in this revision

Viewing changes to lttng/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/statesystem/historytree/HistoryTree.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) 2012 Ericsson
 
3
 * Copyright (c) 2010, 2011 Ć‰cole Polytechnique de MontrĆ©al
 
4
 * Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com>
 
5
 * 
 
6
 * All rights reserved. This program and the accompanying materials are
 
7
 * made available under the terms of the Eclipse Public License v1.0 which
 
8
 * accompanies this distribution, and is available at
 
9
 * http://www.eclipse.org/legal/epl-v10.html
 
10
 * 
 
11
 *******************************************************************************/
 
12
 
 
13
package org.eclipse.linuxtools.internal.tmf.core.statesystem.historytree;
 
14
 
 
15
import java.io.File;
 
16
import java.io.FileInputStream;
 
17
import java.io.IOException;
 
18
import java.io.PrintWriter;
 
19
import java.nio.ByteBuffer;
 
20
import java.nio.ByteOrder;
 
21
import java.nio.channels.FileChannel;
 
22
import java.util.Vector;
 
23
 
 
24
import org.eclipse.linuxtools.tmf.core.exceptions.TimeRangeException;
 
25
 
 
26
/**
 
27
 * Meta-container for the History Tree. This structure contains all the
 
28
 * high-level data relevant to the tree.
 
29
 * 
 
30
 * @author alexmont
 
31
 * 
 
32
 */
 
33
class HistoryTree {
 
34
 
 
35
    private static final int HISTORY_FILE_MAGIC_NUMBER = 0x05FFA900;
 
36
 
 
37
    /**
 
38
     * File format version. Increment minor on backwards-compatible changes.
 
39
     * Increment major + set minor back to 0 when breaking compatibility.
 
40
     */
 
41
    private static final int MAJOR_VERSION = 3;
 
42
    private static final byte MINOR_VERSION = 0;
 
43
 
 
44
    /**
 
45
     * Tree-specific configuration
 
46
     */
 
47
    /* Container for all the configuration constants */
 
48
    protected final HTConfig config;
 
49
 
 
50
    /* Reader/writer object */
 
51
    private final HT_IO treeIO;
 
52
 
 
53
    /**
 
54
     * Variable Fields (will change throughout the existance of the SHT)
 
55
     */
 
56
    /* Latest timestamp found in the tree (at any given moment) */
 
57
    private long treeEnd;
 
58
 
 
59
    /* How many nodes exist in this tree, total */
 
60
    private int nodeCount;
 
61
 
 
62
    /* "Cache" to keep the active nodes in memory */
 
63
    protected Vector<CoreNode> latestBranch;
 
64
 
 
65
    /**
 
66
     * Create a new State History from scratch, using a SHTConfig object for
 
67
     * configuration
 
68
     * 
 
69
     * @param conf
 
70
     * @throws IOException
 
71
     */
 
72
    private HistoryTree(HTConfig conf) throws IOException {
 
73
        /*
 
74
         * Simple assertion to make sure we have enough place in the 0th block
 
75
         * for the tree configuration
 
76
         */
 
77
        assert (conf.blockSize >= getTreeHeaderSize());
 
78
 
 
79
        config = conf;
 
80
        treeEnd = conf.treeStart;
 
81
        nodeCount = 0;
 
82
        latestBranch = new Vector<CoreNode>();
 
83
 
 
84
        /* Prepare the IO object */
 
85
        treeIO = new HT_IO(this, true);
 
86
 
 
87
        /* Add the first node to the tree */
 
88
        CoreNode firstNode = initNewCoreNode(-1, conf.treeStart);
 
89
        latestBranch.add(firstNode);
 
90
    }
 
91
 
 
92
    /**
 
93
     * "New State History" constructor, which doesn't use SHTConfig but the
 
94
     * individual values separately. Kept for now for backwards compatibility,
 
95
     * but you should definitely consider using SHTConfig instead (since its
 
96
     * contents can then change without directly affecting SHT's API).
 
97
     */
 
98
    HistoryTree(File newStateFile, int blockSize, int maxChildren,
 
99
            long startTime) throws IOException {
 
100
        this(new HTConfig(newStateFile, blockSize, maxChildren, startTime));
 
101
    }
 
102
 
 
103
    /**
 
104
     * "Reader" constructor : instantiate a SHTree from an existing tree file on
 
105
     * disk
 
106
     * 
 
107
     * @param existingFileName
 
108
     *            Path/filename of the history-file we are to open
 
109
     * @throws IOException
 
110
     */
 
111
    HistoryTree(File existingStateFile) throws IOException {
 
112
        /*
 
113
         * Open the file ourselves, get the tree header information we need,
 
114
         * then pass on the descriptor to the TreeIO object.
 
115
         */
 
116
        int rootNodeSeqNb, res;
 
117
        int bs, maxc;
 
118
        long startTime;
 
119
 
 
120
        /* Java I/O mumbo jumbo... */
 
121
        if (!existingStateFile.exists()) {
 
122
            throw new IOException("Selected state file does not exist"); //$NON-NLS-1$
 
123
        }
 
124
        if (existingStateFile.length() <= 0) {
 
125
            throw new IOException("Invalid state file selected, " + //$NON-NLS-1$
 
126
                    "target file is empty"); //$NON-NLS-1$
 
127
        }
 
128
 
 
129
        FileInputStream fis = new FileInputStream(existingStateFile);
 
130
        ByteBuffer buffer = ByteBuffer.allocate(getTreeHeaderSize());
 
131
        FileChannel fc = fis.getChannel();
 
132
        buffer.order(ByteOrder.LITTLE_ENDIAN);
 
133
        buffer.clear();
 
134
        fc.read(buffer);
 
135
        buffer.flip();
 
136
 
 
137
        /*
 
138
         * Check the magic number,to make sure we're opening the right type of
 
139
         * file
 
140
         */
 
141
        res = buffer.getInt();
 
142
        if (res != HISTORY_FILE_MAGIC_NUMBER) {
 
143
            fc.close();
 
144
            fis.close();
 
145
            throw new IOException("Selected file does not" + //$NON-NLS-1$
 
146
                    "look like a History Tree file"); //$NON-NLS-1$
 
147
        }
 
148
 
 
149
        res = buffer.getInt(); /* Major version number */
 
150
        if (res != MAJOR_VERSION) {
 
151
            fc.close();
 
152
            fis.close();
 
153
            throw new IOException("Select History Tree file is of an older " //$NON-NLS-1$
 
154
                    + "format. Please use a previous version of " //$NON-NLS-1$
 
155
                    + "the parser to open it."); //$NON-NLS-1$
 
156
        }
 
157
 
 
158
        res = buffer.getInt(); /* Minor version number */
 
159
 
 
160
        bs = buffer.getInt(); /* Block Size */
 
161
        maxc = buffer.getInt(); /* Max nb of children per node */
 
162
 
 
163
        this.nodeCount = buffer.getInt();
 
164
        rootNodeSeqNb = buffer.getInt();
 
165
        startTime = buffer.getLong();
 
166
 
 
167
        this.config = new HTConfig(existingStateFile, bs, maxc, startTime);
 
168
        fc.close();
 
169
        fis.close();
 
170
        /*
 
171
         * FIXME We close fis here and the TreeIO will then reopen the same
 
172
         * file, not extremely elegant. But how to pass the information here to
 
173
         * the SHT otherwise?
 
174
         */
 
175
        this.treeIO = new HT_IO(this, false);
 
176
 
 
177
        rebuildLatestBranch(rootNodeSeqNb);
 
178
        this.treeEnd = latestBranch.firstElement().getNodeEnd();
 
179
 
 
180
        /*
 
181
         * Make sure the history start time we read previously is consistent
 
182
         * with was is actually in the root node.
 
183
         */
 
184
        if (startTime != latestBranch.firstElement().getNodeStart()) {
 
185
            fc.close();
 
186
            fis.close();
 
187
            throw new IOException("Inconsistent start times in the" + //$NON-NLS-1$
 
188
                    "history file, it might be corrupted."); //$NON-NLS-1$
 
189
        }
 
190
    }
 
191
 
 
192
    /**
 
193
     * "Save" the tree to disk. This method will cause the treeIO object to
 
194
     * commit all nodes to disk and then return the RandomAccessFile descriptor
 
195
     * so the Tree object can save its configuration into the header of the
 
196
     * file.
 
197
     * 
 
198
     * @param requestedEndTime
 
199
     */
 
200
    void closeTree(long requestedEndTime) {
 
201
        FileChannel fc;
 
202
        ByteBuffer buffer;
 
203
        int i, res;
 
204
 
 
205
        /* 
 
206
         * Work-around the "empty branches" that get created when the root node
 
207
         * becomes full. Overwrite the tree's end time with the original wanted
 
208
         * end-time, to ensure no queries are sent into those empty nodes.
 
209
         * 
 
210
         * This won't be needed once extended nodes are implemented.
 
211
         */
 
212
        this.treeEnd = requestedEndTime;
 
213
 
 
214
        /* Close off the latest branch of the tree */
 
215
        for (i = 0; i < latestBranch.size(); i++) {
 
216
            latestBranch.get(i).closeThisNode(treeEnd);
 
217
            treeIO.writeNode(latestBranch.get(i));
 
218
        }
 
219
 
 
220
        /* Only use this for debugging purposes, it's VERY slow! */
 
221
        // this.checkIntegrity();
 
222
 
 
223
        fc = treeIO.getFcOut();
 
224
        buffer = ByteBuffer.allocate(getTreeHeaderSize());
 
225
        buffer.order(ByteOrder.LITTLE_ENDIAN);
 
226
        buffer.clear();
 
227
 
 
228
        /* Save the config of the tree to the header of the file */
 
229
        try {
 
230
            fc.position(0);
 
231
 
 
232
            buffer.putInt(HISTORY_FILE_MAGIC_NUMBER);
 
233
 
 
234
            buffer.putInt(MAJOR_VERSION);
 
235
            buffer.putInt(MINOR_VERSION);
 
236
 
 
237
            buffer.putInt(config.blockSize);
 
238
            buffer.putInt(config.maxChildren);
 
239
 
 
240
            buffer.putInt(nodeCount);
 
241
 
 
242
            /* root node seq. nb */
 
243
            buffer.putInt(latestBranch.firstElement().getSequenceNumber());
 
244
 
 
245
            /* start time of this history */
 
246
            buffer.putLong(latestBranch.firstElement().getNodeStart());
 
247
 
 
248
            buffer.flip();
 
249
            res = fc.write(buffer);
 
250
            assert (res <= getTreeHeaderSize());
 
251
            /* done writing the file header */
 
252
 
 
253
        } catch (IOException e) {
 
254
            /* We should not have any problems at this point... */
 
255
            e.printStackTrace();
 
256
        } finally {
 
257
            try {
 
258
                fc.close();
 
259
            } catch (IOException e) {
 
260
                e.printStackTrace();
 
261
            }
 
262
        }
 
263
        return;
 
264
    }
 
265
 
 
266
    /**
 
267
     * @name Accessors
 
268
     */
 
269
 
 
270
    long getTreeStart() {
 
271
        return config.treeStart;
 
272
    }
 
273
 
 
274
    long getTreeEnd() {
 
275
        return treeEnd;
 
276
    }
 
277
 
 
278
    int getNodeCount() {
 
279
        return nodeCount;
 
280
    }
 
281
 
 
282
    HT_IO getTreeIO() {
 
283
        return treeIO;
 
284
    }
 
285
 
 
286
    /**
 
287
     * Rebuild the latestBranch "cache" object by reading the nodes from disk
 
288
     * (When we are opening an existing file on disk and want to append to it,
 
289
     * for example).
 
290
     * 
 
291
     * @param rootNodeSeqNb
 
292
     *            The sequence number of the root node, so we know where to
 
293
     *            start
 
294
     */
 
295
    private void rebuildLatestBranch(int rootNodeSeqNb) {
 
296
        HTNode nextChildNode;
 
297
 
 
298
        this.latestBranch = new Vector<CoreNode>();
 
299
 
 
300
        nextChildNode = treeIO.readNodeFromDisk(rootNodeSeqNb);
 
301
        latestBranch.add((CoreNode) nextChildNode);
 
302
        while (latestBranch.lastElement().getNbChildren() > 0) {
 
303
            nextChildNode = treeIO.readNodeFromDisk(latestBranch.lastElement().getLatestChild());
 
304
            latestBranch.add((CoreNode) nextChildNode);
 
305
        }
 
306
    }
 
307
 
 
308
    /**
 
309
     * Insert an interval in the tree
 
310
     * 
 
311
     * @param interval
 
312
     */
 
313
    void insertInterval(HTInterval interval) throws TimeRangeException {
 
314
        if (interval.getStartTime() < config.treeStart) {
 
315
            throw new TimeRangeException();
 
316
        }
 
317
        tryInsertAtNode(interval, latestBranch.size() - 1);
 
318
    }
 
319
 
 
320
    /**
 
321
     * Inner method to find in which node we should add the interval.
 
322
     * 
 
323
     * @param interval
 
324
     *            The interval to add to the tree
 
325
     * @param indexOfNode
 
326
     *            The index *in the latestBranch* where we are trying the
 
327
     *            insertion
 
328
     */
 
329
    private void tryInsertAtNode(HTInterval interval, int indexOfNode) {
 
330
        HTNode targetNode = latestBranch.get(indexOfNode);
 
331
 
 
332
        /* Verify if there is enough room in this node to store this interval */
 
333
        if (interval.getIntervalSize() > targetNode.getNodeFreeSpace()) {
 
334
            /* Nope, not enough room. Insert in a new sibling instead. */
 
335
            addSiblingNode(indexOfNode);
 
336
            tryInsertAtNode(interval, latestBranch.size() - 1);
 
337
            return;
 
338
        }
 
339
 
 
340
        /* Make sure the interval time range fits this node */
 
341
        if (interval.getStartTime() < targetNode.getNodeStart()) {
 
342
            /*
 
343
             * No, this interval starts before the startTime of this node. We
 
344
             * need to check recursively in parents if it can fit.
 
345
             */
 
346
            assert (indexOfNode >= 1);
 
347
            tryInsertAtNode(interval, indexOfNode - 1);
 
348
            return;
 
349
        }
 
350
 
 
351
        /*
 
352
         * Ok, there is room, and the interval fits in this time slot. Let's add
 
353
         * it.
 
354
         */
 
355
        targetNode.addInterval(interval);
 
356
 
 
357
        /* Update treeEnd if needed */
 
358
        if (interval.getEndTime() > this.treeEnd) {
 
359
            this.treeEnd = interval.getEndTime();
 
360
        }
 
361
        return;
 
362
    }
 
363
 
 
364
    /**
 
365
     * Method to add a sibling to any node in the latest branch. This will add
 
366
     * children back down to the leaf level, if needed.
 
367
     * 
 
368
     * @param indexOfNode
 
369
     *            The index in latestBranch where we start adding
 
370
     */
 
371
    private void addSiblingNode(int indexOfNode) {
 
372
        int i;
 
373
        CoreNode newNode, prevNode;
 
374
        long splitTime = treeEnd;
 
375
 
 
376
        assert (indexOfNode < latestBranch.size());
 
377
 
 
378
        /* Check if we need to add a new root node */
 
379
        if (indexOfNode == 0) {
 
380
            addNewRootNode();
 
381
            return;
 
382
        }
 
383
 
 
384
        /* Check if we can indeed add a child to the target parent */
 
385
        if (latestBranch.get(indexOfNode - 1).getNbChildren() == config.maxChildren) {
 
386
            /* If not, add a branch starting one level higher instead */
 
387
            addSiblingNode(indexOfNode - 1);
 
388
            return;
 
389
        }
 
390
 
 
391
        /* Split off the new branch from the old one */
 
392
        for (i = indexOfNode; i < latestBranch.size(); i++) {
 
393
            latestBranch.get(i).closeThisNode(splitTime);
 
394
            treeIO.writeNode(latestBranch.get(i));
 
395
 
 
396
            prevNode = latestBranch.get(i - 1);
 
397
            newNode = initNewCoreNode(prevNode.getSequenceNumber(),
 
398
                    splitTime + 1);
 
399
            prevNode.linkNewChild(newNode);
 
400
 
 
401
            latestBranch.set(i, newNode);
 
402
        }
 
403
        return;
 
404
    }
 
405
 
 
406
    /**
 
407
     * Similar to the previous method, except here we rebuild a completely new
 
408
     * latestBranch
 
409
     */
 
410
    private void addNewRootNode() {
 
411
        int i, depth;
 
412
        CoreNode oldRootNode, newRootNode, newNode, prevNode;
 
413
        long splitTime = this.treeEnd;
 
414
 
 
415
        oldRootNode = latestBranch.firstElement();
 
416
        newRootNode = initNewCoreNode(-1, config.treeStart);
 
417
 
 
418
        /* Tell the old root node that it isn't root anymore */
 
419
        oldRootNode.setParentSequenceNumber(newRootNode.getSequenceNumber());
 
420
 
 
421
        /* Close off the whole current latestBranch */
 
422
        for (i = 0; i < latestBranch.size(); i++) {
 
423
            latestBranch.get(i).closeThisNode(splitTime);
 
424
            treeIO.writeNode(latestBranch.get(i));
 
425
        }
 
426
 
 
427
        /* Link the new root to its first child (the previous root node) */
 
428
        newRootNode.linkNewChild(oldRootNode);
 
429
 
 
430
        /* Rebuild a new latestBranch */
 
431
        depth = latestBranch.size();
 
432
        latestBranch = new Vector<CoreNode>();
 
433
        latestBranch.add(newRootNode);
 
434
        for (i = 1; i < depth + 1; i++) {
 
435
            prevNode = latestBranch.get(i - 1);
 
436
            newNode = initNewCoreNode(prevNode.getParentSequenceNumber(),
 
437
                    splitTime + 1);
 
438
            prevNode.linkNewChild(newNode);
 
439
            latestBranch.add(newNode);
 
440
        }
 
441
    }
 
442
 
 
443
    /**
 
444
     * Add a new empty node to the tree.
 
445
     * 
 
446
     * @param parentSeqNumber
 
447
     *            Sequence number of this node's parent
 
448
     * @param startTime
 
449
     *            Start time of the new node
 
450
     * @return The newly created node
 
451
     */
 
452
    private CoreNode initNewCoreNode(int parentSeqNumber, long startTime) {
 
453
        CoreNode newNode = new CoreNode(this, this.nodeCount, parentSeqNumber,
 
454
                startTime);
 
455
        this.nodeCount++;
 
456
 
 
457
        /* Update the treeEnd if needed */
 
458
        if (startTime >= this.treeEnd) {
 
459
            this.treeEnd = startTime + 1;
 
460
        }
 
461
        return newNode;
 
462
    }
 
463
 
 
464
    /**
 
465
     * Inner method to select the next child of the current node intersecting
 
466
     * the given timestamp. Useful for moving down the tree following one
 
467
     * branch.
 
468
     * 
 
469
     * @param currentNode
 
470
     * @param t
 
471
     * @return The child node intersecting t
 
472
     */
 
473
    HTNode selectNextChild(CoreNode currentNode, long t) {
 
474
        assert (currentNode.getNbChildren() > 0);
 
475
        int potentialNextSeqNb = currentNode.getSequenceNumber();
 
476
 
 
477
        for (int i = 0; i < currentNode.getNbChildren(); i++) {
 
478
            if (t >= currentNode.getChildStart(i)) {
 
479
                potentialNextSeqNb = currentNode.getChild(i);
 
480
            } else {
 
481
                break;
 
482
            }
 
483
        }
 
484
        /*
 
485
         * Once we exit this loop, we should have found a children to follow. If
 
486
         * we didn't, there's a problem.
 
487
         */
 
488
        assert (potentialNextSeqNb != currentNode.getSequenceNumber());
 
489
 
 
490
        /*
 
491
         * Since this code path is quite performance-critical, avoid iterating
 
492
         * through the whole latestBranch array if we know for sure the next
 
493
         * node has to be on disk
 
494
         */
 
495
        if (currentNode.isDone()) {
 
496
            return treeIO.readNodeFromDisk(potentialNextSeqNb);
 
497
        }
 
498
        return treeIO.readNode(potentialNextSeqNb);
 
499
    }
 
500
 
 
501
    /**
 
502
     * Helper function to get the size of the "tree header" in the tree-file The
 
503
     * nodes will use this offset to know where they should be in the file. This
 
504
     * should always be a multiple of 4K.
 
505
     */
 
506
    static int getTreeHeaderSize() {
 
507
        return 4096;
 
508
    }
 
509
 
 
510
    long getFileSize() {
 
511
        return config.stateFile.length();
 
512
    }
 
513
 
 
514
    /**
 
515
     * @name Test/debugging functions
 
516
     */
 
517
 
 
518
    /* Only used for debugging, shouldn't be externalized */
 
519
    @SuppressWarnings("nls")
 
520
    boolean checkNodeIntegrity(HTNode zenode) {
 
521
 
 
522
        HTNode otherNode;
 
523
        CoreNode node;
 
524
        StringBuffer buf = new StringBuffer();
 
525
        boolean ret = true;
 
526
 
 
527
        // FIXME /* Only testing Core Nodes for now */
 
528
        if (!(zenode instanceof CoreNode)) {
 
529
            return true;
 
530
        }
 
531
 
 
532
        node = (CoreNode) zenode;
 
533
 
 
534
        /*
 
535
         * Test that this node's start and end times match the start of the
 
536
         * first child and the end of the last child, respectively
 
537
         */
 
538
        if (node.getNbChildren() > 0) {
 
539
            otherNode = treeIO.readNode(node.getChild(0));
 
540
            if (node.getNodeStart() != otherNode.getNodeStart()) {
 
541
                buf.append("Start time of node (" + node.getNodeStart() + ") "
 
542
                        + "does not match start time of first child " + "("
 
543
                        + otherNode.getNodeStart() + "), " + "node #"
 
544
                        + otherNode.getSequenceNumber() + ")\n");
 
545
                ret = false;
 
546
            }
 
547
            if (node.isDone()) {
 
548
                otherNode = treeIO.readNode(node.getLatestChild());
 
549
                if (node.getNodeEnd() != otherNode.getNodeEnd()) {
 
550
                    buf.append("End time of node (" + node.getNodeEnd()
 
551
                            + ") does not match end time of last child ("
 
552
                            + otherNode.getNodeEnd() + ", node #"
 
553
                            + otherNode.getSequenceNumber() + ")\n");
 
554
                    ret = false;
 
555
                }
 
556
            }
 
557
        }
 
558
 
 
559
        /*
 
560
         * Test that the childStartTimes[] array matches the real nodes' start
 
561
         * times
 
562
         */
 
563
        for (int i = 0; i < node.getNbChildren(); i++) {
 
564
            otherNode = treeIO.readNode(node.getChild(i));
 
565
            if (otherNode.getNodeStart() != node.getChildStart(i)) {
 
566
                buf.append("  Expected start time of child node #"
 
567
                        + node.getChild(i) + ": " + node.getChildStart(i)
 
568
                        + "\n" + "  Actual start time of node #"
 
569
                        + otherNode.getSequenceNumber() + ": "
 
570
                        + otherNode.getNodeStart() + "\n");
 
571
                ret = false;
 
572
            }
 
573
        }
 
574
 
 
575
        if (!ret) {
 
576
            System.out.println("");
 
577
            System.out.println("SHT: Integrity check failed for node #"
 
578
                    + node.getSequenceNumber() + ":");
 
579
            System.out.println(buf.toString());
 
580
        }
 
581
        return ret;
 
582
    }
 
583
 
 
584
    void checkIntegrity() {
 
585
        for (int i = 0; i < nodeCount; i++) {
 
586
            checkNodeIntegrity(treeIO.readNode(i));
 
587
        }
 
588
    }
 
589
 
 
590
    /* Only used for debugging, shouldn't be externalized */
 
591
    @SuppressWarnings("nls")
 
592
    @Override
 
593
    public String toString() {
 
594
        return "Information on the current tree:\n\n" + "Blocksize: "
 
595
                + config.blockSize + "\n" + "Max nb. of children per node: "
 
596
                + config.maxChildren + "\n" + "Number of nodes: " + nodeCount
 
597
                + "\n" + "Depth of the tree: " + latestBranch.size() + "\n"
 
598
                + "Size of the treefile: " + this.getFileSize() + "\n"
 
599
                + "Root node has sequence number: "
 
600
                + latestBranch.firstElement().getSequenceNumber() + "\n"
 
601
                + "'Latest leaf' has sequence number: "
 
602
                + latestBranch.lastElement().getSequenceNumber();
 
603
    }
 
604
 
 
605
    private int curDepth;
 
606
 
 
607
    /**
 
608
     * Start at currentNode and print the contents of all its children, in
 
609
     * pre-order. Give the root node in parameter to visit the whole tree, and
 
610
     * have a nice overview.
 
611
     */
 
612
    @SuppressWarnings("nls")
 
613
    private void preOrderPrint(PrintWriter writer, boolean printIntervals,
 
614
            CoreNode currentNode) {
 
615
        /* Only used for debugging, shouldn't be externalized */
 
616
        int i, j;
 
617
        HTNode nextNode;
 
618
 
 
619
        writer.println(currentNode.toString());
 
620
        if (printIntervals) {
 
621
            currentNode.debugPrintIntervals(writer);
 
622
        }
 
623
        curDepth++;
 
624
 
 
625
        for (i = 0; i < currentNode.getNbChildren(); i++) {
 
626
            nextNode = treeIO.readNode(currentNode.getChild(i));
 
627
            assert (nextNode instanceof CoreNode); // TODO temporary
 
628
            for (j = 0; j < curDepth - 1; j++) {
 
629
                writer.print("  ");
 
630
            }
 
631
            writer.print("+-");
 
632
            preOrderPrint(writer, printIntervals, (CoreNode) nextNode);
 
633
        }
 
634
        curDepth--;
 
635
        return;
 
636
    }
 
637
 
 
638
    /**
 
639
     * Print out the full tree for debugging purposes
 
640
     * 
 
641
     * @param writer
 
642
     *            PrintWriter in which to write the output
 
643
     * @param printIntervals
 
644
     *            Says if you want to output the full interval information
 
645
     */
 
646
    void debugPrintFullTree(PrintWriter writer, boolean printIntervals) {
 
647
        /* Only used for debugging, shouldn't be externalized */
 
648
        curDepth = 0;
 
649
        this.preOrderPrint(writer, false, latestBranch.firstElement());
 
650
 
 
651
        if (printIntervals) {
 
652
            writer.println("\nDetails of intervals:"); //$NON-NLS-1$
 
653
            curDepth = 0;
 
654
            this.preOrderPrint(writer, true, latestBranch.firstElement());
 
655
        }
 
656
        writer.println('\n');
 
657
    }
 
658
 
 
659
}