Ejemplo n.º 1
0
/**
 * Internal iterator so we can page through the class. This is used by the {@link Dao#iterator}
 * methods.
 *
 * @param <T> The class that the code will be operating on.
 * @param <ID> The class of the ID column associated with the class. The T class does not require an
 *     ID field. The class needs an ID parameter however so you can use Void or Object to satisfy
 *     the compiler.
 * @author graywatson
 */
public class SelectIterator<T, ID> implements CloseableIterator<T> {

  private static final Logger logger = LoggerFactory.getLogger(SelectIterator.class);

  private final Class<?> dataClass;
  private final Dao<T, ID> classDao;
  private final ConnectionSource connectionSource;
  private final DatabaseConnection connection;
  private final CompiledStatement compiledStmt;
  private final DatabaseResults results;
  private final GenericRowMapper<T> rowMapper;
  private final String statement;
  private boolean first = true;
  private boolean closed;
  private boolean alreadyMoved;
  private T last;
  private int rowC;

  /** If the statement parameter is null then this won't log information */
  public SelectIterator(
      Class<?> dataClass,
      Dao<T, ID> classDao,
      GenericRowMapper<T> rowMapper,
      ConnectionSource connectionSource,
      DatabaseConnection connection,
      CompiledStatement compiledStmt,
      String statement,
      ObjectCache objectCache)
      throws SQLException {
    this.dataClass = dataClass;
    this.classDao = classDao;
    this.rowMapper = rowMapper;
    this.connectionSource = connectionSource;
    this.connection = connection;
    this.compiledStmt = compiledStmt;
    this.results = compiledStmt.runQuery(objectCache);
    this.statement = statement;
    if (statement != null) {
      logger.debug("starting iterator @{} for '{}'", hashCode(), statement);
    }
  }

  /**
   * Returns whether or not there are any remaining objects in the table. Can be called before
   * next().
   *
   * @throws SQLException If there was a problem getting more results via SQL.
   */
  public boolean hasNextThrow() throws SQLException {
    if (closed) {
      return false;
    }
    if (alreadyMoved) {
      // we do this so multiple hasNext() calls can be made, result would be true or closed is true
      return true;
    }
    boolean result;
    if (first) {
      first = false;
      result = results.first();
    } else {
      result = results.next();
    }
    if (!result) {
      close();
    }
    alreadyMoved = true;
    return result;
  }

  /**
   * Returns whether or not there are any remaining objects in the table. Can be called before
   * next().
   *
   * @throws IllegalStateException If there was a problem getting more results via SQL.
   */
  public boolean hasNext() {
    try {
      return hasNextThrow();
    } catch (SQLException e) {
      last = null;
      closeQuietly();
      // unfortunately, can't propagate back the SQLException
      throw new IllegalStateException("Errors getting more results of " + dataClass, e);
    }
  }

  public T first() throws SQLException {
    if (closed) {
      return null;
    }
    first = false;
    if (results.first()) {
      return getCurrent();
    } else {
      return null;
    }
  }

  public T previous() throws SQLException {
    if (closed) {
      return null;
    }
    first = false;
    if (results.previous()) {
      return getCurrent();
    } else {
      return null;
    }
  }

  public T current() throws SQLException {
    if (closed) {
      return null;
    }
    if (first) {
      return first();
    } else {
      return getCurrent();
    }
  }

  public T nextThrow() throws SQLException {
    if (closed) {
      return null;
    }
    if (!alreadyMoved) {
      boolean hasResult;
      if (first) {
        first = false;
        hasResult = results.first();
      } else {
        hasResult = results.next();
      }
      // move forward
      if (!hasResult) {
        first = false;
        return null;
      }
    }
    first = false;
    return getCurrent();
  }

  /**
   * Returns the next object in the table.
   *
   * @throws IllegalStateException If there was a problem extracting the object from SQL.
   */
  public T next() {
    SQLException sqlException = null;
    try {
      T result = nextThrow();
      if (result != null) {
        return result;
      }
    } catch (SQLException e) {
      sqlException = e;
    }
    // we have to throw if there is no next or on a SQLException
    last = null;
    closeQuietly();
    throw new IllegalStateException("Could not get next result for " + dataClass, sqlException);
  }

  public T moveRelative(int offset) throws SQLException {
    if (closed) {
      return null;
    }
    first = false;
    if (results.moveRelative(offset)) {
      return getCurrent();
    } else {
      return null;
    }
  }

  /**
   * Removes the last object returned by next() by calling delete on the dao associated with the
   * object.
   *
   * @throws IllegalStateException If there was no previous next() call.
   * @throws SQLException If the delete failed.
   */
  public void removeThrow() throws SQLException {
    if (last == null) {
      throw new IllegalStateException(
          "No last " + dataClass + " object to remove. Must be called after a call to next.");
    }
    if (classDao == null) {
      // we may never be able to get here since it should only be null for queryForAll methods
      throw new IllegalStateException(
          "Cannot remove " + dataClass + " object because classDao not initialized");
    }
    try {
      classDao.delete(last);
    } finally {
      // if we've try to delete it, clear the last marker
      last = null;
    }
  }

  /**
   * Removes the last object returned by next() by calling delete on the dao associated with the
   * object.
   *
   * @throws IllegalStateException If there was no previous next() call or if delete() throws a
   *     SQLException (set as the cause).
   */
  public void remove() {
    try {
      removeThrow();
    } catch (SQLException e) {
      closeQuietly();
      // unfortunately, can't propagate back the SQLException
      throw new IllegalStateException("Could not delete " + dataClass + " object " + last, e);
    }
  }

  public void close() throws SQLException {
    if (!closed) {
      compiledStmt.close();
      closed = true;
      last = null;
      if (statement != null) {
        logger.debug("closed iterator @{} after {} rows", hashCode(), rowC);
      }
      connectionSource.releaseConnection(connection);
    }
  }

  public void closeQuietly() {
    try {
      close();
    } catch (SQLException e) {
      // ignore it
    }
  }

  public DatabaseResults getRawResults() {
    return results;
  }

  public void moveToNext() {
    last = null;
    first = false;
    alreadyMoved = false;
  }

  private T getCurrent() throws SQLException {
    last = rowMapper.mapRow(results);
    alreadyMoved = false;
    rowC++;
    return last;
  }
}
/**
 * Database connection for Android.
 *
 * @author kevingalligan, graywatson
 */
