Add ActiveAndroid source code to our tree and remove content providers

ActiveAndroid is not actively maintained anymore and contains code
related to that Content Providers that makes the application crash on
Android Oreo.
pull/419/merge
Alinson S. Xavier 8 years ago
parent f02c86e61b
commit b4a33cba39

@ -0,0 +1,86 @@
package com.activeandroid;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import com.activeandroid.util.Log;
public final class ActiveAndroid {
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
public static void initialize(Context context) {
initialize(new Configuration.Builder(context).create());
}
public static void initialize(Configuration configuration) {
initialize(configuration, false);
}
public static void initialize(Context context, boolean loggingEnabled) {
initialize(new Configuration.Builder(context).create(), loggingEnabled);
}
public static void initialize(Configuration configuration, boolean loggingEnabled) {
// Set logging enabled first
setLoggingEnabled(loggingEnabled);
Cache.initialize(configuration);
}
public static void clearCache() {
Cache.clear();
}
public static void dispose() {
Cache.dispose();
}
public static void setLoggingEnabled(boolean enabled) {
Log.setEnabled(enabled);
}
public static SQLiteDatabase getDatabase() {
return Cache.openDatabase();
}
public static void beginTransaction() {
Cache.openDatabase().beginTransaction();
}
public static void endTransaction() {
Cache.openDatabase().endTransaction();
}
public static void setTransactionSuccessful() {
Cache.openDatabase().setTransactionSuccessful();
}
public static boolean inTransaction() {
return Cache.openDatabase().inTransaction();
}
public static void execSQL(String sql) {
Cache.openDatabase().execSQL(sql);
}
public static void execSQL(String sql, Object[] bindArgs) {
Cache.openDatabase().execSQL(sql, bindArgs);
}
}

@ -0,0 +1,158 @@
package com.activeandroid;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.util.Collection;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.support.v4.util.LruCache;
import com.activeandroid.serializer.TypeSerializer;
import com.activeandroid.util.Log;
public final class Cache {
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC CONSTANTS
//////////////////////////////////////////////////////////////////////////////////////
public static final int DEFAULT_CACHE_SIZE = 1024;
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE MEMBERS
//////////////////////////////////////////////////////////////////////////////////////
private static Context sContext;
private static ModelInfo sModelInfo;
private static DatabaseHelper sDatabaseHelper;
private static LruCache<String, Model> sEntities;
private static boolean sIsInitialized = false;
//////////////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////
private Cache() {
}
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
public static synchronized void initialize(Configuration configuration) {
if (sIsInitialized) {
Log.v("ActiveAndroid already initialized.");
return;
}
sContext = configuration.getContext();
sModelInfo = new ModelInfo(configuration);
sDatabaseHelper = new DatabaseHelper(configuration);
// TODO: It would be nice to override sizeOf here and calculate the memory
// actually used, however at this point it seems like the reflection
// required would be too costly to be of any benefit. We'll just set a max
// object size instead.
sEntities = new LruCache<String, Model>(configuration.getCacheSize());
openDatabase();
sIsInitialized = true;
Log.v("ActiveAndroid initialized successfully.");
}
public static synchronized void clear() {
sEntities.evictAll();
Log.v("Cache cleared.");
}
public static synchronized void dispose() {
closeDatabase();
sEntities = null;
sModelInfo = null;
sDatabaseHelper = null;
sIsInitialized = false;
Log.v("ActiveAndroid disposed. Call initialize to use library.");
}
// Database access
public static boolean isInitialized() {
return sIsInitialized;
}
public static synchronized SQLiteDatabase openDatabase() {
return sDatabaseHelper.getWritableDatabase();
}
public static synchronized void closeDatabase() {
sDatabaseHelper.close();
}
// Context access
public static Context getContext() {
return sContext;
}
// Entity cache
public static String getIdentifier(Class<? extends Model> type, Long id) {
return getTableName(type) + "@" + id;
}
public static String getIdentifier(Model entity) {
return getIdentifier(entity.getClass(), entity.getId());
}
public static synchronized void addEntity(Model entity) {
sEntities.put(getIdentifier(entity), entity);
}
public static synchronized Model getEntity(Class<? extends Model> type, long id) {
return sEntities.get(getIdentifier(type, id));
}
public static synchronized void removeEntity(Model entity) {
sEntities.remove(getIdentifier(entity));
}
// Model cache
public static synchronized Collection<TableInfo> getTableInfos() {
return sModelInfo.getTableInfos();
}
public static synchronized TableInfo getTableInfo(Class<? extends Model> type) {
return sModelInfo.getTableInfo(type);
}
public static synchronized TypeSerializer getParserForType(Class<?> type) {
return sModelInfo.getTypeSerializer(type);
}
public static synchronized String getTableName(Class<? extends Model> type) {
return sModelInfo.getTableInfo(type).getTableName();
}
}

@ -0,0 +1,318 @@
package com.activeandroid;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import android.content.Context;
import com.activeandroid.serializer.TypeSerializer;
import com.activeandroid.util.Log;
import com.activeandroid.util.ReflectionUtils;
public class Configuration {
public final static String SQL_PARSER_LEGACY = "legacy";
public final static String SQL_PARSER_DELIMITED = "delimited";
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE MEMBERS
//////////////////////////////////////////////////////////////////////////////////////
private Context mContext;
private String mDatabaseName;
private int mDatabaseVersion;
private String mSqlParser;
private List<Class<? extends Model>> mModelClasses;
private List<Class<? extends TypeSerializer>> mTypeSerializers;
private int mCacheSize;
//////////////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////
private Configuration(Context context) {
mContext = context;
}
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
public Context getContext() {
return mContext;
}
public String getDatabaseName() {
return mDatabaseName;
}
public int getDatabaseVersion() {
return mDatabaseVersion;
}
public String getSqlParser() {
return mSqlParser;
}
public List<Class<? extends Model>> getModelClasses() {
return mModelClasses;
}
public List<Class<? extends TypeSerializer>> getTypeSerializers() {
return mTypeSerializers;
}
public int getCacheSize() {
return mCacheSize;
}
public boolean isValid() {
return mModelClasses != null && mModelClasses.size() > 0;
}
//////////////////////////////////////////////////////////////////////////////////////
// INNER CLASSES
//////////////////////////////////////////////////////////////////////////////////////
public static class Builder {
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE CONSTANTS
//////////////////////////////////////////////////////////////////////////////////////
private static final String AA_DB_NAME = "AA_DB_NAME";
private static final String AA_DB_VERSION = "AA_DB_VERSION";
private final static String AA_MODELS = "AA_MODELS";
private final static String AA_SERIALIZERS = "AA_SERIALIZERS";
private final static String AA_SQL_PARSER = "AA_SQL_PARSER";
private static final int DEFAULT_CACHE_SIZE = 1024;
private static final String DEFAULT_DB_NAME = "Application.db";
private static final String DEFAULT_SQL_PARSER = SQL_PARSER_LEGACY;
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE MEMBERS
//////////////////////////////////////////////////////////////////////////////////////
private Context mContext;
private Integer mCacheSize;
private String mDatabaseName;
private Integer mDatabaseVersion;
private String mSqlParser;
private List<Class<? extends Model>> mModelClasses;
private List<Class<? extends TypeSerializer>> mTypeSerializers;
//////////////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////
public Builder(Context context) {
mContext = context.getApplicationContext();
mCacheSize = DEFAULT_CACHE_SIZE;
}
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
public Builder setCacheSize(int cacheSize) {
mCacheSize = cacheSize;
return this;
}
public Builder setDatabaseName(String databaseName) {
mDatabaseName = databaseName;
return this;
}
public Builder setDatabaseVersion(int databaseVersion) {
mDatabaseVersion = databaseVersion;
return this;
}
public Builder setSqlParser(String sqlParser) {
mSqlParser = sqlParser;
return this;
}
public Builder addModelClass(Class<? extends Model> modelClass) {
if (mModelClasses == null) {
mModelClasses = new ArrayList<Class<? extends Model>>();
}
mModelClasses.add(modelClass);
return this;
}
public Builder addModelClasses(Class<? extends Model>... modelClasses) {
if (mModelClasses == null) {
mModelClasses = new ArrayList<Class<? extends Model>>();
}
mModelClasses.addAll(Arrays.asList(modelClasses));
return this;
}
public Builder setModelClasses(Class<? extends Model>... modelClasses) {
mModelClasses = Arrays.asList(modelClasses);
return this;
}
public Builder addTypeSerializer(Class<? extends TypeSerializer> typeSerializer) {
if (mTypeSerializers == null) {
mTypeSerializers = new ArrayList<Class<? extends TypeSerializer>>();
}
mTypeSerializers.add(typeSerializer);
return this;
}
public Builder addTypeSerializers(Class<? extends TypeSerializer>... typeSerializers) {
if (mTypeSerializers == null) {
mTypeSerializers = new ArrayList<Class<? extends TypeSerializer>>();
}
mTypeSerializers.addAll(Arrays.asList(typeSerializers));
return this;
}
public Builder setTypeSerializers(Class<? extends TypeSerializer>... typeSerializers) {
mTypeSerializers = Arrays.asList(typeSerializers);
return this;
}
public Configuration create() {
Configuration configuration = new Configuration(mContext);
configuration.mCacheSize = mCacheSize;
// Get database name from meta-data
if (mDatabaseName != null) {
configuration.mDatabaseName = mDatabaseName;
} else {
configuration.mDatabaseName = getMetaDataDatabaseNameOrDefault();
}
// Get database version from meta-data
if (mDatabaseVersion != null) {
configuration.mDatabaseVersion = mDatabaseVersion;
} else {
configuration.mDatabaseVersion = getMetaDataDatabaseVersionOrDefault();
}
// Get SQL parser from meta-data
if (mSqlParser != null) {
configuration.mSqlParser = mSqlParser;
} else {
configuration.mSqlParser = getMetaDataSqlParserOrDefault();
}
// Get model classes from meta-data
if (mModelClasses != null) {
configuration.mModelClasses = mModelClasses;
} else {
final String modelList = ReflectionUtils.getMetaData(mContext, AA_MODELS);
if (modelList != null) {
configuration.mModelClasses = loadModelList(modelList.split(","));
}
}
// Get type serializer classes from meta-data
if (mTypeSerializers != null) {
configuration.mTypeSerializers = mTypeSerializers;
} else {
final String serializerList = ReflectionUtils.getMetaData(mContext, AA_SERIALIZERS);
if (serializerList != null) {
configuration.mTypeSerializers = loadSerializerList(serializerList.split(","));
}
}
return configuration;
}
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
//////////////////////////////////////////////////////////////////////////////////////
// Meta-data methods
private String getMetaDataDatabaseNameOrDefault() {
String aaName = ReflectionUtils.getMetaData(mContext, AA_DB_NAME);
if (aaName == null) {
aaName = DEFAULT_DB_NAME;
}
return aaName;
}
private int getMetaDataDatabaseVersionOrDefault() {
Integer aaVersion = ReflectionUtils.getMetaData(mContext, AA_DB_VERSION);
if (aaVersion == null || aaVersion == 0) {
aaVersion = 1;
}
return aaVersion;
}
private String getMetaDataSqlParserOrDefault() {
final String mode = ReflectionUtils.getMetaData(mContext, AA_SQL_PARSER);
if (mode == null) {
return DEFAULT_SQL_PARSER;
}
return mode;
}
private List<Class<? extends Model>> loadModelList(String[] models) {
final List<Class<? extends Model>> modelClasses = new ArrayList<Class<? extends Model>>();
final ClassLoader classLoader = mContext.getClass().getClassLoader();
for (String model : models) {
try {
Class modelClass = Class.forName(model.trim(), false, classLoader);
if (ReflectionUtils.isModel(modelClass)) {
modelClasses.add(modelClass);
}
}
catch (ClassNotFoundException e) {
Log.e("Couldn't create class.", e);
}
}
return modelClasses;
}
private List<Class<? extends TypeSerializer>> loadSerializerList(String[] serializers) {
final List<Class<? extends TypeSerializer>> typeSerializers = new ArrayList<Class<? extends TypeSerializer>>();
final ClassLoader classLoader = mContext.getClass().getClassLoader();
for (String serializer : serializers) {
try {
Class serializerClass = Class.forName(serializer.trim(), false, classLoader);
if (ReflectionUtils.isTypeSerializer(serializerClass)) {
typeSerializers.add(serializerClass);
}
}
catch (ClassNotFoundException e) {
Log.e("Couldn't create class.", e);
}
}
return typeSerializers;
}
}
}

