text:
package com.orientechnologies.orient.console;

import com.orientechnologies.common.collection.OMultiValue;
import com.orientechnologies.common.console.TTYConsoleReader;
import com.orientechnologies.common.console.annotation.ConsoleCommand;
import com.orientechnologies.common.console.annotation.ConsoleParameter;
import com.orientechnologies.common.exception.OSystemException;
import com.orientechnologies.common.io.OIOException;
import com.orientechnologies.common.listener.OProgressListener;
import com.orientechnologies.common.log.OLogManager;
import com.orientechnologies.orient.client.remote.OEngineRemote;
import com.orientechnologies.orient.client.remote.OServerAdmin;
import com.orientechnologies.orient.client.remote.OStorageRemoteThread;
import com.orientechnologies.orient.core.OConstants;
import com.orientechnologies.orient.core.OSignalHandler;
import com.orientechnologies.orient.core.Orient;
import com.orientechnologies.orient.core.command.OCommandOutputListener;
import com.orientechnologies.orient.core.command.script.OCommandExecutorScript;
import com.orientechnologies.orient.core.command.script.OCommandScript;
import com.orientechnologies.orient.core.config.OGlobalConfiguration;
import com.orientechnologies.orient.core.config.OStorageConfiguration;
import com.orientechnologies.orient.core.config.OStorageEntryConfiguration;
import com.orientechnologies.orient.core.db.document.ODatabaseDocument;
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.db.record.OIdentifiable;
import com.orientechnologies.orient.core.db.record.ORecordLazyMultiValue;
import com.orientechnologies.orient.core.db.record.ridbag.ORidBag;
import com.orientechnologies.orient.core.db.tool.ODatabaseCompare;
import com.orientechnologies.orient.core.db.tool.ODatabaseExport;
import com.orientechnologies.orient.core.db.tool.ODatabaseExportException;
import com.orientechnologies.orient.core.db.tool.ODatabaseImport;
import com.orientechnologies.orient.core.db.tool.ODatabaseImportException;
import com.orientechnologies.orient.core.exception.OConfigurationException;
import com.orientechnologies.orient.core.exception.ODatabaseException;
import com.orientechnologies.orient.core.id.ORID;
import com.orientechnologies.orient.core.id.ORecordId;
import com.orientechnologies.orient.core.index.OIndex;
import com.orientechnologies.orient.core.index.OIndexDefinition;
import com.orientechnologies.orient.core.intent.OIntentMassiveInsert;
import com.orientechnologies.orient.core.intent.OIntentMassiveRead;
import com.orientechnologies.orient.core.iterator.OIdentifiableIterator;
import com.orientechnologies.orient.core.iterator.ORecordIteratorCluster;
import com.orientechnologies.orient.core.metadata.schema.OClass;
import com.orientechnologies.orient.core.metadata.schema.OProperty;
import com.orientechnologies.orient.core.metadata.security.OUser;
import com.orientechnologies.orient.core.record.ORecord;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.record.impl.ORecordBytes;
import com.orientechnologies.orient.core.security.OSecurityManager;
import com.orientechnologies.orient.core.serialization.OBase64Utils;
import com.orientechnologies.orient.core.serialization.serializer.OStringSerializerHelper;
import com.orientechnologies.orient.core.serialization.serializer.record.ORecordSerializer;
import com.orientechnologies.orient.core.serialization.serializer.record.ORecordSerializerFactory;
import com.orientechnologies.orient.core.serialization.serializer.record.binary.ORecordSerializationDebug;
import com.orientechnologies.orient.core.serialization.serializer.record.binary.ORecordSerializationDebugProperty;
import com.orientechnologies.orient.core.serialization.serializer.record.binary.ORecordSerializerBinaryDebug;
import com.orientechnologies.orient.core.serialization.serializer.record.string.ORecordSerializerStringAbstract;
import com.orientechnologies.orient.core.sql.OCommandSQL;
import com.orientechnologies.orient.core.sql.filter.OSQLPredicate;
import com.orientechnologies.orient.core.sql.query.OSQLSynchQuery;
import com.orientechnologies.orient.core.storage.OCluster;
import com.orientechnologies.orient.core.storage.ORawBuffer;
import com.orientechnologies.orient.core.storage.OStorage;
import com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage;
import com.orientechnologies.orient.core.storage.impl.local.paginated.OClusterPageDebug;
import com.orientechnologies.orient.core.storage.impl.local.paginated.OLocalPaginatedStorage;
import com.orientechnologies.orient.core.storage.impl.local.paginated.OPaginatedCluster;
import com.orientechnologies.orient.core.storage.impl.local.paginated.OPaginatedClusterDebug;
import com.orientechnologies.orient.server.config.OServerConfigurationManager;
import com.orientechnologies.orient.server.config.OServerUserConfiguration;
import sun.misc.Signal;
import sun.misc.SignalHandler;

import java.io.*;
import java.lang.reflect.Array;
import java.util.*;
import java.util.Map.Entry;

public class OConsoleDatabaseApp extends OrientConsole implements OCommandOutputListener, OProgressListener {
  protected static final int    DEFAULT_WIDTH        = 150;

  protected ODatabaseDocumentTx currentDatabase;
  protected String              currentDatabaseName;
  protected ORecord             currentRecord;
  protected int                 currentRecordIdx;
  protected List<OIdentifiable> currentResultSet;
  protected Object              currentResult;
  protected OServerAdmin        serverAdmin;
  private int                   windowSize           = DEFAULT_WIDTH;
  private int                   lastPercentStep;
  private String                currentDatabaseUserName;
  private String                currentDatabaseUserPassword;
  private int                   maxMultiValueEntries = 10;

  public OConsoleDatabaseApp(final String[] args) {
    super(args);
  }

  public static void main(final String[] args) {
    int result = 0;

    try {
      boolean tty = false;
      try {
        if (setTerminalToCBreak())
          tty = true;

        Runtime.getRuntime().addShutdownHook(new Thread() {
          @Override
          public void run() {
            restoreTerminal();
          }
        });

      } catch (Exception ignored) {
      }

      new OSignalHandler().installDefaultSignals(new SignalHandler() {
        public void handle(Signal signal) {
          restoreTerminal();
        }
      });

      final OConsoleDatabaseApp console = new OConsoleDatabaseApp(args);
      if (tty)
        console.setReader(new TTYConsoleReader());

      result = console.run();

    } finally {
      restoreTerminal();
    }

    Orient.instance().shutdown();
    System.exit(result);
  }

  protected static void restoreTerminal() {
    try {
      stty("echo");
    } catch (Exception ignored) {
    }
  }

  protected static boolean setTerminalToCBreak() throws IOException, InterruptedException {
    // set the console to be character-buffered instead of line-buffered
    int result = stty("-icanon min 1");
    if (result != 0) {
      return false;
    }

    // disable character echoing
    stty("-echo");
    return true;
  }

  /**
   * Execute the stty command with the specified arguments against the current active terminal.
   */
  protected static int stty(final String args) throws IOException, InterruptedException {
    String cmd = "stty " + args + " < /dev/tty";

    return exec(new String[] { "sh", "-c", cmd });
  }

  /**
   * Execute the specified command and return the output (both stdout and stderr).
   */
  protected static int exec(final String[] cmd) throws IOException, InterruptedException {
    ByteArrayOutputStream bout = new ByteArrayOutputStream();

    Process p = Runtime.getRuntime().exec(cmd);
    int c;
    InputStream in = p.getInputStream();

    while ((c = in.read()) != -1) {
      bout.write(c);
    }

    in = p.getErrorStream();

    while ((c = in.read()) != -1) {
      bout.write(c);
    }

    p.waitFor();

    return p.exitValue();
  }

  @ConsoleCommand(aliases = { "use database" }, description = "Connect to a database or a remote Server instance", onlineHelp = "Console-Command-Connect")
  public void connect(
      @ConsoleParameter(name = "url", description = "The url of the remote server or the database to connect to in the format '<mode>:<path>'") String iURL,
      @ConsoleParameter(name = "user", description = "User name") String iUserName,
      @ConsoleParameter(name = "password", description = "User password", optional = true) String iUserPassword) throws IOException {
    disconnect();

    if (iUserPassword == null) {
      message("Enter password: ");
      final BufferedReader br = new BufferedReader(new InputStreamReader(this.in));
      iUserPassword = br.readLine();
      message("\n");
    }

    currentDatabaseUserName = iUserName;
    currentDatabaseUserPassword = iUserPassword;

    if (iURL.contains("/")) {
      // OPEN DB
      message("\nConnecting to database [" + iURL + "] with user '" + iUserName + "'...");

      currentDatabase = new ODatabaseDocumentTx(iURL);

      currentDatabase.registerListener(new OConsoleDatabaseListener(this));
      currentDatabase.open(iUserName, iUserPassword);
      currentDatabaseName = currentDatabase.getName();
    } else {
      // CONNECT TO REMOTE SERVER
      message("\nConnecting to remote Server instance [" + iURL + "] with user '" + iUserName + "'...");

      serverAdmin = new OServerAdmin(iURL).connect(iUserName, iUserPassword);
      currentDatabase = null;
      currentDatabaseName = null;
    }

    message("OK");

    dumpDistributedConfiguration(false);
  }

  @ConsoleCommand(aliases = { "close database" }, description = "Disconnect from the current database", onlineHelp = "Console-Command-Disconnect")
  public void disconnect() {
    if (serverAdmin != null) {
      message("\nDisconnecting from remote server [" + serverAdmin.getURL() + "]...");
      serverAdmin.close(true);
      serverAdmin = null;
      message("\nOK");
    }

    if (currentDatabase != null) {
      message("\nDisconnecting from the database [" + currentDatabaseName + "]...");

      final OStorage stg = Orient.instance().getStorage(currentDatabase.getURL());

      currentDatabase.activateOnCurrentThread();
      if (!currentDatabase.isClosed())
        currentDatabase.close();

      // FORCE CLOSING OF STORAGE: THIS CLEAN UP REMOTE CONNECTIONS
      if (stg != null)
        stg.close(true, false);

      currentDatabase = null;
      currentDatabaseName = null;
      currentRecord = null;

      message("OK");
    }
  }

  @ConsoleCommand(description = "Create a new database. For encrypted database or portion of database, set the variable 'storage.encryptionKey' with the key to use", onlineHelp = "Console-Command-Create-Database")
  public void createDatabase(
      @ConsoleParameter(name = "database-url", description = "The url of the database to create in the format '<mode>:<path>'") String iDatabaseURL,
      @ConsoleParameter(name = "user", optional = true, description = "Server administrator name") String iUserName,
      @ConsoleParameter(name = "password", optional = true, description = "Server administrator password") String iUserPassword,
      @ConsoleParameter(name = "storage-type", optional = true, description = "The type of the storage: 'plocal' for disk-based databases and 'memory' for in-memory database") String iStorageType,
      @ConsoleParameter(name = "db-type", optional = true, description = "The type of the database used between 'document' and 'graph'. By default is graph.") String iDatabaseType,
      @ConsoleParameter(name = "[options]", optional = true, description = "Additional options, example: -encryption=aes -compression=snappy") final String iOptions)
      throws IOException {

    if (iUserName == null)
      iUserName = OUser.ADMIN;
    if (iUserPassword == null)
      iUserPassword = OUser.ADMIN;
    if (iStorageType == null) {
      if (iDatabaseURL.startsWith(OEngineRemote.NAME + ":"))
        throw new IllegalArgumentException("Missing storage type for remote database");

      int pos = iDatabaseURL.indexOf(":");
      if (pos == -1)
        throw new IllegalArgumentException("Invalid URL");
      iStorageType = iDatabaseURL.substring(0, pos);
    }
    if (iDatabaseType == null)
      iDatabaseType = "graph";

    message("\nCreating database [" + iDatabaseURL + "] using the storage type [" + iStorageType + "]...");

    currentDatabaseUserName = iUserName;
    currentDatabaseUserPassword = iUserPassword;

    if (iDatabaseURL.startsWith(OEngineRemote.NAME)) {
      // REMOTE CONNECTION
      final String dbURL = iDatabaseURL.substring(OEngineRemote.NAME.length() + 1);
      new OServerAdmin(dbURL).connect(iUserName, iUserPassword).createDatabase(iDatabaseType, iStorageType).close();
      connect(iDatabaseURL, OUser.ADMIN, OUser.ADMIN);

    } else {
      // LOCAL CONNECTION
      if (iStorageType != null) {
        // CHECK STORAGE TYPE
        if (!iDatabaseURL.toLowerCase().startsWith(iStorageType.toLowerCase()))
          throw new IllegalArgumentException("Storage type '" + iStorageType + "' is different by storage type in URL");
      }

      currentDatabase = new ODatabaseDocumentTx(iDatabaseURL);

      if (iOptions != null) {
        final List<String> options = OStringSerializerHelper.smartSplit(iOptions, ',', false);
        for (String option : options) {
          final String[] values = option.split("=");
          if (values.length != 2)
            throw new IllegalArgumentException("Options must have in th format -<option>=<value>[,-<option>=<value>]*");

          if ("-encryption".equalsIgnoreCase(values[0]))
            currentDatabase.setProperty(OGlobalConfiguration.STORAGE_ENCRYPTION_METHOD.getKey(), values[1]);
          else if ("-compression".equalsIgnoreCase(values[0]))
            currentDatabase.setProperty(OGlobalConfiguration.STORAGE_COMPRESSION_METHOD.getKey(), values[1]);
          else
            currentDatabase.setProperty(values[0], values[1]);
        }
      }

      currentDatabase.create();
      currentDatabaseName = currentDatabase.getName();
    }

    message("\nDatabase created successfully.");
    message("\n\nCurrent database is: " + iDatabaseURL);
  }

