@Override
  @SuppressWarnings("unchecked")
  public void visit(BranchNode branchNode, NodeStorageManager storage) {
    assert branchNode.mutable;

    Node<? extends Node<?>> bestChild = findWritableBestBranch(branchNode, item, storage);

    if (bestChild != null) {
      bestChild.accept(this, storage);
      return;
    }

    // Exceptional case - empty node. So do this last
    if (branchNode.getNumChildren() == 0) {
      // We are making a change so clone the node
      BranchNode updateBranch = storage.getWriteableBranch(branchNode.getRef());
      LeafNode newLeaf = storage.createLeafNode(updateBranch.getRef());
      updateBranch.add(new Branch((RefImpl) newLeaf.getRef()));
      newLeaf.accept(this, storage);
      return;
    }
    // FIXME: This happens if an enum splitter has been configured too small
    // we shouldn't need to pre-allocate the branches in that case
    throw new Error("InsertFailedException(item);");
  }
  private void logResults(
      NanoTimer timer, ArrayList<NextItem> results, NodeStorageManager storage) {

    float t = timer.getMillis();

    if (searchCount == 0) {
      searchStartTime = System.currentTimeMillis();
    }

    if (log.isDebugEnabled() && results.size() > 0) {
      LeafNode leaf = (LeafNode) results.get(0).getLeaf();
      ShowSplitsVisitor visitor = new ShowSplitsVisitor();
      leaf.accept(visitor, storage);
      log.debug("Split order of first item: " + visitor.toString());
    }

    // Log some info about the work done
    if (log.isInfoEnabled()) {
      log.info(
          "# results: "
              + results.size()
              + ", Nodes: "
              + getNodesExpanded()
              + ", Leaves: "
              + getLeafNodesExpanded()
              + ", Time (ms): "
              + timer.getMillis());
    }

    totalResults += results.size();
    searchTime += t;
    searchCount++;

    if (searchCount == 1000) {
      float avTime = searchTime / searchCount;
      float avElapsed = (float) (System.currentTimeMillis() - searchStartTime) / searchCount;
      float avResults = (float) (totalResults) / searchCount;
      log.info("====================== SEARCH STATS =============================");
      log.info(
          "Elapsed time per search: "
              + avElapsed
              + "ms (i.e. actual rate: "
              + 1000 / avElapsed
              + " searches per sec)");
      log.info(
          "Mean time doing search: "
              + avTime
              + "ms (i.e. potential rate: "
              + 1000 / avTime
              + " searches per sec)");
      log.info(
          "Mean results per search: "
              + avResults
              + " (=> SearchTime per result ="
              + avTime / avResults
              + "ms)");
      log.info("Non-search time (elapsed - search): " + (avElapsed - avTime) + "ms");
      searchTime = 0.0f;
      searchCount = 0;
      totalResults = 0;
    }
  }