@ -0,0 +1,257 @@
package com.activeandroid;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;
import com.activeandroid.util.IOUtils;
import com.activeandroid.util.Log;
import com.activeandroid.util.NaturalOrderComparator;
import com.activeandroid.util.SQLiteUtils;
import com.activeandroid.util.SqlParser;
public final class DatabaseHelper extends SQLiteOpenHelper {
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC CONSTANTS
//////////////////////////////////////////////////////////////////////////////////////
public final static String MIGRATION_PATH = "migrations";
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE FIELDS
//////////////////////////////////////////////////////////////////////////////////////
private final String mSqlParser;
//////////////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////
public DatabaseHelper(Configuration configuration) {
super(configuration.getContext(), configuration.getDatabaseName(), null, configuration.getDatabaseVersion());
copyAttachedDatabase(configuration.getContext(), configuration.getDatabaseName());
mSqlParser = configuration.getSqlParser();
}
//////////////////////////////////////////////////////////////////////////////////////
// OVERRIDEN METHODS
//////////////////////////////////////////////////////////////////////////////////////
@Override
public void onOpen(SQLiteDatabase db) {
executePragmas(db);
};
@Override
public void onCreate(SQLiteDatabase db) {
executePragmas(db);
executeCreate(db);
executeMigrations(db, -1, db.getVersion());
executeCreateIndex(db);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
executePragmas(db);
executeCreate(db);
executeMigrations(db, oldVersion, newVersion);
}
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
public void copyAttachedDatabase(Context context, String databaseName) {
final File dbPath = context.getDatabasePath(databaseName);
// If the database already exists, return
if (dbPath.exists()) {
return;
}
// Make sure we have a path to the file
dbPath.getParentFile().mkdirs();
// Try to copy database file
try {
final InputStream inputStream = context.getAssets().open(databaseName);
final OutputStream output = new FileOutputStream(dbPath);
byte[] buffer = new byte[8192];
int length;
while ((length = inputStream.read(buffer, 0, 8192)) > 0) {
output.write(buffer, 0, length);
}
output.flush();
output.close();
inputStream.close();
}
catch (IOException e) {
Log.e("Failed to open file", e);
}
}
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
//////////////////////////////////////////////////////////////////////////////////////
private void executePragmas(SQLiteDatabase db) {
if (SQLiteUtils.FOREIGN_KEYS_SUPPORTED) {
db.execSQL("PRAGMA foreign_keys=ON;");
Log.i("Foreign Keys supported. Enabling foreign key features.");
}
}
private void executeCreateIndex(SQLiteDatabase db) {
db.beginTransaction();
try {
for (TableInfo tableInfo : Cache.getTableInfos()) {
String[] definitions = SQLiteUtils.createIndexDefinition(tableInfo);
for (String definition : definitions) {
db.execSQL(definition);
}
}
db.setTransactionSuccessful();
}
finally {
db.endTransaction();
}
}
private void executeCreate(SQLiteDatabase db) {
db.beginTransaction();
try {
for (TableInfo tableInfo : Cache.getTableInfos()) {
db.execSQL(SQLiteUtils.createTableDefinition(tableInfo));
}
db.setTransactionSuccessful();
}
finally {
db.endTransaction();
}
}
private boolean executeMigrations(SQLiteDatabase db, int oldVersion, int newVersion) {
boolean migrationExecuted = false;
try {
final List<String> files = Arrays.asList(Cache.getContext().getAssets().list(MIGRATION_PATH));
Collections.sort(files, new NaturalOrderComparator());
db.beginTransaction();
try {
for (String file : files) {
try {
final int version = Integer.valueOf(file.replace(".sql", ""));
if (version > oldVersion && version <= newVersion) {
executeSqlScript(db, file);
migrationExecuted = true;
Log.i(file + " executed succesfully.");
}
}
catch (NumberFormatException e) {
Log.w("Skipping invalidly named file: " + file, e);
}
}
db.setTransactionSuccessful();
}
finally {
db.endTransaction();
}
}
catch (IOException e) {
Log.e("Failed to execute migrations.", e);
}
return migrationExecuted;
}
private void executeSqlScript(SQLiteDatabase db, String file) {
InputStream stream = null;
try {
stream = Cache.getContext().getAssets().open(MIGRATION_PATH + "/" + file);
if (Configuration.SQL_PARSER_DELIMITED.equalsIgnoreCase(mSqlParser)) {
executeDelimitedSqlScript(db, stream);
} else {
executeLegacySqlScript(db, stream);
}
} catch (IOException e) {
Log.e("Failed to execute " + file, e);
} finally {
IOUtils.closeQuietly(stream);
}
}
private void executeDelimitedSqlScript(SQLiteDatabase db, InputStream stream) throws IOException {
List<String> commands = SqlParser.parse(stream);
for(String command : commands) {
db.execSQL(command);
}
}
private void executeLegacySqlScript(SQLiteDatabase db, InputStream stream) throws IOException {
InputStreamReader reader = null;
BufferedReader buffer = null;
try {
reader = new InputStreamReader(stream);
buffer = new BufferedReader(reader);
String line = null;
while ((line = buffer.readLine()) != null) {
line = line.replace(";", "").trim();
if (!TextUtils.isEmpty(line)) {
db.execSQL(line);
}
}
} finally {
IOUtils.closeQuietly(buffer);
IOUtils.closeQuietly(reader);
}
}
}