  @ConsoleCommand(description = "List all the databases available on the connected server", onlineHelp = "Console-Command-List-Databases")
  public void listDatabases() throws IOException {
    if (serverAdmin != null) {
      final Map<String, String> databases = serverAdmin.listDatabases();
      message("\nFound %d databases:\n", databases.size());
      for (Entry<String, String> database : databases.entrySet()) {
        message("\n* %s (%s)", database.getKey(), database.getValue().substring(0, database.getValue().indexOf(":")));
      }
    } else {
      message("\nNot connected to the Server instance. You've to connect to the Server using server's credentials (look at orientdb-*server-config.xml file)");
    }
    out.println();
  }

  @ConsoleCommand(description = "List all the active connections to the server", onlineHelp = "Console-Command-List-Connections")
  public void listConnections() throws IOException {
    if (serverAdmin != null) {
      final ODocument serverInfo = serverAdmin.getServerInfo();

      final List<OIdentifiable> resultSet = new ArrayList<OIdentifiable>();

      final List<Map<String, Object>> connections = serverInfo.field("connections");
      for (Map<String, Object> conn : connections) {
        final ODocument row = new ODocument();

        String commandDetail = (String) conn.get("commandInfo");

        if (commandDetail != null && ((String) conn.get("commandDetail")).length() > 1)
          commandDetail += " (" + conn.get("commandDetail") + ")";

        row.fields("ID", conn.get("connectionId"), "REMOTE_ADDRESS", conn.get("remoteAddress"), "PROTOC", conn.get("protocol"),
            "LAST_OPERATION_ON", conn.get("lastCommandOn"), "DATABASE", conn.get("db"), "USER", conn.get("user"), "COMMAND",
            commandDetail, "TOT_REQS", conn.get("totalRequests"));
        resultSet.add(row);
      }

      Collections.sort(resultSet, new Comparator<OIdentifiable>() {
        @Override
        public int compare(final OIdentifiable o1, final OIdentifiable o2) {
          final String o1s = ((ODocument) o1).field("LAST_OPERATION_ON");
          final String o2s = ((ODocument) o2).field("LAST_OPERATION_ON");
          return o2s.compareTo(o1s);
        }
      });

      final OTableFormatter formatter = new OTableFormatter(this);
      formatter.writeRecords(resultSet, -1);

    } else {
      message("\nNot connected to the Server instance. You've to connect to the Server using server's credentials (look at orientdb-*server-config.xml file)");
    }
    out.println();
  }

  @ConsoleCommand(description = "Reload the database schema")
  public void reloadSchema() throws IOException {
    message("\nreloading database schema...");
    updateDatabaseInfo();
    message("\n\nDone.");
  }

  @ConsoleCommand(splitInWords = false, description = "Create a new cluster in the current database. The cluster can be physical or memory")
  public void createCluster(
      @ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    sqlCommand("create", iCommandText, "\nCluster created correctly with id #%d\n", true);
    updateDatabaseInfo();
  }

  @ConsoleCommand(description = "Remove a cluster in the current database. The cluster can be physical or memory")
  public void dropCluster(
      @ConsoleParameter(name = "cluster-name", description = "The name or the id of the cluster to remove") String iClusterName) {
    checkForDatabase();

    message("\nDropping cluster [" + iClusterName + "] in database " + currentDatabaseName + "...");

    boolean result = currentDatabase.dropCluster(iClusterName, true);

    if (!result) {
      // TRY TO GET AS CLUSTER ID
      try {
        int clusterId = Integer.parseInt(iClusterName);
        if (clusterId > -1) {
          result = currentDatabase.dropCluster(clusterId, true);
        }
      } catch (Exception ignored) {
      }
    }

    if (result)
      message("\nCluster correctly removed");
    else
      message("\nCannot find the cluster to remove");
    updateDatabaseInfo();
  }

  @ConsoleCommand(splitInWords = false, description = "Alters a cluster in the current database. The cluster can be physical or memory")
  public void alterCluster(@ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    sqlCommand("alter", iCommandText, "\nCluster updated successfully.\n", false);
    updateDatabaseInfo();
  }

  @ConsoleCommand(description = "Begins a transaction. All the changes will remain local")
  public void begin() throws IOException {
    checkForDatabase();

    if (currentDatabase.getTransaction().isActive()) {
      message("\nError: an active transaction is currently open (id=" + currentDatabase.getTransaction().getId()
          + "). Commit or rollback before starting a new one.");
      return;
    }

    currentDatabase.begin();
    message("\nTransaction " + currentDatabase.getTransaction().getId() + " is running");
  }

  @ConsoleCommand(description = "Commits transaction changes to the database")
  public void commit() throws IOException {
    checkForDatabase();

    if (!currentDatabase.getTransaction().isActive()) {
      message("\nError: no active transaction is currently open.");
      return;
    }

    final long begin = System.currentTimeMillis();

    final int txId = currentDatabase.getTransaction().getId();
    currentDatabase.commit();

    message("\nTransaction " + txId + " has been committed in " + (System.currentTimeMillis() - begin) + "ms");
  }

  @ConsoleCommand(description = "Rolls back transaction changes to the previous state")
  public void rollback() throws IOException {
    checkForDatabase();

    if (!currentDatabase.getTransaction().isActive()) {
      message("\nError: no active transaction is running right now.");
      return;
    }

    final long begin = System.currentTimeMillis();

    final int txId = currentDatabase.getTransaction().getId();
    currentDatabase.rollback();
    message("\nTransaction " + txId + " has been rollbacked in " + (System.currentTimeMillis() - begin) + "ms");
  }

  @ConsoleCommand(splitInWords = false, description = "Truncate the class content in the current database")
  public void truncateClass(@ConsoleParameter(name = "text", description = "The name of the class to truncate") String iCommandText) {
    sqlCommand("truncate", iCommandText, "\nTruncated %d record(s) in %f sec(s).\n", true);
  }

  @ConsoleCommand(splitInWords = false, description = "Truncate the cluster content in the current database")
  public void truncateCluster(
      @ConsoleParameter(name = "text", description = "The name of the class to truncate") String iCommandText) {
    sqlCommand("truncate", iCommandText, "\nTruncated %d record(s) in %f sec(s).\n", true);
  }

  @ConsoleCommand(splitInWords = false, description = "Truncate a record deleting it at low level")
  public void truncateRecord(@ConsoleParameter(name = "text", description = "The record(s) to truncate") String iCommandText) {
    sqlCommand("truncate", iCommandText, "\nTruncated %d record(s) in %f sec(s).\n", true);
  }

  @ConsoleCommand(description = "Load a record in memory using passed fetch plan")
  public void loadRecord(
      @ConsoleParameter(name = "record-id", description = "The unique Record Id of the record to load. If you do not have the Record Id, execute a query first") String iRecordId,
      @ConsoleParameter(name = "fetch-plan", description = "The fetch plan to load the record with") String iFetchPlan) {
    loadRecordInternal(iRecordId, iFetchPlan);
  }

  @ConsoleCommand(description = "Load a record in memory and set it as the current")
  public void loadRecord(
      @ConsoleParameter(name = "record-id", description = "The unique Record Id of the record to load. If you do not have the Record Id, execute a query first") String iRecordId) {
    loadRecordInternal(iRecordId, null);
  }

  @ConsoleCommand(description = "Reloads a record using passed fetch plan")
  public void reloadRecord(
      @ConsoleParameter(name = "record-id", description = "The unique Record Id of the record to load. If you do not have the Record Id, execute a query first") String iRecordId,
      @ConsoleParameter(name = "fetch-plan", description = "The fetch plan to load the record with") String iFetchPlan) {
    reloadRecordInternal(iRecordId, iFetchPlan);
  }

  @ConsoleCommand(description = "Reload a record and set it as the current one", onlineHelp = "Console-Command-Reload-Record")
  public void reloadRecord(
      @ConsoleParameter(name = "record-id", description = "The unique Record Id of the record to load. If you do not have the Record Id, execute a query first") String iRecordId) {
    reloadRecordInternal(iRecordId, null);
  }

  @ConsoleCommand(splitInWords = false, description = "Explain how a command is executed profiling it", onlineHelp = "SQL-Explain")
  public void explain(@ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    Object result = sqlCommand("explain", iCommandText, "\nProfiled command '%s' in %f sec(s):\n", true);
    if (result != null && result instanceof ODocument) {
      message(((ODocument) result).toJSON());
    }
  }

  @ConsoleCommand(splitInWords = false, description = "Executes a command inside a transaction")
  public void transactional(@ConsoleParameter(name = "command-text", description = "The command to execute") String iCommandText) {
    sqlCommand("transactional", iCommandText, "\nResult: '%s'. Executed in %f sec(s).\n", true);
  }

  @ConsoleCommand(splitInWords = false, description = "Insert a new record into the database", onlineHelp = "SQL-Insert")
  public void insert(@ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    sqlCommand("insert", iCommandText, "\nInserted record '%s' in %f sec(s).\n", true);
  }

  @ConsoleCommand(splitInWords = false, description = "Create a new vertex into the database", onlineHelp = "SQL-Create-Vertex")
  public void createVertex(@ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    sqlCommand("create", iCommandText, "\nCreated vertex '%s' in %f sec(s).\n", true);
  }

  @ConsoleCommand(splitInWords = false, description = "Create a new edge into the database", onlineHelp = "SQL-Create-Edge")
  public void createEdge(@ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    sqlCommand("create", iCommandText, "\nCreated edge '%s' in %f sec(s).\n", true);
  }

  @ConsoleCommand(splitInWords = false, description = "Update records in the database", onlineHelp = "SQL-Update")
  public void update(@ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    sqlCommand("update", iCommandText, "\nUpdated record(s) '%s' in %f sec(s).\n", true);
    updateDatabaseInfo();
    currentDatabase.getLocalCache().invalidate();
  }

  @ConsoleCommand(splitInWords = false, description = "Move vertices to another position (class/cluster)", priority = 8, onlineHelp = "SQL-Move-Vertex")
  // EVALUATE THIS BEFORE 'MOVE'
  public void moveVertex(@ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    sqlCommand("move", iCommandText, "\nMove vertex command executed with result '%s' in %f sec(s).\n", true);
  }

  @ConsoleCommand(splitInWords = false, description = "Optimizes the current database", onlineHelp = "SQL-Optimize-Database")
  public void optimizeDatabase(
      @ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    sqlCommand("optimize", iCommandText, "\nDatabase optimized in %f sec(s).\n", true);
  }

  @ConsoleCommand(description = "Force calling of JVM Garbage Collection")
  public void gc() {
    System.gc();
  }

  @ConsoleCommand(splitInWords = false, description = "Delete records from the database", onlineHelp = "SQL-Delete")
  public void delete(@ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    sqlCommand("delete", iCommandText, "\nDelete record(s) '%s' in %f sec(s).\n", true);
    updateDatabaseInfo();
    currentDatabase.getLocalCache().invalidate();
  }

  @ConsoleCommand(splitInWords = false, description = "Grant privileges to a role", onlineHelp = "SQL-Grant")
  public void grant(@ConsoleParameter(name = "text", description = "Grant command") String iCommandText) {
    sqlCommand("grant", iCommandText, "\nPrivilege granted to the role: %s.\n", true);
  }

  @ConsoleCommand(splitInWords = false, description = "Revoke privileges to a role", onlineHelp = "SQL-Revoke")
  public void revoke(@ConsoleParameter(name = "text", description = "Revoke command") String iCommandText) {
    sqlCommand("revoke", iCommandText, "\nPrivilege revoked to the role: %s.\n", true);
  }

  @ConsoleCommand(splitInWords = false, description = "Create a link from a JOIN", onlineHelp = "SQL-Create-Link")
  public void createLink(@ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    sqlCommand("create", iCommandText, "\nCreated %d link(s) in %f sec(s).\n", true);
  }

  @ConsoleCommand(splitInWords = false, description = "Find all references the target record id @rid", onlineHelp = "SQL-Find-References")
  public void findReferences(
      @ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    sqlCommand("find", iCommandText, "\nFound %s in %f sec(s).\n", true);
  }

  @ConsoleCommand(splitInWords = false, description = "Alter a database property", onlineHelp = "SQL-Alter-Database")
  public void alterDatabase(
      @ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    sqlCommand("alter", iCommandText, "\nDatabase updated successfully.\n", false);
    updateDatabaseInfo();
  }

