tmf: Synchronize accesses to HistoryTree's latest branch
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.core / src / org / eclipse / linuxtools / internal / tmf / core / statesystem / backends / historytree / HistoryTree.java
index fa2378b8a8cfe044c2d5ba7df8472d76cc062968..5b7b25d1b791e5abcfa4cd311f99080ab34252c2 100644 (file)
@@ -31,10 +31,9 @@ import org.eclipse.linuxtools.tmf.core.statesystem.ITmfStateProvider;
  * Meta-container for the History Tree. This structure contains all the
  * high-level data relevant to the tree.
  *
- * @author alexmont
- *
+ * @author Alexandre Montplaisir
  */
-class HistoryTree {
+public class HistoryTree {
 
     /**
      * Size of the "tree header" in the tree-file The nodes will use this offset
@@ -65,21 +64,27 @@ class HistoryTree {
     /** Latest timestamp found in the tree (at any given moment) */
     private long treeEnd;
 
-    /** How many nodes exist in this tree, total */
+    /** The total number of nodes that exists in this tree */
     private int nodeCount;
 
     /** "Cache" to keep the active nodes in memory */
-    private List<CoreNode> latestBranch;
+    private final List<CoreNode> latestBranch;
 
     // ------------------------------------------------------------------------
     // Constructors/"Destructors"
     // ------------------------------------------------------------------------
 
     /**
-     * Create a new State History from scratch, using a SHTConfig object for
-     * configuration
+     * Create a new State History from scratch, using a {@link HTConfig} object
+     * for configuration.
+     *
+     * @param conf
+     *            The config to use for this History Tree.
+     * @throws IOException
+     *             If an error happens trying to open/write to the file
+     *             specified in the config
      */
-    HistoryTree(HTConfig conf) throws IOException {
+    public HistoryTree(HTConfig conf) throws IOException {
         /*
          * Simple check to make sure we have enough place in the 0th block
          * for the tree configuration
@@ -91,10 +96,10 @@ class HistoryTree {
         config = conf;
         treeEnd = conf.getTreeStart();
         nodeCount = 0;
-        latestBranch = new ArrayList<CoreNode>();
+        latestBranch = Collections.synchronizedList(new ArrayList<CoreNode>());
 
         /* Prepare the IO object */
-        treeIO = new HT_IO(this, true);
+        treeIO = new HT_IO(config, true);
 
         /* Add the first node to the tree */
         CoreNode firstNode = initNewCoreNode(-1, conf.getTreeStart());
@@ -105,13 +110,14 @@ class HistoryTree {
      * "Reader" constructor : instantiate a SHTree from an existing tree file on
      * disk
      *
-     * @param existingFileName
+     * @param existingStateFile
      *            Path/filename of the history-file we are to open
      * @param expProviderVersion
      *            The expected version of the state provider
      * @throws IOException
+     *             If an error happens reading the file
      */
-    HistoryTree(File existingStateFile, int expProviderVersion) throws IOException {
+    public HistoryTree(File existingStateFile, int expProviderVersion) throws IOException {
         /*
          * Open the file ourselves, get the tree header information we need,
          * then pass on the descriptor to the TreeIO object.
@@ -128,77 +134,97 @@ class HistoryTree {
             throw new IOException("Empty target file"); //$NON-NLS-1$
         }
 
-        FileInputStream fis = new FileInputStream(existingStateFile);
-        ByteBuffer buffer = ByteBuffer.allocate(TREE_HEADER_SIZE);
-        FileChannel fc = fis.getChannel();
-        buffer.order(ByteOrder.LITTLE_ENDIAN);
-        buffer.clear();
-        fc.read(buffer);
-        buffer.flip();
+        try (FileInputStream fis = new FileInputStream(existingStateFile);
+                FileChannel fc = fis.getChannel();) {
 
-        /*
-         * Check the magic number,to make sure we're opening the right type of
-         * file
-         */
-        res = buffer.getInt();
-        if (res != HISTORY_FILE_MAGIC_NUMBER) {
-            fc.close();
-            fis.close();
-            throw new IOException("Wrong magic number"); //$NON-NLS-1$
-        }
+            ByteBuffer buffer = ByteBuffer.allocate(TREE_HEADER_SIZE);
 
-        res = buffer.getInt(); /* File format version number */
-        if (res != FILE_VERSION) {
-            fc.close();
-            fis.close();
-            throw new IOException("Mismatching History Tree file format versions"); //$NON-NLS-1$
-        }
+            buffer.order(ByteOrder.LITTLE_ENDIAN);
+            buffer.clear();
+            fc.read(buffer);
+            buffer.flip();
 
-        res = buffer.getInt(); /* Event handler's version number */
-        if (res != expProviderVersion &&
-                expProviderVersion != ITmfStateProvider.IGNORE_PROVIDER_VERSION) {
             /*
-             * The existing history was built using a event handler that doesn't
-             * match the current one in the framework. Information could be all
-             * wrong, so we'll force a rebuild of the history file instead.
+             * Check the magic number to make sure we're opening the right type
+             * of file
              */
-            fc.close();
-            fis.close();
-            throw new IOException("Mismatching event handler versions"); //$NON-NLS-1$
-        }
+            res = buffer.getInt();
+            if (res != HISTORY_FILE_MAGIC_NUMBER) {
+                throw new IOException("Wrong magic number"); //$NON-NLS-1$
+            }
+
+            res = buffer.getInt(); /* File format version number */
+            if (res != FILE_VERSION) {
+                throw new IOException("Mismatching History Tree file format versions"); //$NON-NLS-1$
+            }
 
-        bs = buffer.getInt(); /* Block Size */
-        maxc = buffer.getInt(); /* Max nb of children per node */
+            res = buffer.getInt(); /* Event handler's version number */
+            if (res != expProviderVersion &&
+                    expProviderVersion != ITmfStateProvider.IGNORE_PROVIDER_VERSION) {
+                /*
+                 * The existing history was built using an event handler that
+                 * doesn't match the current one in the framework.
+                 *
+                 * Information could be all wrong. Instead of keeping an
+                 * incorrect history file, a rebuild is done.
+                 */
+                throw new IOException("Mismatching event handler versions"); //$NON-NLS-1$
+            }
+
+            bs = buffer.getInt(); /* Block Size */
+            maxc = buffer.getInt(); /* Max nb of children per node */
 
-        this.nodeCount = buffer.getInt();
-        rootNodeSeqNb = buffer.getInt();
-        startTime = buffer.getLong();
+            this.nodeCount = buffer.getInt();
+            rootNodeSeqNb = buffer.getInt();
+            startTime = buffer.getLong();
+
+            this.config = new HTConfig(existingStateFile, bs, maxc, expProviderVersion, startTime);
+        }
 
-        this.config = new HTConfig(existingStateFile, bs, maxc, expProviderVersion, startTime);
-        fc.close();
-        fis.close();
         /*
          * FIXME We close fis here and the TreeIO will then reopen the same
          * file, not extremely elegant. But how to pass the information here to
          * the SHT otherwise?
          */
-        this.treeIO = new HT_IO(this, false);
+        this.treeIO = new HT_IO(config, false);
 
-        rebuildLatestBranch(rootNodeSeqNb);
-        this.treeEnd = latestBranch.get(0).getNodeEnd();
+        this.latestBranch = buildLatestBranch(rootNodeSeqNb);
+        this.treeEnd = getRootNode().getNodeEnd();
 
         /*
          * Make sure the history start time we read previously is consistent
          * with was is actually in the root node.
          */
-        if (startTime != latestBranch.get(0).getNodeStart()) {
-            fc.close();
-            fis.close();
+        if (startTime != getRootNode().getNodeStart()) {
             throw new IOException("Inconsistent start times in the" + //$NON-NLS-1$
                     "history file, it might be corrupted."); //$NON-NLS-1$
         }
     }
 
+    /**
+     * Rebuild the latestBranch "cache" object by reading the nodes from disk
+     * (When we are opening an existing file on disk and want to append to it,
+     * for example).
+     *
+     * @param rootNodeSeqNb
+     *            The sequence number of the root node, so we know where to
+     *            start
+     * @throws ClosedChannelException
+     */
+    private List<CoreNode> buildLatestBranch(int rootNodeSeqNb) throws ClosedChannelException {
+        HTNode nextChildNode;
+
+        List<CoreNode> list = new ArrayList<>();
+
+        nextChildNode = treeIO.readNode(rootNodeSeqNb);
+        list.add((CoreNode) nextChildNode);
+        while (list.get(list.size() - 1).getNbChildren() > 0) {
+            nextChildNode = treeIO.readNode(list.get(list.size() - 1).getLatestChild());
+            list.add((CoreNode) nextChildNode);
+        }
+        return Collections.synchronizedList(list);
+    }
+
     /**
      * "Save" the tree to disk. This method will cause the treeIO object to
      * commit all nodes to disk and then return the RandomAccessFile descriptor
@@ -206,129 +232,201 @@ class HistoryTree {
      * file.
      *
      * @param requestedEndTime
+     *            The greatest timestamp present in the history tree
      */
-    void closeTree(long requestedEndTime) {
-        FileChannel fc;
-        ByteBuffer buffer;
-        int i, res;
+    public void closeTree(long requestedEndTime) {
+        /* This is an important operation, queries can wait */
+        synchronized (latestBranch) {
+            /*
+             * Work-around the "empty branches" that get created when the root
+             * node becomes full. Overwrite the tree's end time with the
+             * original wanted end-time, to ensure no queries are sent into
+             * those empty nodes.
+             *
+             * This won't be needed once extended nodes are implemented.
+             */
+            this.treeEnd = requestedEndTime;
 
-        /*
-         * Work-around the "empty branches" that get created when the root node
-         * becomes full. Overwrite the tree's end time with the original wanted
-         * end-time, to ensure no queries are sent into those empty nodes.
-         *
-         * This won't be needed once extended nodes are implemented.
-         */
-        this.treeEnd = requestedEndTime;
+            /* Close off the latest branch of the tree */
+            for (int i = 0; i < latestBranch.size(); i++) {
+                latestBranch.get(i).closeThisNode(treeEnd);
+                treeIO.writeNode(latestBranch.get(i));
+            }
 
-        /* Close off the latest branch of the tree */
-        for (i = 0; i < latestBranch.size(); i++) {
-            latestBranch.get(i).closeThisNode(treeEnd);
-            treeIO.writeNode(latestBranch.get(i));
-        }
+            try (FileChannel fc = treeIO.getFcOut();) {
+                ByteBuffer buffer = ByteBuffer.allocate(TREE_HEADER_SIZE);
+                buffer.order(ByteOrder.LITTLE_ENDIAN);
+                buffer.clear();
 
-        fc = treeIO.getFcOut();
-        buffer = ByteBuffer.allocate(TREE_HEADER_SIZE);
-        buffer.order(ByteOrder.LITTLE_ENDIAN);
-        buffer.clear();
+                /* Save the config of the tree to the header of the file */
+                fc.position(0);
 
-        /* Save the config of the tree to the header of the file */
-        try {
-            fc.position(0);
+                buffer.putInt(HISTORY_FILE_MAGIC_NUMBER);
 
-            buffer.putInt(HISTORY_FILE_MAGIC_NUMBER);
+                buffer.putInt(FILE_VERSION);
+                buffer.putInt(config.getProviderVersion());
 
-            buffer.putInt(FILE_VERSION);
-            buffer.putInt(config.getProviderVersion());
+                buffer.putInt(config.getBlockSize());
+                buffer.putInt(config.getMaxChildren());
 
-            buffer.putInt(config.getBlockSize());
-            buffer.putInt(config.getMaxChildren());
+                buffer.putInt(nodeCount);
 
-            buffer.putInt(nodeCount);
+                /* root node seq. nb */
+                buffer.putInt(latestBranch.get(0).getSequenceNumber());
 
-            /* root node seq. nb */
-            buffer.putInt(latestBranch.get(0).getSequenceNumber());
+                /* start time of this history */
+                buffer.putLong(latestBranch.get(0).getNodeStart());
 
-            /* start time of this history */
-            buffer.putLong(latestBranch.get(0).getNodeStart());
+                buffer.flip();
+                int res = fc.write(buffer);
+                assert (res <= TREE_HEADER_SIZE);
+                /* done writing the file header */
 
-            buffer.flip();
-            res = fc.write(buffer);
-            assert (res <= TREE_HEADER_SIZE);
-            /* done writing the file header */
-
-        } catch (IOException e) {
-            /* We should not have any problems at this point... */
-        } finally {
-            try {
-                fc.close();
             } catch (IOException e) {
+                /*
+                 * If we were able to write so far, there should not be any
+                 * problem at this point...
+                 */
+                throw new RuntimeException("State system write error"); //$NON-NLS-1$
             }
         }
-        return;
     }
 
     // ------------------------------------------------------------------------
     // Accessors
     // ------------------------------------------------------------------------
 
-    HTConfig getConfig() {
-        return config;
-    }
-
-    long getTreeStart() {
+    /**
+     * Get the start time of this tree.
+     *
+     * @return The start time
+     */
+    public long getTreeStart() {
         return config.getTreeStart();
     }
 
-    long getTreeEnd() {
+    /**
+     * Get the current end time of this tree.
+     *
+     * @return The end time
+     */
+    public long getTreeEnd() {
         return treeEnd;
     }
 
-    int getNodeCount() {
+    /**
+     * Get the number of nodes in this tree.
+     *
+     * @return The number of nodes
+     */
+    public int getNodeCount() {
         return nodeCount;
     }
 
-    HT_IO getTreeIO() {
-        return treeIO;
-    }
-
-    List<CoreNode> getLatestBranch() {
-        return Collections.unmodifiableList(latestBranch);
+    /**
+     * Get the current root node of this tree
+     *
+     * @return The root node
+     */
+    public CoreNode getRootNode() {
+        return latestBranch.get(0);
     }
 
     // ------------------------------------------------------------------------
-    // Operations
+    // HT_IO interface
     // ------------------------------------------------------------------------
 
     /**
-     * Rebuild the latestBranch "cache" object by reading the nodes from disk
-     * (When we are opening an existing file on disk and want to append to it,
-     * for example).
+     * Return the FileInputStream reader with which we will read an attribute
+     * tree (it will be sought to the correct position).
      *
-     * @param rootNodeSeqNb
-     *            The sequence number of the root node, so we know where to
-     *            start
-     * @throws ClosedChannelException
+     * @return The FileInputStream indicating the file and position from which
+     *         the attribute tree can be read.
      */
-    private void rebuildLatestBranch(int rootNodeSeqNb) throws ClosedChannelException {
-        HTNode nextChildNode;
+    public FileInputStream supplyATReader() {
+        return treeIO.supplyATReader(getNodeCount());
+    }
+
+    /**
+     * Return the file to which we will write the attribute tree.
+     *
+     * @return The file to which we will write the attribute tree
+     */
+    public File supplyATWriterFile() {
+        return config.getStateFile();
+    }
 
-        this.latestBranch = new ArrayList<CoreNode>();
+    /**
+     * Return the position in the file (given by {@link #supplyATWriterFile})
+     * where to start writing the attribute tree.
+     *
+     * @return The position in the file where to start writing
+     */
+    public long supplyATWriterFilePos() {
+        return HistoryTree.TREE_HEADER_SIZE
+                + ((long) getNodeCount() * config.getBlockSize());
+    }
 
-        nextChildNode = treeIO.readNodeFromDisk(rootNodeSeqNb);
-        latestBranch.add((CoreNode) nextChildNode);
-        while (latestBranch.get(latestBranch.size() - 1).getNbChildren() > 0) {
-            nextChildNode = treeIO.readNodeFromDisk(latestBranch.get(latestBranch.size() - 1).getLatestChild());
-            latestBranch.add((CoreNode) nextChildNode);
+    /**
+     * Read a node from the tree.
+     *
+     * @param seqNumber
+     *            The sequence number of the node to read
+     * @return The node
+     * @throws ClosedChannelException
+     *             If the tree IO is unavailable
+     */
+    public HTNode readNode(int seqNumber) throws ClosedChannelException {
+        /* Try to read the node from memory */
+        synchronized (latestBranch) {
+            for (HTNode node : latestBranch) {
+                if (node.getSequenceNumber() == seqNumber) {
+                    return node;
+                }
+            }
         }
+
+        /* Read the node from disk */
+        return treeIO.readNode(seqNumber);
+    }
+
+    /**
+     * Write a node object to the history file.
+     *
+     * @param node
+     *            The node to write to disk
+     */
+    public void writeNode(HTNode node) {
+        treeIO.writeNode(node);
+    }
+
+    /**
+     * Close the history file.
+     */
+    public void closeFile() {
+        treeIO.closeFile();
+    }
+
+    /**
+     * Delete the history file.
+     */
+    public void deleteFile() {
+        treeIO.deleteFile();
     }
 
+    // ------------------------------------------------------------------------
+    // Operations
+    // ------------------------------------------------------------------------
+
     /**
-     * Insert an interval in the tree
+     * Insert an interval in the tree.
      *
      * @param interval
+     *            The interval to be inserted
+     * @throws TimeRangeException
+     *             If the start of end time of the interval are invalid
      */
-    void insertInterval(HTInterval interval) throws TimeRangeException {
+    public void insertInterval(HTInterval interval) throws TimeRangeException {
         if (interval.getStartTime() < config.getTreeStart()) {
             throw new TimeRangeException();
         }
@@ -376,7 +474,6 @@ class HistoryTree {
         if (interval.getEndTime() > this.treeEnd) {
             this.treeEnd = interval.getEndTime();
         }
-        return;
     }
 
     /**
@@ -387,38 +484,37 @@ class HistoryTree {
      *            The index in latestBranch where we start adding
      */
     private void addSiblingNode(int indexOfNode) {
-        int i;
-        CoreNode newNode, prevNode;
-        long splitTime = treeEnd;
+        synchronized (latestBranch) {
+            final long splitTime = treeEnd;
 
-        assert (indexOfNode < latestBranch.size());
+            assert (indexOfNode < latestBranch.size());
 
-        /* Check if we need to add a new root node */
-        if (indexOfNode == 0) {
-            addNewRootNode();
-            return;
-        }
+            /* Check if we need to add a new root node */
+            if (indexOfNode == 0) {
+                addNewRootNode();
+                return;
+            }
 
-        /* Check if we can indeed add a child to the target parent */
-        if (latestBranch.get(indexOfNode - 1).getNbChildren() == config.getMaxChildren()) {
-            /* If not, add a branch starting one level higher instead */
-            addSiblingNode(indexOfNode - 1);
-            return;
-        }
+            /* Check if we can indeed add a child to the target parent */
+            if (latestBranch.get(indexOfNode - 1).getNbChildren() == config.getMaxChildren()) {
+                /* If not, add a branch starting one level higher instead */
+                addSiblingNode(indexOfNode - 1);
+                return;
+            }
 
-        /* Split off the new branch from the old one */
-        for (i = indexOfNode; i < latestBranch.size(); i++) {
-            latestBranch.get(i).closeThisNode(splitTime);
-            treeIO.writeNode(latestBranch.get(i));
+            /* Split off the new branch from the old one */
+            for (int i = indexOfNode; i < latestBranch.size(); i++) {
+                latestBranch.get(i).closeThisNode(splitTime);
+                treeIO.writeNode(latestBranch.get(i));
 
-            prevNode = latestBranch.get(i - 1);
-            newNode = initNewCoreNode(prevNode.getSequenceNumber(),
-                    splitTime + 1);
-            prevNode.linkNewChild(newNode);
+                CoreNode prevNode = latestBranch.get(i - 1);
+                CoreNode newNode = initNewCoreNode(prevNode.getSequenceNumber(),
+                        splitTime + 1);
+                prevNode.linkNewChild(newNode);
 
-            latestBranch.set(i, newNode);
+                latestBranch.set(i, newNode);
+            }
         }
-        return;
     }
 
     /**
@@ -426,18 +522,17 @@ class HistoryTree {
      * latestBranch
      */
     private void addNewRootNode() {
-        int i, depth;
-        CoreNode oldRootNode, newRootNode, newNode, prevNode;
-        long splitTime = this.treeEnd;
+        final long splitTime = this.treeEnd;
 
-        oldRootNode = latestBranch.get(0);
-        newRootNode = initNewCoreNode(-1, config.getTreeStart());
+        CoreNode oldRootNode = latestBranch.get(0);
+        CoreNode newRootNode = initNewCoreNode(-1, config.getTreeStart());
 
         /* Tell the old root node that it isn't root anymore */
         oldRootNode.setParentSequenceNumber(newRootNode.getSequenceNumber());
 
         /* Close off the whole current latestBranch */
-        for (i = 0; i < latestBranch.size(); i++) {
+
+        for (int i = 0; i < latestBranch.size(); i++) {
             latestBranch.get(i).closeThisNode(splitTime);
             treeIO.writeNode(latestBranch.get(i));
         }
@@ -446,12 +541,12 @@ class HistoryTree {
         newRootNode.linkNewChild(oldRootNode);
 
         /* Rebuild a new latestBranch */
-        depth = latestBranch.size();
-        latestBranch = new ArrayList<CoreNode>();
+        int depth = latestBranch.size();
+        latestBranch.clear();
         latestBranch.add(newRootNode);
-        for (i = 1; i < depth + 1; i++) {
-            prevNode = latestBranch.get(i - 1);
-            newNode = initNewCoreNode(prevNode.getParentSequenceNumber(),
+        for (int i = 1; i < depth + 1; i++) {
+            CoreNode prevNode = latestBranch.get(i - 1);
+            CoreNode newNode = initNewCoreNode(prevNode.getParentSequenceNumber(),
                     splitTime + 1);
             prevNode.linkNewChild(newNode);
             latestBranch.add(newNode);
@@ -468,7 +563,7 @@ class HistoryTree {
      * @return The newly created node
      */
     private CoreNode initNewCoreNode(int parentSeqNumber, long startTime) {
-        CoreNode newNode = new CoreNode(this, this.nodeCount, parentSeqNumber,
+        CoreNode newNode = new CoreNode(config, this.nodeCount, parentSeqNumber,
                 startTime);
         this.nodeCount++;
 
@@ -485,12 +580,14 @@ class HistoryTree {
      * branch.
      *
      * @param currentNode
+     *            The node on which the request is made
      * @param t
+     *            The timestamp to choose which child is the next one
      * @return The child node intersecting t
      * @throws ClosedChannelException
      *             If the file channel was closed while we were reading the tree
      */
-    HTNode selectNextChild(CoreNode currentNode, long t) throws ClosedChannelException {
+    public HTNode selectNextChild(CoreNode currentNode, long t) throws ClosedChannelException {
         assert (currentNode.getNbChildren() > 0);
         int potentialNextSeqNb = currentNode.getSequenceNumber();
 
@@ -501,6 +598,7 @@ class HistoryTree {
                 break;
             }
         }
+
         /*
          * Once we exit this loop, we should have found a children to follow. If
          * we didn't, there's a problem.
@@ -512,13 +610,18 @@ class HistoryTree {
          * through the whole latestBranch array if we know for sure the next
          * node has to be on disk
          */
-        if (currentNode.isDone()) {
-            return treeIO.readNodeFromDisk(potentialNextSeqNb);
+        if (currentNode.isOnDisk()) {
+            return treeIO.readNode(potentialNextSeqNb);
         }
-        return treeIO.readNode(potentialNextSeqNb);
+        return readNode(potentialNextSeqNb);
     }
 
-    long getFileSize() {
+    /**
+     * Get the current size of the history file.
+     *
+     * @return The history file size
+     */
+    public long getFileSize() {
         return config.getStateFile().length();
     }
 
@@ -526,10 +629,19 @@ class HistoryTree {
     // Test/debugging methods
     // ------------------------------------------------------------------------
 
-    /* Only used for debugging, shouldn't be externalized */
+    /**
+     * Debugging method to make sure all intervals contained in the given node
+     * have valid start and end times.
+     *
+     * @param zenode
+     *            The node to check
+     * @return True if everything is fine, false if there is at least one
+     *         invalid timestamp (end time < start time, time outside of the
+     *         range of the node, etc.)
+     */
     @SuppressWarnings("nls")
-    boolean checkNodeIntegrity(HTNode zenode) {
-
+    public boolean checkNodeIntegrity(HTNode zenode) {
+        /* Only used for debugging, shouldn't be externalized */
         HTNode otherNode;
         CoreNode node;
         StringBuffer buf = new StringBuffer();
@@ -556,7 +668,7 @@ class HistoryTree {
                             + otherNode.getSequenceNumber() + ")\n");
                     ret = false;
                 }
-                if (node.isDone()) {
+                if (node.isOnDisk()) {
                     otherNode = treeIO.readNode(node.getLatestChild());
                     if (node.getNodeEnd() != otherNode.getNodeEnd()) {
                         buf.append("End time of node (" + node.getNodeEnd()
@@ -597,7 +709,11 @@ class HistoryTree {
         return ret;
     }
 
-    void checkIntegrity() {
+    /**
+     * Check the integrity of all the nodes in the tree. Calls
+     * {@link #checkNodeIntegrity} for every node in the tree.
+     */
+    public void checkIntegrity() {
         try {
             for (int i = 0; i < nodeCount; i++) {
                 checkNodeIntegrity(treeIO.readNode(i));
@@ -621,41 +737,35 @@ class HistoryTree {
                 + latestBranch.get(latestBranch.size() - 1).getSequenceNumber();
     }
 
-    private int curDepth;
-
     /**
      * Start at currentNode and print the contents of all its children, in
      * pre-order. Give the root node in parameter to visit the whole tree, and
      * have a nice overview.
      */
+    /* Only used for debugging, shouldn't be externalized */
     @SuppressWarnings("nls")
     private void preOrderPrint(PrintWriter writer, boolean printIntervals,
-            CoreNode currentNode) {
-        /* Only used for debugging, shouldn't be externalized */
-        int i, j;
-        HTNode nextNode;
+            CoreNode currentNode, int curDepth) {
 
         writer.println(currentNode.toString());
         if (printIntervals) {
             currentNode.debugPrintIntervals(writer);
         }
-        curDepth++;
 
         try {
-            for (i = 0; i < currentNode.getNbChildren(); i++) {
-                nextNode = treeIO.readNode(currentNode.getChild(i));
+            for (int i = 0; i < currentNode.getNbChildren(); i++) {
+                HTNode nextNode = treeIO.readNode(currentNode.getChild(i));
                 assert (nextNode instanceof CoreNode); // TODO temporary
-                for (j = 0; j < curDepth - 1; j++) {
+                for (int j = 0; j < curDepth; j++) {
                     writer.print("  ");
                 }
                 writer.print("+-");
-                preOrderPrint(writer, printIntervals, (CoreNode) nextNode);
+                preOrderPrint(writer, printIntervals, (CoreNode) nextNode,
+                              curDepth + 1);
             }
         } catch (ClosedChannelException e) {
             e.printStackTrace();
         }
-        curDepth--;
-        return;
     }
 
     /**
@@ -664,17 +774,16 @@ class HistoryTree {
      * @param writer
      *            PrintWriter in which to write the output
      * @param printIntervals
-     *            Says if you want to output the full interval information
+     *            Flag to enable full output of the interval information
      */
-    void debugPrintFullTree(PrintWriter writer, boolean printIntervals) {
+    public void debugPrintFullTree(PrintWriter writer, boolean printIntervals) {
         /* Only used for debugging, shouldn't be externalized */
-        curDepth = 0;
-        this.preOrderPrint(writer, false, latestBranch.get(0));
+
+        this.preOrderPrint(writer, false, latestBranch.get(0), 0);
 
         if (printIntervals) {
             writer.println("\nDetails of intervals:"); //$NON-NLS-1$
-            curDepth = 0;
-            this.preOrderPrint(writer, true, latestBranch.get(0));
+            this.preOrderPrint(writer, true, latestBranch.get(0), 0);
         }
         writer.println('\n');
     }
This page took 0.033875 seconds and 5 git commands to generate.