@ -0,0 +1,314 @@
package com.activeandroid;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import com.activeandroid.query.Delete;
import com.activeandroid.query.Select;
import com.activeandroid.serializer.TypeSerializer;
import com.activeandroid.util.Log;
import com.activeandroid.util.ReflectionUtils;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@SuppressWarnings("unchecked")
public abstract class Model {
/** Prime number used for hashcode() implementation. */
private static final int HASH_PRIME = 739;
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE MEMBERS
//////////////////////////////////////////////////////////////////////////////////////
private Long mId = null;
private final TableInfo mTableInfo;
private final String idName;
//////////////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////
public Model() {
mTableInfo = Cache.getTableInfo(getClass());
idName = mTableInfo.getIdName();
}
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
public final Long getId() {
return mId;
}
public final void delete() {
Cache.openDatabase().delete(mTableInfo.getTableName(), idName+"=?", new String[] { getId().toString() });
Cache.removeEntity(this);
}
public final Long save() {
final SQLiteDatabase db = Cache.openDatabase();
final ContentValues values = new ContentValues();
for (Field field : mTableInfo.getFields()) {
final String fieldName = mTableInfo.getColumnName(field);
Class<?> fieldType = field.getType();
field.setAccessible(true);
try {
Object value = field.get(this);
if (value != null) {
final TypeSerializer typeSerializer = Cache.getParserForType(fieldType);
if (typeSerializer != null) {
// serialize data
value = typeSerializer.serialize(value);
// set new object type
if (value != null) {
fieldType = value.getClass();
// check that the serializer returned what it promised
if (!fieldType.equals(typeSerializer.getSerializedType())) {
Log.w(String.format("TypeSerializer returned wrong type: expected a %s but got a %s",
typeSerializer.getSerializedType(), fieldType));
}
}
}
}
// TODO: Find a smarter way to do this? This if block is necessary because we
// can't know the type until runtime.
if (value == null) {
values.putNull(fieldName);
}
else if (fieldType.equals(Byte.class) || fieldType.equals(byte.class)) {
values.put(fieldName, (Byte) value);
}
else if (fieldType.equals(Short.class) || fieldType.equals(short.class)) {
values.put(fieldName, (Short) value);
}
else if (fieldType.equals(Integer.class) || fieldType.equals(int.class)) {
values.put(fieldName, (Integer) value);
}
else if (fieldType.equals(Long.class) || fieldType.equals(long.class)) {
values.put(fieldName, (Long) value);
}
else if (fieldType.equals(Float.class) || fieldType.equals(float.class)) {
values.put(fieldName, (Float) value);
}
else if (fieldType.equals(Double.class) || fieldType.equals(double.class)) {
values.put(fieldName, (Double) value);
}
else if (fieldType.equals(Boolean.class) || fieldType.equals(boolean.class)) {
values.put(fieldName, (Boolean) value);
}
else if (fieldType.equals(Character.class) || fieldType.equals(char.class)) {
values.put(fieldName, value.toString());
}
else if (fieldType.equals(String.class)) {
values.put(fieldName, value.toString());
}
else if (fieldType.equals(Byte[].class) || fieldType.equals(byte[].class)) {
values.put(fieldName, (byte[]) value);
}
else if (ReflectionUtils.isModel(fieldType)) {
values.put(fieldName, ((Model) value).getId());
}
else if (ReflectionUtils.isSubclassOf(fieldType, Enum.class)) {
values.put(fieldName, ((Enum<?>) value).name());
}
}
catch (IllegalArgumentException e) {
Log.e(e.getClass().getName(), e);
}
catch (IllegalAccessException e) {
Log.e(e.getClass().getName(), e);
}
}
if (mId == null) {
mId = db.insert(mTableInfo.getTableName(), null, values);
}
else {
db.update(mTableInfo.getTableName(), values, idName+"=" + mId, null);
}
return mId;
}
// Convenience methods
public static void delete(Class<? extends Model> type, long id) {
TableInfo tableInfo = Cache.getTableInfo(type);
new Delete().from(type).where(tableInfo.getIdName()+"=?", id).execute();
}
public static <T extends Model> T load(Class<T> type, long id) {
TableInfo tableInfo = Cache.getTableInfo(type);
return (T) new Select().from(type).where(tableInfo.getIdName()+"=?", id).executeSingle();
}
// Model population
public final void loadFromCursor(Cursor cursor) {
/**
* Obtain the columns ordered to fix issue #106 (https://github.com/pardom/ActiveAndroid/issues/106)
* when the cursor have multiple columns with same name obtained from join tables.
*/
List<String> columnsOrdered = new ArrayList<String>(Arrays.asList(cursor.getColumnNames()));
for (Field field : mTableInfo.getFields()) {
final String fieldName = mTableInfo.getColumnName(field);
Class<?> fieldType = field.getType();
final int columnIndex = columnsOrdered.indexOf(fieldName);
if (columnIndex < 0) {
continue;
}
field.setAccessible(true);
try {
boolean columnIsNull = cursor.isNull(columnIndex);
TypeSerializer typeSerializer = Cache.getParserForType(fieldType);
Object value = null;
if (typeSerializer != null) {
fieldType = typeSerializer.getSerializedType();
}
// TODO: Find a smarter way to do this? This if block is necessary because we
// can't know the type until runtime.
if (columnIsNull) {
field = null;
}
else if (fieldType.equals(Byte.class) || fieldType.equals(byte.class)) {
value = cursor.getInt(columnIndex);
}
else if (fieldType.equals(Short.class) || fieldType.equals(short.class)) {
value = cursor.getInt(columnIndex);
}
else if (fieldType.equals(Integer.class) || fieldType.equals(int.class)) {
value = cursor.getInt(columnIndex);
}
else if (fieldType.equals(Long.class) || fieldType.equals(long.class)) {
value = cursor.getLong(columnIndex);
}
else if (fieldType.equals(Float.class) || fieldType.equals(float.class)) {
value = cursor.getFloat(columnIndex);
}
else if (fieldType.equals(Double.class) || fieldType.equals(double.class)) {
value = cursor.getDouble(columnIndex);
}
else if (fieldType.equals(Boolean.class) || fieldType.equals(boolean.class)) {
value = cursor.getInt(columnIndex) != 0;
}
else if (fieldType.equals(Character.class) || fieldType.equals(char.class)) {
value = cursor.getString(columnIndex).charAt(0);
}
else if (fieldType.equals(String.class)) {
value = cursor.getString(columnIndex);
}
else if (fieldType.equals(Byte[].class) || fieldType.equals(byte[].class)) {
value = cursor.getBlob(columnIndex);
}
else if (ReflectionUtils.isModel(fieldType)) {
final long entityId = cursor.getLong(columnIndex);
final Class<? extends Model> entityType = (Class<? extends Model>) fieldType;
Model entity = Cache.getEntity(entityType, entityId);
if (entity == null) {
entity = new Select().from(entityType).where(idName+"=?", entityId).executeSingle();
}
value = entity;
}
else if (ReflectionUtils.isSubclassOf(fieldType, Enum.class)) {
@SuppressWarnings("rawtypes")
final Class<? extends Enum> enumType = (Class<? extends Enum>) fieldType;
value = Enum.valueOf(enumType, cursor.getString(columnIndex));
}
// Use a deserializer if one is available
if (typeSerializer != null && !columnIsNull) {
value = typeSerializer.deserialize(value);
}
// Set the field value
if (value != null) {
field.set(this, value);
}
}
catch (IllegalArgumentException e) {
Log.e(e.getClass().getName(), e);
}
catch (IllegalAccessException e) {
Log.e(e.getClass().getName(), e);
}
catch (SecurityException e) {
Log.e(e.getClass().getName(), e);
}
}
if (mId != null) {
Cache.addEntity(this);
}
}
//////////////////////////////////////////////////////////////////////////////////////
// PROTECTED METHODS
//////////////////////////////////////////////////////////////////////////////////////
protected final <T extends Model> List<T> getMany(Class<T> type, String foreignKey) {
return new Select().from(type).where(Cache.getTableName(type) + "." + foreignKey + "=?", getId()).execute();
}
//////////////////////////////////////////////////////////////////////////////////////
// OVERRIDEN METHODS
//////////////////////////////////////////////////////////////////////////////////////
@Override
public String toString() {
return mTableInfo.getTableName() + "@" + getId();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Model && this.mId != null) {
final Model other = (Model) obj;
return this.mId.equals(other.mId)
&& (this.mTableInfo.getTableName().equals(other.mTableInfo.getTableName()));
} else {
return this == obj;
}
}
@Override
public int hashCode() {
int hash = HASH_PRIME;
hash += HASH_PRIME * (mId == null ? super.hashCode() : mId.hashCode()); //if id is null, use Object.hashCode()
hash += HASH_PRIME * mTableInfo.getTableName().hashCode();
return hash; //To change body of generated methods, choose Tools | Templates.
}
}