  @ConsoleCommand(description = "Freeze database and flush on the disk", onlineHelp = "Console-Command-Freeze-Database")
  public void freezeDatabase(
      @ConsoleParameter(name = "storage-type", description = "Storage type of server database", optional = true) String storageType)
      throws IOException {
    checkForDatabase();

    final String dbName = currentDatabase.getName();

    if (currentDatabase.getURL().startsWith(OEngineRemote.NAME)) {
      if (storageType == null)
        storageType = "plocal";

      new OServerAdmin(currentDatabase.getURL()).connect(currentDatabaseUserName, currentDatabaseUserPassword).freezeDatabase(
          storageType);
    } else {
      // LOCAL CONNECTION
      currentDatabase.freeze();
    }

    message("\n\nDatabase '" + dbName + "' was frozen successfully");
  }

  @ConsoleCommand(description = "Release database after freeze", onlineHelp = "Console-Command-Release-Db")
  public void releaseDatabase(
      @ConsoleParameter(name = "storage-type", description = "Storage type of server database", optional = true) String storageType)
      throws IOException {
    checkForDatabase();

    final String dbName = currentDatabase.getName();

    if (currentDatabase.getURL().startsWith(OEngineRemote.NAME)) {
      if (storageType == null)
        storageType = "plocal";

      new OServerAdmin(currentDatabase.getURL()).connect(currentDatabaseUserName, currentDatabaseUserPassword).releaseDatabase(
          storageType);
    } else {
      // LOCAL CONNECTION
      currentDatabase.release();
    }

    message("\n\nDatabase '" + dbName + "' was released successfully");
  }

  @ConsoleCommand(description = "Flushes all database content to the disk")
  public void flushDatabase(
      @ConsoleParameter(name = "storage-type", description = "Storage type of server database", optional = true) String storageType)
      throws IOException {
    freezeDatabase(storageType);
    releaseDatabase(storageType);
  }

  @ConsoleCommand(description = "Display current record")
  public void current() {
    dumpRecordDetails();
  }

  @ConsoleCommand(description = "Move the current record cursor to the next one in result set")
  public void next() {
    setCurrentRecord(currentRecordIdx + 1);
    dumpRecordDetails();
  }

  @ConsoleCommand(description = "Move the current record cursor to the previous one in result set")
  public void prev() {
    setCurrentRecord(currentRecordIdx - 1);
    dumpRecordDetails();
  }

  @ConsoleCommand(splitInWords = false, description = "Alter a class in the database schema")
  public void alterClass(@ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    sqlCommand("alter", iCommandText, "\nClass updated successfully.\n", false);
    updateDatabaseInfo();
  }

  @ConsoleCommand(splitInWords = false, description = "Create a class", onlineHelp = "SQL-Create-Class")
  public void createClass(@ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    sqlCommand("create", iCommandText, "\nClass created successfully. Total classes in database now: %d.\n", true);
    updateDatabaseInfo();
  }

  @ConsoleCommand(splitInWords = false, description = "Create a sequence in the database")
  public void createSequence(
      @ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    sqlCommand("create", iCommandText, "\nSequence created successfully. Total sequences in database now: %d.\n", true);
    updateDatabaseInfo();
  }

  @ConsoleCommand(splitInWords = false, description = "Alter an existent sequence in the database")
  public void alterSequence(
      @ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    sqlCommand("alter", iCommandText, "\nSequence altered successfully.\n", true);
    updateDatabaseInfo();
  }

  @ConsoleCommand(splitInWords = false, description = "Remove a sequence from the database")
  public void dropSequence(@ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    sqlCommand("drop", iCommandText, "Sequence removed successfully.\n", false);
    updateDatabaseInfo();
  }

  @ConsoleCommand(splitInWords = false, description = "Create a user", onlineHelp = "SQL-Create-User")
  public void createUser(@ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    sqlCommand("create", iCommandText, "\nUser created successfully.\n", false);
    updateDatabaseInfo();
  }

  @ConsoleCommand(splitInWords = false, description = "Drop a user", onlineHelp = "SQL-Drop-User")
  public void dropUser(@ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    sqlCommand("drop", iCommandText, "\nUser dropped successfully.\n", false);
    updateDatabaseInfo();
  }

  @ConsoleCommand(splitInWords = false, description = "Alter a class property in the database schema", onlineHelp = "SQL-Alter-Property")
  public void alterProperty(
      @ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    sqlCommand("alter", iCommandText, "\nProperty updated successfully.\n", false);
    updateDatabaseInfo();
  }

  @ConsoleCommand(splitInWords = false, description = "Create a property", onlineHelp = "SQL-Create-Property")
  public void createProperty(
      @ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    sqlCommand("create", iCommandText, "\nProperty created successfully with id=%d.\n", true);
    updateDatabaseInfo();
  }

  /***
   * Creates a function.
   *
   * @author Claudio Tesoriero
   * @param iCommandText
   *          the command text to execute
   */
  @ConsoleCommand(splitInWords = false, description = "Create a stored function", onlineHelp = "SQL-Create-Function")
  public void createFunction(
      @ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText) {
    sqlCommand("create", iCommandText, "\nFunction created successfully with id=%s.\n", true);
    updateDatabaseInfo();
  }

  @ConsoleCommand(splitInWords = false, description = "Traverse records and display the results", onlineHelp = "SQL-Traverse")
  public void traverse(@ConsoleParameter(name = "query-text", description = "The traverse to execute") String iQueryText) {
    final int limit;
    if (iQueryText.contains("limit")) {
      // RESET CONSOLE FLAG
      limit = -1;
    } else {
      limit = Integer.parseInt(properties.get("limit"));
    }

    long start = System.currentTimeMillis();
    setResultset((List<OIdentifiable>) currentDatabase.command(new OCommandSQL("traverse " + iQueryText)).execute());

    float elapsedSeconds = getElapsedSecs(start);

    dumpResultSet(limit);

    message("\n\n" + currentResultSet.size() + " item(s) found. Traverse executed in " + elapsedSeconds + " sec(s).");
  }

  @ConsoleCommand(splitInWords = false, description = "Execute a query against the database and display the results", onlineHelp = "SQL-Query")
  public void select(@ConsoleParameter(name = "query-text", description = "The query to execute") String iQueryText) {
    checkForDatabase();

    if (iQueryText == null)
      return;

    iQueryText = iQueryText.trim();

    if (iQueryText.length() == 0 || iQueryText.equalsIgnoreCase("select"))
      return;

    iQueryText = "select " + iQueryText;

    final int limit;
    if (iQueryText.contains("limit")) {
      limit = -1;
    } else {
      limit = Integer.parseInt(properties.get("limit"));
    }

    final long start = System.currentTimeMillis();
    setResultset((List<OIdentifiable>) currentDatabase.query(new OSQLSynchQuery<ODocument>(iQueryText, limit).setFetchPlan("*:0")));

    float elapsedSeconds = getElapsedSecs(start);

    dumpResultSet(limit);

    message("\n\n" + currentResultSet.size() + " item(s) found. Query executed in " + elapsedSeconds + " sec(s).");
  }

  @ConsoleCommand(splitInWords = false, description = "Move from current record by evaluating a predicate against current record")
  public void move(@ConsoleParameter(name = "text", description = "The sql predicate to evaluate") final String iText) {
    if (iText == null)
      return;

    if (currentRecord == null)
      return;

    final Object result = new OSQLPredicate(iText).evaluate(currentRecord, null, null);

    if (result != null) {
      if (result instanceof OIdentifiable) {
        setResultset(new ArrayList<OIdentifiable>());
        currentRecord = ((OIdentifiable) result).getRecord();
        dumpRecordDetails();
      } else if (result instanceof List<?>) {
        setResultset((List<OIdentifiable>) result);
        dumpResultSet(-1);
      } else if (result instanceof Iterator<?>) {
        final List<OIdentifiable> list = new ArrayList<OIdentifiable>();
        while (((Iterator) result).hasNext())
          list.add(((Iterator<OIdentifiable>) result).next());
        setResultset(list);
        dumpResultSet(-1);
      } else
        setResultset(new ArrayList<OIdentifiable>());
    }
  }

  @ConsoleCommand(splitInWords = false, description = "Evaluate a predicate against current record")
  public void eval(@ConsoleParameter(name = "text", description = "The sql predicate to evaluate") final String iText) {
    if (iText == null)
      return;

    if (currentRecord == null)
      return;

    final Object result = new OSQLPredicate(iText).evaluate(currentRecord, null, null);
    if (result != null)
      out.println("\n" + result);
  }

  @SuppressWarnings("unchecked")
  @ConsoleCommand(splitInWords = false, description = "Execute a script containing multiple commands separated by ; or new line")
  public void script(@ConsoleParameter(name = "text", description = "Commands to execute, one per line") String iText) {
    final String language;
    final int languageEndPos = iText.indexOf(";");
    if (languageEndPos > -1) {
      // EXTRACT THE SCRIPT LANGUAGE
      language = iText.substring(0, languageEndPos);
      iText = iText.substring(languageEndPos + 1);
    } else
      throw new IllegalArgumentException("Missing language in script (sql, js, gremlin, etc.) as first argument");

    executeServerSideScript(language, iText);
  }

  @SuppressWarnings("unchecked")
  @ConsoleCommand(splitInWords = false, description = "Execute javascript commands in the console")
  public void js(
      @ConsoleParameter(name = "text", description = "The javascript to execute. Use 'db' to reference to a document database, 'gdb' for a graph database") final String iText) {
    if (iText == null)
      return;

    resetResultSet();

    final OCommandExecutorScript cmd = new OCommandExecutorScript();
    cmd.parse(new OCommandScript("Javascript", iText));

    long start = System.currentTimeMillis();
    currentResult = cmd.execute(null);
    float elapsedSeconds = getElapsedSecs(start);

    parseResult();

    if (currentResultSet != null) {
      dumpResultSet(-1);
      message("\nClient side script executed in %f sec(s). Returned %d records", elapsedSeconds, currentResultSet.size());
    } else
      message("\nClient side script executed in %f sec(s). Value returned is: %s", elapsedSeconds, currentResult);
  }

  @SuppressWarnings("unchecked")
  @ConsoleCommand(splitInWords = false, description = "Execute javascript commands against a remote server")
  public void jss(
      @ConsoleParameter(name = "text", description = "The javascript to execute. Use 'db' to reference to a document database, 'gdb' for a graph database") final String iText) {
    checkForRemoteServer();

    executeServerSideScript("javascript", iText);

  }

  @SuppressWarnings("unchecked")
  @ConsoleCommand(description = "Set a server user. If the user already exists, the password and permissions are updated. For more information look at http://orientdb.com/docs/last/Security.html#orientdb-server-security", onlineHelp = "Console-Command-Set-Server-User")
  public void setServerUser(
      @ConsoleParameter(name = "user-name", description = "User name") String iServerUserName,
      @ConsoleParameter(name = "user-password", description = "User password") String iServerUserPasswd,
      @ConsoleParameter(name = "user-permissions", description = "Permissions, look at http://orientdb.com/docs/last/Security.html#servers-resources") String iPermissions) {

    if (iServerUserName == null || iServerUserName.length() == 0)
      throw new IllegalArgumentException("User name null or empty");

    if (iPermissions == null || iPermissions.length() == 0)
      throw new IllegalArgumentException("User permissions null or empty");

    final File serverCfgFile = new File("../config/orientdb-server-config.xml");
    if (!serverCfgFile.exists())
      throw new OConfigurationException("Cannot access to file " + serverCfgFile);

    try {
      final OServerConfigurationManager serverCfg = new OServerConfigurationManager(serverCfgFile);

      // AUTO GENERATE PASSWORD
      final String hashedPassword = OSecurityManager.instance().createHash(iServerUserPasswd,
          OSecurityManager.PBKDF2_ALGORITHM_PREFIX, true);

      serverCfg.setUser(iServerUserName, hashedPassword, iPermissions);
      serverCfg.saveConfiguration();

      message("\nServer user '%s' set correctly", iServerUserName);

    } catch (Exception e) {
      error("\nError on loading %s file: %s", serverCfgFile, e.toString());
    }
  }

  @SuppressWarnings("unchecked")
  @ConsoleCommand(description = "Drop a server user. For more information look at http://orientdb.com/docs/last/Security.html#orientdb-server-security", onlineHelp = "Console-Command-Drop-Server-User")
  public void dropServerUser(@ConsoleParameter(name = "user-name", description = "User name") String iServerUserName) {

    if (iServerUserName == null || iServerUserName.length() == 0)
      throw new IllegalArgumentException("User name null or empty");

    final File serverCfgFile = new File("../config/orientdb-server-config.xml");
    if (!serverCfgFile.exists())
      throw new OConfigurationException("Cannot access to file " + serverCfgFile);

    try {
      final OServerConfigurationManager serverCfg = new OServerConfigurationManager(serverCfgFile);

      if (!serverCfg.existsUser(iServerUserName)) {
        error("\nServer user '%s' not found in configuration", iServerUserName);
        return;
      }

      serverCfg.dropUser(iServerUserName);
      serverCfg.saveConfiguration();

      message("\nServer user '%s' dropped correctly", iServerUserName);

    } catch (Exception e) {
      error("\nError on loading %s file: %s", serverCfgFile, e.toString());
    }
  }