public class AndroidDatabaseConnection implements DatabaseConnection {

  private static final String ANDROID_VERSION = "VERSION__4.49-SNAPSHOT__";

  private static Logger logger = LoggerFactory.getLogger(AndroidDatabaseConnection.class);
  private static final String[] NO_STRING_ARGS = new String[0];

  private final SQLiteDatabase db;
  private final boolean readWrite;
  private final boolean cancelQueriesEnabled;

  static {
    VersionUtils.checkCoreVersusAndroidVersions(ANDROID_VERSION);
  }

  public AndroidDatabaseConnection(SQLiteDatabase db, boolean readWrite) {
    this(db, readWrite, false);
  }

  public AndroidDatabaseConnection(
      SQLiteDatabase db, boolean readWrite, boolean cancelQueriesEnabled) {
    this.db = db;
    this.readWrite = readWrite;
    this.cancelQueriesEnabled = cancelQueriesEnabled;
    logger.trace("{}: db {} opened, read-write = {}", this, db, readWrite);
  }

  public boolean isAutoCommitSupported() {
    return true;
  }

  public boolean isAutoCommit() throws SQLException {
    try {
      boolean inTransaction = db.inTransaction();
      logger.trace("{}: in transaction is {}", this, inTransaction);
      // You have to explicitly commit your transactions, so this is sort of correct
      return !inTransaction;
    } catch (android.database.SQLException e) {
      throw SqlExceptionUtil.create("problems getting auto-commit from database", e);
    }
  }

  public void setAutoCommit(boolean autoCommit) {
    /*
     * Sqlite does not support auto-commit. The various JDBC drivers seem to implement it with the use of a
     * transaction. That's what we are doing here.
     */
    if (autoCommit) {
      if (db.inTransaction()) {
        db.setTransactionSuccessful();
        db.endTransaction();
      }
    } else {
      if (!db.inTransaction()) {
        db.beginTransaction();
      }
    }
  }

  public Savepoint setSavePoint(String name) throws SQLException {
    try {
      db.beginTransaction();
      logger.trace("{}: save-point set with name {}", this, name);
      return new OurSavePoint(name);
    } catch (android.database.SQLException e) {
      throw SqlExceptionUtil.create("problems beginning transaction " + name, e);
    }
  }

  /** Return whether this connection is read-write or not (real-only). */
  public boolean isReadWrite() {
    return readWrite;
  }

  public void commit(Savepoint savepoint) throws SQLException {
    try {
      db.setTransactionSuccessful();
      db.endTransaction();
      if (savepoint == null) {
        logger.trace("{}: transaction is successfuly ended", this);
      } else {
        logger.trace("{}: transaction {} is successfuly ended", this, savepoint.getSavepointName());
      }
    } catch (android.database.SQLException e) {
      if (savepoint == null) {
        throw SqlExceptionUtil.create("problems commiting transaction", e);
      } else {
        throw SqlExceptionUtil.create(
            "problems commiting transaction " + savepoint.getSavepointName(), e);
      }
    }
  }

  public void rollback(Savepoint savepoint) throws SQLException {
    try {
      // no setTransactionSuccessful() means it is a rollback
      db.endTransaction();
      if (savepoint == null) {
        logger.trace("{}: transaction is ended, unsuccessfuly", this);
      } else {
        logger.trace(
            "{}: transaction {} is ended, unsuccessfuly", this, savepoint.getSavepointName());
      }
    } catch (android.database.SQLException e) {
      if (savepoint == null) {
        throw SqlExceptionUtil.create("problems rolling back transaction", e);
      } else {
        throw SqlExceptionUtil.create(
            "problems rolling back transaction " + savepoint.getSavepointName(), e);
      }
    }
  }

  public int executeStatement(String statementStr, int resultFlags) throws SQLException {
    return AndroidCompiledStatement.execSql(db, statementStr, statementStr, NO_STRING_ARGS);
  }

  public CompiledStatement compileStatement(
      String statement, StatementType type, FieldType[] argFieldTypes, int resultFlags) {
    // resultFlags argument is not used in Android-land since the {@link Cursor} is bi-directional.
    CompiledStatement stmt =
        new AndroidCompiledStatement(statement, db, type, cancelQueriesEnabled);
    logger.trace("{}: compiled statement got {}: {}", this, stmt, statement);
    return stmt;
  }

  public int insert(
      String statement, Object[] args, FieldType[] argFieldTypes, GeneratedKeyHolder keyHolder)
      throws SQLException {
    SQLiteStatement stmt = null;
    try {
      stmt = db.compileStatement(statement);
      bindArgs(stmt, args, argFieldTypes);
      long rowId = stmt.executeInsert();
      if (keyHolder != null) {
        keyHolder.addKey(rowId);
      }
      /*
       * I've decided to not do the CHANGES() statement here like we do down below in UPDATE because we know that
       * it worked (since it didn't throw) so we know that 1 is right.
       */
      int result = 1;
      logger.trace(
          "{}: insert statement is compiled and executed, changed {}: {}", this, result, statement);
      return result;
    } catch (android.database.SQLException e) {
      throw SqlExceptionUtil.create("inserting to database failed: " + statement, e);
    } finally {
      closeQuietly(stmt);
    }
  }

  public int update(String statement, Object[] args, FieldType[] argFieldTypes)
      throws SQLException {
    return update(statement, args, argFieldTypes, "updated");
  }

  public int delete(String statement, Object[] args, FieldType[] argFieldTypes)
      throws SQLException {
    // delete is the same as update
    return update(statement, args, argFieldTypes, "deleted");
  }

  public <T> Object queryForOne(
      String statement,
      Object[] args,
      FieldType[] argFieldTypes,
      GenericRowMapper<T> rowMapper,
      ObjectCache objectCache)
      throws SQLException {
    Cursor cursor = null;
    try {
      cursor = db.rawQuery(statement, toStrings(args));
      AndroidDatabaseResults results = new AndroidDatabaseResults(cursor, objectCache);
      logger.trace("{}: queried for one result: {}", this, statement);
      if (!results.first()) {
        return null;
      } else {
        T first = rowMapper.mapRow(results);
        if (results.next()) {
          return MORE_THAN_ONE;
        } else {
          return first;
        }
      }
    } catch (android.database.SQLException e) {
      throw SqlExceptionUtil.create("queryForOne from database failed: " + statement, e);
    } finally {
      closeQuietly(cursor);
    }
  }