@ -0,0 +1,209 @@
package com.activeandroid;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.content.Context;
import com.activeandroid.serializer.CalendarSerializer;
import com.activeandroid.serializer.SqlDateSerializer;
import com.activeandroid.serializer.TypeSerializer;
import com.activeandroid.serializer.UtilDateSerializer;
import com.activeandroid.serializer.FileSerializer;
import com.activeandroid.util.Log;
import com.activeandroid.util.ReflectionUtils;
import dalvik.system.DexFile;
final class ModelInfo {
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
//////////////////////////////////////////////////////////////////////////////////////
private Map<Class<? extends Model>, TableInfo> mTableInfos = new HashMap<Class<? extends Model>, TableInfo>();
private Map<Class<?>, TypeSerializer> mTypeSerializers = new HashMap<Class<?>, TypeSerializer>() {
{
put(Calendar.class, new CalendarSerializer());
put(java.sql.Date.class, new SqlDateSerializer());
put(java.util.Date.class, new UtilDateSerializer());
put(java.io.File.class, new FileSerializer());
}
};
//////////////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////
public ModelInfo(Configuration configuration) {
if (!loadModelFromMetaData(configuration)) {
try {
scanForModel(configuration.getContext());
}
catch (IOException e) {
Log.e("Couldn't open source path.", e);
}
}
Log.i("ModelInfo loaded.");
}
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
public Collection<TableInfo> getTableInfos() {
return mTableInfos.values();
}
public TableInfo getTableInfo(Class<? extends Model> type) {
return mTableInfos.get(type);
}
public TypeSerializer getTypeSerializer(Class<?> type) {
return mTypeSerializers.get(type);
}
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
//////////////////////////////////////////////////////////////////////////////////////
private boolean loadModelFromMetaData(Configuration configuration) {
if (!configuration.isValid()) {
return false;
}
final List<Class<? extends Model>> models = configuration.getModelClasses();
if (models != null) {
for (Class<? extends Model> model : models) {
mTableInfos.put(model, new TableInfo(model));
}
}
final List<Class<? extends TypeSerializer>> typeSerializers = configuration.getTypeSerializers();
if (typeSerializers != null) {
for (Class<? extends TypeSerializer> typeSerializer : typeSerializers) {
try {
TypeSerializer instance = typeSerializer.newInstance();
mTypeSerializers.put(instance.getDeserializedType(), instance);
}
catch (InstantiationException e) {
Log.e("Couldn't instantiate TypeSerializer.", e);
}
catch (IllegalAccessException e) {
Log.e("IllegalAccessException", e);
}
}
}
return true;
}
private void scanForModel(Context context) throws IOException {
String packageName = context.getPackageName();
String sourcePath = context.getApplicationInfo().sourceDir;
List<String> paths = new ArrayList<String>();
if (sourcePath != null && !(new File(sourcePath).isDirectory())) {
DexFile dexfile = new DexFile(sourcePath);
Enumeration<String> entries = dexfile.entries();
while (entries.hasMoreElements()) {
paths.add(entries.nextElement());
}
}
// Robolectric fallback
else {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Enumeration<URL> resources = classLoader.getResources("");
while (resources.hasMoreElements()) {
String path = resources.nextElement().getFile();
if (path.contains("bin") || path.contains("classes")) {
paths.add(path);
}
}
}
for (String path : paths) {
File file = new File(path);
scanForModelClasses(file, packageName, context.getClassLoader());
}
}
private void scanForModelClasses(File path, String packageName, ClassLoader classLoader) {
if (path.isDirectory()) {
for (File file : path.listFiles()) {
scanForModelClasses(file, packageName, classLoader);
}
}
else {
String className = path.getName();
// Robolectric fallback
if (!path.getPath().equals(className)) {
className = path.getPath();
if (className.endsWith(".class")) {
className = className.substring(0, className.length() - 6);
}
else {
return;
}
className = className.replace(System.getProperty("file.separator"), ".");
int packageNameIndex = className.lastIndexOf(packageName);
if (packageNameIndex < 0) {
return;
}
className = className.substring(packageNameIndex);
}
try {
Class<?> discoveredClass = Class.forName(className, false, classLoader);
if (ReflectionUtils.isModel(discoveredClass)) {
@SuppressWarnings("unchecked")
Class<? extends Model> modelClass = (Class<? extends Model>) discoveredClass;
mTableInfos.put(modelClass, new TableInfo(modelClass));
}
else if (ReflectionUtils.isTypeSerializer(discoveredClass)) {
TypeSerializer instance = (TypeSerializer) discoveredClass.newInstance();
mTypeSerializers.put(instance.getDeserializedType(), instance);
}
}
catch (ClassNotFoundException e) {
Log.e("Couldn't create class.", e);
}
catch (InstantiationException e) {
Log.e("Couldn't instantiate TypeSerializer.", e);
}
catch (IllegalAccessException e) {
Log.e("IllegalAccessException", e);
}
}
}
}

@ -0,0 +1,124 @@
package com.activeandroid;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import android.text.TextUtils;
import android.util.Log;
import com.activeandroid.annotation.Column;
import com.activeandroid.annotation.Table;
import com.activeandroid.util.ReflectionUtils;
public final class TableInfo {
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE MEMBERS
//////////////////////////////////////////////////////////////////////////////////////
private Class<? extends Model> mType;
private String mTableName;
private String mIdName = Table.DEFAULT_ID_NAME;
private Map<Field, String> mColumnNames = new LinkedHashMap<Field, String>();
//////////////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////
public TableInfo(Class<? extends Model> type) {
mType = type;
final Table tableAnnotation = type.getAnnotation(Table.class);
if (tableAnnotation != null) {
mTableName = tableAnnotation.name();
mIdName = tableAnnotation.id();
}
else {
mTableName = type.getSimpleName();
}
// Manually add the id column since it is not declared like the other columns.
Field idField = getIdField(type);
mColumnNames.put(idField, mIdName);
List<Field> fields = new LinkedList<Field>(ReflectionUtils.getDeclaredColumnFields(type));
Collections.reverse(fields);
for (Field field : fields) {
if (field.isAnnotationPresent(Column.class)) {
final Column columnAnnotation = field.getAnnotation(Column.class);
String columnName = columnAnnotation.name();
if (TextUtils.isEmpty(columnName)) {
columnName = field.getName();
}
mColumnNames.put(field, columnName);
}
}
}
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
public Class<? extends Model> getType() {
return mType;
}
public String getTableName() {
return mTableName;
}
public String getIdName() {
return mIdName;
}
public Collection<Field> getFields() {
return mColumnNames.keySet();
}
public String getColumnName(Field field) {
return mColumnNames.get(field);
}
private Field getIdField(Class<?> type) {
if (type.equals(Model.class)) {
try {
return type.getDeclaredField("mId");
}
catch (NoSuchFieldException e) {
Log.e("Impossible!", e.toString());
}
}
else if (type.getSuperclass() != null) {
return getIdField(type.getSuperclass());
}
return null;
}
}

@ -0,0 +1,110 @@
package com.activeandroid.annotation;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
public enum ConflictAction {
ROLLBACK, ABORT, FAIL, IGNORE, REPLACE
}
public enum ForeignKeyAction {
SET_NULL, SET_DEFAULT, CASCADE, RESTRICT, NO_ACTION
}
public String name() default "";
public int length() default -1;
public boolean notNull() default false;
public ConflictAction onNullConflict() default ConflictAction.FAIL;
public ForeignKeyAction onDelete() default ForeignKeyAction.NO_ACTION;
public ForeignKeyAction onUpdate() default ForeignKeyAction.NO_ACTION;
public boolean unique() default false;
public ConflictAction onUniqueConflict() default ConflictAction.FAIL;
/*
* If set uniqueGroups = {"group_name"}, we will create a table constraint with group.
*
* Example:
*
* @Table(name = "table_name")
* public class Table extends Model {
* @Column(name = "member1", uniqueGroups = {"group1"}, onUniqueConflicts = {ConflictAction.FAIL})
* public String member1;
*
* @Column(name = "member2", uniqueGroups = {"group1", "group2"}, onUniqueConflicts = {ConflictAction.FAIL, ConflictAction.IGNORE})
* public String member2;
*
* @Column(name = "member3", uniqueGroups = {"group2"}, onUniqueConflicts = {ConflictAction.IGNORE})
* public String member3;
* }
*
* CREATE TABLE table_name (..., UNIQUE (member1, member2) ON CONFLICT FAIL, UNIQUE (member2, member3) ON CONFLICT IGNORE)
*/
public String[] uniqueGroups() default {};
public ConflictAction[] onUniqueConflicts() default {};
/*
* If set index = true, we will create a index with single column.
*
* Example:
*
* @Table(name = "table_name")
* public class Table extends Model {
* @Column(name = "member", index = true)
* public String member;
* }
*
* Execute CREATE INDEX index_table_name_member on table_name(member)
*/
public boolean index() default false;
/*
* If set indexGroups = {"group_name"}, we will create a index with group.
*
* Example:
*
* @Table(name = "table_name")
* public class Table extends Model {
* @Column(name = "member1", indexGroups = {"group1"})
* public String member1;
*
* @Column(name = "member2", indexGroups = {"group1", "group2"})
* public String member2;
*
* @Column(name = "member3", indexGroups = {"group2"})
* public String member3;
* }
*
* Execute CREATE INDEX index_table_name_group1 on table_name(member1, member2)
* Execute CREATE INDEX index_table_name_group2 on table_name(member2, member3)
*/
public String[] indexGroups() default {};
}

@ -0,0 +1,31 @@
package com.activeandroid.annotation;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
public static final String DEFAULT_ID_NAME = "Id";
public String name();
public String id() default DEFAULT_ID_NAME;
}

@ -0,0 +1,33 @@
package com.activeandroid.query;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.activeandroid.Model;
public final class Delete implements Sqlable {
public Delete() {
}
public From from(Class<? extends Model> table) {
return new From(table, this);
}
@Override
public String toSql() {
return "DELETE ";
}
}