  @SuppressWarnings("unchecked")
  @ConsoleCommand(description = "Display all the server user names. For more information look at http://orientdb.com/docs/last/Security.html#orientdb-server-security", onlineHelp = "Console-Command-List-Server-User")
  public void listServerUsers() {

    final File serverCfgFile = new File("../config/orientdb-server-config.xml");
    if (!serverCfgFile.exists())
      throw new OConfigurationException("Cannot access to file " + serverCfgFile);

    try {
      final OServerConfigurationManager serverCfg = new OServerConfigurationManager(serverCfgFile);

      message("\nSERVER USERS\n");
      final Set<OServerUserConfiguration> users = serverCfg.getUsers();
      if (users.isEmpty())
        message("\nNo users found");
      else
        for (OServerUserConfiguration u : users) {
          message("\n- '%s', permissions: %s", u.name, u.resources);
        }

    } catch (Exception e) {
      error("\nError on loading %s file: %s", serverCfgFile, e.toString());
    }
  }

  @ConsoleCommand(splitInWords = false, description = "Create an index against a property", onlineHelp = "SQL-Create-Index")
  public void createIndex(@ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText)
      throws IOException {
    message("\n\nCreating index...");

    sqlCommand("create", iCommandText, "\nCreated index successfully with %d entries in %f sec(s).\n", true);
    updateDatabaseInfo();
    message("\n\nIndex created successfully");
  }

  @ConsoleCommand(description = "Delete the current database", onlineHelp = "Console-Command-Drop-Database")
  public void dropDatabase(
      @ConsoleParameter(name = "storage-type", description = "Storage type of server database", optional = true) String storageType)
      throws IOException {
    checkForDatabase();

    final String dbName = currentDatabase.getName();

    if (currentDatabase.getURL().startsWith(OEngineRemote.NAME)) {
      if (serverAdmin == null) {
        message("\n\nCannot drop a remote database without connecting to the server with a valid server's user");
        return;
      }

      if (storageType == null)
        storageType = "plocal";

      // REMOTE CONNECTION
      final String dbURL = currentDatabase.getURL().substring(OEngineRemote.NAME.length() + 1);
      new OServerAdmin(dbURL).connect(currentDatabaseUserName, currentDatabaseUserPassword).dropDatabase(storageType);
    } else {
      // LOCAL CONNECTION
      currentDatabase.drop();
      currentDatabase = null;
      currentDatabaseName = null;
    }

    message("\n\nDatabase '" + dbName + "' deleted successfully");
  }

  @ConsoleCommand(description = "Delete the specified database", onlineHelp = "Console-Command-Drop-Database")
  public void dropDatabase(
      @ConsoleParameter(name = "database-url", description = "The url of the database to drop in the format '<mode>:<path>'") String iDatabaseURL,
      @ConsoleParameter(name = "user", description = "Server administrator name") String iUserName,
      @ConsoleParameter(name = "password", description = "Server administrator password") String iUserPassword,
      @ConsoleParameter(name = "storage-type", description = "Storage type of server database", optional = true) String storageType)
      throws IOException {

    if (iDatabaseURL.startsWith(OEngineRemote.NAME)) {
      // REMOTE CONNECTION
      final String dbURL = iDatabaseURL.substring(OEngineRemote.NAME.length() + 1);

      if (serverAdmin != null)
        serverAdmin.close();

      serverAdmin = new OServerAdmin(dbURL).connect(iUserName, iUserPassword);
      serverAdmin.dropDatabase(storageType);
      disconnect();
    } else {
      // LOCAL CONNECTION
      currentDatabase = new ODatabaseDocumentTx(iDatabaseURL);
      if (currentDatabase.exists()) {
        currentDatabase.open(iUserName, iUserPassword);
        currentDatabase.drop();
      } else
        message("\n\nCannot drop database '" + iDatabaseURL + "' because was not found");
    }

    currentDatabase = null;
    currentDatabaseName = null;

    message("\n\nDatabase '" + iDatabaseURL + "' deleted successfully");
  }

  @ConsoleCommand(splitInWords = false, description = "Remove an index", onlineHelp = "SQL-Drop-Index")
  public void dropIndex(@ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText)
      throws IOException {
    message("\n\nRemoving index...");

    sqlCommand("drop", iCommandText, "\nDropped index in %f sec(s).\n", false);
    updateDatabaseInfo();
    message("\n\nIndex removed successfully");
  }

  @ConsoleCommand(splitInWords = false, description = "Rebuild an index if it is automatic", onlineHelp = "SQL-Rebuild-Index")
  public void rebuildIndex(@ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText)
      throws IOException {
    message("\n\nRebuilding index(es)...");

    sqlCommand("rebuild", iCommandText, "\nRebuilt index(es). Found %d link(s) in %f sec(s).\n", true);
    updateDatabaseInfo();
    message("\n\nIndex(es) rebuilt successfully");
  }

  @ConsoleCommand(splitInWords = false, description = "Remove a class from the schema", onlineHelp = "SQL-Drop-Class")
  public void dropClass(@ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText)
      throws IOException {
    sqlCommand("drop", iCommandText, "\nRemoved class in %f sec(s).\n", false);
    updateDatabaseInfo();
  }

  @ConsoleCommand(splitInWords = false, description = "Remove a property from a class", onlineHelp = "SQL-Drop-Property")
  public void dropProperty(@ConsoleParameter(name = "command-text", description = "The command text to execute") String iCommandText)
      throws IOException {
    sqlCommand("drop", iCommandText, "\nRemoved class property in %f sec(s).\n", false);
    updateDatabaseInfo();
  }

  @ConsoleCommand(description = "Browse all records of a class", onlineHelp = "Console-Command-Browse-Class")
  public void browseClass(@ConsoleParameter(name = "class-name", description = "The name of the class") final String iClassName) {
    checkForDatabase();

    resetResultSet();

    final int limit = Integer.parseInt(properties.get("limit"));

    OIdentifiableIterator<?> it = currentDatabase.browseClass(iClassName);

    browseRecords(limit, it);
  }

  @ConsoleCommand(description = "Browse all records of a cluster", onlineHelp = "Console-Command-Browse-Cluster")
  public void browseCluster(
      @ConsoleParameter(name = "cluster-name", description = "The name of the cluster") final String iClusterName) {
    checkForDatabase();

    resetResultSet();

    final int limit = Integer.parseInt(properties.get("limit"));

    final ORecordIteratorCluster<?> it = currentDatabase.browseCluster(iClusterName);

    browseRecords(limit, it);
  }

  @ConsoleCommand(aliases = { "display" }, description = "Display current record attributes", onlineHelp = "Console-Command-Display-Record")
  public void displayRecord(
      @ConsoleParameter(name = "number", description = "The number of the record in the most recent result set") final String iRecordNumber) {
    checkForDatabase();

    if (iRecordNumber == null || currentResultSet == null)
      checkCurrentObject();
    else {
      int recNumber = Integer.parseInt(iRecordNumber);
      if (currentResultSet.size() == 0)
        throw new OSystemException("No result set where to find the requested record. Execute a query first.");

      if (currentResultSet.size() <= recNumber)
        throw new OSystemException("The record requested is not part of current result set (0"
            + (currentResultSet.size() > 0 ? "-" + (currentResultSet.size() - 1) : "") + ")");

      setCurrentRecord(recNumber);
    }

    dumpRecordDetails();
  }

  @ConsoleCommand(description = "Display a record as raw bytes", onlineHelp = "Console-Command-Display-Raw-Record")
  public void displayRawRecord(@ConsoleParameter(name = "rid", description = "The record id to display") final String iRecordId)
      throws IOException {
    checkForDatabase();

    ORecordId rid;
    if (iRecordId.indexOf(':') > -1)
      rid = new ORecordId(iRecordId);
    else {
      OIdentifiable rec = setCurrentRecord(Integer.parseInt(iRecordId));
      if (rec != null)
        rid = (ORecordId) rec.getIdentity();
      else
        return;
    }

    ORawBuffer record;
    ORecordId id = new ORecordId(rid);
    if (!(currentDatabase.getStorage() instanceof OLocalPaginatedStorage)) {
      record = currentDatabase.getStorage().readRecord(rid, null, false, null).getResult();
      if (record != null) {
        String content;
        if (Integer.parseInt(properties.get("maxBinaryDisplay")) < record.buffer.length)
          content = new String(Arrays.copyOf(record.buffer, Integer.parseInt(properties.get("maxBinaryDisplay"))));
        else
          content = new String(record.buffer);
        out.println("\nRaw record content. The size is " + record.buffer.length + " bytes, while settings force to print first "
            + content.length() + " bytes:\n\n" + content);
      }
    } else {
      OLocalPaginatedStorage storage = (OLocalPaginatedStorage) currentDatabase.getStorage();
      OPaginatedCluster cluster = (OPaginatedCluster) storage.getClusterById(id.getClusterId());
      if (cluster == null) {
        message("\n cluster with id %i does not exist", id.getClusterId());
        return;
      }
      OPaginatedClusterDebug debugInfo = cluster.readDebug(id.clusterPosition);
      message("\n\nLOW LEVEL CLUSTER INFO");
      message("\n cluster fieldId: %d", debugInfo.fileId);
      message("\n cluster name: %s", cluster.getName());

      message("\n in cluster position: %d", debugInfo.clusterPosition);
      message("\n empty: %b", debugInfo.empty);
      message("\n contentSize: %d", debugInfo.contentSize);
      message("\n n-pages: %d", debugInfo.pages.size());
      message("\n\n +----------PAGE_ID---------------+------IN_PAGE_POSITION----------+---------IN_PAGE_SIZE-----------+----PAGE_CONTENT---->> ");
      for (OClusterPageDebug page : debugInfo.pages) {
        message("\n |%30d ", page.pageIndex);
        message(" |%30d ", page.inPagePosition);
        message(" |%30d ", page.inPageSize);
        message(" |%s", OBase64Utils.encodeBytes(page.content));
      }
      record = cluster.readRecord(id.clusterPosition);
    }
    if (record == null)
      throw new OSystemException("The record has been deleted");
    if ("ORecordSerializerBinary".equals(currentDatabase.getSerializer().toString())) {
      byte[] buff = record.getBuffer();
      ORecordSerializerBinaryDebug debugger = new ORecordSerializerBinaryDebug();
      ORecordSerializationDebug deserializeDebug = debugger.deserializeDebug(buff, currentDatabase);
      message("\n\nRECORD CONTENT INFO");
      message("\n class name: %s", deserializeDebug.className);
      message("\n fail on Reading: %b", deserializeDebug.readingFailure);
      message("\n fail position: %d", deserializeDebug.failPosition);
      if (deserializeDebug.readingException != null) {
        StringWriter writer = new StringWriter();
        deserializeDebug.readingException.printStackTrace(new PrintWriter(writer));
        message("\n Exception On Reading: %s", writer.getBuffer().toString());
      }

      message("\n number of properties : %d", deserializeDebug.properties.size());
      message("\n\n PROPERTIES");
      for (ORecordSerializationDebugProperty prop : deserializeDebug.properties) {
        message("\n  property name: %s", prop.name);
        message("\n  property type: %s", prop.type.name());
        message("\n  property globlaId: %d", prop.globalId);
        message("\n  fail on reading: %b", prop.faildToRead);
        if (prop.faildToRead) {
          message("\n  failed on reading position: %b", prop.failPosition);
          StringWriter writer = new StringWriter();
          prop.readingException.printStackTrace(new PrintWriter(writer));
          message("\n  Exception on reading: %s", writer.getBuffer().toString());
        } else {
          if (prop.value instanceof ORidBag) {
            message("\n  property value: ORidBug ");
            ((ORidBag) prop.value).debugPrint(System.out);
          } else
            message("\n  property value: %s", prop.value != null ? prop.value.toString() : "null");
        }
        message("\n");
      }
    }
  }

  @ConsoleCommand(aliases = { "status" }, description = "Display information about the database", onlineHelp = "Console-Command-Info")
  public void info() {
    if (currentDatabaseName != null) {
      message("\nCurrent database: " + currentDatabaseName + " (url=" + currentDatabase.getURL() + ")");

      final OStorage stg = currentDatabase.getStorage();

      if (stg instanceof OStorageRemoteThread) {
        dumpDistributedConfiguration(true);
      }

      listProperties();
      listClusters();
      listClasses();
      listIndexes();
    }
  }