  public long queryForLong(String statement) throws SQLException {
    SQLiteStatement stmt = null;
    try {
      stmt = db.compileStatement(statement);
      long result = stmt.simpleQueryForLong();
      logger.trace("{}: query for long simple query returned {}: {}", this, result, statement);
      return result;
    } catch (android.database.SQLException e) {
      throw SqlExceptionUtil.create("queryForLong from database failed: " + statement, e);
    } finally {
      closeQuietly(stmt);
    }
  }

  public long queryForLong(String statement, Object[] args, FieldType[] argFieldTypes)
      throws SQLException {
    Cursor cursor = null;
    AndroidDatabaseResults results = null;
    try {
      cursor = db.rawQuery(statement, toStrings(args));
      results = new AndroidDatabaseResults(cursor, null);
      long result;
      if (results.first()) {
        result = results.getLong(0);
      } else {
        result = 0L;
      }
      logger.trace("{}: query for long raw query returned {}: {}", this, result, statement);
      return result;
    } catch (android.database.SQLException e) {
      throw SqlExceptionUtil.create("queryForLong from database failed: " + statement, e);
    } finally {
      closeQuietly(cursor);
      IOUtils.closeQuietly(results);
    }
  }

  public void close() throws IOException {
    try {
      db.close();
      logger.trace("{}: db {} closed", this, db);
    } catch (android.database.SQLException e) {
      throw new IOException("problems closing the database connection", e);
    }
  }

  public void closeQuietly() {
    IOUtils.closeQuietly(this);
  }

  public boolean isClosed() throws SQLException {
    try {
      boolean isOpen = db.isOpen();
      logger.trace("{}: db {} isOpen returned {}", this, db, isOpen);
      return !isOpen;
    } catch (android.database.SQLException e) {
      throw SqlExceptionUtil.create("problems detecting if the database is closed", e);
    }
  }

  public boolean isTableExists(String tableName) {
    Cursor cursor =
        db.rawQuery(
            "SELECT DISTINCT tbl_name FROM sqlite_master WHERE tbl_name = '" + tableName + "'",
            null);
    try {
      boolean result;
      if (cursor.getCount() > 0) {
        result = true;
      } else {
        result = false;
      }
      logger.trace("{}: isTableExists '{}' returned {}", this, tableName, result);
      return result;
    } finally {
      cursor.close();
    }
  }

  private int update(String statement, Object[] args, FieldType[] argFieldTypes, String label)
      throws SQLException {
    SQLiteStatement stmt = null;
    try {
      stmt = db.compileStatement(statement);
      bindArgs(stmt, args, argFieldTypes);
      stmt.execute();
    } catch (android.database.SQLException e) {
      throw SqlExceptionUtil.create("updating database failed: " + statement, e);
    } finally {
      closeQuietly(stmt);
      stmt = null;
    }
    int result;
    try {
      stmt = db.compileStatement("SELECT CHANGES()");
      result = (int) stmt.simpleQueryForLong();
    } catch (android.database.SQLException e) {
      // ignore the exception and just return 1
      result = 1;
    } finally {
      closeQuietly(stmt);
    }
    logger.trace("{} statement is compiled and executed, changed {}: {}", label, result, statement);
    return result;
  }

  private void bindArgs(SQLiteStatement stmt, Object[] args, FieldType[] argFieldTypes)
      throws SQLException {
    if (args == null) {
      return;
    }
    for (int i = 0; i < args.length; i++) {
      Object arg = args[i];
      if (arg == null) {
        stmt.bindNull(i + 1);
      } else {
        SqlType sqlType = argFieldTypes[i].getSqlType();
        switch (sqlType) {
          case STRING:
          case LONG_STRING:
          case CHAR:
            stmt.bindString(i + 1, arg.toString());
            break;
          case BOOLEAN:
          case BYTE:
          case SHORT:
          case INTEGER:
          case LONG:
            stmt.bindLong(i + 1, ((Number) arg).longValue());
            break;
          case FLOAT:
          case DOUBLE:
            stmt.bindDouble(i + 1, ((Number) arg).doubleValue());
            break;
          case BYTE_ARRAY:
          case SERIALIZABLE:
            stmt.bindBlob(i + 1, (byte[]) arg);
            break;
          case DATE:
            // this is mapped to a STRING under Android
          case BLOB:
            // this is only for derby serializable
          case BIG_DECIMAL:
            // this should be handled as a STRING
            throw new SQLException("Invalid Android type: " + sqlType);
          case UNKNOWN:
          default:
            throw new SQLException("Unknown sql argument type: " + sqlType);
        }
      }
    }
  }

  private String[] toStrings(Object[] args) {
    if (args == null || args.length == 0) {
      return null;
    }
    String[] strings = new String[args.length];
    for (int i = 0; i < args.length; i++) {
      Object arg = args[i];
      if (arg == null) {
        strings[i] = null;
      } else {
        strings[i] = arg.toString();
      }
    }

    return strings;
  }

  @Override
  public String toString() {
    return getClass().getSimpleName() + "@" + Integer.toHexString(super.hashCode());
  }

  /** We can't use IOUtils here because older versions didn't implement Closeable. */
  private void closeQuietly(Cursor cursor) {
    if (cursor != null) {
      cursor.close();
    }
  }

  /** We can't use IOUtils here because older versions didn't implement Closeable. */
  private void closeQuietly(SQLiteStatement statement) {
    if (statement != null) {
      statement.close();
    }
  }

  private static class OurSavePoint implements Savepoint {

    private String name;

    public OurSavePoint(String name) {
      this.name = name;
    }

    public int getSavepointId() {
      return 0;
    }

    public String getSavepointName() {
      return name;
    }
  }
}
/**
 * Proxy to a {@link Dao} that wraps each Exception and rethrows it as RuntimeException. You can use
 * this if your usage pattern is to ignore all exceptions. That's not a pattern that I like so it's
 * not the default.
 *
 * <pre>
 * RuntimeExceptionDao&lt;Account, String&gt; accountDao = RuntimeExceptionDao.createDao(connectionSource, Account.class);
 * </pre>
 *
 * @author graywatson
 */