@ -0,0 +1,344 @@
package com.activeandroid.query;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.text.TextUtils;
import com.activeandroid.Cache;
import com.activeandroid.Model;
import com.activeandroid.query.Join.JoinType;
import com.activeandroid.util.Log;
import com.activeandroid.util.SQLiteUtils;
import java.util.ArrayList;
import java.util.List;
public final class From implements Sqlable {
private Sqlable mQueryBase;
private Class<? extends Model> mType;
private String mAlias;
private List<Join> mJoins;
private final StringBuilder mWhere = new StringBuilder();
private String mGroupBy;
private String mHaving;
private String mOrderBy;
private String mLimit;
private String mOffset;
private List<Object> mArguments;
public From(Class<? extends Model> table, Sqlable queryBase) {
mType = table;
mJoins = new ArrayList<Join>();
mQueryBase = queryBase;
mJoins = new ArrayList<Join>();
mArguments = new ArrayList<Object>();
}
public From as(String alias) {
mAlias = alias;
return this;
}
public Join join(Class<? extends Model> table) {
Join join = new Join(this, table, null);
mJoins.add(join);
return join;
}
public Join leftJoin(Class<? extends Model> table) {
Join join = new Join(this, table, JoinType.LEFT);
mJoins.add(join);
return join;
}
public Join outerJoin(Class<? extends Model> table) {
Join join = new Join(this, table, JoinType.OUTER);
mJoins.add(join);
return join;
}
public Join innerJoin(Class<? extends Model> table) {
Join join = new Join(this, table, JoinType.INNER);
mJoins.add(join);
return join;
}
public Join crossJoin(Class<? extends Model> table) {
Join join = new Join(this, table, JoinType.CROSS);
mJoins.add(join);
return join;
}
public From where(String clause) {
// Chain conditions if a previous condition exists.
if (mWhere.length() > 0) {
mWhere.append(" AND ");
}
mWhere.append(clause);
return this;
}
public From where(String clause, Object... args) {
where(clause).addArguments(args);
return this;
}
public From and(String clause) {
return where(clause);
}
public From and(String clause, Object... args) {
return where(clause, args);
}
public From or(String clause) {
if (mWhere.length() > 0) {
mWhere.append(" OR ");
}
mWhere.append(clause);
return this;
}
public From or(String clause, Object... args) {
or(clause).addArguments(args);
return this;
}
public From groupBy(String groupBy) {
mGroupBy = groupBy;
return this;
}
public From having(String having) {
mHaving = having;
return this;
}
public From orderBy(String orderBy) {
mOrderBy = orderBy;
return this;
}
public From limit(int limit) {
return limit(String.valueOf(limit));
}
public From limit(String limit) {
mLimit = limit;
return this;
}
public From offset(int offset) {
return offset(String.valueOf(offset));
}
public From offset(String offset) {
mOffset = offset;
return this;
}
void addArguments(Object[] args) {
for(Object arg : args) {
if (arg.getClass() == boolean.class || arg.getClass() == Boolean.class) {
arg = (arg.equals(true) ? 1 : 0);
}
mArguments.add(arg);
}
}
private void addFrom(final StringBuilder sql) {
sql.append("FROM ");
sql.append(Cache.getTableName(mType)).append(" ");
if (mAlias != null) {
sql.append("AS ");
sql.append(mAlias);
sql.append(" ");
}
}
private void addJoins(final StringBuilder sql) {
for (final Join join : mJoins) {
sql.append(join.toSql());
}
}
private void addWhere(final StringBuilder sql) {
if (mWhere.length() > 0) {
sql.append("WHERE ");
sql.append(mWhere);
sql.append(" ");
}
}
private void addGroupBy(final StringBuilder sql) {
if (mGroupBy != null) {
sql.append("GROUP BY ");
sql.append(mGroupBy);
sql.append(" ");
}
}
private void addHaving(final StringBuilder sql) {
if (mHaving != null) {
sql.append("HAVING ");
sql.append(mHaving);
sql.append(" ");
}
}
private void addOrderBy(final StringBuilder sql) {
if (mOrderBy != null) {
sql.append("ORDER BY ");
sql.append(mOrderBy);
sql.append(" ");
}
}
private void addLimit(final StringBuilder sql) {
if (mLimit != null) {
sql.append("LIMIT ");
sql.append(mLimit);
sql.append(" ");
}
}
private void addOffset(final StringBuilder sql) {
if (mOffset != null) {
sql.append("OFFSET ");
sql.append(mOffset);
sql.append(" ");
}
}
private String sqlString(final StringBuilder sql) {
final String sqlString = sql.toString().trim();
// Don't waste time building the string
// unless we're going to log it.
if (Log.isEnabled()) {
Log.v(sqlString + " " + TextUtils.join(",", getArguments()));
}
return sqlString;
}
@Override
public String toSql() {
final StringBuilder sql = new StringBuilder();
sql.append(mQueryBase.toSql());
addFrom(sql);
addJoins(sql);
addWhere(sql);
addGroupBy(sql);
addHaving(sql);
addOrderBy(sql);
addLimit(sql);
addOffset(sql);
return sqlString(sql);
}
public String toExistsSql() {
final StringBuilder sql = new StringBuilder();
sql.append("SELECT EXISTS(SELECT 1 ");
addFrom(sql);
addJoins(sql);
addWhere(sql);
addGroupBy(sql);
addHaving(sql);
addLimit(sql);
addOffset(sql);
sql.append(")");
return sqlString(sql);
}
public String toCountSql() {
final StringBuilder sql = new StringBuilder();
sql.append("SELECT COUNT(*) ");
addFrom(sql);
addJoins(sql);
addWhere(sql);
addGroupBy(sql);
addHaving(sql);
addLimit(sql);
addOffset(sql);
return sqlString(sql);
}
public <T extends Model> List<T> execute() {
if (mQueryBase instanceof Select) {
return SQLiteUtils.rawQuery(mType, toSql(), getArguments());
} else {
SQLiteUtils.execSql(toSql(), getArguments());
return null;
}
}
public <T extends Model> T executeSingle() {
if (mQueryBase instanceof Select) {
limit(1);
return (T) SQLiteUtils.rawQuerySingle(mType, toSql(), getArguments());
} else {
limit(1);
SQLiteUtils.rawQuerySingle(mType, toSql(), getArguments()).delete();
return null;
}
}
/**
* Gets a value indicating whether the query returns any rows.
* @return <code>true</code> if the query returns at least one row; otherwise, <code>false</code>.
*/
public boolean exists() {
return SQLiteUtils.intQuery(toExistsSql(), getArguments()) != 0;
}
/**
* Gets the number of rows returned by the query.
*/
public int count() {
return SQLiteUtils.intQuery(toCountSql(), getArguments());
}
public String[] getArguments() {
final int size = mArguments.size();
final String[] args = new String[size];
for (int i = 0; i < size; i++) {
args[i] = mArguments.get(i).toString();
}
return args;
}
}

@ -0,0 +1,94 @@
package com.activeandroid.query;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.text.TextUtils;
import com.activeandroid.Cache;
import com.activeandroid.Model;
public final class Join implements Sqlable {
static enum JoinType {
LEFT, OUTER, INNER, CROSS
}
private From mFrom;
private Class<? extends Model> mType;
private String mAlias;
private JoinType mJoinType;
private String mOn;
private String[] mUsing;
Join(From from, Class<? extends Model> table, JoinType joinType) {
mFrom = from;
mType = table;
mJoinType = joinType;
}
public Join as(String alias) {
mAlias = alias;
return this;
}
public From on(String on) {
mOn = on;
return mFrom;
}
public From on(String on, Object... args) {
mOn = on;
mFrom.addArguments(args);
return mFrom;
}
public From using(String... columns) {
mUsing = columns;
return mFrom;
}
@Override
public String toSql() {
StringBuilder sql = new StringBuilder();
if (mJoinType != null) {
sql.append(mJoinType.toString()).append(" ");
}
sql.append("JOIN ");
sql.append(Cache.getTableName(mType));
sql.append(" ");
if (mAlias != null) {
sql.append("AS ");
sql.append(mAlias);
sql.append(" ");
}
if (mOn != null) {
sql.append("ON ");
sql.append(mOn);
sql.append(" ");
}
else if (mUsing != null) {
sql.append("USING (");
sql.append(TextUtils.join(", ", mUsing));
sql.append(") ");
}
return sql.toString();
}
}

@ -0,0 +1,93 @@
package com.activeandroid.query;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.text.TextUtils;
import com.activeandroid.Model;
public final class Select implements Sqlable {
private String[] mColumns;
private boolean mDistinct = false;
private boolean mAll = false;
public Select() {
}
public Select(String... columns) {
mColumns = columns;
}
public Select(Column... columns) {
final int size = columns.length;
mColumns = new String[size];
for (int i = 0; i < size; i++) {
mColumns[i] = columns[i].name + " AS " + columns[i].alias;
}
}
public Select distinct() {
mDistinct = true;
mAll = false;
return this;
}
public Select all() {
mDistinct = false;
mAll = true;
return this;
}
public From from(Class<? extends Model> table) {
return new From(table, this);
}
public static class Column {
String name;
String alias;
public Column(String name, String alias) {
this.name = name;
this.alias = alias;
}
}
@Override
public String toSql() {
StringBuilder sql = new StringBuilder();
sql.append("SELECT ");
if (mDistinct) {
sql.append("DISTINCT ");
}
else if (mAll) {
sql.append("ALL ");
}
if (mColumns != null && mColumns.length > 0) {
sql.append(TextUtils.join(", ", mColumns) + " ");
}
else {
sql.append("* ");
}
return sql.toString();
}
}

@ -0,0 +1,103 @@
package com.activeandroid.query;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.activeandroid.util.SQLiteUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public final class Set implements Sqlable {
private Update mUpdate;
private String mSet;
private String mWhere;
private List<Object> mSetArguments;
private List<Object> mWhereArguments;
public Set(Update queryBase, String set) {
mUpdate = queryBase;
mSet = set;
mSetArguments = new ArrayList<Object>();
mWhereArguments = new ArrayList<Object>();
}
public Set(Update queryBase, String set, Object... args) {
mUpdate = queryBase;
mSet = set;
mSetArguments = new ArrayList<Object>();
mWhereArguments = new ArrayList<Object>();
mSetArguments.addAll(Arrays.asList(args));
}
public Set where(String where) {
mWhere = where;
mWhereArguments.clear();
return this;
}
public Set where(String where, Object... args) {
mWhere = where;
mWhereArguments.clear();
mWhereArguments.addAll(Arrays.asList(args));
return this;
}
@Override
public String toSql() {
StringBuilder sql = new StringBuilder();
sql.append(mUpdate.toSql());
sql.append("SET ");
sql.append(mSet);
sql.append(" ");
if (mWhere != null) {
sql.append("WHERE ");
sql.append(mWhere);
sql.append(" ");
}
return sql.toString();
}
public void execute() {
SQLiteUtils.execSql(toSql(), getArguments());
}
public String[] getArguments() {
final int setSize = mSetArguments.size();
final int whereSize = mWhereArguments.size();
final String[] args = new String[setSize + whereSize];
for (int i = 0; i < setSize; i++) {
args[i] = mSetArguments.get(i).toString();
}
for (int i = 0; i < whereSize; i++) {
args[i + setSize] = mWhereArguments.get(i).toString();
}
return args;
}
}