  @ConsoleCommand(description = "Display the database properties")
  public void listProperties() {
    if (currentDatabase == null)
      return;

    final OStorage stg = currentDatabase.getStorage();

    final OStorageConfiguration dbCfg = stg.getConfiguration();

    message("\n\nDATABASE PROPERTIES");

    if (dbCfg.getProperties() != null) {
      message("\n--------------------------------+----------------------------------------------------+");
      message("\n NAME                           | VALUE                                              |");
      message("\n--------------------------------+----------------------------------------------------+");
      message("\n %-30s | %-50s |", "Name", format(dbCfg.name, 50));
      message("\n %-30s | %-50s |", "Version", format("" + dbCfg.version, 50));
      message("\n %-30s | %-50s |", "Conflict Strategy", format(dbCfg.getConflictStrategy(), 50));
      message("\n %-30s | %-50s |", "Date format", format(dbCfg.dateFormat, 50));
      message("\n %-30s | %-50s |", "Datetime format", format(dbCfg.dateTimeFormat, 50));
      message("\n %-30s | %-50s |", "Timezone", format(dbCfg.getTimeZone().getID(), 50));
      message("\n %-30s | %-50s |", "Locale Country", format(dbCfg.getLocaleCountry(), 50));
      message("\n %-30s | %-50s |", "Locale Language", format(dbCfg.getLocaleLanguage(), 50));
      message("\n %-30s | %-50s |", "Charset", format(dbCfg.getCharset(), 50));
      message("\n %-30s | %-50s |", "Schema RID", format(dbCfg.schemaRecordId, 50));
      message("\n %-30s | %-50s |", "Index Manager RID", format(dbCfg.indexMgrRecordId, 50));
      message("\n %-30s | %-50s |", "Dictionary RID", format(dbCfg.dictionaryRecordId, 50));
      message("\n--------------------------------+----------------------------------------------------+");

      if (!dbCfg.getProperties().isEmpty()) {
        message("\n\nDATABASE CUSTOM PROPERTIES:");
        message("\n +-------------------------------+--------------------------------------------------+");
        message("\n | NAME                          | VALUE                                            |");
        message("\n +-------------------------------+--------------------------------------------------+");
        for (OStorageEntryConfiguration cfg : dbCfg.getProperties())
          message("\n | %-29s | %-49s|", cfg.name, format(cfg.value, 49));
        message("\n +-------------------------------+--------------------------------------------------+");
      }
    }
  }

  @ConsoleCommand(aliases = { "desc" }, description = "Display a class in the schema", onlineHelp = "Console-Command-Info-Class")
  public void infoClass(@ConsoleParameter(name = "class-name", description = "The name of the class") final String iClassName) {
    checkForDatabase();

    final OClass cls = currentDatabase.getMetadata().getImmutableSchemaSnapshot().getClass(iClassName);

    if (cls == null) {
      message("\n! Class '" + iClassName + "' does not exist in the database '" + currentDatabaseName + "'");
      return;
    }

    message("\nCLASS '" + cls.getName() + "'\n");

    if (cls.getShortName() != null)
      message("\nAlias................: " + cls.getShortName());
    if (cls.hasSuperClasses())
      message("\nSuper classes........: " + Arrays.toString(cls.getSuperClassesNames().toArray()));
    message("\nDefault cluster......: " + currentDatabase.getClusterNameById(cls.getDefaultClusterId()) + " (id="
        + cls.getDefaultClusterId() + ")");

    final StringBuilder clusters = new StringBuilder();
    for (int clId : cls.getClusterIds()) {
      if (clusters.length() > 0)
        clusters.append(", ");

      clusters.append(currentDatabase.getClusterNameById(clId));
      clusters.append("(");
      clusters.append(clId);
      clusters.append(")");
    }
    message("\nSupported clusters...: " + clusters.toString());

    message("\nCluster selection....: " + cls.getClusterSelection().getName());
    message("\nOversize.............: " + cls.getClassOverSize());

    if (!cls.getSubclasses().isEmpty()) {
      message("\nSubclasses.........: ");
      int i = 0;
      for (OClass c : cls.getSubclasses()) {
        if (i > 0)
          message(", ");
        message(c.getName());
        ++i;
      }
      out.println();
    }

    if (cls.properties().size() > 0) {
      message("\n\nPROPERTIES");
      message("\n-------------------------------+-------------+-------------------------------+-----------+----------+----------+-----------+-----------+----------+");
      message("\n NAME                          | TYPE        | LINKED TYPE/CLASS             | MANDATORY | READONLY | NOT NULL |    MIN    |    MAX    | COLLATE  |");
      message("\n-------------------------------+-------------+-------------------------------+-----------+----------+----------+-----------+-----------+----------+");

      for (final OProperty p : cls.properties()) {
        try {
          message("\n %-30s| %-12s| %-30s| %-10s| %-9s| %-9s| %-10s| %-10s| %-9s|", p.getName(), p.getType(),
              p.getLinkedClass() != null ? p.getLinkedClass() : p.getLinkedType(), p.isMandatory(), p.isReadonly(), p.isNotNull(),
              p.getMin() != null ? p.getMin() : "", p.getMax() != null ? p.getMax() : "", p.getCollate() != null ? p.getCollate()
                  .getName() : "");
        } catch (Exception ignored) {
        }
      }
      message("\n-------------------------------+-------------+-------------------------------+-----------+----------+----------+-----------+-----------+----------+");
    }

    final Set<OIndex<?>> indexes = cls.getClassIndexes();
    if (!indexes.isEmpty()) {
      message("\n\nINDEXES (" + indexes.size() + " altogether)");
      message("\n-------------------------------+----------------+");
      message("\n NAME                          | PROPERTIES     |");
      message("\n-------------------------------+----------------+");
      for (final OIndex<?> index : indexes) {
        final OIndexDefinition indexDefinition = index.getDefinition();
        if (indexDefinition != null) {
          final List<String> fields = indexDefinition.getFields();
          message("\n %-30s| %-15s|", index.getName(), fields.get(0) + (fields.size() > 1 ? " (+)" : ""));

          for (int i = 1; i < fields.size(); i++) {
            if (i < fields.size() - 1)
              message("\n %-30s| %-15s|", "", fields.get(i) + " (+)");
            else
              message("\n %-30s| %-15s|", "", fields.get(i));
          }
        } else {
          message("\n %-30s| %-15s|", index.getName(), "");
        }
      }
      message("\n-------------------------------+----------------+");
    }

    if (cls.getCustomKeys().size() > 0) {
      message("\n\nCUSTOM ATTRIBUTES");
      message("\n-------------------------------+-----------------------------------------+");
      message("\n NAME                          | VALUE                                   |");
      message("\n-------------------------------+-----------------------------------------+");

      for (final String k : cls.getCustomKeys()) {
        try {
          message("\n %-30s| %-40s|", k, cls.getCustom(k));
        } catch (Exception ignored) {
        }
      }
      message("\n-------------------------------+-----------------------------------------+");
    }
  }

  @ConsoleCommand(description = "Display a class property", onlineHelp = "Console-Command-Info-Property")
  public void infoProperty(
      @ConsoleParameter(name = "property-name", description = "The name of the property as <class>.<property>") final String iPropertyName) {
    checkForDatabase();

    if (iPropertyName.indexOf('.') == -1)
      throw new OSystemException("Property name is in the format <class>.<property>");

    final String[] parts = iPropertyName.split("\\.");

    final OClass cls = currentDatabase.getMetadata().getImmutableSchemaSnapshot().getClass(parts[0]);

    if (cls == null) {
      message("\n! Class '" + parts[0] + "' does not exist in the database '" + currentDatabaseName + "'");
      return;
    }

    final OProperty prop = cls.getProperty(parts[1]);

    if (prop == null) {
      message("\n! Property '" + parts[1] + "' does not exist in class '" + parts[0] + "'");
      return;
    }

    message("\nPROPERTY '" + prop.getFullName() + "'\n");
    message("\nType.................: " + prop.getType());
    message("\nMandatory............: " + prop.isMandatory());
    message("\nNot null.............: " + prop.isNotNull());
    message("\nRead only............: " + prop.isReadonly());
    message("\nDefault value........: " + prop.getDefaultValue());
    message("\nMinimum value........: " + prop.getMin());
    message("\nMaximum value........: " + prop.getMax());
    message("\nREGEXP...............: " + prop.getRegexp());
    message("\nCollate..............: " + prop.getCollate());
    message("\nLinked class.........: " + prop.getLinkedClass());
    message("\nLinked type..........: " + prop.getLinkedType());

    if (prop.getCustomKeys().size() > 0) {
      message("\n\nCUSTOM ATTRIBUTES");
      message("\n-------------------------------+-----------------------------------------+");
      message("\n NAME                          | VALUE                                   |");
      message("\n-------------------------------+-----------------------------------------+");

      for (final String k : prop.getCustomKeys()) {
        try {
          message("\n %-30s| %-40s|", k, prop.getCustom(k));
        } catch (Exception ignored) {
        }
      }
      message("\n-------------------------------+-----------------------------------------+");
    }

    final Collection<OIndex<?>> indexes = prop.getAllIndexes();
    if (!indexes.isEmpty()) {
      message("\n\nINDEXES (" + indexes.size() + " altogether)");
      message("\n-------------------------------+----------------+");
      message("\n NAME                          | PROPERTIES     |");
      message("\n-------------------------------+----------------+");
      for (final OIndex<?> index : indexes) {
        final OIndexDefinition indexDefinition = index.getDefinition();
        if (indexDefinition != null) {
          final List<String> fields = indexDefinition.getFields();
          message("\n %-30s| %-15s|", index.getName(), fields.get(0) + (fields.size() > 1 ? " (+)" : ""));

          for (int i = 1; i < fields.size(); i++) {
            if (i < fields.size() - 1)
              message("\n %-30s| %-15s|", "", fields.get(i) + " (+)");
            else
              message("\n %-30s| %-15s|", "", fields.get(i));
          }
        } else {
          message("\n %-30s| %-15s|", index.getName(), "");
        }
      }
      message("\n-------------------------------+----------------+");
    }
  }

  @ConsoleCommand(description = "Display all indexes", aliases = { "indexes" }, onlineHelp = "Console-Command-List-Indexes")
  public void listIndexes() {
    if (currentDatabaseName != null) {
      message("\n\nINDEXES");
      message("\n----------------------------------------------+------------+-----------------------+----------------+------------+");
      message("\n NAME                                         | TYPE       |         CLASS         |     FIELDS     | RECORDS    |");
      message("\n----------------------------------------------+------------+-----------------------+----------------+------------+");

      int totalIndexes = 0;
      long totalRecords = 0;

      final List<OIndex<?>> indexes = new ArrayList<OIndex<?>>(currentDatabase.getMetadata().getIndexManager().getIndexes());
      Collections.sort(indexes, new Comparator<OIndex<?>>() {
        public int compare(OIndex<?> o1, OIndex<?> o2) {
          return o1.getName().compareToIgnoreCase(o2.getName());
        }
      });

      for (final OIndex<?> index : indexes) {
        try {
          final OIndexDefinition indexDefinition = index.getDefinition();
          final long size = index.getKeySize();
          if (indexDefinition == null || indexDefinition.getClassName() == null) {
            message("\n %-45s| %-10s | %-22s| %-15s|%11d |", format(index.getName(), 45), format(index.getType(), 10), "", "", size);
          } else {
            final List<String> fields = indexDefinition.getFields();
            if (fields.size() == 1) {
              message("\n %-45s| %-10s | %-22s| %-15s|%11d |", format(index.getName(), 45), format(index.getType(), 10),
                  format(indexDefinition.getClassName(), 22), format(fields.get(0), 10), size);
            } else {
              message("\n %-45s| %-10s | %-22s| %-15s|%11d |", format(index.getName(), 45), format(index.getType(), 10),
                  format(indexDefinition.getClassName(), 22), format(fields.get(0), 10), size);
              for (int i = 1; i < fields.size(); i++) {
                message("\n %-45s| %-10s | %-22s| %-15s|%11s |", "", "", "", fields.get(i), "");
              }
            }
          }

          totalIndexes++;
          totalRecords += size;
        } catch (Exception ignored) {
        }
      }
      message("\n----------------------------------------------+------------+-----------------------+----------------+------------+");
      message("\n TOTAL = %-3d                                                                                %15d |",
          totalIndexes, totalRecords);
      message("\n-----------------------------------------------------------------------------------------------------------------+");
    } else
      message("\nNo database selected yet.");
  }

  @ConsoleCommand(description = "Display all the configured clusters", aliases = { "clusters" }, onlineHelp = "Console-Command-List-Clusters")
  public void listClusters() {
    if (currentDatabaseName != null) {
      message("\n\nCLUSTERS (collections)");
      message("\n----------------------------------------------+-------+-------------------+----------------+");
      message("\n NAME                                         | ID    | CONFLICT STRATEGY | RECORDS        |");
      message("\n----------------------------------------------+-------+-------------------+----------------+");

      int clusterId;
      String clusterType;
      long totalElements = 0;
      long count;

      final List<String> clusters = new ArrayList<String>(currentDatabase.getClusterNames());
      Collections.sort(clusters);

      for (String clusterName : clusters) {
        try {
          clusterId = currentDatabase.getClusterIdByName(clusterName);
          final OCluster cluster = currentDatabase.getStorage().getClusterById(clusterId);

          final String conflictStrategy = cluster.getRecordConflictStrategy() != null ? cluster.getRecordConflictStrategy()
              .getName() : "";

          count = currentDatabase.countClusterElements(clusterName);
          totalElements += count;

          message("\n %-45s| %5d | %-17s |%15d |", format(clusterName, 45), clusterId, format(conflictStrategy, 15), count);
        } catch (Exception e) {
          if (e instanceof OIOException)
            break;
        }
      }
      message("\n----------------------------------------------+-------+-------------------+----------------+");
      message("\n TOTAL = %-3d                                                              |%15d |", clusters.size(),
          totalElements);
      message("\n------------------------------------------------------+-------------------+----------------+");
    } else
      message("\nNo database selected yet.");
  }

