@ -1,173 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.pebble;
|
||||
|
||||
import android.content.*;
|
||||
import android.support.annotation.*;
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
|
||||
import com.getpebble.android.kit.*;
|
||||
import com.getpebble.android.kit.util.*;
|
||||
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.receivers.*;
|
||||
import org.json.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
import static com.getpebble.android.kit.Constants.*;
|
||||
import static org.hamcrest.MatcherAssert.*;
|
||||
import static org.hamcrest.core.IsEqual.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@MediumTest
|
||||
public class PebbleReceiverTest extends BaseAndroidTest
|
||||
{
|
||||
|
||||
private Habit habit1;
|
||||
|
||||
private Habit habit2;
|
||||
|
||||
@Override
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
|
||||
fixtures.purgeHabits(habitList);
|
||||
|
||||
habit1 = fixtures.createEmptyHabit();
|
||||
habit1.setName("Exercise");
|
||||
|
||||
habit2 = fixtures.createEmptyHabit();
|
||||
habit2.setName("Meditate");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCount() throws Exception
|
||||
{
|
||||
onPebbleReceived((dict) -> {
|
||||
assertThat(dict.getString(0), equalTo("COUNT"));
|
||||
assertThat(dict.getInteger(1), equalTo(2L));
|
||||
});
|
||||
|
||||
PebbleDictionary dict = buildCountRequest();
|
||||
sendFromPebbleToAndroid(dict);
|
||||
awaitLatch();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFetch() throws Exception
|
||||
{
|
||||
onPebbleReceived((dict) -> {
|
||||
assertThat(dict.getString(0), equalTo("HABIT"));
|
||||
assertThat(dict.getInteger(1), equalTo(habit2.getId()));
|
||||
assertThat(dict.getString(2), equalTo(habit2.getName()));
|
||||
assertThat(dict.getInteger(3), equalTo(0L));
|
||||
});
|
||||
|
||||
PebbleDictionary dict = buildFetchRequest(1);
|
||||
sendFromPebbleToAndroid(dict);
|
||||
awaitLatch();
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void testToggle() throws Exception
|
||||
// {
|
||||
// int v = habit1.getCheckmarks().getTodayValue();
|
||||
// assertThat(v, equalTo(Checkmark.UNCHECKED));
|
||||
//
|
||||
// onPebbleReceived((dict) -> {
|
||||
// assertThat(dict.getString(0), equalTo("OK"));
|
||||
// int value = habit1.getCheckmarks().getTodayValue();
|
||||
// assertThat(value, equalTo(200)); //Checkmark.CHECKED_EXPLICITLY));
|
||||
// });
|
||||
//
|
||||
// PebbleDictionary dict = buildToggleRequest(habit1.getId());
|
||||
// sendFromPebbleToAndroid(dict);
|
||||
// awaitLatch();
|
||||
// }
|
||||
|
||||
@NonNull
|
||||
protected PebbleDictionary buildCountRequest()
|
||||
{
|
||||
PebbleDictionary dict = new PebbleDictionary();
|
||||
dict.addString(0, "COUNT");
|
||||
return dict;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
protected PebbleDictionary buildFetchRequest(int position)
|
||||
{
|
||||
PebbleDictionary dict = new PebbleDictionary();
|
||||
dict.addString(0, "FETCH");
|
||||
dict.addInt32(1, position);
|
||||
return dict;
|
||||
}
|
||||
|
||||
protected void onPebbleReceived(PebbleProcessor processor)
|
||||
{
|
||||
BroadcastReceiver pebbleReceiver = new BroadcastReceiver()
|
||||
{
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent)
|
||||
{
|
||||
try
|
||||
{
|
||||
String jsonData = intent.getStringExtra(MSG_DATA);
|
||||
PebbleDictionary dict = PebbleDictionary.fromJson(jsonData);
|
||||
processor.process(dict);
|
||||
latch.countDown();
|
||||
targetContext.unregisterReceiver(this);
|
||||
}
|
||||
catch (JSONException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
IntentFilter filter = new IntentFilter(Constants.INTENT_APP_SEND);
|
||||
targetContext.registerReceiver(pebbleReceiver, filter);
|
||||
}
|
||||
|
||||
protected void sendFromPebbleToAndroid(PebbleDictionary dict)
|
||||
{
|
||||
Intent intent = new Intent(Constants.INTENT_APP_RECEIVE);
|
||||
intent.putExtra(Constants.APP_UUID, PebbleReceiver.WATCHAPP_UUID);
|
||||
intent.putExtra(Constants.TRANSACTION_ID, 0);
|
||||
intent.putExtra(Constants.MSG_DATA, dict.toJsonString());
|
||||
targetContext.sendBroadcast(intent);
|
||||
}
|
||||
|
||||
private PebbleDictionary buildToggleRequest(long habitId)
|
||||
{
|
||||
PebbleDictionary dict = new PebbleDictionary();
|
||||
dict.addString(0, "TOGGLE");
|
||||
dict.addInt32(1, (int) habitId);
|
||||
return dict;
|
||||
}
|
||||
|
||||
interface PebbleProcessor
|
||||
{
|
||||
void process(PebbleDictionary dict);
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 49 KiB |
@ -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<? extends T> 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 5.4 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 15 KiB |
@ -0,0 +1,178 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--Generated by crowdin.com-->
|
||||
<!--
|
||||
~ Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
~
|
||||
~ This file is part of Loop Habit Tracker.
|
||||
~
|
||||
~ Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by the
|
||||
~ Free Software Foundation, either version 3 of the License, or (at your
|
||||
~ option) any later version.
|
||||
~
|
||||
~ Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License along
|
||||
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">பழக்க தடப்பாதை</string>
|
||||
<string name="main_activity_title">பழக்கங்கள்</string>
|
||||
<string name="action_settings">அமைப்புகள்</string>
|
||||
<string name="edit">திருத்துக</string>
|
||||
<string name="delete">நீக்கு</string>
|
||||
<string name="archive">காப்பகம்</string>
|
||||
<string name="unarchive">உயிர்க்க</string>
|
||||
<string name="add_habit">சேர்க்க</string>
|
||||
<string name="color_picker_default_title">நிறம் மாற்ற</string>
|
||||
<string name="toast_habit_created">பழக்கம் உருவாக்கப்பட்டது</string>
|
||||
<string name="toast_habit_deleted">பழக்கம் நீக்கப்பட்டது</string>
|
||||
<string name="toast_habit_restored">பழக்கம் மீட்கப்பட்டது</string>
|
||||
<string name="toast_nothing_to_undo">மீட்க ஒன்றும் இல்லை</string>
|
||||
<string name="toast_nothing_to_redo">திருத்த எதுவும் இல்லை</string>
|
||||
<string name="toast_habit_changed">பழக்கம் மாற்றப்பட்டது</string>
|
||||
<string name="toast_habit_changed_back">பழக்கம் திரும்ப பழைய நிலைக்கு மாற்றப்பட்டது</string>
|
||||
<string name="toast_habit_archived">காப்பகப்படுத்தியப் பழக்கம்</string>
|
||||
<string name="toast_habit_unarchived">பழக்கங்கள் ஆவண காப்பகத்தில் இருந்து நீக்கப் பட்டது</string>
|
||||
<string name="overview">மேற்பார்வை</string>
|
||||
<string name="habit_strength">பழக்கத்தின் வலிமை</string>
|
||||
<string name="history">வரலாறு</string>
|
||||
<string name="clear">அழி</string>
|
||||
<string name="description_hint">கேள்வி (இன்று ... செய்தீர்களா?)</string>
|
||||
<string name="repeat">மீண்டும் செய்க</string>
|
||||
<string name="times_every">காலங்களில்</string>
|
||||
<string name="days">நாட்கள்</string>
|
||||
<string name="reminder">நினைவூட்டல்கள்</string>
|
||||
<string name="discard">நிராகரி</string>
|
||||
<string name="save">சேமிக்கவும்</string>
|
||||
<string name="streaks">சாதனைகள்</string>
|
||||
<string name="no_habits_found">நடப்பு பழக்கம் எதுவும் இல்லை</string>
|
||||
<string name="long_press_to_toggle">குறிக்க அல்லது குறிப்பை நீக்க அழுத்தி பிடிக்கவும்</string>
|
||||
<string name="reminder_off">வேண்டாம்</string>
|
||||
<string name="validation_name_should_not_be_blank">பெயர் காலியாக இருக்க கூடாது.</string>
|
||||
<string name="validation_number_should_be_positive">நேர்மறை எண்ணாக இருக்க வேண்டும் (பூஜியத்தை விட அதிகம்).</string>
|
||||
<string name="validation_at_most_one_rep_per_day">ஒரு நாளைக்கு அதிகப்பட்சம் ஒரு முறை மீள் நிகழ்வை பெற முடியும்</string>
|
||||
<string name="create_habit">புதிய பழக்கம்</string>
|
||||
<string name="edit_habit">பழக்கத்தை திருத்த</string>
|
||||
<string name="check">சரிப்பார்ப்பு குறி</string>
|
||||
<string name="snooze">பிறகு</string>
|
||||
<!-- App introduction -->
|
||||
<string name="intro_title_1">வருக</string>
|
||||
<string name="intro_description_1">இந்த செயலி நல்ல பழக்க வழக்கங்களை துவங்க மற்றும் தொடர உதவுகிறது.</string>
|
||||
<string name="intro_title_2">சில புது பழக்கங்களை துவங்கவும்!</string>
|
||||
<string name="intro_description_2">தினமும் உங்கள் புதிய பழக்கத்தை முடித்தவுடன் இந்த செயலியில் அதை குறிக்கவும்.</string>
|
||||
<string name="intro_title_3">மனம் தளராமல் தொடரவும்</string>
|
||||
<string name="intro_description_3">தொடர்ச்சியாக செய்யும் பழக்கங்கள் ஒரு முழு நட்சத்திரத்தை பெற்று தரும்.</string>
|
||||
<string name="intro_title_4">உங்கள் முன்னேற்றத்தை கண்காணிக்கவும்</string>
|
||||
<string name="intro_description_4">நாளடைவில் நீங்கள் அடைந்த முன்னேற்றத்தை வரைபடத்தின் மூலம் அறியலாம்.</string>
|
||||
<string name="interval_15_minutes">15 நிமிடங்கள்</string>
|
||||
<string name="interval_30_minutes">30 நிமிடங்கள்</string>
|
||||
<string name="interval_1_hour">1 மணி நேரம்</string>
|
||||
<string name="interval_2_hour">2 மணி நேரம்</string>
|
||||
<string name="interval_4_hour">4 மணி நேரம்</string>
|
||||
<string name="interval_8_hour">8 மணி நேரம்</string>
|
||||
<string name="interval_24_hour">24 மணி நேரம்</string>
|
||||
<string name="pref_toggle_title">சிறிய அழுத்தலின் மூலம் தாவு</string>
|
||||
<string name="pref_toggle_description">சரிப் பார்ப்பு குறி யை இட அழுத்தி பிடிப்பதற்கு பதில் ஒரு முறை தட்டலாம். இது முன்னதை விட எளிமையானது. ஆனால் இது தற்செயலான தாவல்களுக்கு வழி வகுக்கும்.</string>
|
||||
<string name="pref_snooze_interval_title">எச்சரிகையை தள்ளி வைக்க வேண்டிய நேரம்</string>
|
||||
<string name="pref_rate_this_app">Google Play-ல் இந்த செயலியை மதிப்பிட</string>
|
||||
<string name="pref_send_feedback">இந்த செயலியை மேம்படுத்த உங்கள் கருத்துகளை பகிர</string>
|
||||
<string name="pref_view_source_code">இந்த செயலியின் மூல நிரலை GitHub வலைதளத்தில் பார்க்கவும்</string>
|
||||
<string name="pref_view_app_introduction">இந்த செயலியின் முன்னோட்டத்தை பார்க்க</string>
|
||||
<string name="links">இணைப்புகள்</string>
|
||||
<string name="behavior">செயல்பாடு</string>
|
||||
<string name="name">பெயர்</string>
|
||||
<string name="settings">அமைப்புகள்</string>
|
||||
<string name="snooze_interval">தாமத காலம்</string>
|
||||
<string name="hint_title">உங்களுக்கு தெரியுமா?</string>
|
||||
<string name="hint_drag">பதிவுகளை மறுசீரைமக்க, தேவையான பழக்க பதிவின் மீது அழுத்தி பிடித்து பின் தேவையான இடத்திற்கு அதை இழுக்கவும்.</string>
|
||||
<string name="hint_landscape">உங்கள் கைப்பேசியை அகலவாக்கில் வைக்கும்போது இன்னும் அதிக நாட்களை காண முடியும்.</string>
|
||||
<string name="delete_habits">பழக்கங்களை நீக்கவும்</string>
|
||||
<string name="delete_habits_message">பழக்கங்கள் நிரந்தரமாக நீக்கப்படும். இந்த செயலை மீட்டமைக்க இயலாது.</string>
|
||||
<string name="habit_not_found">பழக்கம் நீக்கப்பட்டுவிட்டது / காணவில்லை</string>
|
||||
<string name="weekends">வார இறுதிகள்</string>
|
||||
<string name="any_weekday">திங்கள் முதல் வெள்ளி வரை</string>
|
||||
<string name="any_day">வாரத்தின் எந்த நாளிலும்</string>
|
||||
<string name="select_weekdays">நாட்களை தேர்வு செய்யவும்</string>
|
||||
<string name="export_to_csv">CSV நிரல் வகையில் ஏற்றுமதி செய்யவும்</string>
|
||||
<string name="done_label">முடிந்தது</string>
|
||||
<string name="clear_label">அழி</string>
|
||||
<string name="select_hours">மணி நேரங்களை தேர்வு செய்யவும்</string>
|
||||
<string name="select_minutes">நிமிடங்களை தேர்வு செய்யவும்</string>
|
||||
<string name="about">இதை பற்றி</string>
|
||||
<string name="translators">மொழிப்பெயர்ப்பாளர்கள்</string>
|
||||
<string name="developers">மென்பொருள் ஆசிரியர்கள்</string>
|
||||
<string name="version_n">மென்பொருள் பதிப்பு %s</string>
|
||||
<string name="frequency">கால இடைவெளி</string>
|
||||
<string name="checkmark">சரிபார்ப்பு குறி</string>
|
||||
<string name="strength">வலிமை</string>
|
||||
<string name="best_streaks">சிறந்த சாதனைகள்</string>
|
||||
<string name="current_streaks">நடப்பு சாதனை</string>
|
||||
<string name="number_of_repetitions">மீள் நிகழ்வுகளின் எண்ணிக்கை</string>
|
||||
<string name="last_x_days">கடந்த %d நாட்கள்</string>
|
||||
<string name="last_x_weeks">கடந்த %d வாரங்கள்</string>
|
||||
<string name="last_x_months">கடந்த %d மாதங்கள்</string>
|
||||
<string name="last_x_years">கடந்த %d வருடங்கள்</string>
|
||||
<string name="all_time">எல்லா நேரமும்</string>
|
||||
<string name="every_day">எல்லா நாளும்</string>
|
||||
<string name="every_week">எல்லா வாரமும்</string>
|
||||
<string name="two_times_per_week">வாரத்திற்கு இரண்டு முறை</string>
|
||||
<string name="five_times_per_week">வாரத்துக்கு 5 முறை</string>
|
||||
<string name="custom_frequency">விருப்பத்திற்கு ஏற்றபடி…</string>
|
||||
<string name="help">உதவி & அதிகம் கேட்கப்படும் கேள்விகள்</string>
|
||||
<string name="could_not_export">தரவுகளை ஏற்றுமதி செய்ய முடியவில்லை.</string>
|
||||
<string name="could_not_import">தரவை இறக்குமதி செய்ய முடியவில்லை.</string>
|
||||
<string name="file_not_recognized">இது எந்த வகையான ஆவணம் என்பதை உறுதி செய்ய முடியவில்லை.</string>
|
||||
<string name="habits_imported">பழக்கங்களை வெற்றிகரமாக இறக்குமதி செய்யப்பட்டது.</string>
|
||||
<string name="full_backup_success">முழு ஆவணக் காப்பு நகல் வெற்றிகரமாக ஏற்றுமதி செய்யப்பட்டது.</string>
|
||||
<string name="import_data">தரவு இறக்குமதி</string>
|
||||
<string name="export_full_backup">ஆவணக் காப்பு நகல் முழுமையாக ஏற்றுமதி செய்</string>
|
||||
<string name="import_data_summary">இந்த செயலி மூலம் ஆவண காப்பு நகல் முழுவதுமாக ஏற்றுமதி செய்யவும் மற்றும் Tickmate, HabitBull அல்லது Rewire செயலிகள் மூலம் உருவாக்கப்படும் ஆவணங்களும் இந்த செயலியில் பயண்படுத்தலாம் மேலும் தகவல்களுக்கு அதிகம் கேட்கப்படும் கேள்விகளை (FAQ) பார்க்கவும்.</string>
|
||||
<string name="export_as_csv_summary">உருவாக்கப்பட்ட ஆவணங்களை விரித்தாள் மென்பொருள்களான Microsoft Excel அல்லது OpenOffice Calc மூலம் திறக்கலாம். ஆனால் இவற்றை திரும்ப இறக்குமதி செய்ய முடியாது.</string>
|
||||
<string name="export_full_backup_summary">உங்களின் அனைத்து தரவுகளையும் கொண்ட ஒரு ஆவணம் உருவாக்கப்படும். இந்த ஆவணத்தை மீண்டும் இந்த செயலியில் இறக்குமதி செய்யலாம்.</string>
|
||||
<string name="bug_report_failed">செயலி பிழை அறிக்கை உருவாக்க முடியவில்லை.</string>
|
||||
<string name="generate_bug_report">பிழை அறிக்கை உருவாக்கு</string>
|
||||
<string name="troubleshooting">பழுது இடமறிதல்</string>
|
||||
<string name="help_translate">இந்த செயலியை மற்ற மொழிகளில் மொழிபெயர்க்க உதவி செய்யவும்</string>
|
||||
<string name="night_mode">இருள் வண்ண பாங்கு</string>
|
||||
<string name="use_pure_black">இருள் பாங்கில் முழு கருப்பு நிறத்தை பயண்படுத்து</string>
|
||||
<string name="pure_black_description">இதன் மூலம் செயலியில் உள்ள பழுப்பு பின்புலங்கள் நீக்கப்பட்டு முழுவதும் கருப்பு நிற பின்புலங்களாக மாற்றப்படும். இது AMOLED திரை கொண்ட கைப்பேசிகளில் மின்கல பயன்பாட்டை குறைக்கும்.</string>
|
||||
<string name="interface_preferences">இடைமுகம்</string>
|
||||
<string name="reverse_days">தலைகீழ் வரிசையில் நாட்கள்</string>
|
||||
<string name="reverse_days_description">பிரதான திரையில் நாட்களை தலை கீழ் வரிசையில் காட்டு</string>
|
||||
<string name="day">நாள்</string>
|
||||
<string name="week">வாரம்</string>
|
||||
<string name="month">மாதம்</string>
|
||||
<string name="quarter">காற் பங்கு</string>
|
||||
<string name="year">வருடம்</string>
|
||||
<string name="total">மொத்தம்</string>
|
||||
<!-- Middle part of the sentence '1 time in xx days' -->
|
||||
<string name="time_every">நேரத்தை</string>
|
||||
<string name="every_x_days">ஓவ்வொரு %d நாளும்</string>
|
||||
<string name="every_x_weeks">ஒவ்வொரு %d வாரங்களும்</string>
|
||||
<string name="every_x_months">ஒவ்வொரு %d மாதங்களு</string>
|
||||
<string name="score">மதிப்பெண்கள்</string>
|
||||
<string name="reminder_sound">நினைவூட்டல் சத்தம்</string>
|
||||
<string name="none">எதுவும் இல்லை</string>
|
||||
<string name="filter">வடிகட்டவும்</string>
|
||||
<string name="hide_completed">மறைத்தல் முடிந்தது</string>
|
||||
<string name="hide_archived">ஆவணக் காப்பை மறைக்கவும்</string>
|
||||
<string name="sticky_notifications">நினைவூட்டல்களை நிலைத்து நிற்க வை</string>
|
||||
<string name="sticky_notifications_description">நினைவூட்டல்களை விரல்களால் தள்ளி விட முடியாத படி செய்கிறது.</string>
|
||||
<string name="repair_database">தரவு தளத்தை பழுது பார்க்கவும்</string>
|
||||
<string name="database_repaired">தரவுதளம் பழுதடைந்து விட்டது.</string>
|
||||
<string name="uncheck">சரிப்பார்க்காமல் அப்படியே விடு</string>
|
||||
<string name="toggle">தாவு</string>
|
||||
<string name="action">செயல்</string>
|
||||
<string name="habit">பழக்கம்</string>
|
||||
<string name="sort">வரிசைப்படுத்தவும்</string>
|
||||
<string name="manually">கைமுறை</string>
|
||||
<string name="by_name">பெயரின் மூலம்</string>
|
||||
<string name="by_color">நிறத்தின் மூலம்</string>
|
||||
<string name="by_score">மதிப்பெண்களின் மூலம்</string>
|
||||
<string name="download">பதிவிறக்கம்</string>
|
||||
<string name="export">ஏற்றுமதி</string>
|
||||
</resources>
|
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#1976D2</color>
|
||||
</resources>
|