@ -0,0 +1,21 @@
package com.activeandroid.query;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public interface Sqlable {
public String toSql();
}

@ -0,0 +1,50 @@
package com.activeandroid.query;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.activeandroid.Cache;
import com.activeandroid.Model;
public final class Update implements Sqlable {
private Class<? extends Model> mType;
public Update(Class<? extends Model> table) {
mType = table;
}
public Set set(String set) {
return new Set(this, set);
}
public Set set(String set, Object... args) {
return new Set(this, set, args);
}
Class<? extends Model> getType() {
return mType;
}
@Override
public String toSql() {
StringBuilder sql = new StringBuilder();
sql.append("UPDATE ");
sql.append(Cache.getTableName(mType));
sql.append(" ");
return sql.toString();
}
}

@ -0,0 +1,29 @@
package com.activeandroid.serializer;
import java.math.BigDecimal;
public final class BigDecimalSerializer extends TypeSerializer {
public Class<?> getDeserializedType() {
return BigDecimal.class;
}
public Class<?> getSerializedType() {
return String.class;
}
public String serialize(Object data) {
if (data == null) {
return null;
}
return ((BigDecimal) data).toString();
}
public BigDecimal deserialize(Object data) {
if (data == null) {
return null;
}
return new BigDecimal((String) data);
}
}

@ -0,0 +1,40 @@
package com.activeandroid.serializer;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.util.Calendar;
public final class CalendarSerializer extends TypeSerializer {
public Class<?> getDeserializedType() {
return Calendar.class;
}
public Class<?> getSerializedType() {
return long.class;
}
public Long serialize(Object data) {
return ((Calendar) data).getTimeInMillis();
}
public Calendar deserialize(Object data) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis((Long) data);
return calendar;
}
}

@ -0,0 +1,46 @@
package com.activeandroid.serializer;
import java.io.File;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public final class FileSerializer extends TypeSerializer {
public Class<?> getDeserializedType() {
return File.class;
}
public Class<?> getSerializedType() {
return String.class;
}
public String serialize(Object data) {
if (data == null) {
return null;
}
return ((File) data).toString();
}
public File deserialize(Object data) {
if (data == null) {
return null;
}
return new File((String) data);
}
}

@ -0,0 +1,45 @@
package com.activeandroid.serializer;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.sql.Date;
public final class SqlDateSerializer extends TypeSerializer {
public Class<?> getDeserializedType() {
return Date.class;
}
public Class<?> getSerializedType() {
return long.class;
}
public Long serialize(Object data) {
if (data == null) {
return null;
}
return ((Date) data).getTime();
}
public Date deserialize(Object data) {
if (data == null) {
return null;
}
return new Date((Long) data);
}
}

@ -0,0 +1,27 @@
package com.activeandroid.serializer;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public abstract class TypeSerializer {
public abstract Class<?> getDeserializedType();
public abstract Class<?> getSerializedType();
public abstract Object serialize(Object data);
public abstract Object deserialize(Object data);
}

@ -0,0 +1,29 @@
package com.activeandroid.serializer;
import java.util.UUID;
public final class UUIDSerializer extends TypeSerializer {
public Class<?> getDeserializedType() {
return UUID.class;
}
public Class<?> getSerializedType() {
return String.class;
}
public String serialize(Object data) {
if (data == null) {
return null;
}
return ((UUID) data).toString();
}
public UUID deserialize(Object data) {
if (data == null) {
return null;
}
return UUID.fromString((String)data);
}
}

@ -0,0 +1,45 @@
package com.activeandroid.serializer;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.util.Date;
public final class UtilDateSerializer extends TypeSerializer {
public Class<?> getDeserializedType() {
return Date.class;
}
public Class<?> getSerializedType() {
return long.class;
}
public Long serialize(Object data) {
if (data == null) {
return null;
}
return ((Date) data).getTime();
}
public Date deserialize(Object data) {
if (data == null) {
return null;
}
return new Date((Long) data);
}
}

@ -0,0 +1,71 @@
package com.activeandroid.util;
/*
* Copyright (C) 2014 Markus Pfeiffer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.database.Cursor;
import java.io.Closeable;
import java.io.IOException;
import com.activeandroid.util.Log;
public class IOUtils {
/**
* <p>
* Unconditionally close a {@link Closeable}.
* </p>
* Equivalent to {@link Closeable#close()}, except any exceptions will be ignored. This is
* typically used in finally blocks.
* @param closeable A {@link Closeable} to close.
*/
public static void closeQuietly(final Closeable closeable) {
if (closeable == null) {
return;
}
try {
closeable.close();
} catch (final IOException e) {
Log.e("Couldn't close closeable.", e);
}
}
/**
* <p>
* Unconditionally close a {@link Cursor}.
* </p>
* Equivalent to {@link Cursor#close()}, except any exceptions will be ignored. This is
* typically used in finally blocks.
* @param cursor A {@link Cursor} to close.
*/
public static void closeQuietly(final Cursor cursor) {
if (cursor == null) {
return;
}
try {
cursor.close();
} catch (final Exception e) {
Log.e("Couldn't close cursor.", e);
}
}
}

@ -0,0 +1,196 @@
package com.activeandroid.util;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public final class Log {
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC MEMBERS
//////////////////////////////////////////////////////////////////////////////////////
private static String sTag = "ActiveAndroid";
private static boolean sEnabled = false;
//////////////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////
private Log() {
}
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
public static boolean isEnabled() {
return sEnabled;
}
public static void setEnabled(boolean enabled) {
sEnabled = enabled;
}
public static boolean isLoggingEnabled() {
return sEnabled;
}
public static int v(String msg) {
if (sEnabled) {
return android.util.Log.v(sTag, msg);
}
return 0;
}
public static int v(String tag, String msg) {
if (sEnabled) {
return android.util.Log.v(tag, msg);
}
return 0;
}
public static int v(String msg, Throwable tr) {
if (sEnabled) {
return android.util.Log.v(sTag, msg, tr);
}
return 0;
}
public static int v(String tag, String msg, Throwable tr) {
if (sEnabled) {
return android.util.Log.v(tag, msg, tr);
}
return 0;
}
public static int d(String msg) {
if (sEnabled) {
return android.util.Log.d(sTag, msg);
}
return 0;
}
public static int d(String tag, String msg) {
if (sEnabled) {
return android.util.Log.d(tag, msg);
}
return 0;
}
public static int d(String msg, Throwable tr) {
if (sEnabled) {
return android.util.Log.d(sTag, msg, tr);
}
return 0;
}
public static int d(String tag, String msg, Throwable tr) {
if (sEnabled) {
return android.util.Log.d(tag, msg, tr);
}
return 0;
}
public static int i(String msg) {
if (sEnabled) {
return android.util.Log.i(sTag, msg);
}
return 0;
}
public static int i(String tag, String msg) {
if (sEnabled) {
return android.util.Log.i(tag, msg);
}
return 0;
}
public static int i(String msg, Throwable tr) {
if (sEnabled) {
return android.util.Log.i(sTag, msg, tr);
}
return 0;
}
public static int i(String tag, String msg, Throwable tr) {
if (sEnabled) {
return android.util.Log.i(tag, msg, tr);
}
return 0;
}
public static int w(String msg) {
if (sEnabled) {
return android.util.Log.w(sTag, msg);
}
return 0;
}
public static int w(String tag, String msg) {
if (sEnabled) {
return android.util.Log.w(tag, msg);
}
return 0;
}
public static int w(String msg, Throwable tr) {
if (sEnabled) {
return android.util.Log.w(sTag, msg, tr);
}
return 0;
}
public static int w(String tag, String msg, Throwable tr) {
if (sEnabled) {
return android.util.Log.w(tag, msg, tr);
}
return 0;
}
public static int e(String msg) {
if (sEnabled) {
return android.util.Log.e(sTag, msg);
}
return 0;
}
public static int e(String tag, String msg) {
if (sEnabled) {
return android.util.Log.e(tag, msg);
}
return 0;
}
public static int e(String msg, Throwable tr) {
if (sEnabled) {
return android.util.Log.e(sTag, msg, tr);
}
return 0;
}
public static int e(String tag, String msg, Throwable tr) {
if (sEnabled) {
return android.util.Log.e(tag, msg, tr);
}
return 0;
}
public static int t(String msg, Object... args) {
if (sEnabled) {
return android.util.Log.v("test", String.format(msg, args));
}
return 0;
}
}