  @ConsoleCommand(description = "Display all the configured classes", aliases = { "classes" }, onlineHelp = "Console-Command-List-Classes")
  public void listClasses() {
    if (currentDatabaseName != null) {
      message("\n\nCLASSES");
      message("\n----------------------------------------------+------------------------------------+------------+----------------+");
      message("\n NAME                                         | SUPERCLASS                         | CLUSTERS   | RECORDS        |");
      message("\n----------------------------------------------+------------------------------------+------------+----------------+");

      long totalElements = 0;
      long count;

      final List<OClass> classes = new ArrayList<OClass>(currentDatabase.getMetadata().getImmutableSchemaSnapshot().getClasses());
      Collections.sort(classes, new Comparator<OClass>() {
        public int compare(OClass o1, OClass o2) {
          return o1.getName().compareToIgnoreCase(o2.getName());
        }
      });

      for (OClass cls : classes) {
        try {
          final StringBuilder clusters = new StringBuilder(1024);
          if (cls.isAbstract())
            clusters.append("-");
          else
            for (int i = 0; i < cls.getClusterIds().length; ++i) {
              if (i > 0)
                clusters.append(",");
              clusters.append(cls.getClusterIds()[i]);
            }

          count = currentDatabase.countClass(cls.getName(), false);
          totalElements += count;

          final String superClasses = cls.hasSuperClasses() ? Arrays.toString(cls.getSuperClassesNames().toArray()) : "";

          message("\n %-45s| %-35s| %-11s|%15d |", format(cls.getName(), 45), format(superClasses, 35), clusters.toString(), count);
        } catch (Exception ignored) {
        }
      }
      message("\n----------------------------------------------+------------------------------------+------------+----------------+");
      message("\n TOTAL = %-3d                                                                                %15d |",
          classes.size(), totalElements);
      message("\n----------------------------------------------+------------------------------------+------------+----------------+");

    } else
      message("\nNo database selected yet.");
  }

  @ConsoleCommand(description = "Loook up a record using the dictionary. If found, set it as the current record", onlineHelp = "Console-Command-Dictionary-Get")
  public void dictionaryGet(@ConsoleParameter(name = "key", description = "The key to search") final String iKey) {
    checkForDatabase();

    currentRecord = currentDatabase.getDictionary().get(iKey);
    if (currentRecord == null)
      message("\nEntry not found in dictionary.");
    else {
      currentRecord = (ORecord) currentRecord.load();
      displayRecord(null);
    }
  }

  @ConsoleCommand(description = "Insert or modify an entry in the database dictionary. The entry is comprised of key=String, value=record-id", onlineHelp = "Console-Command-Dictionary-Put")
  public void dictionaryPut(@ConsoleParameter(name = "key", description = "The key to bind") final String iKey,
      @ConsoleParameter(name = "record-id", description = "The record-id of the record to bind to the key") final String iRecordId) {
    checkForDatabase();

    currentRecord = currentDatabase.load(new ORecordId(iRecordId));
    if (currentRecord == null)
      message("\nError: record with id '" + iRecordId + "' was not found in database");
    else {
      currentDatabase.getDictionary().put(iKey, currentRecord);
      displayRecord(null);
      message("\nThe entry " + iKey + "=" + iRecordId + " has been inserted in the database dictionary");
    }
  }

  @ConsoleCommand(description = "Remove the association in the dictionary", onlineHelp = "Console-Command-Dictionary-Remove")
  public void dictionaryRemove(@ConsoleParameter(name = "key", description = "The key to remove") final String iKey) {
    checkForDatabase();

    boolean result = currentDatabase.getDictionary().remove(iKey);
    if (!result)
      message("\nEntry not found in dictionary.");
    else
      message("\nEntry removed from the dictionary.");
  }

  @ConsoleCommand(description = "Copy a database to a remote server")
  public void copyDatabase(
      @ConsoleParameter(name = "db-name", description = "Name of the database to share") final String iDatabaseName,
      @ConsoleParameter(name = "db-user", description = "Database user") final String iDatabaseUserName,
      @ConsoleParameter(name = "db-password", description = "Database password") String iDatabaseUserPassword,
      @ConsoleParameter(name = "server-name", description = "Remote server's name as <address>:<port>") final String iRemoteName,
      @ConsoleParameter(name = "engine-name", description = "Remote server's engine to use between 'local' or 'memory'") final String iRemoteEngine)
      throws IOException {

    try {
      if (serverAdmin == null)
        throw new IllegalStateException("You must be connected to a remote server to share a database");

      message("\nCopying database '" + iDatabaseName + "' to the server '" + iRemoteName + "' via network streaming...");

      serverAdmin.copyDatabase(iDatabaseName, iDatabaseUserName, iDatabaseUserPassword, iRemoteName, iRemoteEngine);

      message("\nDatabase '" + iDatabaseName + "' has been copied to the server '" + iRemoteName + "'");

    } catch (Exception e) {
      printError(e);
    }
  }

  @ConsoleCommand(description = "Displays the status of the cluster nodes")
  public void clusterStatus() throws IOException {
    if (serverAdmin == null)
      throw new IllegalStateException("You must be connected to a remote server to get the cluster status");

    checkForRemoteServer();
    try {

      message("\nCluster status:");
      out.println(serverAdmin.clusterStatus().toJSON("attribSameRow,alwaysFetchEmbedded,fetchPlan:*:0"));

    } catch (Exception e) {
      printError(e);
    }
  }

  @ConsoleCommand(description = "Check database integrity")
  public void checkDatabase(@ConsoleParameter(name = "options", description = "Options: -v", optional = true) final String iOptions)
      throws IOException {
    checkForDatabase();

    if (!(currentDatabase.getStorage() instanceof OAbstractPaginatedStorage)) {
      message("\nCannot check integrity of non-local database. Connect to it using local mode.");
      return;
    }

    boolean verbose = iOptions != null && iOptions.contains("-v");

    try {
      ((OAbstractPaginatedStorage) currentDatabase.getStorage()).check(verbose, this);
    } catch (ODatabaseImportException e) {
      printError(e);
    }
  }

  @ConsoleCommand(description = "Repair database structure")
  public void repairDatabase(@ConsoleParameter(name = "options", description = "Options: -v", optional = true) final String iOptions)
      throws IOException {
    checkForDatabase();

    message("\nRepairing database...");

    boolean verbose = iOptions != null && iOptions.contains("-v");

    long fixedLinks = 0l;
    long modifiedDocuments = 0l;
    long errors = 0l;

    try {

      message("\n- Fixing dirty links...");
      for (String clusterName : currentDatabase.getClusterNames()) {
        for (ORecord rec : currentDatabase.browseCluster(clusterName)) {
          try {
            if (rec instanceof ODocument) {
              boolean changed = false;

              final ODocument doc = (ODocument) rec;
              for (String fieldName : doc.fieldNames()) {
                final Object fieldValue = doc.rawField(fieldName);

                if (fieldValue instanceof OIdentifiable) {
                  if (fixLink(fieldValue)) {
                    doc.field(fieldName, (OIdentifiable) null);
                    fixedLinks++;
                    changed = true;
                    if (verbose)
                      message("\n--- reset link " + ((OIdentifiable) fieldValue).getIdentity() + " in field '" + fieldName
                          + "' (rid=" + doc.getIdentity() + ")");
                  }
                } else if (fieldValue instanceof Iterable<?>) {
                  if (fieldValue instanceof ORecordLazyMultiValue)
                    ((ORecordLazyMultiValue) fieldValue).setAutoConvertToRecord(false);

                  final Iterator<Object> it = ((Iterable) fieldValue).iterator();
                  for (int i = 0; it.hasNext(); ++i) {
                    final Object v = it.next();
                    if (fixLink(v)) {
                      it.remove();
                      fixedLinks++;
                      changed = true;
                      if (verbose)
                        message("\n--- reset link " + ((OIdentifiable) v).getIdentity() + " as item " + i
                            + " in collection of field '" + fieldName + "' (rid=" + doc.getIdentity() + ")");
                    }
                  }
                }
              }

              if (changed) {
                modifiedDocuments++;
                doc.save();

                if (verbose)
                  message("\n-- updated document " + doc.getIdentity());
              }
            }
          } catch (Exception e) {
            errors++;
          }
        }
      }
      if (verbose)
        message("\n");

      message("Done! Fixed links: " + fixedLinks + ", modified documents: " + modifiedDocuments);

      message("\nRepair database complete (" + errors + " errors)");
    } catch (Exception e) {
      printError(e);
    }
  }

  @ConsoleCommand(description = "Compare two databases")
  public void compareDatabases(
      @ConsoleParameter(name = "db1-url", description = "URL of the first database") final String iDb1URL,
      @ConsoleParameter(name = "db2-url", description = "URL of the second database") final String iDb2URL,
      @ConsoleParameter(name = "username", description = "User name", optional = true) final String iUserName,
      @ConsoleParameter(name = "password", description = "User password", optional = true) final String iUserPassword,
      @ConsoleParameter(name = "detect-mapping-data", description = "Whether RID mapping data after DB import should be tried to found on the disk", optional = true) String autoDiscoveringMappingData)
      throws IOException {
    try {
      final ODatabaseCompare compare;
      if (iUserName == null)
        compare = new ODatabaseCompare(iDb1URL, iDb2URL, this);
      else
        compare = new ODatabaseCompare(iDb1URL, iDb2URL, iUserName, iUserPassword, this);

      compare.setAutoDetectExportImportMap(autoDiscoveringMappingData != null ? Boolean.valueOf(autoDiscoveringMappingData) : true);
      compare.setCompareIndexMetadata(true);
      compare.compare();
    } catch (ODatabaseExportException e) {
      printError(e);
    }
  }

  @ConsoleCommand(description = "Import a database into the current one", splitInWords = false, onlineHelp = "Console-Command-Import")
  public void importDatabase(@ConsoleParameter(name = "options", description = "Import options") final String text)
      throws IOException {
    checkForDatabase();

    message("\nImporting database " + text + "...");

    final List<String> items = OStringSerializerHelper.smartSplit(text, ' ');
    final String fileName = items.size() <= 0 || (items.get(1)).charAt(0) == '-' ? null : items.get(1);
    final String options = fileName != null ? text.substring((items.get(0)).length() + (items.get(1)).length() + 1).trim() : text;

    try {
      ODatabaseImport databaseImport = new ODatabaseImport(currentDatabase, fileName, this);

      databaseImport.setOptions(options).importDatabase();

      databaseImport.close();
    } catch (ODatabaseImportException e) {
      printError(e);
    }
  }

  @ConsoleCommand(description = "Backup a database", splitInWords = false, onlineHelp = "Console-Command-Backup")
  public void backupDatabase(@ConsoleParameter(name = "options", description = "Backup options") final String iText)
      throws IOException {
    checkForDatabase();

    final List<String> items = OStringSerializerHelper.smartSplit(iText, ' ');

    if (items.size() < 2)
      try {
        syntaxError("backupDatabase", getClass().getMethod("backupDatabase", String.class));
        return;
      } catch (NoSuchMethodException ignored) {
      }

    final String fileName = items.size() <= 0 || items.get(1).charAt(0) == '-' ? null : items.get(1);

    int bufferSize = Integer.parseInt(properties.get("backupBufferSize"));
    int compressionLevel = Integer.parseInt(properties.get("backupCompressionLevel"));
    boolean incremental = false;

    for (int i = 2; i < items.size(); ++i) {
      final String item = items.get(i);
      final int sep = item.indexOf('=');

      final String parName;
      final String parValue;
      if (sep > -1) {
        parName = item.substring(1, sep);
        parValue = item.substring(sep + 1);
      } else {
        parName = item.substring(1);
        parValue = null;
      }

      if (parName.equalsIgnoreCase("incremental"))
        incremental = true;
      else if (parName.equalsIgnoreCase("bufferSize"))
        bufferSize = Integer.parseInt(parValue);
      else if (parName.equalsIgnoreCase("compressionLevel"))
        compressionLevel = Integer.parseInt(parValue);
    }

    final long startTime = System.currentTimeMillis();
    try {
      if (incremental) {
        out.println(new StringBuilder("Executing incremental backup of database '" + currentDatabaseName + "' to: ").append(iText)
            .append("..."));
        currentDatabase.incrementalBackup(fileName);
      } else {
        out.println(new StringBuilder("Executing full backup of database '" + currentDatabaseName + "' to: ").append(iText).append(
            "..."));
        final FileOutputStream fos = new FileOutputStream(fileName);
        try {
          currentDatabase.backup(fos, null, null, this, compressionLevel, bufferSize);

        } finally {
          fos.flush();
          fos.close();

          message("\nBackup executed in %.2f seconds", ((float) (System.currentTimeMillis() - startTime) / 1000));
        }
      }
    } catch (ODatabaseExportException e) {
      printError(e);
    }
  }

