/**
   * Return the instance of an entity with a matching id.
   *
   * @param <T> Any ActiveRecordBase class.
   * @param type The class of the entity to return.
   * @param id The desired ID.
   * @return The matching entity if reocrd found in DB, null otherwise
   * @throws ActiveRecordException
   */
  public <T extends ActiveRecordBase> T findByID(Class<T> type, long id)
      throws ActiveRecordException {
    if (m_Database == null) throw new ActiveRecordException("Set database first");
    T entity = s_EntitiesMap.get(type, id);
    if (entity != null) return entity;

    try {
      entity = type.newInstance();
    } catch (IllegalAccessException e) {
      throw new ActiveRecordException(e.getLocalizedMessage());
    } catch (InstantiationException e) {
      throw new ActiveRecordException(e.getLocalizedMessage());
    }

    Cursor c =
        m_Database.query(entity.getTableName(), null, "_id = ?", new String[] {String.valueOf(id)});
    try {
      if (!c.moveToNext()) {
        return null;
      } else {
        entity.inflate(c);
        entity.m_NeedsInsert = false;
        entity.m_Database = m_Database;
      }
    } finally {
      c.close();
    }
    return entity;
  }
 /**
  * Return all instances of an entity that match the given criteria. Use whereClause to specify
  * condition, using reqular SQL syntax for WHERE clause.
  *
  * <p>For example selecting all JOHNs born in 2001 from USERS table may look like:
  *
  * <pre>
  * users.find(Users.class, "NAME='?' and YEAR=?", new String[] {"John", "2001"});
  * </pre>
  *
  * @param <T> Any ActiveRecordBase class.
  * @param type The class of the entities to return.
  * @param whereClause The condition to match (Don't include "where").
  * @param whereArgs The arguments to replace "?" with.
  * @return A generic list of all matching entities.
  * @throws IllegalArgumentException
  * @throws IllegalAccessException
  * @throws InstantiationException
  */
 public <T extends ActiveRecordBase> List<T> find(
     Class<T> type, String whereClause, String[] whereArgs) throws ActiveRecordException {
   if (m_Database == null) throw new ActiveRecordException("Set database first");
   T entity = null;
   try {
     entity = type.newInstance();
   } catch (IllegalAccessException e1) {
     throw new ActiveRecordException(e1.getLocalizedMessage());
   } catch (InstantiationException e1) {
     throw new ActiveRecordException(e1.getLocalizedMessage());
   }
   List<T> toRet = new ArrayList<T>();
   Cursor c = m_Database.query(entity.getTableName(), null, whereClause, whereArgs);
   try {
     while (c.moveToNext()) {
       entity = s_EntitiesMap.get(type, c.getLong(c.getColumnIndex("_id")));
       if (entity == null) {
         entity = type.newInstance();
         entity.m_Database = m_Database;
         entity.m_NeedsInsert = false;
         entity.inflate(c);
       }
       toRet.add(entity);
     }
   } catch (IllegalAccessException e) {
     throw new ActiveRecordException(e.getLocalizedMessage());
   } catch (InstantiationException e) {
     throw new ActiveRecordException(e.getLocalizedMessage());
   } finally {
     c.close();
   }
   return toRet;
 }
  /**
   * Saves this entity to the database, inserts or updates as needed.
   *
   * @return number of rows affected on success, -1 on failure
   * @throws ActiveRecordException
   */
  public long save() throws ActiveRecordException {
    long r = -1;

    if (m_Database == null) throw new ActiveRecordException("Set database first");

    if (null == findByID(this.getClass(), _id)) {
      if (insert() > 0) {
        r = 1;
      }
    } else {
      r = update();
    }
    for (Field column : getColumnFields()) {
      if (column.getType() == List.class) {
        try {
          List<ActiveRecordBase> listObjects = (List<ActiveRecordBase>) column.get(this);
          Field itemOwner = null;
          if (listObjects != null && listObjects.size() > 0) {
            for (Field childColumn : listObjects.get(0).getColumnFields()) {
              // Try to find the parent column and set it
              // automatically to save time from those setters
              // and getters
              if (childColumn.getType() == this.getClass()) {
                itemOwner = childColumn;
                break;
              }
            }
            for (ActiveRecordBase item : listObjects) {
              if (item.getDatabase() == null) {
                item.setDatabase(getDatabase());
              }
              if (itemOwner != null) {
                itemOwner.set(item, this);
              }
              item.save();
            }
          }
        } catch (IllegalAccessException e) {
          e.printStackTrace();
        } catch (IllegalArgumentException e) {
          e.printStackTrace();
        }
      }
    }
    s_EntitiesMap.set(this);

    return r;
  }
  /**
   * Inflate this entity using the current row from the given cursor.
   *
   * @param cursor The cursor to get object data from.
   * @throws IllegalArgumentException
   * @throws IllegalAccessException
   * @throws InstantiationException
   */
  @SuppressWarnings("unchecked")
  void inflate(Cursor cursor) throws ActiveRecordException {
    HashMap<Field, Long> entities = new HashMap<Field, Long>();
    for (Field field : getColumnFields()) {
      try {
        String typeString = field.getType().getName();
        String colName = CamelNotationHelper.toSQLName(field.getName());
        if (typeString.equals("long")) {
          field.setLong(this, cursor.getLong(cursor.getColumnIndex(colName)));
        } else if (typeString.equals("java.lang.String")) {
          String val = cursor.getString(cursor.getColumnIndex(colName));
          field.set(this, val.equals("null") ? null : val);
        } else if (typeString.equals("double")) {
          field.setDouble(this, cursor.getDouble(cursor.getColumnIndex(colName)));
        } else if (typeString.equals("boolean")) {
          field.setBoolean(this, cursor.getString(cursor.getColumnIndex(colName)).equals("true"));
        } else if (typeString.equals("[B")) {
          field.set(this, cursor.getBlob(cursor.getColumnIndex(colName)));
        } else if (typeString.equals("int")) {
          field.setInt(this, cursor.getInt(cursor.getColumnIndex(colName)));
        } else if (typeString.equals("float")) {
          field.setFloat(this, cursor.getFloat(cursor.getColumnIndex(colName)));
        } else if (typeString.equals("short")) {
          field.setShort(this, cursor.getShort(cursor.getColumnIndex(colName)));
        } else if (typeString.equals("java.sql.Timestamp")) {
          long l = cursor.getLong(cursor.getColumnIndex(colName));
          field.set(this, new Timestamp(l));
        } else if (field.getType().getSuperclass() == ActiveRecordBase.class) {
          long id = cursor.getLong(cursor.getColumnIndex(colName));
          if (id > 0) entities.put(field, id);
          else field.set(this, null);
        } else if (field.getType() == List.class) {
          String classKey = field.getName();
          // hack
          classKey =
              classKey.replaceFirst(
                  "" + classKey.charAt(0), "" + Character.toUpperCase(classKey.charAt(0)));
          if (field.getName().endsWith("s")) {
            classKey = classKey.substring(0, classKey.length() - 1);
          }
          DatabaseBuilder ownBuilder = getDatabase().getOwnBuilder();
          Class fieldClass = ownBuilder.getClassForName(classKey);
          if (fieldClass != null) {
            List<ActiveRecordBase> ownObjects =
                (List<ActiveRecordBase>)
                    findByColumn(
                        fieldClass,
                        CamelNotationHelper.toSQLName(getTableName()),
                        Long.toString(getID()));
            field.set(this, ownObjects);
          }
        } else if (field.getAnnotation(ActiveRecordIgnoreAttribute.class) != null) {
          continue;
        } else
          throw new ActiveRecordException(
              field.getName()
                  + " of type "
                  + field.getType()
                  + " cannot be read from Sqlite3 database.");
      } catch (IllegalArgumentException e) {
        throw new ActiveRecordException(e.getLocalizedMessage());
      } catch (IllegalAccessException e) {
        throw new ActiveRecordException(e.getLocalizedMessage());
      }
    }

    s_EntitiesMap.set(this);
    for (Field f : entities.keySet()) {
      try {
        f.set(
            this, this.findByID((Class<? extends ActiveRecordBase>) f.getType(), entities.get(f)));
      } catch (SQLiteException e) {
        throw new ActiveRecordException(e.getLocalizedMessage());
      } catch (IllegalArgumentException e) {
        throw new ActiveRecordException(e.getLocalizedMessage());
      } catch (IllegalAccessException e) {
        throw new ActiveRecordException(e.getLocalizedMessage());
      }
    }
  }