public class RuntimeExceptionDao<T, ID> implements CloseableIterable<T> {

  /*
   * We use debug here because we don't want these messages to be logged by default. The user will need to turn on
   * logging for this class to DEBUG to see the messages.
   */
  private static final Level LOG_LEVEL = Level.DEBUG;

  private Dao<T, ID> dao;
  private static final Logger logger = LoggerFactory.getLogger(RuntimeExceptionDao.class);

  public RuntimeExceptionDao(Dao<T, ID> dao) {
    this.dao = dao;
  }

  /**
   * Call through to {@link DaoManager#createDao(ConnectionSource, Class)} with the returned DAO
   * wrapped in a RuntimeExceptionDao.
   */
  public static <T, ID> RuntimeExceptionDao<T, ID> createDao(
      ConnectionSource connectionSource, Class<T> clazz) throws SQLException {
    @SuppressWarnings("unchecked")
    Dao<T, ID> castDao = (Dao<T, ID>) DaoManager.createDao(connectionSource, clazz);
    return new RuntimeExceptionDao<T, ID>(castDao);
  }

  /**
   * Call through to {@link DaoManager#createDao(ConnectionSource, DatabaseTableConfig)} with the
   * returned DAO wrapped in a RuntimeExceptionDao.
   */
  public static <T, ID> RuntimeExceptionDao<T, ID> createDao(
      ConnectionSource connectionSource, DatabaseTableConfig<T> tableConfig) throws SQLException {
    @SuppressWarnings("unchecked")
    Dao<T, ID> castDao = (Dao<T, ID>) DaoManager.createDao(connectionSource, tableConfig);
    return new RuntimeExceptionDao<T, ID>(castDao);
  }