  @ConsoleCommand(description = "Restore a database into the current one", splitInWords = false, onlineHelp = "Console-Command-Restore")
  public void restoreDatabase(@ConsoleParameter(name = "options", description = "Restore options") final String text)
      throws IOException {
    checkForDatabase();

    final List<String> items = OStringSerializerHelper.smartSplit(text, ' ');

    if (items.size() < 2)
      try {
        syntaxError("restoreDatabase", getClass().getMethod("restoreDatabase", String.class));
        return;
      } catch (NoSuchMethodException e) {
      }

    final String fileName = items.size() <= 0 || (items.get(1)).charAt(0) == '-' ? null : items.get(1);

    final long startTime = System.currentTimeMillis();
    try {
      if (new File(fileName).isDirectory()) {
        // INCREMENTAL RESTORE
        message("\nRestoring database '%s' from incremental backups...", text);
        currentDatabase.incrementalRestore(fileName);

      } else {
        // FULL RESTORE
        message("\nRestoring database '%s' from full backup...", text);
        final FileInputStream f = new FileInputStream(fileName);
        try {
          currentDatabase.restore(f, null, null, this);
        } finally {
          f.close();
        }
      }
    } catch (ODatabaseImportException e) {
      printError(e);
    } finally {
      message("\nDatabase restored in %.2f seconds", ((float) (System.currentTimeMillis() - startTime) / 1000));
    }
  }

  @ConsoleCommand(description = "Export a database", splitInWords = false, onlineHelp = "Console-Command-Export")
  public void exportDatabase(@ConsoleParameter(name = "options", description = "Export options") final String iText)
      throws IOException {
    checkForDatabase();

    out.println(new StringBuilder("Exporting current database to: ").append(iText).append(" in GZipped JSON format ..."));
    final List<String> items = OStringSerializerHelper.smartSplit(iText, ' ');
    final String fileName = items.size() <= 1 || items.get(1).charAt(0) == '-' ? null : items.get(1);
    final String options = fileName != null ? iText.substring(items.get(0).length() + items.get(1).length() + 1).trim() : iText;

    try {
      new ODatabaseExport(currentDatabase, fileName, this).setOptions(options).exportDatabase().close();
    } catch (ODatabaseExportException e) {
      printError(e);
    }
  }

  @ConsoleCommand(description = "Export a database schema")
  public void exportSchema(@ConsoleParameter(name = "output-file", description = "Output file path") final String iOutputFilePath)
      throws IOException {
    checkForDatabase();

    message("\nExporting current database to: " + iOutputFilePath + "...");

    try {
      ODatabaseExport exporter = new ODatabaseExport(currentDatabase, iOutputFilePath, this);
      exporter.setIncludeRecords(false);
      exporter.exportDatabase().close();
    } catch (ODatabaseExportException e) {
      printError(e);
    }
  }

  @ConsoleCommand(description = "Export the current record in the requested format", onlineHelp = "Console-Command-Export-Record")
  public void exportRecord(@ConsoleParameter(name = "format", description = "Format, such as 'json'") final String iFormat,
      @ConsoleParameter(name = "options", description = "Options", optional = true) String iOptions) throws IOException {
    checkForDatabase();
    checkCurrentObject();

    final ORecordSerializer serializer = ORecordSerializerFactory.instance().getFormat(iFormat.toLowerCase());

    if (serializer == null) {
      message("\nERROR: Format '" + iFormat + "' was not found.");
      printSupportedSerializerFormat();
      return;
    } else if (!(serializer instanceof ORecordSerializerStringAbstract)) {
      message("\nERROR: Format '" + iFormat + "' does not export as text.");
      printSupportedSerializerFormat();
      return;
    }

    if (iOptions == null || iOptions.length() <= 0) {
      iOptions = "rid,version,class,type,keepTypes,alwaysFetchEmbedded,fetchPlan:*:0,prettyPrint";
    }

    try {
      out.println(currentRecord.toJSON(iOptions));
    } catch (ODatabaseExportException e) {
      printError(e);
    }
  }

  @ConsoleCommand(description = "Return all configured properties")
  public void properties() {
    message("\nPROPERTIES:");
    message("\n+-------------------------------+--------------------------------+");
    message("\n| %-30s| %-30s |", "NAME", "VALUE");
    message("\n+-------------------------------+--------------------------------+");
    for (Entry<String, String> p : properties.entrySet()) {
      message("\n| %-30s| %-30s |", p.getKey(), p.getValue());
    }
    message("\n+-------------------------------+--------------------------------+");
  }

  @ConsoleCommand(description = "Return the value of a property")
  public void get(@ConsoleParameter(name = "property-name", description = "Name of the property") final String iPropertyName) {
    Object value = properties.get(iPropertyName);

    out.println();

    if (value == null)
      message("\nProperty '" + iPropertyName + "' is not setted");
    else
      out.println(iPropertyName + " = " + value);
  }

  @ConsoleCommand(description = "Change the value of a property", onlineHelp = "Console-Command-Set")
  public void set(@ConsoleParameter(name = "property-name", description = "Name of the property") final String iPropertyName,
      @ConsoleParameter(name = "property-value", description = "Value to set") final String iPropertyValue) {
    Object prevValue = properties.get(iPropertyName);

    out.println();

    if (iPropertyName.equalsIgnoreCase("limit") && (Integer.parseInt(iPropertyValue) == 0 || Integer.parseInt(iPropertyValue) < -1)) {
      message("\nERROR: Limit must be > 0 or = -1 (no limit)");
    } else {

      if (prevValue != null)
        message("\nPrevious value was: " + prevValue);

      properties.put(iPropertyName, iPropertyValue);

      out.println();
      out.println(iPropertyName + " = " + iPropertyValue);
    }
  }

  @ConsoleCommand(description = "Declare an intent", onlineHelp = "")
  public void declareIntent(
      @ConsoleParameter(name = "Intent name", description = "name of the intent to execute") final String iIntentName) {
    checkForDatabase();

    message("\nDeclaring intent '" + iIntentName + "'...");

    if (iIntentName.equalsIgnoreCase("massiveinsert"))
      currentDatabase.declareIntent(new OIntentMassiveInsert());
    else if (iIntentName.equalsIgnoreCase("massiveread"))
      currentDatabase.declareIntent(new OIntentMassiveRead());
    else
      throw new IllegalArgumentException("Intent '" + iIntentName
          + "' not supported. Available ones are: massiveinsert, massiveread");

    message("\nIntent '" + iIntentName + "' setted successfully");
  }

  @ConsoleCommand(description = "Execute a command against the profiler")
  public void profiler(
      @ConsoleParameter(name = "profiler command", description = "command to execute against the profiler") final String iCommandName) {
    if (iCommandName.equalsIgnoreCase("on")) {
      Orient.instance().getProfiler().startRecording();
      message("\nProfiler is ON now, use 'profiler off' to turn off.");
    } else if (iCommandName.equalsIgnoreCase("off")) {
      Orient.instance().getProfiler().stopRecording();
      message("\nProfiler is OFF now, use 'profiler on' to turn on.");
    } else if (iCommandName.equalsIgnoreCase("dump")) {
      out.println(Orient.instance().getProfiler().dump());
    }
  }

  @ConsoleCommand(description = "Return the value of a configuration value")
  public void configGet(@ConsoleParameter(name = "config-name", description = "Name of the configuration") final String iConfigName)
      throws IOException {
    final OGlobalConfiguration config = OGlobalConfiguration.findByKey(iConfigName);
    if (config == null)
      throw new IllegalArgumentException("Configuration variable '" + iConfigName + "' wasn't found");

    final String value;
    if (serverAdmin != null) {
      value = serverAdmin.getGlobalConfiguration(config);
      message("\nRemote configuration: ");
    } else {
      value = config.getValueAsString();
      message("\nLocal configuration: ");
    }
    out.println(iConfigName + " = " + value);
  }

  @ConsoleCommand(description = "Sleep X milliseconds")
  public void sleep(final String iTime) {
    try {
      Thread.sleep(Long.parseLong(iTime));
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
    }
  }

  @ConsoleCommand(description = "Change the value of a configuration value")
  public void configSet(
      @ConsoleParameter(name = "config-name", description = "Name of the configuration") final String iConfigName,
      @ConsoleParameter(name = "config-value", description = "Value to set") final String iConfigValue) throws IOException {
    final OGlobalConfiguration config = OGlobalConfiguration.findByKey(iConfigName);
    if (config == null)
      throw new IllegalArgumentException("Configuration variable '" + iConfigName + "' not found");

    if (serverAdmin != null) {
      serverAdmin.setGlobalConfiguration(config, iConfigValue);
      message("\nRemote configuration value changed correctly");
    } else {
      config.setValue(iConfigValue);
      message("\nLocal configuration value changed correctly");
    }
    out.println();
  }

  @ConsoleCommand(description = "Return all the configuration values")
  public void config() throws IOException {
    if (serverAdmin != null) {
      // REMOTE STORAGE
      final Map<String, String> values = serverAdmin.getGlobalConfigurations();

      message("\nREMOTE SERVER CONFIGURATION:");
      message("\n+--------------------------------------------------------------------------------------+---------------------------+");
      message("\n| %-85s| %-25s |", "NAME", "VALUE");
      message("\n+--------------------------------------------------------------------------------------+---------------------------+");
      for (Entry<String, String> p : values.entrySet()) {
        message("\n| %-85s| %-25s |", p.getKey(), p.getValue());
      }
    } else {
      // LOCAL STORAGE
      message("\nLOCAL SERVER CONFIGURATION:");
      message("\n+--------------------------------------------------------------------------------------+---------------------------+");
      message("\n| %-85s| %-25s |", "NAME", "VALUE");
      message("\n+--------------------------------------------------------------------------------------+---------------------------+");
      for (OGlobalConfiguration cfg : OGlobalConfiguration.values()) {
        message("\n| %-85s| %-25s |", cfg.getKey(), cfg.getValue());
      }
    }

    message("\n+--------------------------------------------------------------------------------------+---------------------------+");
  }

  /** Should be used only by console commands */
  public ODatabaseDocument getCurrentDatabase() {
    return currentDatabase;
  }

  /**
   * Pass an existent database instance to be used as current.
   */
  public OConsoleDatabaseApp setCurrentDatabase(final ODatabaseDocumentTx iCurrentDatabase) {
    currentDatabase = iCurrentDatabase;
    currentDatabaseName = iCurrentDatabase.getName();
    return this;
  }

  /** Should be used only by console commands */
  public String getCurrentDatabaseName() {
    return currentDatabaseName;
  }

  /** Should be used only by console commands */
  public String getCurrentDatabaseUserName() {
    return currentDatabaseUserName;
  }

  /** Should be used only by console commands */
  public String getCurrentDatabaseUserPassword() {
    return currentDatabaseUserPassword;
  }

  /** Should be used only by console commands */
  public ORecord getCurrentRecord() {
    return currentRecord;
  }

  /** Should be used only by console commands */
  public List<OIdentifiable> getCurrentResultSet() {
    return currentResultSet;
  }

  /** Should be used only by console commands */
  public void loadRecordInternal(String iRecordId, String iFetchPlan) {
    checkForDatabase();

    currentRecord = currentDatabase.load(new ORecordId(iRecordId), iFetchPlan);
    displayRecord(null);

    message("\nOK");
  }

  /** Should be used only by console commands */
  public void reloadRecordInternal(String iRecordId, String iFetchPlan) {
    checkForDatabase();

    currentRecord = currentDatabase.executeReadRecord(new ORecordId(iRecordId), null, -1, iFetchPlan, true, false, false,
        OStorage.LOCKING_STRATEGY.NONE, new ODatabaseDocumentTx.SimpleRecordReader());
    displayRecord(null);

    message("\nOK");
  }

  /** Should be used only by console commands */
  protected void checkForRemoteServer() {
    if (serverAdmin == null
        && (currentDatabase == null || !(currentDatabase.getStorage() instanceof OStorageRemoteThread) || currentDatabase
            .isClosed()))
      throw new OSystemException(
          "Remote server is not connected. Use 'connect remote:<host>[:<port>][/<database-name>]' to connect");
  }

  /** Should be used only by console commands */
  protected void checkForDatabase() {
    if (currentDatabase == null)
      throw new OSystemException("Database not selected. Use 'connect <database-name>' to connect to a database.");
    if (currentDatabase.isClosed())
      throw new ODatabaseException("Database '" + currentDatabaseName + "' is closed");
  }

