/** Preload all dup DBs to be converted. */
  private void preloadAllDatabases() {

    final ArrayList<DatabaseImpl> dbsToConvert = new ArrayList<DatabaseImpl>();
    try {
      for (DatabaseId dbId : dbTree.getDbNamesAndIds().keySet()) {
        final DatabaseImpl dbImpl = dbTree.getDb(dbId);
        boolean releaseDbImpl = true;
        try {
          if (!needsConversion(dbImpl)) {
            continue;
          }
          dbsToConvert.add(dbImpl);
          releaseDbImpl = false;
        } finally {
          if (releaseDbImpl) {
            dbTree.releaseDb(dbImpl);
          }
        }
      }

      if (dbsToConvert.size() == 0) {
        return;
      }

      final DatabaseImpl[] dbArray = new DatabaseImpl[dbsToConvert.size()];
      dbsToConvert.toArray(dbArray);

      envImpl.preload(dbArray, preloadConfig);
    } finally {
      for (DatabaseImpl dbImpl : dbsToConvert) {
        dbTree.releaseDb(dbImpl);
      }
    }
  }
  /** Set the mapping tree from the log. Called during recovery. */
  public void readMapTreeFromLog(long rootLsn) throws DatabaseException {

    dbMapTree = (DbTree) logManager.get(rootLsn);
    dbMapTree.setEnvironmentImpl(this);

    /* Set the map tree root */
    mapTreeRootLsn = rootLsn;
  }
  /** Converts all dup DBs that need conversion. */
  public void convertDatabases() {
    if (DEBUG) {
      System.out.println("DupConvert.convertDatabases");
    }
    if (preloadAll) {
      preloadAllDatabases();
    }
    for (DatabaseId dbId : dbTree.getDbNamesAndIds().keySet()) {
      final DatabaseImpl dbImpl = dbTree.getDb(dbId);
      try {
        if (!needsConversion(dbImpl)) {
          continue;
        }
        convertDatabase(dbImpl);
      } finally {
        dbTree.releaseDb(dbImpl);
      }
    }

    assert noDupNodesPresent();
  }
  /** Rename a database. */
  public void dbRename(Locker locker, String databaseName, String newName)
      throws DatabaseException {

    dbMapTree.dbRename(locker, databaseName, newName);
  }
  /** For debugging. */
  public void dumpMapTree() throws DatabaseException {

    dbMapTree.dump();
  }
  public List getDbNames() throws DatabaseException {

    return dbMapTree.getDbNames();
  }
  /**
   * Get a database object given a database name.
   *
   * @param databaseName target database.
   * @return null if database doesn't exist.
   */
  public DatabaseImpl getDb(Locker locker, String databaseName, Database databaseHandle)
      throws DatabaseException {

    return dbMapTree.getDb(locker, databaseName, databaseHandle);
  }
  /* DatabaseImpl access. */
  public DatabaseImpl createDb(
      Locker locker, String databaseName, DatabaseConfig dbConfig, Database databaseHandle)
      throws DatabaseException {

    return dbMapTree.createDb(locker, databaseName, dbConfig, databaseHandle);
  }
  /** Flush the target IN. */
  private void flushIN(
      IN target, LogManager logManager, Map dirtyMap, boolean logProvisionally, boolean allowDeltas)
      throws DatabaseException {

    DatabaseImpl db = target.getDatabase();
    Tree tree = db.getTree();
    boolean targetWasRoot = false;

    if (target.isDbRoot()) {
      /* We're trying to flush the root. */
      target.releaseLatch();
      RootFlusher flusher = new RootFlusher(db, logManager, target);
      tree.withRootLatched(flusher);
      boolean flushed = flusher.getFlushed();

      /*
       * We have to check if the root split between target.releaseLatch
       * and the execution of the root flusher. If it did split, this
       * target has to get handled like a regular node.
       */
      targetWasRoot = flusher.stillRoot();

      /*
       * Update the tree's owner, whether it's the env root or the
       * dbmapping tree.
       */
      if (flushed) {
        DbTree dbTree = db.getDbEnvironment().getDbMapTree();
        dbTree.modifyDbRoot(db);
        nFullINFlushThisRun++;
        nFullINFlush++;
      }
      if (!targetWasRoot) {
        /*
         * re-latch for another attempt, now that this is no longer
         * the root.
         */
        target.latch();
      }
    }

    if (!targetWasRoot) {
      SearchResult result = tree.getParentINForChildIN(target, true);

      /*
       * Found a parent, do the flush. If no parent found, the
       * compressor deleted this item before we got to processing it.
       */
      if (result.exactParentFound) {
        try {
          ChildReference entry = result.parent.getEntry(result.index);
          IN renewedTarget = (IN) entry.fetchTarget(db, result.parent);
          renewedTarget.latch();
          DbLsn newLsn = null;
          try {

            /* Still dirty? */
            if (renewedTarget.getDirty()) {
              if (allowDeltas) {
                newLsn = renewedTarget.logAllowDeltas(logManager, logProvisionally);
                if (newLsn == null) {
                  nDeltaINFlushThisRun++;
                  nDeltaINFlush++;
                }
              } else {
                newLsn = renewedTarget.log(logManager, logProvisionally);
              }
            }
          } finally {
            renewedTarget.releaseLatch();
          }

          /* Update parent if logging occurred */
          if (newLsn != null) {
            nFullINFlushThisRun++;
            nFullINFlush++;
            if (renewedTarget instanceof BIN) {
              nFullBINFlush++;
            }
            result.parent.updateEntry(result.index, newLsn);
            addToDirtyMap(dirtyMap, result.parent);
          }
        } finally {
          result.parent.releaseLatch();
        }
      }
    }
  }