@ -0,0 +1,141 @@
package com.activeandroid.util;
/*
NaturalOrderComparator.java -- Perform 'natural order' comparisons of strings in Java.
Copyright (C) 2003 by Pierre-Luc Paour <natorder@paour.com>
Based on the C version by Martin Pool, of which this is more or less a straight conversion.
Copyright (C) 2000 by Martin Pool <mbp@humbug.org.au>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
import java.util.Comparator;
public class NaturalOrderComparator implements Comparator<Object> {
int compareRight(String a, String b) {
int bias = 0;
int ia = 0;
int ib = 0;
// The longest run of digits wins. That aside, the greatest
// value wins, but we can't know that it will until we've scanned
// both numbers to know that they have the same magnitude, so we
// remember it in BIAS.
for (;; ia++, ib++) {
char ca = charAt(a, ia);
char cb = charAt(b, ib);
if (!Character.isDigit(ca) && !Character.isDigit(cb)) {
return bias;
}
else if (!Character.isDigit(ca)) {
return -1;
}
else if (!Character.isDigit(cb)) {
return +1;
}
else if (ca < cb) {
if (bias == 0) {
bias = -1;
}
}
else if (ca > cb) {
if (bias == 0)
bias = +1;
}
else if (ca == 0 && cb == 0) {
return bias;
}
}
}
public int compare(Object o1, Object o2) {
String a = o1.toString();
String b = o2.toString();
int ia = 0, ib = 0;
int nza = 0, nzb = 0;
char ca, cb;
int result;
while (true) {
// only count the number of zeroes leading the last number compared
nza = nzb = 0;
ca = charAt(a, ia);
cb = charAt(b, ib);
// skip over leading spaces or zeros
while (Character.isSpaceChar(ca) || ca == '0') {
if (ca == '0') {
nza++;
}
else {
// only count consecutive zeroes
nza = 0;
}
ca = charAt(a, ++ia);
}
while (Character.isSpaceChar(cb) || cb == '0') {
if (cb == '0') {
nzb++;
}
else {
// only count consecutive zeroes
nzb = 0;
}
cb = charAt(b, ++ib);
}
// process run of digits
if (Character.isDigit(ca) && Character.isDigit(cb)) {
if ((result = compareRight(a.substring(ia), b.substring(ib))) != 0) {
return result;
}
}
if (ca == 0 && cb == 0) {
// The strings compare the same. Perhaps the caller
// will want to call strcmp to break the tie.
return nza - nzb;
}
if (ca < cb) {
return -1;
}
else if (ca > cb) {
return +1;
}
++ia;
++ib;
}
}
static char charAt(String s, int i) {
if (i >= s.length()) {
return 0;
}
else {
return s.charAt(i);
}
}
}

@ -0,0 +1,110 @@
package com.activeandroid.util;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.Set;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import com.activeandroid.Model;
import com.activeandroid.annotation.Column;
import com.activeandroid.serializer.TypeSerializer;
public final class ReflectionUtils {
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
public static boolean isModel(Class<?> type) {
return isSubclassOf(type, Model.class) && (!Modifier.isAbstract(type.getModifiers()));
}
public static boolean isTypeSerializer(Class<?> type) {
return isSubclassOf(type, TypeSerializer.class);
}
// Meta-data
@SuppressWarnings("unchecked")
public static <T> T getMetaData(Context context, String name) {
try {
final ApplicationInfo ai = context.getPackageManager().getApplicationInfo(context.getPackageName(),
PackageManager.GET_META_DATA);
if (ai.metaData != null) {
return (T) ai.metaData.get(name);
}
}
catch (Exception e) {
Log.w("Couldn't find meta-data: " + name);
}
return null;
}
public static Set<Field> getDeclaredColumnFields(Class<?> type) {
Set<Field> declaredColumnFields = Collections.emptySet();
if (ReflectionUtils.isSubclassOf(type, Model.class) || Model.class.equals(type)) {
declaredColumnFields = new LinkedHashSet<Field>();
Field[] fields = type.getDeclaredFields();
Arrays.sort(fields, new Comparator<Field>() {
@Override
public int compare(Field field1, Field field2) {
return field2.getName().compareTo(field1.getName());
}
});
for (Field field : fields) {
if (field.isAnnotationPresent(Column.class)) {
declaredColumnFields.add(field);
}
}
Class<?> parentType = type.getSuperclass();
if (parentType != null) {
declaredColumnFields.addAll(getDeclaredColumnFields(parentType));
}
}
return declaredColumnFields;
}
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
//////////////////////////////////////////////////////////////////////////////////////
public static boolean isSubclassOf(Class<?> type, Class<?> superClass) {
if (type.getSuperclass() != null) {
if (type.getSuperclass().equals(superClass)) {
return true;
}
return isSubclassOf(type.getSuperclass(), superClass);
}
return false;
}
}

@ -0,0 +1,406 @@
package com.activeandroid.util;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.database.Cursor;
import android.os.Build;
import android.text.TextUtils;
import com.activeandroid.Cache;
import com.activeandroid.Model;
import com.activeandroid.TableInfo;
import com.activeandroid.annotation.Column;
import com.activeandroid.annotation.Column.ConflictAction;
import com.activeandroid.serializer.TypeSerializer;
import java.lang.Long;
import java.lang.String;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public final class SQLiteUtils {
//////////////////////////////////////////////////////////////////////////////////////
// ENUMERATIONS
//////////////////////////////////////////////////////////////////////////////////////
public enum SQLiteType {
INTEGER, REAL, TEXT, BLOB
}
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC CONSTANTS
//////////////////////////////////////////////////////////////////////////////////////
public static final boolean FOREIGN_KEYS_SUPPORTED = Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO;
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE CONTSANTS
//////////////////////////////////////////////////////////////////////////////////////
@SuppressWarnings("serial")
private static final HashMap<Class<?>, SQLiteType> TYPE_MAP = new HashMap<Class<?>, SQLiteType>() {
{
put(byte.class, SQLiteType.INTEGER);
put(short.class, SQLiteType.INTEGER);
put(int.class, SQLiteType.INTEGER);
put(long.class, SQLiteType.INTEGER);
put(float.class, SQLiteType.REAL);
put(double.class, SQLiteType.REAL);
put(boolean.class, SQLiteType.INTEGER);
put(char.class, SQLiteType.TEXT);
put(byte[].class, SQLiteType.BLOB);
put(Byte.class, SQLiteType.INTEGER);
put(Short.class, SQLiteType.INTEGER);
put(Integer.class, SQLiteType.INTEGER);
put(Long.class, SQLiteType.INTEGER);
put(Float.class, SQLiteType.REAL);
put(Double.class, SQLiteType.REAL);
put(Boolean.class, SQLiteType.INTEGER);
put(Character.class, SQLiteType.TEXT);
put(String.class, SQLiteType.TEXT);
put(Byte[].class, SQLiteType.BLOB);
}
};
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE MEMBERS
//////////////////////////////////////////////////////////////////////////////////////
private static HashMap<String, List<String>> sIndexGroupMap;
private static HashMap<String, List<String>> sUniqueGroupMap;
private static HashMap<String, ConflictAction> sOnUniqueConflictsMap;
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
public static void execSql(String sql) {
Cache.openDatabase().execSQL(sql);
}
public static void execSql(String sql, Object[] bindArgs) {
Cache.openDatabase().execSQL(sql, bindArgs);
}
public static <T extends Model> List<T> rawQuery(Class<? extends Model> type, String sql, String[] selectionArgs) {
Cursor cursor = Cache.openDatabase().rawQuery(sql, selectionArgs);
List<T> entities = processCursor(type, cursor);
cursor.close();
return entities;
}
public static int intQuery(final String sql, final String[] selectionArgs) {
final Cursor cursor = Cache.openDatabase().rawQuery(sql, selectionArgs);
final int number = processIntCursor(cursor);
cursor.close();
return number;
}
public static <T extends Model> T rawQuerySingle(Class<? extends Model> type, String sql, String[] selectionArgs) {
List<T> entities = rawQuery(type, sql, selectionArgs);
if (entities.size() > 0) {
return entities.get(0);
}
return null;
}
// Database creation
public static ArrayList<String> createUniqueDefinition(TableInfo tableInfo) {
final ArrayList<String> definitions = new ArrayList<String>();
sUniqueGroupMap = new HashMap<String, List<String>>();
sOnUniqueConflictsMap = new HashMap<String, ConflictAction>();
for (Field field : tableInfo.getFields()) {
createUniqueColumnDefinition(tableInfo, field);
}
if (sUniqueGroupMap.isEmpty()) {
return definitions;
}
Set<String> keySet = sUniqueGroupMap.keySet();
for (String key : keySet) {
List<String> group = sUniqueGroupMap.get(key);
ConflictAction conflictAction = sOnUniqueConflictsMap.get(key);
definitions.add(String.format("UNIQUE (%s) ON CONFLICT %s",
TextUtils.join(", ", group), conflictAction.toString()));
}
return definitions;
}
public static void createUniqueColumnDefinition(TableInfo tableInfo, Field field) {
final String name = tableInfo.getColumnName(field);
final Column column = field.getAnnotation(Column.class);
if (field.getName().equals("mId")) {
return;
}
String[] groups = column.uniqueGroups();
ConflictAction[] conflictActions = column.onUniqueConflicts();
if (groups.length != conflictActions.length)
return;
for (int i = 0; i < groups.length; i++) {
String group = groups[i];
ConflictAction conflictAction = conflictActions[i];
if (TextUtils.isEmpty(group))
continue;
List<String> list = sUniqueGroupMap.get(group);
if (list == null) {
list = new ArrayList<String>();
}
list.add(name);
sUniqueGroupMap.put(group, list);
sOnUniqueConflictsMap.put(group, conflictAction);
}
}
public static String[] createIndexDefinition(TableInfo tableInfo) {
final ArrayList<String> definitions = new ArrayList<String>();
sIndexGroupMap = new HashMap<String, List<String>>();
for (Field field : tableInfo.getFields()) {
createIndexColumnDefinition(tableInfo, field);
}
if (sIndexGroupMap.isEmpty()) {
return new String[0];
}
for (Map.Entry<String, List<String>> entry : sIndexGroupMap.entrySet()) {
definitions.add(String.format("CREATE INDEX IF NOT EXISTS %s on %s(%s);",
"index_" + tableInfo.getTableName() + "_" + entry.getKey(),
tableInfo.getTableName(), TextUtils.join(", ", entry.getValue())));
}
return definitions.toArray(new String[definitions.size()]);
}
public static void createIndexColumnDefinition(TableInfo tableInfo, Field field) {
final String name = tableInfo.getColumnName(field);
final Column column = field.getAnnotation(Column.class);
if (field.getName().equals("mId")) {
return;
}
if (column.index()) {
List<String> list = new ArrayList<String>();
list.add(name);
sIndexGroupMap.put(name, list);
}
String[] groups = column.indexGroups();
for (String group : groups) {
if (TextUtils.isEmpty(group))
continue;
List<String> list = sIndexGroupMap.get(group);
if (list == null) {
list = new ArrayList<String>();
}
list.add(name);
sIndexGroupMap.put(group, list);
}
}
public static String createTableDefinition(TableInfo tableInfo) {
final ArrayList<String> definitions = new ArrayList<String>();
for (Field field : tableInfo.getFields()) {
String definition = createColumnDefinition(tableInfo, field);
if (!TextUtils.isEmpty(definition)) {
definitions.add(definition);
}
}
definitions.addAll(createUniqueDefinition(tableInfo));
return String.format("CREATE TABLE IF NOT EXISTS %s (%s);", tableInfo.getTableName(),
TextUtils.join(", ", definitions));
}
@SuppressWarnings("unchecked")
public static String createColumnDefinition(TableInfo tableInfo, Field field) {
StringBuilder definition = new StringBuilder();
Class<?> type = field.getType();
final String name = tableInfo.getColumnName(field);
final TypeSerializer typeSerializer = Cache.getParserForType(field.getType());
final Column column = field.getAnnotation(Column.class);
if (typeSerializer != null) {
type = typeSerializer.getSerializedType();
}
if (TYPE_MAP.containsKey(type)) {
definition.append(name);
definition.append(" ");
definition.append(TYPE_MAP.get(type).toString());
}
else if (ReflectionUtils.isModel(type)) {
definition.append(name);
definition.append(" ");
definition.append(SQLiteType.INTEGER.toString());
}
else if (ReflectionUtils.isSubclassOf(type, Enum.class)) {
definition.append(name);
definition.append(" ");
definition.append(SQLiteType.TEXT.toString());
}
if (!TextUtils.isEmpty(definition)) {
if (name.equals(tableInfo.getIdName())) {
definition.append(" PRIMARY KEY AUTOINCREMENT");
}else if(column!=null){
if (column.length() > -1) {
definition.append("(");
definition.append(column.length());
definition.append(")");
}
if (column.notNull()) {
definition.append(" NOT NULL ON CONFLICT ");
definition.append(column.onNullConflict().toString());
}
if (column.unique()) {
definition.append(" UNIQUE ON CONFLICT ");
definition.append(column.onUniqueConflict().toString());
}
}
if (FOREIGN_KEYS_SUPPORTED && ReflectionUtils.isModel(type)) {
definition.append(" REFERENCES ");
definition.append(Cache.getTableInfo((Class<? extends Model>) type).getTableName());
definition.append("("+tableInfo.getIdName()+")");
definition.append(" ON DELETE ");
definition.append(column.onDelete().toString().replace("_", " "));
definition.append(" ON UPDATE ");
definition.append(column.onUpdate().toString().replace("_", " "));
}
}
else {
Log.e("No type mapping for: " + type.toString());
}
return definition.toString();
}
@SuppressWarnings("unchecked")
public static <T extends Model> List<T> processCursor(Class<? extends Model> type, Cursor cursor) {
TableInfo tableInfo = Cache.getTableInfo(type);
String idName = tableInfo.getIdName();
final List<T> entities = new ArrayList<T>();
try {
Constructor<?> entityConstructor = type.getConstructor();
if (cursor.moveToFirst()) {
/**
* Obtain the columns ordered to fix issue #106 (https://github.com/pardom/ActiveAndroid/issues/106)
* when the cursor have multiple columns with same name obtained from join tables.
*/
List<String> columnsOrdered = new ArrayList<String>(Arrays.asList(cursor.getColumnNames()));
do {
Model entity = Cache.getEntity(type, cursor.getLong(columnsOrdered.indexOf(idName)));
if (entity == null) {
entity = (T) entityConstructor.newInstance();
}
entity.loadFromCursor(cursor);
entities.add((T) entity);
}
while (cursor.moveToNext());
}
}
catch (NoSuchMethodException e) {
throw new RuntimeException(
"Your model " + type.getName() + " does not define a default " +
"constructor. The default constructor is required for " +
"now in ActiveAndroid models, as the process to " +
"populate the ORM model is : " +
"1. instantiate default model " +
"2. populate fields"
);
}
catch (Exception e) {
Log.e("Failed to process cursor.", e);
}
return entities;
}
private static int processIntCursor(final Cursor cursor) {
if (cursor.moveToFirst()) {
return cursor.getInt(0);
}
return 0;
}
public static List<String> lexSqlScript(String sqlScript) {
ArrayList<String> sl = new ArrayList<String>();
boolean inString = false, quoteNext = false;
StringBuilder b = new StringBuilder(100);
for (int i = 0; i < sqlScript.length(); i++) {
char c = sqlScript.charAt(i);
if (c == ';' && !inString && !quoteNext) {
sl.add(b.toString());
b = new StringBuilder(100);
inString = false;
quoteNext = false;
continue;
}
if (c == '\'' && !quoteNext) {
inString = !inString;
}
quoteNext = c == '\\' && !quoteNext;
b.append(c);
}
if (b.length() > 0) {
sl.add(b.toString());
}
return sl;
}
}