  /** Should be used only by console commands */
  protected void checkCurrentObject() {
    if (currentRecord == null)
      throw new OSystemException("The is no current object selected: create a new one or load it");
  }

  public String ask(final String iText) {
    out.print(iText);
    final Scanner scanner = new Scanner(in);
    final String answer = scanner.nextLine();
    scanner.close();
    return answer;
  }

  public void onMessage(final String iText) {
    message(iText);
  }

  @Override
  public void onBegin(final Object iTask, final long iTotal, Object metadata) {
    lastPercentStep = 0;

    message("[");
    if (interactiveMode) {
      for (int i = 0; i < 10; ++i)
        message(" ");
      message("]   0%");
    }
  }

  public boolean onProgress(final Object iTask, final long iCounter, final float iPercent) {
    final int completitionBar = (int) iPercent / 10;

    if (((int) (iPercent * 10)) == lastPercentStep)
      return true;

    final StringBuilder buffer = new StringBuilder(64);

    if (interactiveMode) {
      buffer.append("\r[");
      for (int i = 0; i < completitionBar; ++i)
        buffer.append('=');
      for (int i = completitionBar; i < 10; ++i)
        buffer.append(' ');
      message("] %3.1f%% ", iPercent);
    } else {
      for (int i = lastPercentStep / 100; i < completitionBar; ++i)
        buffer.append('=');
    }

    message(buffer.toString());

    lastPercentStep = (int) (iPercent * 10);
    return true;
  }

  @ConsoleCommand(description = "Display the current path")
  public void pwd() {
    message("\nCurrent path: " + new File("").getAbsolutePath());
  }

  public void onCompletition(final Object iTask, final boolean iSucceed) {
    if (interactiveMode)
      if (iSucceed)
        message("\r[==========] 100% Done.");
      else
        message(" Error!");
    else
      message(iSucceed ? "] Done." : " Error!");
  }

  /**
   * Closes the console freeing all the used resources.
   */
  public void close() {
    if (currentDatabase != null) {
      currentDatabase.activateOnCurrentThread();
      currentDatabase.close();
      currentDatabase = null;
    }

    if (serverAdmin != null) {
      serverAdmin.close(true);
      serverAdmin = null;
    }

    currentResultSet = null;
    currentRecord = null;
    currentResult = null;
    commandBuffer.setLength(0);
  }

  /**
   * Checks if the link must be fixed.
   *
   * @param fieldValue
   *          Field containing the OIdentifiable (RID or Record)
   * @return true to fix it, otherwise false
   */
  protected boolean fixLink(final Object fieldValue) {
    if (fieldValue instanceof OIdentifiable) {
      final ORID id = ((OIdentifiable) fieldValue).getIdentity();

      if (id.isValid())
        if (id.isPersistent()) {
          final ORecord connected = ((OIdentifiable) fieldValue).getRecord();
          if (connected == null)
            return true;
        } else
          return true;
    }
    return false;
  }

  protected void dumpDistributedConfiguration(final boolean iForce) {
    if (currentDatabase == null)
      return;

    final OStorage stg = currentDatabase.getStorage();
    if (stg instanceof OStorageRemoteThread) {
      final ODocument distributedCfg = ((OStorageRemoteThread) stg).getClusterConfiguration();
      if (distributedCfg != null && !distributedCfg.isEmpty()) {
        message("\n\nDISTRIBUTED CONFIGURATION:\n" + distributedCfg.toJSON("prettyPrint"));
      } else if (iForce)
        message("\n\nDISTRIBUTED CONFIGURATION: none (OrientDB is running in standalone mode)");
    }
  }

  @Override
  protected boolean isCollectingCommands(final String iLine) {
    return iLine.startsWith("js") || iLine.startsWith("script");
  }

  @Override
  protected void onBefore() {
    super.onBefore();

    setResultset(new ArrayList<OIdentifiable>());

    // DISABLE THE NETWORK AND STORAGE TIMEOUTS
    properties.put("limit", "20");
    properties.put("width", "150");
    properties.put("debug", "false");
    properties.put("collectionMaxItems", "10");
    properties.put("maxBinaryDisplay", "150");
    properties.put("verbose", "2");
    properties.put("ignoreErrors", "false");
    properties.put("backupCompressionLevel", "9"); // 9 = MAX
    properties.put("backupBufferSize", "1048576"); // 1MB
  }

  protected OIdentifiable setCurrentRecord(final int iIndex) {
    currentRecordIdx = iIndex;
    if (iIndex < currentResultSet.size())
      currentRecord = (ORecord) currentResultSet.get(iIndex);
    else
      currentRecord = null;
    return currentRecord;
  }

  protected void printApplicationInfo() {
    message("\nOrientDB console v." + OConstants.getVersion() + " " + OConstants.ORIENT_URL);
    message("\nType 'help' to display all the supported commands.");
  }

  protected void dumpResultSet(final int limit) {
    new OTableFormatter(this).setMaxWidthSize(getWindowSize()).setMaxMultiValueEntries(getMaxMultiValueEntries())
        .writeRecords(currentResultSet, limit);
  }

  protected float getElapsedSecs(final long start) {
    return (float) (System.currentTimeMillis() - start) / 1000;
  }

  protected void printError(final Exception e) {
    if (properties.get("debug") != null && Boolean.parseBoolean(properties.get("debug"))) {
      message("\n\n!ERROR:");
      e.printStackTrace(err);
    } else {
      // SHORT FORM
      message("\n\n!ERROR: " + e.getMessage());

      if (e.getCause() != null) {
        Throwable t = e.getCause();
        while (t != null) {
          message("\n-> " + t.getMessage());
          t = t.getCause();
        }
      }
    }
  }

  protected void updateDatabaseInfo() {
    currentDatabase.getStorage().reload();
    currentDatabase.getMetadata().getSchema().reload();
    currentDatabase.getMetadata().getIndexManager().reload();
  }

  @Override
  protected String getContext() {
    if (currentDatabase != null && currentDatabaseName != null) {
      currentDatabase.activateOnCurrentThread();

      final StringBuilder buffer = new StringBuilder(64);
      buffer.append(" {db=");
      buffer.append(currentDatabaseName);
      if (currentDatabase.getTransaction().isActive()) {
        buffer.append(" tx=[");
        buffer.append(currentDatabase.getTransaction().getEntryCount());
        buffer.append(" entries]");
      }

      buffer.append("}");
      return buffer.toString();
    } else if (serverAdmin != null)
      return " {server=" + serverAdmin.getURL() + "}";
    return "";
  }

  @Override
  protected String getPrompt() {
    return String.format("orientdb%s> ", getContext());
  }

  protected void parseResult() {
    setResultset(null);

    if (currentResult instanceof Map<?, ?>)
      return;

    final Object first = OMultiValue.getFirstValue(currentResult);

    if (first instanceof OIdentifiable) {
      if (currentResult instanceof List<?>)
        currentResultSet = (List<OIdentifiable>) currentResult;
      else if (currentResult instanceof Collection<?>) {
        currentResultSet = new ArrayList<OIdentifiable>();
        currentResultSet.addAll((Collection<? extends OIdentifiable>) currentResult);
      } else if (currentResult.getClass().isArray()) {
        currentResultSet = new ArrayList<OIdentifiable>();
        Collections.addAll(currentResultSet, (OIdentifiable[]) currentResult);
      }

      setResultset(currentResultSet);
    }
  }

  protected void setResultset(final List<OIdentifiable> iResultset) {
    currentResultSet = iResultset;
    currentRecordIdx = 0;
    currentRecord = iResultset == null || iResultset.isEmpty() ? null : (ORecord) iResultset.get(0).getRecord();
  }

  protected void resetResultSet() {
    currentResultSet = null;
    currentRecord = null;
  }

  protected void executeServerSideScript(final String iLanguage, final String iText) {
    if (iText == null)
      return;

    resetResultSet();

    long start = System.currentTimeMillis();
    currentResult = currentDatabase.command(new OCommandScript(iLanguage, iText)).execute();
    float elapsedSeconds = getElapsedSecs(start);

    parseResult();
    if (currentResultSet != null) {
      dumpResultSet(-1);
      message("\nServer side script executed in %f sec(s). Returned %d records", elapsedSeconds, currentResultSet.size());
    } else {
      String lineFeed = currentResult instanceof Map<?, ?> ? "\n" : "";
      message("\nServer side script executed in %f sec(s). Value returned is: %s%s", elapsedSeconds, lineFeed, currentResult);
    }
  }

  protected Map<String, List<String>> parseOptions(final String iOptions) {
    final Map<String, List<String>> options = new HashMap<String, List<String>>();
    if (iOptions != null) {
      final List<String> opts = OStringSerializerHelper.smartSplit(iOptions, ' ');
      for (String o : opts) {
        final int sep = o.indexOf('=');
        if (sep == -1) {
          OLogManager.instance().warn(this, "Unrecognized option %s, skipped", o);
          continue;
        }

        final String option = o.substring(0, sep);
        final List<String> items = OStringSerializerHelper.smartSplit(o.substring(sep + 1), ' ');

        options.put(option, items);
      }
    }
    return options;
  }

  protected int getWindowSize() {
    if (properties.containsKey("width"))
      return Integer.parseInt(properties.get("width"));
    return windowSize;
  }

  public int getMaxMultiValueEntries() {
    if (properties.containsKey("maxMultiValueEntries"))
      return Integer.parseInt(properties.get("maxMultiValueEntries"));
    return maxMultiValueEntries;
  }

  private void dumpRecordDetails() {
    if (currentRecord == null)
      return;
    else if (currentRecord instanceof ODocument) {
      ODocument rec = (ODocument) currentRecord;
      message("\n+-------------------------------------------------------------------------------------------------+");
      message("\n| Document - @class: %-37s @rid: %-15s @version: %-6d |", rec.getClassName(), rec.getIdentity().toString(), rec
          .getVersion());
      message("\n+-------------------------------------------------------------------------------------------------+");
      message("\n| %24s | %-68s |", "Name", "Value");
      message("\n+-------------------------------------------------------------------------------------------------+");
      Object value;
      for (String fieldName : rec.fieldNames()) {
        value = rec.field(fieldName);
        if (value instanceof byte[])
          value = "byte[" + ((byte[]) value).length + "]";
        else if (value instanceof Iterator<?>) {
          final List<Object> coll = new ArrayList<Object>();
          while (((Iterator<?>) value).hasNext())
            coll.add(((Iterator<?>) value).next());
          value = coll;
        } else if (OMultiValue.isMultiValue(value)) {
          value = OTableFormatter.getPrettyFieldMultiValue(OMultiValue.getMultiValueIterator(value), getMaxMultiValueEntries());
        }

        message("\n| %24s | %-68s |", fieldName, value);
      }

    } else if (currentRecord instanceof ORecordBytes) {
      ORecordBytes rec = (ORecordBytes) currentRecord;
      message("\n+-------------------------------------------------------------------------------------------------+");
      message("\n| Bytes    - @rid: %s @version: %d", rec.getIdentity().toString(), rec.getVersion());
      message("\n+-------------------------------------------------------------------------------------------------+");

      final byte[] value = rec.toStream();
      final int max = Math.min(Integer.parseInt(properties.get("maxBinaryDisplay")), Array.getLength(value));
      for (int i = 0; i < max; ++i) {
        message("%03d", Array.getByte(value, i));
      }

    } else {
      message("\n+-------------------------------------------------------------------------------------------------+");
      message("\n| %s - record id: %s   v.%d", currentRecord.getClass().getSimpleName(), currentRecord.getIdentity().toString(),
          currentRecord.getVersion());
    }
    message("\n+-------------------------------------------------------------------------------------------------+");
    out.println();
  }

  private void printSupportedSerializerFormat() {
    message("\nSupported formats are:");

    for (ORecordSerializer s : ORecordSerializerFactory.instance().getFormats()) {
      if (s instanceof ORecordSerializerStringAbstract)
        message("\n- " + s.toString());
    }
  }

  private void browseRecords(final int limit, final OIdentifiableIterator<?> it) {
    final OTableFormatter tableFormatter = new OTableFormatter(this).setMaxWidthSize(getWindowSize()).setMaxMultiValueEntries(
        maxMultiValueEntries);

    setResultset(new ArrayList<OIdentifiable>());
    while (it.hasNext() && currentResultSet.size() <= limit)
      currentResultSet.add(it.next());

    tableFormatter.writeRecords(currentResultSet, limit);
  }

  private Object sqlCommand(final String iExpectedCommand, String iReceivedCommand, final String iMessage,
      final boolean iIncludeResult) {
    checkForDatabase();

    if (iReceivedCommand == null)
      return null;

    iReceivedCommand = iExpectedCommand + " " + iReceivedCommand.trim();

    resetResultSet();

    final long start = System.currentTimeMillis();

    final Object result = new OCommandSQL(iReceivedCommand).setProgressListener(this).execute();

    float elapsedSeconds = getElapsedSecs(start);

    if (iIncludeResult)
      message(iMessage, result, elapsedSeconds);
    else
      message(iMessage, elapsedSeconds);

    return result;
  }
}