/**
   * Constructs a new Database object.
   *
   * @param type is the type of the database: "mem", "file", "res"
   * @param path is the fiven path to the database files
   * @param name is the combination of type and canonical path
   * @param props property overrides placed on the connect URL
   * @exception HsqlException if the specified name and path combination is illegal or unavailable,
   *     or the database files the name and path resolves to are in use by another process
   */
  Database(String type, String path, String name, HsqlProperties props) throws HsqlException {

    urlProperties = props;

    setState(Database.DATABASE_SHUTDOWN);

    sName = name;
    sType = type;
    sPath = path;

    if (sType == DatabaseURL.S_RES) {
      filesInJar = true;
      filesReadOnly = true;
    }

    // does not need to be done more than once
    try {
      classLoader = getClass().getClassLoader();
    } catch (Exception e) {

      // strict security policy:  just use the system/boot loader
      classLoader = null;
    }

    // [email protected] - changed to file access api
    String fileaccess_class_name = (String) urlProperties.getProperty("fileaccess_class_name");

    if (fileaccess_class_name != null) {
      String storagekey = urlProperties.getProperty("storage_key");

      try {
        Class fileAccessClass = null;
        try {
          ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
          fileAccessClass = classLoader.loadClass(fileaccess_class_name);
        } catch (ClassNotFoundException e) {
          fileAccessClass = Class.forName(fileaccess_class_name);
        }
        Constructor constructor = fileAccessClass.getConstructor(new Class[] {Object.class});

        fileaccess = (FileAccess) constructor.newInstance(new Object[] {storagekey});
        isStoredFileAccess = true;
      } catch (Exception e) {
        throw Trace.error(Trace.INVALID_FILE_ACCESS_CLASS, new Object[] {e.toString()});
      }
    } else {
      fileaccess = FileUtil.getDefaultInstance();
    }

    shutdownOnNoConnection = urlProperties.getProperty("shutdown", "false").equals("true");
    logger = new Logger();
    compiledStatementManager = new CompiledStatementManager(this);
  }
  /** Closes the source file and deletes it if it is not read-only. */
  void purge() throws HsqlException {

    if (rFile == null) {
      return;
    }

    try {
      if (readOnly) {
        close();
      } else {
        rFile.close();

        rFile = null;

        FileUtil.delete(sName);
      }
    } catch (Exception e) {
      throw Trace.error(
          Trace.FILE_IO_ERROR, Trace.TextCache_purging_file_error, new Object[] {sName, e});
    }
  }
  /**
   * Writes newly created rows to disk. In the current implentation, such rows have already been
   * saved, so this method just removes a source file that has no rows.
   */
  void close() throws HsqlException {

    if (rFile == null) {
      return;
    }

    try {
      saveAll();

      boolean empty = (rFile.length() <= NL.length());

      rFile.close();

      rFile = null;

      if (empty && !readOnly) {
        FileUtil.delete(sName);
      }
    } catch (Exception e) {
      throw Trace.error(
          Trace.FILE_IO_ERROR, Trace.TextCache_closing_file_error, new Object[] {sName, e});
    }
  }
  /** Writes out all the rows to a new file without fragmentation. */
  public void defrag() throws HsqlException {

    if (cacheReadonly) {
      return;
    }

    if (fileFreePosition == INITIAL_FREE_POS) {
      return;
    }

    try {
      boolean wasNio = dataFile.wasNio();
      DataFileDefrag dfd = new DataFileDefrag(database, this, fileName);

      dfd.process();
      close(false);
      Trace.printSystemOut("closed old cache");

      // first attemp to delete
      fa.removeElement(fileName);

      if (wasNio) {
        System.gc();

        if (fa.isStreamElement(fileName)) {
          fa.removeElement(fileName);

          if (fa.isStreamElement(fileName)) {
            fa.renameElement(fileName, fileName + ".old");

            File oldfile = new File(fileName + ".old");

            FileUtil.deleteOnExit(oldfile);
          }
        }
      }

      // [email protected] - change to file access api
      fa.renameElement(fileName + ".new", fileName);
      backup();
      database
          .getProperties()
          .setProperty("hsqldb.cache_version", HsqlDatabaseProperties.VERSION_STRING_1_7_0);
      database.getProperties().save();
      initParams();

      cache = new Cache(this);

      open(cacheReadonly);
      dfd.updateTableIndexRoots();
      Trace.printSystemOut("opened cache");
    } catch (Exception e) {
      database.logger.appLog.logContext(e);

      throw new HsqlException(e, Trace.getMessage(Trace.GENERAL_IO_ERROR), Trace.GENERAL_IO_ERROR);
      /*
      Trace.error(Trace.FILE_IO_ERROR, Trace.DataFileCache_defrag, new Object[] {
          e, fileName
      });
      */
    }
  }