  /** @see Dao#queryForId(Object) */
  public T queryForId(ID id) {
    try {
      return dao.queryForId(id);
    } catch (SQLException e) {
      logMessage(e, "queryForId threw exception on: " + id);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#queryForFirst(PreparedQuery) */
  public T queryForFirst(PreparedQuery<T> preparedQuery) {
    try {
      return dao.queryForFirst(preparedQuery);
    } catch (SQLException e) {
      logMessage(e, "queryForFirst threw exception on: " + preparedQuery);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#queryForAll() */
  public List<T> queryForAll() {
    try {
      return dao.queryForAll();
    } catch (SQLException e) {
      logMessage(e, "queryForAll threw exception");
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#queryForEq(String, Object) */
  public List<T> queryForEq(String fieldName, Object value) {
    try {
      return dao.queryForEq(fieldName, value);
    } catch (SQLException e) {
      logMessage(e, "queryForEq threw exception on: " + fieldName);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#queryForMatching(Object) */
  public List<T> queryForMatching(T matchObj) {
    try {
      return dao.queryForMatching(matchObj);
    } catch (SQLException e) {
      logMessage(e, "queryForMatching threw exception on: " + matchObj);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#queryForMatchingArgs(Object) */
  public List<T> queryForMatchingArgs(T matchObj) {
    try {
      return dao.queryForMatchingArgs(matchObj);
    } catch (SQLException e) {
      logMessage(e, "queryForMatchingArgs threw exception on: " + matchObj);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#queryForFieldValues(Map) */
  public List<T> queryForFieldValues(Map<String, Object> fieldValues) {
    try {
      return dao.queryForFieldValues(fieldValues);
    } catch (SQLException e) {
      logMessage(e, "queryForFieldValues threw exception");
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#queryForFieldValuesArgs(Map) */
  public List<T> queryForFieldValuesArgs(Map<String, Object> fieldValues) {
    try {
      return dao.queryForFieldValuesArgs(fieldValues);
    } catch (SQLException e) {
      logMessage(e, "queryForFieldValuesArgs threw exception");
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#queryForSameId(Object) */
  public T queryForSameId(T data) {
    try {
      return dao.queryForSameId(data);
    } catch (SQLException e) {
      logMessage(e, "queryForSameId threw exception on: " + data);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#queryBuilder() */
  public QueryBuilder<T, ID> queryBuilder() {
    return dao.queryBuilder();
  }

  /** @see Dao#updateBuilder() */
  public UpdateBuilder<T, ID> updateBuilder() {
    return dao.updateBuilder();
  }

  /** @see Dao#deleteBuilder() */
  public DeleteBuilder<T, ID> deleteBuilder() {
    return dao.deleteBuilder();
  }

  /** @see Dao#query(PreparedQuery) */
  public List<T> query(PreparedQuery<T> preparedQuery) {
    try {
      return dao.query(preparedQuery);
    } catch (SQLException e) {
      logMessage(e, "query threw exception on: " + preparedQuery);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#create(Object) */
  public int create(T data) {
    try {
      return dao.create(data);
    } catch (SQLException e) {
      logMessage(e, "create threw exception on: " + data);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#create(Collection) */
  public int create(Collection<T> datas) {
    try {
      return dao.create(datas);
    } catch (SQLException e) {
      logMessage(e, "create threw exception on: " + datas);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#createIfNotExists(Object) */
  public T createIfNotExists(T data) {
    try {
      return dao.createIfNotExists(data);
    } catch (SQLException e) {
      logMessage(e, "createIfNotExists threw exception on: " + data);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#createOrUpdate(Object) */
  public CreateOrUpdateStatus createOrUpdate(T data) {
    try {
      return dao.createOrUpdate(data);
    } catch (SQLException e) {
      logMessage(e, "createOrUpdate threw exception on: " + data);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#update(Object) */
  public int update(T data) {
    try {
      return dao.update(data);
    } catch (SQLException e) {
      logMessage(e, "update threw exception on: " + data);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#updateId(Object, Object) */
  public int updateId(T data, ID newId) {
    try {
      return dao.updateId(data, newId);
    } catch (SQLException e) {
      logMessage(e, "updateId threw exception on: " + data);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#update(PreparedUpdate) */
  public int update(PreparedUpdate<T> preparedUpdate) {
    try {
      return dao.update(preparedUpdate);
    } catch (SQLException e) {
      logMessage(e, "update threw exception on: " + preparedUpdate);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#refresh(Object) */
  public int refresh(T data) {
    try {
      return dao.refresh(data);
    } catch (SQLException e) {
      logMessage(e, "refresh threw exception on: " + data);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#delete(Object) */
  public int delete(T data) {
    try {
      return dao.delete(data);
    } catch (SQLException e) {
      logMessage(e, "delete threw exception on: " + data);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#deleteById(Object) */
  public int deleteById(ID id) {
    try {
      return dao.deleteById(id);
    } catch (SQLException e) {
      logMessage(e, "deleteById threw exception on: " + id);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#delete(Collection) */
  public int delete(Collection<T> datas) {
    try {
      return dao.delete(datas);
    } catch (SQLException e) {
      logMessage(e, "delete threw exception on: " + datas);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#deleteIds(Collection) */
  public int deleteIds(Collection<ID> ids) {
    try {
      return dao.deleteIds(ids);
    } catch (SQLException e) {
      logMessage(e, "deleteIds threw exception on: " + ids);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#delete(PreparedDelete) */
  public int delete(PreparedDelete<T> preparedDelete) {
    try {
      return dao.delete(preparedDelete);
    } catch (SQLException e) {
      logMessage(e, "delete threw exception on: " + preparedDelete);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#iterator() */
  public CloseableIterator<T> iterator() {
    return dao.iterator();
  }

  public CloseableIterator<T> closeableIterator() {
    return dao.closeableIterator();
  }

  /** @see Dao#iterator(int) */
  public CloseableIterator<T> iterator(int resultFlags) {
    return dao.iterator(resultFlags);
  }

  /** @see Dao#getWrappedIterable() */
  public CloseableWrappedIterable<T> getWrappedIterable() {
    return dao.getWrappedIterable();
  }

  /** @see Dao#getWrappedIterable(PreparedQuery) */
  public CloseableWrappedIterable<T> getWrappedIterable(PreparedQuery<T> preparedQuery) {
    return dao.getWrappedIterable(preparedQuery);
  }

  /** @see Dao#closeLastIterator() */
  public void closeLastIterator() {
    try {
      dao.closeLastIterator();
    } catch (IOException e) {
      logMessage(e, "closeLastIterator threw exception");
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#iterator(PreparedQuery) */
  public CloseableIterator<T> iterator(PreparedQuery<T> preparedQuery) {
    try {
      return dao.iterator(preparedQuery);
    } catch (SQLException e) {
      logMessage(e, "iterator threw exception on: " + preparedQuery);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#iterator(PreparedQuery, int) */
  public CloseableIterator<T> iterator(PreparedQuery<T> preparedQuery, int resultFlags) {
    try {
      return dao.iterator(preparedQuery, resultFlags);
    } catch (SQLException e) {
      logMessage(e, "iterator threw exception on: " + preparedQuery);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#queryRaw(String, String...) */
  public GenericRawResults<String[]> queryRaw(String query, String... arguments) {
    try {
      return dao.queryRaw(query, arguments);
    } catch (SQLException e) {
      logMessage(e, "queryRaw threw exception on: " + query);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#queryRawValue(String, String...) */
  public long queryRawValue(String query, String... arguments) {
    try {
      return dao.queryRawValue(query, arguments);
    } catch (SQLException e) {
      logMessage(e, "queryRawValue threw exception on: " + query);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#queryRaw(String, RawRowMapper, String...) */
  public <UO> GenericRawResults<UO> queryRaw(
      String query, RawRowMapper<UO> mapper, String... arguments) {
    try {
      return dao.queryRaw(query, mapper, arguments);
    } catch (SQLException e) {
      logMessage(e, "queryRaw threw exception on: " + query);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#queryRaw(String, DataType[], RawRowObjectMapper, String...) */
  public <UO> GenericRawResults<UO> queryRaw(
      String query, DataType[] columnTypes, RawRowObjectMapper<UO> mapper, String... arguments) {
    try {
      return dao.queryRaw(query, columnTypes, mapper, arguments);
    } catch (SQLException e) {
      logMessage(e, "queryRaw threw exception on: " + query);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#queryRaw(String, DataType[], String...) */
  public GenericRawResults<Object[]> queryRaw(
      String query, DataType[] columnTypes, String... arguments) {
    try {
      return dao.queryRaw(query, columnTypes, arguments);
    } catch (SQLException e) {
      logMessage(e, "queryRaw threw exception on: " + query);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#queryRaw(String, DatabaseResultsMapper, String...) */
  public <UO> GenericRawResults<UO> queryRaw(
      String query, DatabaseResultsMapper<UO> mapper, String... arguments) {
    try {
      return dao.queryRaw(query, mapper, arguments);
    } catch (SQLException e) {
      logMessage(e, "queryRaw threw exception on: " + query);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#executeRaw(String, String...) */
  public int executeRaw(String statement, String... arguments) {
    try {
      return dao.executeRaw(statement, arguments);
    } catch (SQLException e) {
      logMessage(e, "executeRaw threw exception on: " + statement);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#executeRawNoArgs(String) */
  public int executeRawNoArgs(String statement) {
    try {
      return dao.executeRawNoArgs(statement);
    } catch (SQLException e) {
      logMessage(e, "executeRawNoArgs threw exception on: " + statement);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#updateRaw(String, String...) */
  public int updateRaw(String statement, String... arguments) {
    try {
      return dao.updateRaw(statement, arguments);
    } catch (SQLException e) {
      logMessage(e, "updateRaw threw exception on: " + statement);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#callBatchTasks(Callable) */
  public <CT> CT callBatchTasks(Callable<CT> callable) {
    try {
      return dao.callBatchTasks(callable);
    } catch (Exception e) {
      logMessage(e, "callBatchTasks threw exception on: " + callable);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#objectToString(Object) */
  public String objectToString(T data) {
    return dao.objectToString(data);
  }

  /** @see Dao#objectsEqual(Object, Object) */
  public boolean objectsEqual(T data1, T data2) {
    try {
      return dao.objectsEqual(data1, data2);
    } catch (SQLException e) {
      logMessage(e, "objectsEqual threw exception on: " + data1 + " and " + data2);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#extractId(Object) */
  public ID extractId(T data) {
    try {
      return dao.extractId(data);
    } catch (SQLException e) {
      logMessage(e, "extractId threw exception on: " + data);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#getDataClass() */
  public Class<T> getDataClass() {
    return dao.getDataClass();
  }

  /** @see Dao#findForeignFieldType(Class) */
  public FieldType findForeignFieldType(Class<?> clazz) {
    return dao.findForeignFieldType(clazz);
  }

  /** @see Dao#isUpdatable() */
  public boolean isUpdatable() {
    return dao.isUpdatable();
  }

  /** @see Dao#isTableExists() */
  public boolean isTableExists() {
    try {
      return dao.isTableExists();
    } catch (SQLException e) {
      logMessage(e, "isTableExists threw exception");
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#countOf() */
  public long countOf() {
    try {
      return dao.countOf();
    } catch (SQLException e) {
      logMessage(e, "countOf threw exception");
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#countOf(PreparedQuery) */
  public long countOf(PreparedQuery<T> preparedQuery) {
    try {
      return dao.countOf(preparedQuery);
    } catch (SQLException e) {
      logMessage(e, "countOf threw exception on " + preparedQuery);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#assignEmptyForeignCollection(Object, String) */
  public void assignEmptyForeignCollection(T parent, String fieldName) {
    try {
      dao.assignEmptyForeignCollection(parent, fieldName);
    } catch (SQLException e) {
      logMessage(e, "assignEmptyForeignCollection threw exception on " + fieldName);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#getEmptyForeignCollection(String) */
  public <FT> ForeignCollection<FT> getEmptyForeignCollection(String fieldName) {
    try {
      return dao.getEmptyForeignCollection(fieldName);
    } catch (SQLException e) {
      logMessage(e, "getEmptyForeignCollection threw exception on " + fieldName);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#setObjectCache(boolean) */
  public void setObjectCache(boolean enabled) {
    try {
      dao.setObjectCache(enabled);
    } catch (SQLException e) {
      logMessage(e, "setObjectCache(" + enabled + ") threw exception");
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#getObjectCache() */
  public ObjectCache getObjectCache() {
    return dao.getObjectCache();
  }

  /** @see Dao#setObjectCache(ObjectCache) */
  public void setObjectCache(ObjectCache objectCache) {
    try {
      dao.setObjectCache(objectCache);
    } catch (SQLException e) {
      logMessage(e, "setObjectCache threw exception on " + objectCache);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#clearObjectCache() */
  public void clearObjectCache() {
    dao.clearObjectCache();
  }

  /** @see Dao#mapSelectStarRow(DatabaseResults) */
  public T mapSelectStarRow(DatabaseResults results) {
    try {
      return dao.mapSelectStarRow(results);
    } catch (SQLException e) {
      logMessage(e, "mapSelectStarRow threw exception on results");
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#getSelectStarRowMapper() */
  public GenericRowMapper<T> getSelectStarRowMapper() {
    try {
      return dao.getSelectStarRowMapper();
    } catch (SQLException e) {
      logMessage(e, "getSelectStarRowMapper threw exception");
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#idExists(Object) */
  public boolean idExists(ID id) {
    try {
      return dao.idExists(id);
    } catch (SQLException e) {
      logMessage(e, "idExists threw exception on " + id);
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#startThreadConnection() */
  public DatabaseConnection startThreadConnection() {
    try {
      return dao.startThreadConnection();
    } catch (SQLException e) {
      logMessage(e, "startThreadConnection() threw exception");
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#endThreadConnection(DatabaseConnection) */
  public void endThreadConnection(DatabaseConnection connection) {
    try {
      dao.endThreadConnection(connection);
    } catch (SQLException e) {
      logMessage(e, "endThreadConnection(" + connection + ") threw exception");
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#setAutoCommit(boolean) */
  @Deprecated
  public void setAutoCommit(boolean autoCommit) {
    try {
      dao.setAutoCommit(autoCommit);
    } catch (SQLException e) {
      logMessage(e, "setAutoCommit(" + autoCommit + ") threw exception");
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#setAutoCommit(DatabaseConnection, boolean) */
  public void setAutoCommit(DatabaseConnection connection, boolean autoCommit) {
    try {
      dao.setAutoCommit(connection, autoCommit);
    } catch (SQLException e) {
      logMessage(e, "setAutoCommit(" + connection + "," + autoCommit + ") threw exception");
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#isAutoCommit() */
  @Deprecated
  public boolean isAutoCommit() {
    try {
      return dao.isAutoCommit();
    } catch (SQLException e) {
      logMessage(e, "isAutoCommit() threw exception");
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#isAutoCommit(DatabaseConnection) */
  public boolean isAutoCommit(DatabaseConnection connection) {
    try {
      return dao.isAutoCommit(connection);
    } catch (SQLException e) {
      logMessage(e, "isAutoCommit(" + connection + ") threw exception");
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#commit(DatabaseConnection) */
  public void commit(DatabaseConnection connection) {
    try {
      dao.commit(connection);
    } catch (SQLException e) {
      logMessage(e, "commit(" + connection + ") threw exception");
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#rollBack(DatabaseConnection) */
  public void rollBack(DatabaseConnection connection) {
    try {
      dao.rollBack(connection);
    } catch (SQLException e) {
      logMessage(e, "rollBack(" + connection + ") threw exception");
      throw new RuntimeException(e);
    }
  }

  /** @see Dao#setObjectFactory(ObjectFactory) */
  public void setObjectFactory(ObjectFactory<T> objectFactory) {
    dao.setObjectFactory(objectFactory);
  }

  /** @see Dao#getRawRowMapper() */
  public RawRowMapper<T> getRawRowMapper() {
    return dao.getRawRowMapper();
  }

  /** @see Dao#getConnectionSource() */
  public ConnectionSource getConnectionSource() {
    return dao.getConnectionSource();
  }

  public void registerObserver(DaoObserver observer) {
    dao.registerObserver(observer);
  }

  public void unregisterObserver(DaoObserver observer) {
    dao.unregisterObserver(observer);
  }

  public void notifyChanges() {
    dao.notifyChanges();
  }

  private void logMessage(Exception e, String message) {
    logger.log(LOG_LEVEL, e, message);
  }
}
public class AndroidConnectionSource extends BaseConnectionSource implements ConnectionSource {

  private static final Logger logger =
      LoggerFactory.getLogger(com / j256 / ormlite / android / AndroidConnectionSource);
  private AndroidDatabaseConnection connection;
  private final DatabaseType databaseType;
  private final SQLiteOpenHelper helper;
  private volatile boolean isOpen;
  private final SQLiteDatabase sqliteDatabase;

  public AndroidConnectionSource(SQLiteDatabase sqlitedatabase) {
    connection = null;
    isOpen = true;
    databaseType = new SqliteAndroidDatabaseType();
    helper = null;
    sqliteDatabase = sqlitedatabase;
  }

  public AndroidConnectionSource(SQLiteOpenHelper sqliteopenhelper) {
    connection = null;
    isOpen = true;
    databaseType = new SqliteAndroidDatabaseType();
    helper = sqliteopenhelper;
    sqliteDatabase = null;
  }

  public void clearSpecialConnection(DatabaseConnection databaseconnection) {
    clearSpecial(databaseconnection, logger);
  }

  public void close() {
    isOpen = false;
  }

  public DatabaseType getDatabaseType() {
    return databaseType;
  }

  public DatabaseConnection getReadOnlyConnection() {
    return getReadWriteConnection();
  }

  public DatabaseConnection getReadWriteConnection() {
    DatabaseConnection databaseconnection = getSavedConnection();
    if (databaseconnection != null) {
      return databaseconnection;
    }
    if (connection == null) {
      if (sqliteDatabase == null) {
        connection = new AndroidDatabaseConnection(helper.getWritableDatabase(), true);
      } else {
        connection = new AndroidDatabaseConnection(sqliteDatabase, true);
      }
    }
    return connection;
  }

  public boolean isOpen() {
    return isOpen;
  }

  public void releaseConnection(DatabaseConnection databaseconnection) {}

  public boolean saveSpecialConnection(DatabaseConnection databaseconnection) {
    return saveSpecial(databaseconnection);
  }
}
/**
 * This helps organize and access database connections to optimize connection sharing. There are
 * several schemes to manage the database connections in an Android app, but as an app gets more
 * complicated, there are many potential places where database locks can occur. This class allows
 * database connection sharing between multiple threads in a single app.
 *
 * <p>This gets injected or called with the {@link OrmLiteSqliteOpenHelper} class that is used to
 * manage the database connection. The helper instance will be kept in a static field and only
 * released once its internal usage count goes to 0.
 *
 * <p>The {@link SQLiteOpenHelper} and database classes maintain one connection under the hood, and
 * prevent locks in the java code. Creating multiple connections can potentially be a source of
 * trouble. This class shares the same connection instance between multiple clients, which will
 * allow multiple activities and services to run at the same time.
 *
 * <p>Every time you use the helper, you should call {@link #getHelper(Context)} or {@link
 * #getHelper(Context, Class)}. When you are done with the helper you should call {@link
 * #releaseHelper()}.
 *
 * @author graywatson, kevingalligan
 */
public class OpenHelperManager {

  private static final String HELPER_CLASS_RESOURCE_NAME = "open_helper_classname";
  private static Logger logger = LoggerFactory.getLogger(OpenHelperManager.class);

  private static Class<? extends OrmLiteSqliteOpenHelper> helperClass = null;
  private static volatile OrmLiteSqliteOpenHelper helper = null;
  private static boolean wasClosed = false;
  private static int instanceCount = 0;

  /**
   * If you are _not_ using the {@link OrmLiteBaseActivity} type classes then you will need to call
   * this in a static method in your code.
   */
  public static synchronized void setOpenHelperClass(
      Class<? extends OrmLiteSqliteOpenHelper> openHelperClass) {
    if (openHelperClass == null) {
      helperClass = null;
    } else {
      innerSetHelperClass(openHelperClass);
    }
  }

  /**
   * Set the helper for the manager. This is most likely used for testing purposes and should only
   * be called if you _really_ know what you are doing. If you do use it then it should be in a
   * static {} initializing block to make sure you have one helper instance for your application.
   */
  public static synchronized void setHelper(OrmLiteSqliteOpenHelper helper) {
    OpenHelperManager.helper = helper;
  }

  /**
   * Create a static instance of our open helper from the helper class. This has a usage counter on
   * it so make sure all calls to this method have an associated call to {@link #releaseHelper()}.
   * This should be called during an onCreate() type of method when the application or service is
   * starting. The caller should then keep the helper around until it is shutting down when {@link
   * #releaseHelper()} should be called.
   */
  public static synchronized <T extends OrmLiteSqliteOpenHelper> T getHelper(
      Context context, Class<T> openHelperClass) {
    innerSetHelperClass(openHelperClass);
    return loadHelper(context, openHelperClass);
  }

  /**
   * Similar to {@link #getHelper(Context, Class)} (which is recommended) except we have to find the
   * helper class through other means. This method requires that the Context be a class that extends
   * one of ORMLite's Android base classes such as {@link OrmLiteBaseActivity}. Either that or the
   * helper class needs to be set in the strings.xml.
   *
   * <p>To find the helper class, this does the following: <br>
   * 1) If the class has been set with a call to {@link #setOpenHelperClass(Class)}, it will be used
   * to construct a helper. <br>
   * 2) If the resource class name is configured in the strings.xml file it will be used. <br>
   * 3) The context class hierarchy is walked looking at the generic parameters for a class
   * extending OrmLiteSqliteOpenHelper. This is used by the {@link OrmLiteBaseActivity} and other
   * base classes. <br>
   * 4) An exception is thrown saying that it was not able to set the helper class.
   *
   * @deprecated Should use {@link #getHelper(Context, Class)}
   */
  @Deprecated
  public static synchronized OrmLiteSqliteOpenHelper getHelper(Context context) {
    if (helperClass == null) {
      if (context == null) {
        throw new IllegalArgumentException("context argument is null");
      }
      Context appContext = context.getApplicationContext();
      innerSetHelperClass(lookupHelperClass(appContext, context.getClass()));
    }
    return loadHelper(context, helperClass);
  }

  /** @deprecated This has been renamed to be {@link #releaseHelper()}. */
  @Deprecated
  public static void release() {
    releaseHelper();
  }

  /**
   * Release the helper that was previously returned by a call {@link #getHelper(Context)} or {@link
   * #getHelper(Context, Class)}. This will decrement the usage counter and close the helper if the
   * counter is 0.
   *
   * <p><b> WARNING: </b> This should be called in an onDestroy() type of method when your
   * application or service is terminating or if your code is no longer going to use the helper or
   * derived DAOs in any way. _Don't_ call this method if you expect to call {@link
   * #getHelper(Context)} again before the application terminates.
   */
  public static synchronized void releaseHelper() {
    instanceCount--;
    logger.trace("releasing helper {}, instance count = {}", helper, instanceCount);
    if (instanceCount <= 0) {
      if (helper != null) {
        logger.trace("zero instances, closing helper {}", helper);
        helper.close();
        helper = null;
        wasClosed = true;
      }
      if (instanceCount < 0) {
        logger.error("too many calls to release helper, instance count = {}", instanceCount);
      }
    }
  }

  /** Set the helper class and make sure we aren't changing it to another class. */
  private static void innerSetHelperClass(
      Class<? extends OrmLiteSqliteOpenHelper> openHelperClass) {
    // make sure if that there are not 2 helper classes in an application
    if (helperClass == null) {
      helperClass = openHelperClass;
    } else if (helperClass != openHelperClass) {
      throw new IllegalStateException(
          "Helper class was " + helperClass + " but is trying to be reset to " + openHelperClass);
    }
  }

  private static <T extends OrmLiteSqliteOpenHelper> T loadHelper(
      Context context, Class<T> openHelperClass) {
    if (helper == null) {
      if (wasClosed) {
        // this can happen if you are calling get/release and then get again
        logger.info("helper was already closed and is being re-opened");
      }
      if (context == null) {
        throw new IllegalArgumentException("context argument is null");
      }
      Context appContext = context.getApplicationContext();
      helper = constructHelper(appContext, helperClass);
      logger.trace("zero instances, created helper {}", helper);
      /*
       * Filipe Leandro and I worked on this bug for like 10 hours straight. It's a doosey.
       *
       * Each ForeignCollection has internal DAO objects that are holding a ConnectionSource. Each Android
       * ConnectionSource is tied to a particular database connection. What Filipe was seeing was that when all of
       * his views we closed (onDestroy), but his application WAS NOT FULLY KILLED, the first View.onCreate()
       * method would open a new connection to the database. Fine. But because he application was still in memory,
       * the static BaseDaoImpl default cache had not been cleared and was containing cached objects with
       * ForeignCollections. The ForeignCollections still had references to the DAOs that had been opened with old
       * ConnectionSource objects and therefore the old database connection. Using those cached collections would
       * cause exceptions saying that you were trying to work with a database that had already been close.
       *
       * Now, whenever we create a new helper object, we must make sure that the internal object caches have been
       * fully cleared. This is a good lesson for anyone that is holding objects around after they have closed
       * connections to the database or re-created the DAOs on a different connection somehow.
       */
      BaseDaoImpl.clearAllInternalObjectCaches();
      /*
       * Might as well do this also since if the helper changes then the ConnectionSource will change so no one is
       * going to have a cache hit on the old DAOs anyway. All they are doing is holding memory.
       *
       * NOTE: we don't want to clear the config map.
       */
      DaoManager.clearDaoCache();
      instanceCount = 0;
    }

    instanceCount++;
    logger.trace("returning helper {}, instance count = {} ", helper, instanceCount);
    @SuppressWarnings("unchecked")
    T castHelper = (T) helper;
    return castHelper;
  }

  /** Call the constructor on our helper class. */
  private static OrmLiteSqliteOpenHelper constructHelper(
      Context context, Class<? extends OrmLiteSqliteOpenHelper> openHelperClass) {
    Constructor<?> constructor;
    try {
      constructor = openHelperClass.getConstructor(Context.class);
    } catch (Exception e) {
      throw new IllegalStateException(
          "Could not find constructor that hast just a (Context) argument for helper class "
              + openHelperClass,
          e);
    }
    try {
      return (OrmLiteSqliteOpenHelper) constructor.newInstance(context);
    } catch (Exception e) {
      throw new IllegalStateException(
          "Could not construct instance of helper class " + openHelperClass, e);
    }
  }

  /**
   * Lookup the helper class either from the resource string or by looking for a generic parameter.
   */
  private static Class<? extends OrmLiteSqliteOpenHelper> lookupHelperClass(
      Context context, Class<?> componentClass) {

    // see if we have the magic resource class name set
    Resources resources = context.getResources();
    int resourceId =
        resources.getIdentifier(HELPER_CLASS_RESOURCE_NAME, "string", context.getPackageName());
    if (resourceId != 0) {
      String className = resources.getString(resourceId);
      try {
        @SuppressWarnings("unchecked")
        Class<? extends OrmLiteSqliteOpenHelper> castClass =
            (Class<? extends OrmLiteSqliteOpenHelper>) Class.forName(className);
        return castClass;
      } catch (Exception e) {
        throw new IllegalStateException(
            "Could not create helper instance for class " + className, e);
      }
    }

    // try walking the context class to see if we can get the OrmLiteSqliteOpenHelper from a generic
    // parameter
    for (Class<?> componentClassWalk = componentClass;
        componentClassWalk != null;
        componentClassWalk = componentClassWalk.getSuperclass()) {
      Type superType = componentClassWalk.getGenericSuperclass();
      if (superType == null || !(superType instanceof ParameterizedType)) {
        continue;
      }
      // get the generic type arguments
      Type[] types = ((ParameterizedType) superType).getActualTypeArguments();
      // defense
      if (types == null || types.length == 0) {
        continue;
      }
      for (Type type : types) {
        // defense
        if (!(type instanceof Class)) {
          continue;
        }
        Class<?> clazz = (Class<?>) type;
        if (OrmLiteSqliteOpenHelper.class.isAssignableFrom(clazz)) {
          @SuppressWarnings("unchecked")
          Class<? extends OrmLiteSqliteOpenHelper> castOpenHelperClass =
              (Class<? extends OrmLiteSqliteOpenHelper>) clazz;
          return castOpenHelperClass;
        }
      }
    }
    throw new IllegalStateException(
        "Could not find OpenHelperClass because none of the generic parameters of class "
            + componentClass
            + " extends OrmLiteSqliteOpenHelper.  You should use getHelper(Context, Class) instead.");
  }
}