@ -0,0 +1,110 @@
package com.activeandroid.util;
/*
* Copyright (C) 2014 Markus Pfeiffer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class SqlParser {
public final static int STATE_NONE = 0;
public final static int STATE_STRING = 1;
public final static int STATE_COMMENT = 2;
public final static int STATE_COMMENT_BLOCK = 3;
public static List<String> parse(final InputStream stream) throws IOException {
final BufferedInputStream buffer = new BufferedInputStream(stream);
final List<String> commands = new ArrayList<String>();
final StringBuffer sb = new StringBuffer();
try {
final Tokenizer tokenizer = new Tokenizer(buffer);
int state = STATE_NONE;
while (tokenizer.hasNext()) {
final char c = (char) tokenizer.next();
if (state == STATE_COMMENT_BLOCK) {
if (tokenizer.skip("*/")) {
state = STATE_NONE;
}
continue;
} else if (state == STATE_COMMENT) {
if (isNewLine(c)) {
state = STATE_NONE;
}
continue;
} else if (state == STATE_NONE && tokenizer.skip("/*")) {
state = STATE_COMMENT_BLOCK;
continue;
} else if (state == STATE_NONE && tokenizer.skip("--")) {
state = STATE_COMMENT;
continue;
} else if (state == STATE_NONE && c == ';') {
final String command = sb.toString().trim();
commands.add(command);
sb.setLength(0);
continue;
} else if (state == STATE_NONE && c == '\'') {
state = STATE_STRING;
} else if (state == STATE_STRING && c == '\'') {
state = STATE_NONE;
}
if (state == STATE_NONE || state == STATE_STRING) {
if (state == STATE_NONE && isWhitespace(c)) {
if (sb.length() > 0 && sb.charAt(sb.length() - 1) != ' ') {
sb.append(' ');
}
} else {
sb.append(c);
}
}
}
} finally {
IOUtils.closeQuietly(buffer);
}
if (sb.length() > 0) {
commands.add(sb.toString().trim());
}
return commands;
}
private static boolean isNewLine(final char c) {
return c == '\r' || c == '\n';
}
private static boolean isWhitespace(final char c) {
return c == '\r' || c == '\n' || c == '\t' || c == ' ';
}
}

@ -0,0 +1,76 @@
package com.activeandroid.util;
/*
* Copyright (C) 2014 Markus Pfeiffer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOException;
import java.io.InputStream;
public class Tokenizer {
private final InputStream mStream;
private boolean mIsNext;
private int mCurrent;
public Tokenizer(final InputStream in) {
this.mStream = in;
}
public boolean hasNext() throws IOException {
if (!this.mIsNext) {
this.mIsNext = true;
this.mCurrent = this.mStream.read();
}
return this.mCurrent != -1;
}
public int next() throws IOException {
if (!this.mIsNext) {
this.mCurrent = this.mStream.read();
}
this.mIsNext = false;
return this.mCurrent;
}
public boolean skip(final String s) throws IOException {
if (s == null || s.length() == 0) {
return false;
}
if (s.charAt(0) != this.mCurrent) {
return false;
}
final int len = s.length();
this.mStream.mark(len - 1);
for (int n = 1; n < len; n++) {
final int value = this.mStream.read();
if (value != s.charAt(n)) {
this.mStream.reset();
return false;
}
}
return true;
}
}

@ -0,0 +1,57 @@
package com.activeandroid.widget;
import java.util.Collection;
import java.util.List;
import android.content.Context;
import android.widget.ArrayAdapter;
import com.activeandroid.Model;
public class ModelAdapter<T extends Model> extends ArrayAdapter<T> {
public ModelAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
}
public ModelAdapter(Context context, int resource, int textViewResourceId) {
super(context, resource, textViewResourceId);
}
public ModelAdapter(Context context, int textViewResourceId, List<T> objects) {
super(context, textViewResourceId, objects);
}
public ModelAdapter(Context context, int resource, int textViewResourceId, List<T> objects) {
super(context, resource, textViewResourceId, objects);
}
/**
* Clears the adapter and, if data != null, fills if with new Items.
*
* @param collection A Collection&lt;? extends T&gt; which members get added to the adapter.
*/
public void setData(Collection<? extends T> collection) {
clear();
if (collection != null) {
for (T item : collection) {
add(item);
}
}
}
/**
* @return The Id of the record at position.
*/
@Override
public long getItemId(int position) {
T item = getItem(position);
if (item != null) {
return item.getId();
}
else {
return -1;
}
}
}
Loading…
Cancel
Save