From 4c0ca0249d7a96c8593ef10269e886a387bc477b Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Wed, 25 Jun 2014 20:52:52 -0700 Subject: [PATCH 001/305] Add strict count overload to simplify interface --- library/src/com/orm/SugarRecord.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/src/com/orm/SugarRecord.java b/library/src/com/orm/SugarRecord.java index da414a66..e3c11847 100644 --- a/library/src/com/orm/SugarRecord.java +++ b/library/src/com/orm/SugarRecord.java @@ -222,6 +222,10 @@ public static > List find(Class type, } return toRet; } + + public static > long count(Class type) { + return count(type, null, null, null, null, null); + } public static > long count(Class type, String whereClause, String[] whereArgs) { From bd36185a1d35765df5f05400420fc468c1a414c8 Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Wed, 25 Jun 2014 21:18:10 -0700 Subject: [PATCH 002/305] Return id by default on save --- library/src/com/orm/SugarRecord.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/library/src/com/orm/SugarRecord.java b/library/src/com/orm/SugarRecord.java index da414a66..5eef6e5a 100644 --- a/library/src/com/orm/SugarRecord.java +++ b/library/src/com/orm/SugarRecord.java @@ -43,8 +43,8 @@ public static > void deleteAll(Class type, String wh sqLiteDatabase.delete(getTableName(type), whereClause, whereArgs); } - public void save() { - save(getSugarContext().getDatabase().getDB()); + public long save() { + return save(getSugarContext().getDatabase().getDB()); } @SuppressWarnings("deprecation") @@ -72,7 +72,7 @@ public static > void saveInTx(Collection objects ) { } - void save(SQLiteDatabase db) { + long save(SQLiteDatabase db) { List columns = getTableFields(); ContentValues values = new ContentValues(columns.size()); @@ -130,6 +130,7 @@ else if (Calendar.class.equals(columnType)) { db.update(getSqlName(), values, "ID = ?", new String[]{String.valueOf(id)}); Log.i("Sugar", getClass().getSimpleName() + " saved : " + id); + return id; } public static > List listAll(Class type) { From 78f6ae86ae6a6f8df00d38d17e8395b9c84cd277 Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Thu, 26 Jun 2014 10:27:40 -0700 Subject: [PATCH 003/305] Allow null values for Date and Calendar objects --- library/src/com/orm/QueryBuilder.java | 9 ++++++--- library/src/com/orm/SugarRecord.java | 12 ++++++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/library/src/com/orm/QueryBuilder.java b/library/src/com/orm/QueryBuilder.java index 8ff00beb..6ca26fc9 100644 --- a/library/src/com/orm/QueryBuilder.java +++ b/library/src/com/orm/QueryBuilder.java @@ -5,9 +5,6 @@ public class QueryBuilder { public static String getColumnType(Class type) { if ((type.equals(Boolean.class)) || (type.equals(Boolean.TYPE)) || - (type.equals(java.util.Date.class)) || - (type.equals(java.util.Calendar.class)) || - (type.equals(java.sql.Date.class)) || (type.equals(Integer.class)) || (type.equals(Integer.TYPE)) || (type.equals(Long.class)) || @@ -17,6 +14,12 @@ public static String getColumnType(Class type) { return "INTEGER"; } + if ((type.equals(java.util.Date.class)) || + (type.equals(java.sql.Date.class)) || + (type.equals(java.util.Calendar.class))) { + return "INTEGER NULL"; + } + if (type.getName().equals("[B")) { return "BLOB"; } diff --git a/library/src/com/orm/SugarRecord.java b/library/src/com/orm/SugarRecord.java index da414a66..0c31cfbc 100644 --- a/library/src/com/orm/SugarRecord.java +++ b/library/src/com/orm/SugarRecord.java @@ -108,10 +108,18 @@ else if (columnType.equals(Boolean.class) || columnType.equals(boolean.class)) { values.put(columnName, (Boolean) columnValue); } else if (Date.class.equals(columnType)) { - values.put(columnName, ((Date) column.get(this)).getTime()); + try { + values.put(columnName, ((Date) column.get(this)).getTime()); + } catch (NullPointerException e) { + values.put(columnName, (Long) null); + } } else if (Calendar.class.equals(columnType)) { - values.put(columnName, ((Calendar) column.get(this)).getTimeInMillis()); + try { + values.put(columnName, ((Calendar) column.get(this)).getTimeInMillis()); + } catch (NullPointerException e) { + values.put(columnName, (Long) null); + } }else{ values.put(columnName, String.valueOf(columnValue)); } From 3b0db0a6ed5b6a4427c9edad5cee3b093eec2ff7 Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Mon, 30 Jun 2014 08:58:08 -0700 Subject: [PATCH 004/305] Overload findById to take an Integer --- library/src/com/orm/SugarRecord.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/src/com/orm/SugarRecord.java b/library/src/com/orm/SugarRecord.java index da414a66..d00477fb 100644 --- a/library/src/com/orm/SugarRecord.java +++ b/library/src/com/orm/SugarRecord.java @@ -142,6 +142,10 @@ public static > T findById(Class type, Long id) { return list.get(0); } + public static > T findById(Class type, Integer id) { + return findById(type, Long.valueOf(id)); + } + public static > Iterator findAll(Class type) { return findAsIterator(type, null, null, null, null, null); } From 052f43144996aba28e1eef0d1bfdd9ee9d2c0248 Mon Sep 17 00:00:00 2001 From: Satya Narayan Date: Fri, 4 Jul 2014 11:25:01 -0700 Subject: [PATCH 005/305] removed artifacts from source control --- dist/sugar-1.0.jar | Bin 12491 -> 0 bytes dist/sugar-1.1.jar | Bin 12546 -> 0 bytes dist/sugar-1.2.jar | Bin 18988 -> 0 bytes dist/sugar-1.3_beta.jar | Bin 23231 -> 0 bytes 4 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 dist/sugar-1.0.jar delete mode 100644 dist/sugar-1.1.jar delete mode 100644 dist/sugar-1.2.jar delete mode 100644 dist/sugar-1.3_beta.jar diff --git a/dist/sugar-1.0.jar b/dist/sugar-1.0.jar deleted file mode 100644 index 2bf1abd30830500ac87220047c0ab1aa51930083..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12491 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 LecD9#^WXmiwR9{= diff --git a/dist/sugar-1.2.jar b/dist/sugar-1.2.jar deleted file mode 100644 index 2e6f02901411a0a1ed6ec3af225aa2f05e5a6efc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18988 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 z 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 From d8687da0d91e1dafe4238f5b163ff7bc8f474db0 Mon Sep 17 00:00:00 2001 From: Satya Narayan <81.narayan@gmail.com> Date: Mon, 7 Jul 2014 14:02:16 -0700 Subject: [PATCH 006/305] Update README.md --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index e6916fb1..a47a9ff9 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,15 @@ Sugar is now available as a library project also. Add it to your project and uti The project is available in the folder "library" http://developer.android.com/tools/projects/index.html#LibraryProjects +# New in version 1.3 + +Transaction Support +Bulk Insert of records +Encrypted datastore (branch : sugar-cipher using sqlcipher) +Removed Constructor with context parameter. Needs default constructor now. +Enhancements to QueryBuilder +Bug fixes and other improvements. + # New in version 1.2 1. package restriction for domain classes. From 5c25fec55fe689a340ea01c1ffc8b42923aa22c0 Mon Sep 17 00:00:00 2001 From: Satya Narayan <81.narayan@gmail.com> Date: Mon, 7 Jul 2014 14:02:59 -0700 Subject: [PATCH 007/305] Update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a47a9ff9..4f42fe65 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,12 @@ http://developer.android.com/tools/projects/index.html#LibraryProjects # New in version 1.3 -Transaction Support -Bulk Insert of records -Encrypted datastore (branch : sugar-cipher using sqlcipher) -Removed Constructor with context parameter. Needs default constructor now. -Enhancements to QueryBuilder -Bug fixes and other improvements. +1. Transaction Support +2. Bulk Insert of records +3. Encrypted datastore (branch : sugar-cipher using sqlcipher) +4. Removed Constructor with context parameter. Needs default constructor now. +5. Enhancements to QueryBuilder +6. Bug fixes and other improvements. # New in version 1.2 From 3c47b24d5419b68a7d64837f540dc0c451360cce Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Mon, 7 Jul 2014 20:10:28 -0700 Subject: [PATCH 008/305] Fallback class loader for Robolectric Closes #96 --- library/src/com/orm/SugarDb.java | 35 ++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/library/src/com/orm/SugarDb.java b/library/src/com/orm/SugarDb.java index 0f859afc..ca7a2b47 100644 --- a/library/src/com/orm/SugarDb.java +++ b/library/src/com/orm/SugarDb.java @@ -15,7 +15,12 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; -import java.util.*; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; import static com.orm.SugarConfig.getDatabaseVersion; import static com.orm.SugarConfig.getDebugEnabled; @@ -32,11 +37,7 @@ public SugarDb(Context context) { private > List getDomainClasses(Context context) { List domainClasses = new ArrayList(); try { - Enumeration allClasses = getAllClasses(context); - - while (allClasses.hasMoreElements()) { - String className = (String) allClasses.nextElement(); - + for (String className : getAllClasses(context)) { if (className.startsWith(SugarConfig.getDomainPackageName(context))) { T domainClass = getDomainClass(className, context); if (domainClass != null) domainClasses.add(domainClass); @@ -84,10 +85,26 @@ private > T getDomainClass(String className, Context co } - private Enumeration getAllClasses(Context context) throws PackageManager.NameNotFoundException, IOException { + private List getAllClasses(Context context) throws PackageManager.NameNotFoundException, IOException { String path = getSourcePath(context); - DexFile dexfile = new DexFile(path); - return dexfile.entries(); + List classNames = new ArrayList(); + try { + DexFile dexfile = new DexFile(path); + Enumeration dexEntries = dexfile.entries(); + while (dexEntries.hasMoreElements()) { + classNames.add(dexEntries.nextElement()); + } + } catch (NullPointerException e) { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + Enumeration urls = classLoader.getResources(""); + while (urls.hasMoreElements()) { + String urlPath = urls.nextElement().getFile(); + if (urlPath.contains("bin") || urlPath.contains("classes")) { + classNames.add(urlPath); + } + } + } + return classNames; } private String getSourcePath(Context context) throws PackageManager.NameNotFoundException { From e2bb783d96f13dc224d848be8759120dc9b08968 Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Mon, 7 Jul 2014 20:20:42 -0700 Subject: [PATCH 009/305] Add documentation on onTerminate to clarify usage scenarios Closes #106 --- library/src/com/orm/SugarApp.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/src/com/orm/SugarApp.java b/library/src/com/orm/SugarApp.java index d15c411f..ac7da99d 100644 --- a/library/src/com/orm/SugarApp.java +++ b/library/src/com/orm/SugarApp.java @@ -11,6 +11,12 @@ public void onCreate(){ this.database = new Database(this); } + /* + * Per issue #106 on Github, this method won't be called in + * any real Android device. This method is used purely in + * emulated process environments such as an emulator or + * Robolectric Android mock. + */ public void onTerminate(){ if (this.database != null) { this.database.getDB().close(); From 3e3c718d83c89a455a6b859cb095cb250476a741 Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Mon, 7 Jul 2014 21:26:23 -0700 Subject: [PATCH 010/305] Update README.md --- README.md | 78 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 63 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 4f42fe65..5189146d 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,62 @@ -Please follow the documentation at -http://satyan.github.io/sugar +# Sugar ORM -The example application is provided in the "example" folder in the source. +Insanely easy way to work with Android databases. +Official documentation can be found [here](http://satyan.github.io/sugar). The example application is provided in the **example** folder in the source. -# Getting Started: +## Features -Sugar is now available as a library project also. Add it to your project and utilize the latest changes. -The project is available in the folder "library" -http://developer.android.com/tools/projects/index.html#LibraryProjects +Sugar ORM was built in contrast to other ORM's to have: -# New in version 1.3 +1. A simple, concise, and clean integration process with minimal configuration. +3. Automatic table and column naming through reflection. +4. Support for migrations between different schema versions. + +## Installing + +There are four ways to install Sugar: + +#### As a Gradle dependency + +This is the preferred way. Simply add: + +```groovy +compile 'com.github.satyan:sugar:1.3' +``` + +to your project dependencies and run `gradle build` or `gradle assemble`. + +#### As a Maven dependency + +Declare the dependency in Maven: + +```xml + + com.github.satyan + sugar + 1.3 + +``` + +#### As a library project + +Download the source code and import it as a library project in Eclipse. The project is available in the folder **library**. For more information on how to do this, read [here](http://developer.android.com/tools/projects/index.html#LibraryProjects). + +#### Use a jar + +Visit the [releases](https://github.com/satyan/sugar/releases) to download jars directly. You can drop them into your `libs` folder and configure the Java build path to include the library. See this [tutorial](http://www.vogella.com/tutorials/AndroidLibraryProjects/article.html) for an excellent guide on how to do this. + +=================== + +After installing, check out how to set up your first database and models [here](http://satyan.github.io/sugar/getting-started.html). + +## Contributing + +Please fork this repository and contribute back using [pull requests](https://github.com/satyan/sugar/pulls). Features can be requested using [issues](https://github.com/satyan/sugar/issues). All code, comments, and critiques are greatly appreciated. + +## Changelog + +#### 1.3 [[jar](https://github.com/satyan/sugar/releases/download/v1.3/sugar-1.3.jar)] 1. Transaction Support 2. Bulk Insert of records @@ -19,7 +65,7 @@ http://developer.android.com/tools/projects/index.html#LibraryProjects 5. Enhancements to QueryBuilder 6. Bug fixes and other improvements. -# New in version 1.2 +#### 1.2 [[jar](https://github.com/satyan/sugar/releases/download/v1.2/sugar-1.2.jar)] 1. package restriction for domain classes. 2. metadata caching @@ -28,16 +74,18 @@ http://developer.android.com/tools/projects/index.html#LibraryProjects 5. Provision for Raw queries 6. Better and more organized api guide and usage instructions. -# What's new in 1.1: +#### 1.1 [[jar](https://github.com/satyan/sugar/releases/download/v1.1/sugar-1.1.jar)] 1. Static api doesn't take context anymore. Hence - Book.findById(context, Book.class, 1); +```java +Book.findById(context, Book.class, 1); +``` - becomes - - Book.findById(Book.class, 1); +becomes +```java +Book.findById(Book.class, 1); +``` 2. Some cleanup in the code. - From 5ccf5e1f84048f94065f6ab9891f89c5ed4477cf Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Mon, 7 Jul 2014 21:26:51 -0700 Subject: [PATCH 011/305] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5189146d..179c824d 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Please fork this repository and contribute back using [pull requests](https://gi ## Changelog -#### 1.3 [[jar](https://github.com/satyan/sugar/releases/download/v1.3/sugar-1.3.jar)] +#### v1.3 [[jar](https://github.com/satyan/sugar/releases/download/v1.3/sugar-1.3.jar)] 1. Transaction Support 2. Bulk Insert of records @@ -65,7 +65,7 @@ Please fork this repository and contribute back using [pull requests](https://gi 5. Enhancements to QueryBuilder 6. Bug fixes and other improvements. -#### 1.2 [[jar](https://github.com/satyan/sugar/releases/download/v1.2/sugar-1.2.jar)] +#### v1.2 [[jar](https://github.com/satyan/sugar/releases/download/v1.2/sugar-1.2.jar)] 1. package restriction for domain classes. 2. metadata caching @@ -74,7 +74,7 @@ Please fork this repository and contribute back using [pull requests](https://gi 5. Provision for Raw queries 6. Better and more organized api guide and usage instructions. -#### 1.1 [[jar](https://github.com/satyan/sugar/releases/download/v1.1/sugar-1.1.jar)] +#### v1.1 [[jar](https://github.com/satyan/sugar/releases/download/v1.1/sugar-1.1.jar)] 1. Static api doesn't take context anymore. Hence From a7eb1989c07c944894db48d5655962b167813bad Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Tue, 8 Jul 2014 09:07:21 -0700 Subject: [PATCH 012/305] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 179c824d..225f1682 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Download the source code and import it as a library project in Eclipse. The proj #### Use a jar -Visit the [releases](https://github.com/satyan/sugar/releases) to download jars directly. You can drop them into your `libs` folder and configure the Java build path to include the library. See this [tutorial](http://www.vogella.com/tutorials/AndroidLibraryProjects/article.html) for an excellent guide on how to do this. +Visit the [releases](https://github.com/satyan/sugar/releases) page to download jars directly. You can drop them into your `libs` folder and configure the Java build path to include the library. See this [tutorial](http://www.vogella.com/tutorials/AndroidLibraryProjects/article.html) for an excellent guide on how to do this. =================== From 71be7aebeaa11201714674a152f991ac71b6937e Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Wed, 9 Jul 2014 10:55:30 -0700 Subject: [PATCH 013/305] Fix Robolectric class finder --- library/src/com/orm/SugarDb.java | 52 ++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/library/src/com/orm/SugarDb.java b/library/src/com/orm/SugarDb.java index ca7a2b47..227e55cf 100644 --- a/library/src/com/orm/SugarDb.java +++ b/library/src/com/orm/SugarDb.java @@ -1,14 +1,10 @@ package com.orm; -import android.content.Context; -import android.content.pm.PackageManager; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteException; -import android.database.sqlite.SQLiteOpenHelper; -import android.util.Log; -import dalvik.system.DexFile; +import static com.orm.SugarConfig.getDatabaseVersion; +import static com.orm.SugarConfig.getDebugEnabled; import java.io.BufferedReader; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -22,8 +18,13 @@ import java.util.Enumeration; import java.util.List; -import static com.orm.SugarConfig.getDatabaseVersion; -import static com.orm.SugarConfig.getDebugEnabled; +import android.content.Context; +import android.content.pm.PackageManager; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; +import android.database.sqlite.SQLiteOpenHelper; +import android.util.Log; +import dalvik.system.DexFile; public class SugarDb extends SQLiteOpenHelper { private Context context; @@ -97,15 +98,42 @@ private List getAllClasses(Context context) throws PackageManager.NameNo } catch (NullPointerException e) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); Enumeration urls = classLoader.getResources(""); + List fileNames = new ArrayList(); while (urls.hasMoreElements()) { - String urlPath = urls.nextElement().getFile(); - if (urlPath.contains("bin") || urlPath.contains("classes")) { - classNames.add(urlPath); + String classDirectoryName = urls.nextElement().getFile(); + if (classDirectoryName.contains("bin") || classDirectoryName.contains("classes")) { + File classDirectory = new File(classDirectoryName); + for (File filePath : classDirectory.listFiles()) { + populateFiles(filePath, fileNames, ""); + } + classNames.addAll(fileNames); } } } return classNames; } + + private void populateFiles(File path, List fileNames, String parent) { + if (path.isDirectory()) { + for (File newPath : path.listFiles()) { + if ("".equals(parent)) { + populateFiles(newPath, fileNames, path.getName()); + } else { + populateFiles(newPath, fileNames, parent + "." + path.getName()); + } + } + } else { + String pathName = path.getName(); + String classSuffix = ".class"; + pathName = pathName.endsWith(classSuffix) ? + pathName.substring(0, pathName.length() - classSuffix.length()) : pathName; + if ("".equals(parent)) { + fileNames.add(pathName); + } else { + fileNames.add(parent + "." + pathName); + } + } + } private String getSourcePath(Context context) throws PackageManager.NameNotFoundException { return context.getPackageManager().getApplicationInfo(context.getPackageName(), 0).sourceDir; From 1fa556ae488ea5d63394010699fa4c5ad67356fc Mon Sep 17 00:00:00 2001 From: Nick Jones Date: Fri, 28 Mar 2014 14:00:51 +0000 Subject: [PATCH 014/305] Style improvements to SugarRecord --- library/src/com/orm/SugarRecord.java | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/library/src/com/orm/SugarRecord.java b/library/src/com/orm/SugarRecord.java index 4bedcd1c..55bfd758 100644 --- a/library/src/com/orm/SugarRecord.java +++ b/library/src/com/orm/SugarRecord.java @@ -91,39 +91,31 @@ long save(SQLiteDatabase db) { if (!"id".equalsIgnoreCase(column.getName())) { if (columnType.equals(Short.class) || columnType.equals(short.class)) { values.put(columnName, (Short) columnValue); - } - else if (columnType.equals(Integer.class) || columnType.equals(int.class)) { + } else if (columnType.equals(Integer.class) || columnType.equals(int.class)) { values.put(columnName, (Integer) columnValue); - } - else if (columnType.equals(Long.class) || columnType.equals(long.class)) { + } else if (columnType.equals(Long.class) || columnType.equals(long.class)) { values.put(columnName, (Long) columnValue); - } - else if (columnType.equals(Float.class) || columnType.equals(float.class)) { + } else if (columnType.equals(Float.class) || columnType.equals(float.class)) { values.put(columnName, (Float) columnValue); - } - else if (columnType.equals(Double.class) || columnType.equals(double.class)) { + } else if (columnType.equals(Double.class) || columnType.equals(double.class)) { values.put(columnName, (Double) columnValue); - } - else if (columnType.equals(Boolean.class) || columnType.equals(boolean.class)) { + } else if (columnType.equals(Boolean.class) || columnType.equals(boolean.class)) { values.put(columnName, (Boolean) columnValue); - } - else if (Date.class.equals(columnType)) { + } else if (Date.class.equals(columnType)) { try { values.put(columnName, ((Date) column.get(this)).getTime()); } catch (NullPointerException e) { values.put(columnName, (Long) null); } - } - else if (Calendar.class.equals(columnType)) { + } else if (Calendar.class.equals(columnType)) { try { values.put(columnName, ((Calendar) column.get(this)).getTimeInMillis()); } catch (NullPointerException e) { values.put(columnName, (Long) null); } - }else{ + } else { values.put(columnName, String.valueOf(columnValue)); } - } } From 1dcd185943a2bb0ba1a4faf153cceca9164dd8d8 Mon Sep 17 00:00:00 2001 From: Nick Jones Date: Fri, 28 Mar 2014 14:43:42 +0000 Subject: [PATCH 015/305] Adding build folder to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b2d2fc5b..88c78310 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ library/bin +library/build library/gen library/.project library/.classpath From 9c1e19be49ab73772e86dda232f5349d4f5dcc24 Mon Sep 17 00:00:00 2001 From: Nick Jones Date: Mon, 31 Mar 2014 11:54:13 +0100 Subject: [PATCH 016/305] Updating ignore file Closes #78 --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 88c78310..2adde673 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,6 @@ library/build library/gen library/.project library/.classpath +library/library.iml *.class .DS_Store From 02e1d4974d55733c3857736c832f3f948b26fa2c Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Wed, 9 Jul 2014 21:07:38 -0700 Subject: [PATCH 017/305] Allow id to be modified directly by model Thanks to Cristian Palos Rejano (@cpalosrejano) for the suggestion. Closes #77 --- library/AndroidManifest.xml | 2 +- library/src/com/orm/SugarRecord.java | 57 +++++++++++++--------------- 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/library/AndroidManifest.xml b/library/AndroidManifest.xml index 65bb4873..a48c294b 100644 --- a/library/AndroidManifest.xml +++ b/library/AndroidManifest.xml @@ -3,6 +3,6 @@ package="com.orm.dsl" android:versionCode="1" android:versionName="1.0"> - + \ No newline at end of file diff --git a/library/src/com/orm/SugarRecord.java b/library/src/com/orm/SugarRecord.java index 55bfd758..764b655e 100644 --- a/library/src/com/orm/SugarRecord.java +++ b/library/src/com/orm/SugarRecord.java @@ -88,34 +88,32 @@ long save(SQLiteDatabase db) { ? String.valueOf(((SugarRecord) columnValue).id) : "0"); } else { - if (!"id".equalsIgnoreCase(column.getName())) { - if (columnType.equals(Short.class) || columnType.equals(short.class)) { - values.put(columnName, (Short) columnValue); - } else if (columnType.equals(Integer.class) || columnType.equals(int.class)) { - values.put(columnName, (Integer) columnValue); - } else if (columnType.equals(Long.class) || columnType.equals(long.class)) { - values.put(columnName, (Long) columnValue); - } else if (columnType.equals(Float.class) || columnType.equals(float.class)) { - values.put(columnName, (Float) columnValue); - } else if (columnType.equals(Double.class) || columnType.equals(double.class)) { - values.put(columnName, (Double) columnValue); - } else if (columnType.equals(Boolean.class) || columnType.equals(boolean.class)) { - values.put(columnName, (Boolean) columnValue); - } else if (Date.class.equals(columnType)) { - try { - values.put(columnName, ((Date) column.get(this)).getTime()); - } catch (NullPointerException e) { - values.put(columnName, (Long) null); - } - } else if (Calendar.class.equals(columnType)) { - try { - values.put(columnName, ((Calendar) column.get(this)).getTimeInMillis()); - } catch (NullPointerException e) { - values.put(columnName, (Long) null); - } - } else { - values.put(columnName, String.valueOf(columnValue)); + if (columnType.equals(Short.class) || columnType.equals(short.class)) { + values.put(columnName, (Short) columnValue); + } else if (columnType.equals(Integer.class) || columnType.equals(int.class)) { + values.put(columnName, (Integer) columnValue); + } else if (columnType.equals(Long.class) || columnType.equals(long.class)) { + values.put(columnName, (Long) columnValue); + } else if (columnType.equals(Float.class) || columnType.equals(float.class)) { + values.put(columnName, (Float) columnValue); + } else if (columnType.equals(Double.class) || columnType.equals(double.class)) { + values.put(columnName, (Double) columnValue); + } else if (columnType.equals(Boolean.class) || columnType.equals(boolean.class)) { + values.put(columnName, (Boolean) columnValue); + } else if (Date.class.equals(columnType)) { + try { + values.put(columnName, ((Date) column.get(this)).getTime()); + } catch (NullPointerException e) { + values.put(columnName, (Long) null); } + } else if (Calendar.class.equals(columnType)) { + try { + values.put(columnName, ((Calendar) column.get(this)).getTimeInMillis()); + } catch (NullPointerException e) { + values.put(columnName, (Long) null); + } + } else { + values.put(columnName, String.valueOf(columnValue)); } } @@ -124,10 +122,7 @@ long save(SQLiteDatabase db) { } } - if (id == null) - id = db.insert(getSqlName(), null, values); - else - db.update(getSqlName(), values, "ID = ?", new String[]{String.valueOf(id)}); + id = db.insertWithOnConflict(getSqlName(), null, values, SQLiteDatabase.CONFLICT_REPLACE); Log.i("Sugar", getClass().getSimpleName() + " saved : " + id); return id; From a75e97fe77791b2552eef947e6d1902a3718420d Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Wed, 9 Jul 2014 21:31:46 -0700 Subject: [PATCH 018/305] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 225f1682..b8d2c181 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,8 @@ Official documentation can be found [here](http://satyan.github.io/sugar). The e Sugar ORM was built in contrast to other ORM's to have: 1. A simple, concise, and clean integration process with minimal configuration. -3. Automatic table and column naming through reflection. -4. Support for migrations between different schema versions. +2. Automatic table and column naming through reflection. +3. Support for migrations between different schema versions. ## Installing From cc295b3a4fe49236d10d3b6edc7db3ca000a6fe3 Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Wed, 9 Jul 2014 21:34:50 -0700 Subject: [PATCH 019/305] Update README.md --- README.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index b8d2c181..2cea7303 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ Official documentation can be found [here](http://satyan.github.io/sugar). The e Sugar ORM was built in contrast to other ORM's to have: -1. A simple, concise, and clean integration process with minimal configuration. -2. Automatic table and column naming through reflection. -3. Support for migrations between different schema versions. +- A simple, concise, and clean integration process with minimal configuration. +- Automatic table and column naming through reflection. +- Support for migrations between different schema versions. ## Installing @@ -58,25 +58,25 @@ Please fork this repository and contribute back using [pull requests](https://gi #### v1.3 [[jar](https://github.com/satyan/sugar/releases/download/v1.3/sugar-1.3.jar)] -1. Transaction Support -2. Bulk Insert of records -3. Encrypted datastore (branch : sugar-cipher using sqlcipher) -4. Removed Constructor with context parameter. Needs default constructor now. -5. Enhancements to QueryBuilder -6. Bug fixes and other improvements. +- Transaction Support +- Bulk Insert of records +- Encrypted datastore (branch : sugar-cipher using sqlcipher) +- Removed Constructor with context parameter. Needs default constructor now. +- Enhancements to QueryBuilder +- Bug fixes and other improvements. #### v1.2 [[jar](https://github.com/satyan/sugar/releases/download/v1.2/sugar-1.2.jar)] -1. package restriction for domain classes. -2. metadata caching -3. QueryBuilder v1 -4. Database Migrations -5. Provision for Raw queries -6. Better and more organized api guide and usage instructions. +- package restriction for domain classes. +- metadata caching +- QueryBuilder v1 +- Database Migrations +- Provision for Raw queries +- Better and more organized api guide and usage instructions. #### v1.1 [[jar](https://github.com/satyan/sugar/releases/download/v1.1/sugar-1.1.jar)] -1. Static api doesn't take context anymore. Hence +- Static api doesn't take context anymore. Hence ```java Book.findById(context, Book.class, 1); @@ -88,4 +88,4 @@ becomes Book.findById(Book.class, 1); ``` -2. Some cleanup in the code. +- Some cleanup in the code. From 913dbf3c8410a1b8b963377adec6c963204864cb Mon Sep 17 00:00:00 2001 From: androdevcafe Date: Mon, 30 Jun 2014 00:59:25 +0200 Subject: [PATCH 020/305] Added 'unique' and 'not null' support Closes #112 --- library/src/com/orm/SugarDb.java | 10 ++++++++++ library/src/com/orm/dsl/NotNull.java | 8 ++++++++ library/src/com/orm/dsl/Unique.java | 8 ++++++++ 3 files changed, 26 insertions(+) create mode 100644 library/src/com/orm/dsl/NotNull.java create mode 100644 library/src/com/orm/dsl/Unique.java diff --git a/library/src/com/orm/SugarDb.java b/library/src/com/orm/SugarDb.java index 227e55cf..1e7acc86 100644 --- a/library/src/com/orm/SugarDb.java +++ b/library/src/com/orm/SugarDb.java @@ -24,6 +24,10 @@ import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; + +import com.orm.dsl.NotNull; +import com.orm.dsl.Unique; + import dalvik.system.DexFile; public class SugarDb extends SQLiteOpenHelper { @@ -168,6 +172,12 @@ private > void createTable(T table, SQLiteDatabase sqLi continue; } sb.append(", ").append(columnName).append(" ").append(columnType); + if(column.isAnnotationPresent(NotNull.class)) { + sb.append(" NOT NULL"); + } + if(column.isAnnotationPresent(Unique.class)) { + sb.append(" UNIQUE"); + } } } sb.append(" ) "); diff --git a/library/src/com/orm/dsl/NotNull.java b/library/src/com/orm/dsl/NotNull.java new file mode 100644 index 00000000..b2361851 --- /dev/null +++ b/library/src/com/orm/dsl/NotNull.java @@ -0,0 +1,8 @@ +package com.orm.dsl; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface NotNull { +} diff --git a/library/src/com/orm/dsl/Unique.java b/library/src/com/orm/dsl/Unique.java new file mode 100644 index 00000000..7384b749 --- /dev/null +++ b/library/src/com/orm/dsl/Unique.java @@ -0,0 +1,8 @@ +package com.orm.dsl; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Unique { +} From 2ca05f4c4711fe0cfc1d66216d30fb6c05aa8a5f Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Thu, 10 Jul 2014 21:28:04 -0700 Subject: [PATCH 021/305] =?UTF-8?q?Insert=20a=20true=20null=20instead=20of?= =?UTF-8?q?=20a=20string=20=E2=80=9Cnull=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- library/src/com/orm/SugarRecord.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/src/com/orm/SugarRecord.java b/library/src/com/orm/SugarRecord.java index 764b655e..f87630b4 100644 --- a/library/src/com/orm/SugarRecord.java +++ b/library/src/com/orm/SugarRecord.java @@ -113,7 +113,11 @@ long save(SQLiteDatabase db) { values.put(columnName, (Long) null); } } else { - values.put(columnName, String.valueOf(columnValue)); + if (columnValue == null) { + values.putNull(columnName); + } else { + values.put(columnName, String.valueOf(columnValue)); + } } } From 336d093253d1df8aaa793b2f981656c661a4b0bc Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Thu, 10 Jul 2014 21:31:38 -0700 Subject: [PATCH 022/305] Clean up table creation for date and calendar fields --- library/src/com/orm/SugarDb.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/src/com/orm/SugarDb.java b/library/src/com/orm/SugarDb.java index 1e7acc86..dafdd97b 100644 --- a/library/src/com/orm/SugarDb.java +++ b/library/src/com/orm/SugarDb.java @@ -173,6 +173,9 @@ private > void createTable(T table, SQLiteDatabase sqLi } sb.append(", ").append(columnName).append(" ").append(columnType); if(column.isAnnotationPresent(NotNull.class)) { + if (columnType.endsWith(" NULL")) { + sb.delete(sb.length() - 5, sb.length()); + } sb.append(" NOT NULL"); } if(column.isAnnotationPresent(Unique.class)) { From 458a583165d2eae54029f6b7707cde898700ab53 Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Mon, 14 Jul 2014 21:11:01 -0700 Subject: [PATCH 023/305] Clean up Select sql method --- library/src/com/orm/query/Select.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/library/src/com/orm/query/Select.java b/library/src/com/orm/query/Select.java index ceaac65c..aa2462fa 100644 --- a/library/src/com/orm/query/Select.java +++ b/library/src/com/orm/query/Select.java @@ -122,9 +122,7 @@ public T first() { String toSql() { StringBuilder sql = new StringBuilder(); - sql.append("SELECT * FROM"); - - sql.append("FROM "); + sql.append("SELECT * FROM "); sql.append(SugarRecord.getTableName(this.record) + " "); From a753b4dabc9553bf3adf2cde6f0db3bf72c8508b Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Mon, 14 Jul 2014 21:12:44 -0700 Subject: [PATCH 024/305] =?UTF-8?q?Fix=20nesting=20of=20=E2=80=9Cor?= =?UTF-8?q?=E2=80=9D=20and=20=E2=80=9Cand=E2=80=9D=20query=20building=20st?= =?UTF-8?q?atements=20Closes=20#104?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- library/src/com/orm/query/Select.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/library/src/com/orm/query/Select.java b/library/src/com/orm/query/Select.java index aa2462fa..95688297 100644 --- a/library/src/com/orm/query/Select.java +++ b/library/src/com/orm/query/Select.java @@ -56,23 +56,29 @@ public Select where(Condition... condition) { } private void mergeConditions(Condition[] conditions, Condition.Type type) { + StringBuilder toAppend = new StringBuilder(""); for (Condition condition : conditions) { - if (!"".equals(whereClause)) { - whereClause += " " + type.name() + " "; + if (toAppend.length() != 0) { + toAppend.append(" " + type.name() + " "); } if(Condition.Check.LIKE.equals(condition.getCheck()) || Condition.Check.NOT_LIKE.equals(condition.getCheck())){ - whereClause += condition.getProperty() + condition.getCheckSymbol() + "'" + condition.getValue().toString() +"'"; + toAppend.append(condition.getProperty() + condition.getCheckSymbol() + "'" + condition.getValue().toString() + "'"); }else{ - whereClause += condition.getProperty() + condition.getCheckSymbol() + "? "; + toAppend.append(condition.getProperty() + condition.getCheckSymbol() + "? "); args.add(condition.getValue()); } } + + if (!"".equals(whereClause)) { + whereClause += " " + type.name() + " "; + } + whereClause += "(" + toAppend + ")"; } public Select whereOr(Condition... args) { From 25588f3e2606afc1ef4da82047d4b24b74f918b3 Mon Sep 17 00:00:00 2001 From: Satya Narayan Date: Tue, 15 Jul 2014 19:40:51 -0700 Subject: [PATCH 025/305] Added Column annotation for custom naming of fields. It also combines @Unique and @NotNull annotations apart from name. Should be used as a holder for all column properties in future. --- example/src/com/example/Note.java | 5 ++- example/src/com/example/SugarActivity.java | 6 +-- library/src/com/orm/StringUtil.java | 14 ++++++- library/src/com/orm/SugarDb.java | 46 +++++++++++++++++----- library/src/com/orm/SugarRecord.java | 8 ++-- library/src/com/orm/dsl/Column.java | 11 ++++++ library/test/com/orm/StringUtilTest.java | 14 +++---- 7 files changed, 77 insertions(+), 27 deletions(-) create mode 100644 library/src/com/orm/dsl/Column.java diff --git a/example/src/com/example/Note.java b/example/src/com/example/Note.java index 468b074c..8e6308f9 100755 --- a/example/src/com/example/Note.java +++ b/example/src/com/example/Note.java @@ -1,10 +1,13 @@ package com.example; -import android.content.Context; import com.orm.SugarRecord; +import com.orm.dsl.Column; public class Note extends SugarRecord{ + + @Column(name = "noteId", unique = true, notNull = true) private int noteId; + private String title; private String description; private String name; diff --git a/example/src/com/example/SugarActivity.java b/example/src/com/example/SugarActivity.java index 22dd9c1c..f5b8fe9f 100755 --- a/example/src/com/example/SugarActivity.java +++ b/example/src/com/example/SugarActivity.java @@ -32,9 +32,9 @@ private void initDb() { t2.save(); Note n1 = new Note( 10, "note1", "description1", t1); - Note n2 = new Note(10, "note12", "description2", t1); - Note n3 = new Note( 10, "note13", "description3", t2); - Note n4 = new Note( 10, "note4", "description4", t2); + Note n2 = new Note(11, "note12", "description2", t1); + Note n3 = new Note( 12, "note13", "description3", t2); + Note n4 = new Note( 13, "note4", "description4", t2); TextNote textNote = new TextNote(); textNote.desc = "Test"; diff --git a/library/src/com/orm/StringUtil.java b/library/src/com/orm/StringUtil.java index 40fabf48..4f837a1b 100644 --- a/library/src/com/orm/StringUtil.java +++ b/library/src/com/orm/StringUtil.java @@ -1,7 +1,11 @@ package com.orm; +import com.orm.dsl.Column; + +import java.lang.reflect.Field; + public class StringUtil { - public static String toSQLName(String javaNotation) { + public static String toSQLNameDefault(String javaNotation) { if(javaNotation.equalsIgnoreCase("_id")) return "_id"; @@ -35,4 +39,12 @@ public static String toSQLName(String javaNotation) { return sb.toString(); } + public static String toSQLName(Field field){ + if(field.isAnnotationPresent(Column.class)){ + Column annotation = field.getAnnotation(Column.class); + return annotation.name(); + } + + return toSQLNameDefault(field.getName()); + } } diff --git a/library/src/com/orm/SugarDb.java b/library/src/com/orm/SugarDb.java index dafdd97b..ceab2243 100644 --- a/library/src/com/orm/SugarDb.java +++ b/library/src/com/orm/SugarDb.java @@ -25,6 +25,7 @@ import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; +import com.orm.dsl.Column; import com.orm.dsl.NotNull; import com.orm.dsl.Unique; @@ -60,7 +61,6 @@ private > List getDomainClasses(Context context) { @SuppressWarnings("unchecked") private > T getDomainClass(String className, Context context) { - Log.i("Sugar", "domain class"); Class discoveredClass = null; try { discoveredClass = Class.forName(className, true, context.getClass().getClassLoader()); @@ -70,11 +70,14 @@ private > T getDomainClass(String className, Context co if ((discoveredClass == null) || (!SugarRecord.class.isAssignableFrom(discoveredClass)) || + SugarRecord.class.equals(discoveredClass) || Modifier.isAbstract(discoveredClass.getModifiers())) { return null; } else { try { + Log.i("Sugar", "domain class : " + discoveredClass.getSimpleName()); return (T) discoveredClass.getDeclaredConstructor().newInstance(); + } catch (InstantiationException e) { Log.e("Sugar", e.getMessage()); } catch (IllegalAccessException e) { @@ -163,7 +166,7 @@ private > void createTable(T table, SQLiteDatabase sqLi " ( ID INTEGER PRIMARY KEY AUTOINCREMENT "); for (Field column : fields) { - String columnName = StringUtil.toSQLName(column.getName()); + String columnName = StringUtil.toSQLName(column); String columnType = QueryBuilder.getColumnType(column.getType()); if (columnType != null) { @@ -171,15 +174,38 @@ private > void createTable(T table, SQLiteDatabase sqLi if (columnName.equalsIgnoreCase("Id")) { continue; } - sb.append(", ").append(columnName).append(" ").append(columnType); - if(column.isAnnotationPresent(NotNull.class)) { - if (columnType.endsWith(" NULL")) { - sb.delete(sb.length() - 5, sb.length()); + + if(column.isAnnotationPresent(Column.class)){ + Column columnAnnotation = column.getAnnotation(Column.class); + columnName = columnAnnotation.name(); + + sb.append(", ").append(columnName).append(" ").append(columnType); + + if (columnAnnotation.notNull()) { + if (columnType.endsWith(" NULL")) { + sb.delete(sb.length() - 5, sb.length()); + } + sb.append(" NOT NULL"); + } + + if (columnAnnotation.unique()) { + sb.append(" UNIQUE"); + } + + }else { + + sb.append(", ").append(columnName).append(" ").append(columnType); + + if (column.isAnnotationPresent(NotNull.class)) { + if (columnType.endsWith(" NULL")) { + sb.delete(sb.length() - 5, sb.length()); + } + sb.append(" NOT NULL"); + } + + if (column.isAnnotationPresent(Unique.class)) { + sb.append(" UNIQUE"); } - sb.append(" NOT NULL"); - } - if(column.isAnnotationPresent(Unique.class)) { - sb.append(" UNIQUE"); } } } diff --git a/library/src/com/orm/SugarRecord.java b/library/src/com/orm/SugarRecord.java index f87630b4..30eba6ee 100644 --- a/library/src/com/orm/SugarRecord.java +++ b/library/src/com/orm/SugarRecord.java @@ -1,7 +1,6 @@ package com.orm; import android.content.ContentValues; -import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; @@ -10,7 +9,6 @@ import android.util.Log; import com.orm.dsl.Ignore; -import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -80,7 +78,7 @@ long save(SQLiteDatabase db) { column.setAccessible(true); Class columnType = column.getType(); try { - String columnName = StringUtil.toSQLName(column.getName()); + String columnName = StringUtil.toSQLName(column); Object columnValue = column.get(this); if (SugarRecord.class.isAssignableFrom(columnType)) { values.put(columnName, @@ -272,7 +270,7 @@ void inflate(Cursor cursor) { field.setAccessible(true); try { Class fieldType = field.getType(); - String colName = StringUtil.toSQLName(field.getName()); + String colName = StringUtil.toSQLName(field); int columnIndex = cursor.getColumnIndex(colName); @@ -390,7 +388,7 @@ public String getSqlName() { public static String getTableName(Class type) { - return StringUtil.toSQLName(type.getSimpleName()); + return StringUtil.toSQLNameDefault(type.getSimpleName()); } public Long getId() { diff --git a/library/src/com/orm/dsl/Column.java b/library/src/com/orm/dsl/Column.java new file mode 100644 index 00000000..2c47e3c0 --- /dev/null +++ b/library/src/com/orm/dsl/Column.java @@ -0,0 +1,11 @@ +package com.orm.dsl; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Column { + String name(); + boolean unique() default false; + boolean notNull() default false; +} diff --git a/library/test/com/orm/StringUtilTest.java b/library/test/com/orm/StringUtilTest.java index 923bf98d..8f2b8786 100644 --- a/library/test/com/orm/StringUtilTest.java +++ b/library/test/com/orm/StringUtilTest.java @@ -7,17 +7,17 @@ public class StringUtilTest { @Test public void testToSQLNameCaseConversion() throws Exception { - assertEquals("TESTLOWERCASE", StringUtil.toSQLName("testlowercase")); - assertEquals("TESTUPPERCASE", StringUtil.toSQLName("TESTUPPERCASE")); + assertEquals("TESTLOWERCASE", StringUtil.toSQLNameDefault("testlowercase")); + assertEquals("TESTUPPERCASE", StringUtil.toSQLNameDefault("TESTUPPERCASE")); } @Test public void testToSQLNameUnderscore(){ - assertEquals("TEST_UNDERSCORE", StringUtil.toSQLName("testUnderscore")); - assertEquals("AB_CD", StringUtil.toSQLName("AbCd")); - assertEquals("AB_CD", StringUtil.toSQLName("ABCd")); - assertEquals("AB_CD", StringUtil.toSQLName("AbCD")); - assertEquals("SOME_DETAILS_OBJECT", StringUtil.toSQLName("SomeDetailsObject")); + assertEquals("TEST_UNDERSCORE", StringUtil.toSQLNameDefault("testUnderscore")); + assertEquals("AB_CD", StringUtil.toSQLNameDefault("AbCd")); + assertEquals("AB_CD", StringUtil.toSQLNameDefault("ABCd")); + assertEquals("AB_CD", StringUtil.toSQLNameDefault("AbCD")); + assertEquals("SOME_DETAILS_OBJECT", StringUtil.toSQLNameDefault("SomeDetailsObject")); } From 36649d5514aae4b006ce3d6bff0028507ecba1da Mon Sep 17 00:00:00 2001 From: Satya Narayan Date: Tue, 15 Jul 2014 20:42:06 -0700 Subject: [PATCH 026/305] introduced Table annotation to handle custom table naming and removing the SugarRecord dependency --- example/src/com/example/Note.java | 14 +- example/src/com/example/SugarActivity.java | 11 +- library/src/com/orm/StringUtil.java | 69 ++++--- library/src/com/orm/SugarDb.java | 137 +++++++------- library/src/com/orm/SugarRecord.java | 206 ++++++++------------- library/src/com/orm/dsl/Table.java | 9 + library/src/com/orm/query/Select.java | 3 +- 7 files changed, 215 insertions(+), 234 deletions(-) create mode 100644 library/src/com/orm/dsl/Table.java diff --git a/example/src/com/example/Note.java b/example/src/com/example/Note.java index 8e6308f9..0ede586c 100755 --- a/example/src/com/example/Note.java +++ b/example/src/com/example/Note.java @@ -2,7 +2,9 @@ import com.orm.SugarRecord; import com.orm.dsl.Column; +import com.orm.dsl.Table; +@Table(name = "Note") public class Note extends SugarRecord{ @Column(name = "noteId", unique = true, notNull = true) @@ -42,20 +44,20 @@ public String getTitle() { return title; } - public String getDescription() { - return description; + public void setTitle(String title) { + this.title = title; } - public Tag getTag() { - return tag; + public String getDescription() { + return description; } public void setDescription(String description){ this.description = description; } - public void setTitle(String title) { - this.title = title; + public Tag getTag() { + return tag; } @Override diff --git a/example/src/com/example/SugarActivity.java b/example/src/com/example/SugarActivity.java index f5b8fe9f..88b0d29c 100755 --- a/example/src/com/example/SugarActivity.java +++ b/example/src/com/example/SugarActivity.java @@ -2,11 +2,8 @@ import android.app.Activity; import android.content.Intent; -import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; -import android.util.Log; -import com.orm.Database; -import com.orm.SugarApp; +import com.orm.SugarRecord; public class SugarActivity extends Activity { @@ -16,9 +13,9 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); - Note.deleteAll(Note.class); - TextNote.deleteAll(TextNote.class); - Tag.deleteAll(Tag.class); + SugarRecord.deleteAll(Note.class); + SugarRecord.deleteAll(TextNote.class); + SugarRecord.deleteAll(Tag.class); initDb(); Intent intent = new Intent(this, NoteListActivity.class); startActivity(intent); diff --git a/library/src/com/orm/StringUtil.java b/library/src/com/orm/StringUtil.java index 4f837a1b..2c9767ff 100644 --- a/library/src/com/orm/StringUtil.java +++ b/library/src/com/orm/StringUtil.java @@ -1,50 +1,59 @@ package com.orm; import com.orm.dsl.Column; +import com.orm.dsl.Table; import java.lang.reflect.Field; public class StringUtil { public static String toSQLNameDefault(String javaNotation) { - if(javaNotation.equalsIgnoreCase("_id")) - return "_id"; - - StringBuilder sb = new StringBuilder(); - char[] buf = javaNotation.toCharArray(); - - for (int i = 0; i < buf.length; i++) { - char prevChar = (i > 0) ? buf[i - 1] : 0; - char c = buf[i]; - char nextChar = (i < buf.length - 1) ? buf[i + 1] : 0; - boolean isFirstChar = (i == 0); - - if (isFirstChar || Character.isLowerCase(c) || Character.isDigit(c)) { - sb.append(Character.toUpperCase(c)); - } else if (Character.isUpperCase(c)) { - if (Character.isLetterOrDigit(prevChar)) { - if (Character.isLowerCase(prevChar)) { - sb.append('_').append(Character.toUpperCase(c)); - } else if (nextChar > 0 && Character.isLowerCase(nextChar)) { - sb.append('_').append(Character.toUpperCase(c)); - } else { - sb.append(c); - } - } - else { - sb.append(c); - } + if (javaNotation.equalsIgnoreCase("_id")) + return "_id"; + + StringBuilder sb = new StringBuilder(); + char[] buf = javaNotation.toCharArray(); + + for (int i = 0; i < buf.length; i++) { + char prevChar = (i > 0) ? buf[i - 1] : 0; + char c = buf[i]; + char nextChar = (i < buf.length - 1) ? buf[i + 1] : 0; + boolean isFirstChar = (i == 0); + + if (isFirstChar || Character.isLowerCase(c) || Character.isDigit(c)) { + sb.append(Character.toUpperCase(c)); + } else if (Character.isUpperCase(c)) { + if (Character.isLetterOrDigit(prevChar)) { + if (Character.isLowerCase(prevChar)) { + sb.append('_').append(Character.toUpperCase(c)); + } else if (nextChar > 0 && Character.isLowerCase(nextChar)) { + sb.append('_').append(Character.toUpperCase(c)); + } else { + sb.append(c); } + } else { + sb.append(c); + } } + } - return sb.toString(); + return sb.toString(); } - public static String toSQLName(Field field){ - if(field.isAnnotationPresent(Column.class)){ + public static String toSQLName(Field field) { + if (field.isAnnotationPresent(Column.class)) { Column annotation = field.getAnnotation(Column.class); return annotation.name(); } return toSQLNameDefault(field.getName()); } + + public static String toSQLName(Class table) { + + if (table.isAnnotationPresent(Table.class)) { + Table annotation = table.getAnnotation(Table.class); + return annotation.name(); + } + return StringUtil.toSQLNameDefault(table.getSimpleName()); + } } diff --git a/library/src/com/orm/SugarDb.java b/library/src/com/orm/SugarDb.java index ceab2243..0b3db0de 100644 --- a/library/src/com/orm/SugarDb.java +++ b/library/src/com/orm/SugarDb.java @@ -1,35 +1,22 @@ package com.orm; -import static com.orm.SugarConfig.getDatabaseVersion; -import static com.orm.SugarConfig.getDebugEnabled; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Modifier; -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Enumeration; -import java.util.List; - import android.content.Context; import android.content.pm.PackageManager; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; +import com.orm.dsl.*; +import dalvik.system.DexFile; -import com.orm.dsl.Column; -import com.orm.dsl.NotNull; -import com.orm.dsl.Unique; +import java.io.*; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.util.*; -import dalvik.system.DexFile; +import static com.orm.SugarConfig.getDatabaseVersion; +import static com.orm.SugarConfig.getDebugEnabled; public class SugarDb extends SQLiteOpenHelper { private Context context; @@ -40,16 +27,45 @@ public SugarDb(Context context) { } - private > List getDomainClasses(Context context) { - List domainClasses = new ArrayList(); + public static List getTableFields(Class table) { + List fieldList = SugarConfig.getFields(table); + if (fieldList != null) return fieldList; + + Log.d("Sugar", "Fetching properties"); + List typeFields = new ArrayList(); + + getAllFields(typeFields, table); + + List toStore = new ArrayList(); + for (Field field : typeFields) { + if (!field.isAnnotationPresent(Ignore.class) && !Modifier.isStatic(field.getModifiers()) && !Modifier.isTransient(field.getModifiers())) { + toStore.add(field); + } + } + + SugarConfig.setFields(table, toStore); + return toStore; + } + + private static List getAllFields(List fields, Class type) { + Collections.addAll(fields, type.getDeclaredFields()); + + if (type.getSuperclass() != null) { + fields = getAllFields(fields, type.getSuperclass()); + } + + return fields; + } + + private List getDomainClasses(Context context) { + List domainClasses = new ArrayList(); try { for (String className : getAllClasses(context)) { if (className.startsWith(SugarConfig.getDomainPackageName(context))) { - T domainClass = getDomainClass(className, context); + Class domainClass = getDomainClass(className, context); if (domainClass != null) domainClasses.add(domainClass); } } - } catch (IOException e) { Log.e("Sugar", e.getMessage()); } catch (PackageManager.NameNotFoundException e) { @@ -59,8 +75,7 @@ private > List getDomainClasses(Context context) { return domainClasses; } - @SuppressWarnings("unchecked") - private > T getDomainClass(String className, Context context) { + private Class getDomainClass(String className, Context context) { Class discoveredClass = null; try { discoveredClass = Class.forName(className, true, context.getClass().getClassLoader()); @@ -68,29 +83,18 @@ private > T getDomainClass(String className, Context co Log.e("Sugar", e.getMessage()); } - if ((discoveredClass == null) || - (!SugarRecord.class.isAssignableFrom(discoveredClass)) || - SugarRecord.class.equals(discoveredClass) || - Modifier.isAbstract(discoveredClass.getModifiers())) { - return null; - } else { - try { - Log.i("Sugar", "domain class : " + discoveredClass.getSimpleName()); - return (T) discoveredClass.getDeclaredConstructor().newInstance(); - - } catch (InstantiationException e) { - Log.e("Sugar", e.getMessage()); - } catch (IllegalAccessException e) { - Log.e("Sugar", e.getMessage()); - } catch (NoSuchMethodException e) { - Log.e("Sugar", e.getMessage()); - } catch (InvocationTargetException e) { - Log.e("Sugar", e.getMessage()); - } - } + if ((discoveredClass != null) && + ((SugarRecord.class.isAssignableFrom(discoveredClass) && + !SugarRecord.class.equals(discoveredClass)) || + discoveredClass.isAnnotationPresent(Table.class)) && + !Modifier.isAbstract(discoveredClass.getModifiers())) { - return null; + Log.i("Sugar", "domain class : " + discoveredClass.getSimpleName()); + return discoveredClass; + } else { + return null; + } } private List getAllClasses(Context context) throws PackageManager.NameNotFoundException, IOException { @@ -119,7 +123,7 @@ private List getAllClasses(Context context) throws PackageManager.NameNo } return classNames; } - + private void populateFiles(File path, List fileNames, String parent) { if (path.isDirectory()) { for (File newPath : path.listFiles()) { @@ -152,17 +156,20 @@ public void onCreate(SQLiteDatabase sqLiteDatabase) { createDatabase(sqLiteDatabase); } - private > void createDatabase(SQLiteDatabase sqLiteDatabase) { - List domainClasses = getDomainClasses(context); - for (T domain : domainClasses) { + private void createDatabase(SQLiteDatabase sqLiteDatabase) { + List domainClasses = getDomainClasses(context); + for (Class domain : domainClasses) { createTable(domain, sqLiteDatabase); } } - private > void createTable(T table, SQLiteDatabase sqLiteDatabase) { + private > void createTable(Class table, SQLiteDatabase sqLiteDatabase) { Log.i("Sugar", "create table"); - List fields = table.getTableFields(); - StringBuilder sb = new StringBuilder("CREATE TABLE ").append(table.getSqlName()).append( + List fields = getTableFields(table); + + String tableName = StringUtil.toSQLName(table); + + StringBuilder sb = new StringBuilder("CREATE TABLE ").append(tableName).append( " ( ID INTEGER PRIMARY KEY AUTOINCREMENT "); for (Field column : fields) { @@ -211,7 +218,7 @@ private > void createTable(T table, SQLiteDatabase sqLi } sb.append(" ) "); - Log.i("Sugar", "creating table " + table.getSqlName()); + Log.i("Sugar", "creating table " + tableName); if (!"".equals(sb.toString())) sqLiteDatabase.execSQL(sb.toString()); @@ -232,11 +239,11 @@ public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVers /** * Create the tables that do not exist. */ - private > void doUpgrade(SQLiteDatabase sqLiteDatabase) { - List domainClasses = getDomainClasses(context); - for (T domain : domainClasses) { + private void doUpgrade(SQLiteDatabase sqLiteDatabase) { + List domainClasses = getDomainClasses(context); + for (Class domain : domainClasses) { try {// we try to do a select, if fails then (?) there isn't the table - sqLiteDatabase.query(domain.tableName, null, null, null, null, null, null); + sqLiteDatabase.query(StringUtil.toSQLName(domain), null, null, null, null, null, null); } catch (SQLiteException e) { Log.i("Sugar", String.format("creating table on update (error was '%s')", e.getMessage())); createTable(domain, sqLiteDatabase); @@ -244,10 +251,10 @@ private > void doUpgrade(SQLiteDatabase sqLiteDatabase) } } - private > void deleteTables(SQLiteDatabase sqLiteDatabase) { - List tables = getDomainClasses(this.context); - for (T table : tables) { - sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + table.getSqlName()); + private void deleteTables(SQLiteDatabase sqLiteDatabase) { + List tables = getDomainClasses(this.context); + for (Class table : tables) { + sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + StringUtil.toSQLName(table)); } } diff --git a/library/src/com/orm/SugarRecord.java b/library/src/com/orm/SugarRecord.java index 30eba6ee..dfc80158 100644 --- a/library/src/com/orm/SugarRecord.java +++ b/library/src/com/orm/SugarRecord.java @@ -7,11 +7,9 @@ import android.database.sqlite.SQLiteStatement; import android.text.TextUtils; import android.util.Log; -import com.orm.dsl.Ignore; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.sql.Timestamp; import java.util.*; @@ -19,30 +17,18 @@ public class SugarRecord{ - @Ignore - String tableName = getSqlName(); - protected Long id = null; - public void delete() { - SQLiteDatabase db = getSugarContext().getDatabase().getDB(); - db.delete(this.tableName, "Id=?", new String[]{getId().toString()}); - } - public static > void deleteAll(Class type) { Database db = getSugarContext().getDatabase(); SQLiteDatabase sqLiteDatabase = db.getDB(); - sqLiteDatabase.delete(getTableName(type), null, null); + sqLiteDatabase.delete(StringUtil.toSQLName(type), null, null); } public static > void deleteAll(Class type, String whereClause, String... whereArgs ) { Database db = getSugarContext().getDatabase(); SQLiteDatabase sqLiteDatabase = db.getDB(); - sqLiteDatabase.delete(getTableName(type), whereClause, whereArgs); - } - - public long save() { - return save(getSugarContext().getDatabase().getDB()); + sqLiteDatabase.delete(StringUtil.toSQLName(type), whereClause, whereArgs); } @SuppressWarnings("deprecation") @@ -70,72 +56,12 @@ public static > void saveInTx(Collection objects ) { } - long save(SQLiteDatabase db) { - - List columns = getTableFields(); - ContentValues values = new ContentValues(columns.size()); - for (Field column : columns) { - column.setAccessible(true); - Class columnType = column.getType(); - try { - String columnName = StringUtil.toSQLName(column); - Object columnValue = column.get(this); - if (SugarRecord.class.isAssignableFrom(columnType)) { - values.put(columnName, - (columnValue != null) - ? String.valueOf(((SugarRecord) columnValue).id) - : "0"); - } else { - if (columnType.equals(Short.class) || columnType.equals(short.class)) { - values.put(columnName, (Short) columnValue); - } else if (columnType.equals(Integer.class) || columnType.equals(int.class)) { - values.put(columnName, (Integer) columnValue); - } else if (columnType.equals(Long.class) || columnType.equals(long.class)) { - values.put(columnName, (Long) columnValue); - } else if (columnType.equals(Float.class) || columnType.equals(float.class)) { - values.put(columnName, (Float) columnValue); - } else if (columnType.equals(Double.class) || columnType.equals(double.class)) { - values.put(columnName, (Double) columnValue); - } else if (columnType.equals(Boolean.class) || columnType.equals(boolean.class)) { - values.put(columnName, (Boolean) columnValue); - } else if (Date.class.equals(columnType)) { - try { - values.put(columnName, ((Date) column.get(this)).getTime()); - } catch (NullPointerException e) { - values.put(columnName, (Long) null); - } - } else if (Calendar.class.equals(columnType)) { - try { - values.put(columnName, ((Calendar) column.get(this)).getTimeInMillis()); - } catch (NullPointerException e) { - values.put(columnName, (Long) null); - } - } else { - if (columnValue == null) { - values.putNull(columnName); - } else { - values.put(columnName, String.valueOf(columnValue)); - } - } - } - - } catch (IllegalAccessException e) { - Log.e("Sugar", e.getMessage()); - } - } - - id = db.insertWithOnConflict(getSqlName(), null, values, SQLiteDatabase.CONFLICT_REPLACE); - - Log.i("Sugar", getClass().getSimpleName() + " saved : " + id); - return id; - } - public static > List listAll(Class type) { return find(type, null, null, null, null, null); } public static > T findById(Class type, Long id) { - List list = find( type, "id=?", new String[]{String.valueOf(id)}, null, null, "1"); + List list = find(type, "id=?", new String[]{String.valueOf(id)}, null, null, "1"); if (list.isEmpty()) return null; return list.get(0); } @@ -166,7 +92,7 @@ public static > Iterator findAsIterator(Class typ Database db = getSugarContext().getDatabase(); SQLiteDatabase sqLiteDatabase = db.getDB(); - Cursor c = sqLiteDatabase.query(getTableName(type), null, + Cursor c = sqLiteDatabase.query(StringUtil.toSQLName(type), null, whereClause, whereArgs, groupBy, null, orderBy, limit); return new CursorIterator(type, c); } @@ -209,7 +135,7 @@ public static > List find(Class type, SQLiteDatabase sqLiteDatabase = db.getDB(); T entity; List toRet = new ArrayList(); - Cursor c = sqLiteDatabase.query(getTableName(type), null, + Cursor c = sqLiteDatabase.query(StringUtil.toSQLName(type), null, whereClause, whereArgs, groupBy, null, orderBy, limit); try { while (c.moveToNext()) { @@ -228,22 +154,22 @@ public static > List find(Class type, public static > long count(Class type) { return count(type, null, null, null, null, null); } - + public static > long count(Class type, String whereClause, String[] whereArgs) { return count(type, whereClause, whereArgs, null, null, null); } - + public static > long count(Class type, String whereClause, String[] whereArgs, String groupBy, String orderBy, String limit) { - + Database db = getSugarContext().getDatabase(); SQLiteDatabase sqLiteDatabase = db.getDB(); long toRet = -1; String filter = (!TextUtils.isEmpty(whereClause)) ? " where " + whereClause : ""; - SQLiteStatement sqLiteStatament = sqLiteDatabase.compileStatement("SELECT count(*) FROM " + getTableName(type) + filter); + SQLiteStatement sqLiteStatament = sqLiteDatabase.compileStatement("SELECT count(*) FROM " + StringUtil.toSQLName(type) + filter); if (whereArgs != null) { for (int i = whereArgs.length; i != 0; i--) { @@ -258,14 +184,83 @@ public static > long count(Class type, } finally { sqLiteStatament.close(); } - - return toRet; + + return toRet; + } + + public void delete() { + SQLiteDatabase db = getSugarContext().getDatabase().getDB(); + db.delete(StringUtil.toSQLName(getClass()), "Id=?", new String[]{getId().toString()}); + } + + public long save() { + return save(getSugarContext().getDatabase().getDB()); + } + + long save(SQLiteDatabase db) { + + List columns = SugarDb.getTableFields(getClass()); + ContentValues values = new ContentValues(columns.size()); + for (Field column : columns) { + column.setAccessible(true); + Class columnType = column.getType(); + try { + String columnName = StringUtil.toSQLName(column); + Object columnValue = column.get(this); + if (SugarRecord.class.isAssignableFrom(columnType)) { + values.put(columnName, + (columnValue != null) + ? String.valueOf(((SugarRecord) columnValue).id) + : "0"); + } else { + if (columnType.equals(Short.class) || columnType.equals(short.class)) { + values.put(columnName, (Short) columnValue); + } else if (columnType.equals(Integer.class) || columnType.equals(int.class)) { + values.put(columnName, (Integer) columnValue); + } else if (columnType.equals(Long.class) || columnType.equals(long.class)) { + values.put(columnName, (Long) columnValue); + } else if (columnType.equals(Float.class) || columnType.equals(float.class)) { + values.put(columnName, (Float) columnValue); + } else if (columnType.equals(Double.class) || columnType.equals(double.class)) { + values.put(columnName, (Double) columnValue); + } else if (columnType.equals(Boolean.class) || columnType.equals(boolean.class)) { + values.put(columnName, (Boolean) columnValue); + } else if (Date.class.equals(columnType)) { + try { + values.put(columnName, ((Date) column.get(this)).getTime()); + } catch (NullPointerException e) { + values.put(columnName, (Long) null); + } + } else if (Calendar.class.equals(columnType)) { + try { + values.put(columnName, ((Calendar) column.get(this)).getTimeInMillis()); + } catch (NullPointerException e) { + values.put(columnName, (Long) null); + } + } else { + if (columnValue == null) { + values.putNull(columnName); + } else { + values.put(columnName, String.valueOf(columnValue)); + } + } + } + + } catch (IllegalAccessException e) { + Log.e("Sugar", e.getMessage()); + } + } + + id = db.insertWithOnConflict(StringUtil.toSQLName(getClass()), null, values, SQLiteDatabase.CONFLICT_REPLACE); + + Log.i("Sugar", getClass().getSimpleName() + " saved : " + id); + return id; } @SuppressWarnings("unchecked") void inflate(Cursor cursor) { Map entities = new HashMap(); - List columns = getTableFields(); + List columns = SugarDb.getTableFields(getClass()); for (Field field : columns) { field.setAccessible(true); try { @@ -352,45 +347,6 @@ void inflate(Cursor cursor) { } } - public List getTableFields() { - List fieldList = SugarConfig.getFields(getClass()); - if(fieldList != null) return fieldList; - - Log.d("Sugar", "Fetching properties"); - List typeFields = new ArrayList(); - - getAllFields(typeFields, getClass()); - - List toStore = new ArrayList(); - for (Field field : typeFields) { - if (!field.isAnnotationPresent(Ignore.class) && !Modifier.isStatic(field.getModifiers())&& !Modifier.isTransient(field.getModifiers())) { - toStore.add(field); - } - } - - SugarConfig.setFields(getClass(), toStore); - return toStore; - } - - private static List getAllFields(List fields, Class type) { - Collections.addAll(fields, type.getDeclaredFields()); - - if (type.getSuperclass() != null) { - fields = getAllFields(fields, type.getSuperclass()); - } - - return fields; - } - - public String getSqlName() { - return getTableName(getClass()); - } - - - public static String getTableName(Class type) { - return StringUtil.toSQLNameDefault(type.getSimpleName()); - } - public Long getId() { return id; } diff --git a/library/src/com/orm/dsl/Table.java b/library/src/com/orm/dsl/Table.java new file mode 100644 index 00000000..a801d9d7 --- /dev/null +++ b/library/src/com/orm/dsl/Table.java @@ -0,0 +1,9 @@ +package com.orm.dsl; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Table { + String name(); +} diff --git a/library/src/com/orm/query/Select.java b/library/src/com/orm/query/Select.java index 95688297..8c78bd43 100644 --- a/library/src/com/orm/query/Select.java +++ b/library/src/com/orm/query/Select.java @@ -1,5 +1,6 @@ package com.orm.query; +import com.orm.StringUtil; import com.orm.SugarRecord; import java.util.ArrayList; @@ -130,7 +131,7 @@ String toSql() { sql.append("SELECT * FROM "); - sql.append(SugarRecord.getTableName(this.record) + " "); + sql.append(StringUtil.toSQLName(this.record) + " "); if (whereClause != null) { sql.append("WHERE " + whereClause + " "); From 93b1f91a25ef4f6c9a45a22bca53e99118b4e5a0 Mon Sep 17 00:00:00 2001 From: Satya Narayan Date: Wed, 16 Jul 2014 17:43:28 -0700 Subject: [PATCH 027/305] remove dependency to extend SugarRecord. Now entities have a choice of extending SugarRecord or not. Both type of models will be supported. Refactored the code to enable models without extending SugarRecord class. Following is a snapshot of changes: 1. Moved all naming to NamingHelper class. 2. All reflection related stuff moves to ReflectionUtil 3. Remove SugarRecord references from Generics. Class need not extend SugarRecord. 4. Improved the inflate logic for faster child record population. 5. Removed Database class. Wasn't adding much value. 6. Changed the example project to reflect the latest changes --- example/src/com/example/AddNoteActivity.java | 6 +- example/src/com/example/NewNote.java | 11 + example/src/com/example/Note.java | 5 +- example/src/com/example/NoteListActivity.java | 5 +- example/src/com/example/NoteRelation.java | 5 +- example/src/com/example/SugarActivity.java | 31 +- example/src/com/example/Tag.java | 6 +- example/src/com/example/TextNote.java | 3 + library/src/com/orm/Database.java | 24 -- library/src/com/orm/DatabaseCreator.java | 173 +++++++++++ library/src/com/orm/SugarApp.java | 20 +- library/src/com/orm/SugarDb.java | 290 +----------------- library/src/com/orm/SugarRecord.java | 253 +++++---------- .../src/com/orm/SugarTransactionHelper.java | 2 +- library/src/com/orm/query/Select.java | 14 +- .../NamingHelper.java} | 6 +- .../com/orm/{ => util}/NumberComparator.java | 18 +- .../src/com/orm/{ => util}/QueryBuilder.java | 4 +- library/src/com/orm/util/ReflectionUtil.java | 281 +++++++++++++++++ .../src/com/orm/{ => util}/SugarConfig.java | 2 +- .../orm/{ => util}/SugarCursorFactory.java | 2 +- library/test/com/orm/NamingHelperTest.java | 25 ++ library/test/com/orm/StringUtilTest.java | 24 -- library/test/com/orm/query/TestRecord.java | 3 +- 24 files changed, 648 insertions(+), 565 deletions(-) create mode 100644 example/src/com/example/NewNote.java delete mode 100644 library/src/com/orm/Database.java create mode 100644 library/src/com/orm/DatabaseCreator.java rename library/src/com/orm/{StringUtil.java => util/NamingHelper.java} (93%) rename library/src/com/orm/{ => util}/NumberComparator.java (99%) rename library/src/com/orm/{ => util}/QueryBuilder.java (95%) create mode 100644 library/src/com/orm/util/ReflectionUtil.java rename library/src/com/orm/{ => util}/SugarConfig.java (99%) rename library/src/com/orm/{ => util}/SugarCursorFactory.java (97%) create mode 100644 library/test/com/orm/NamingHelperTest.java delete mode 100644 library/test/com/orm/StringUtilTest.java diff --git a/example/src/com/example/AddNoteActivity.java b/example/src/com/example/AddNoteActivity.java index 06a6c62f..aba5e236 100755 --- a/example/src/com/example/AddNoteActivity.java +++ b/example/src/com/example/AddNoteActivity.java @@ -9,6 +9,8 @@ import android.widget.LinearLayout; import android.widget.TextView; +import static com.orm.SugarRecord.save; + public class AddNoteActivity extends Activity { public void onCreate(Bundle savedInstanceState) { @@ -39,8 +41,8 @@ public void onCreate(Bundle savedInstanceState) { save.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { Tag tag = new Tag(tagBox.getText().toString()); - tag.save(); - new Note( 10, titleBox.getText().toString(), descBox.getText().toString(),tag).save(); + save(tag); + save(new Note(10 + (int) (10 * Math.random()), titleBox.getText().toString(), descBox.getText().toString(), tag)); Intent intent = new Intent(AddNoteActivity.this, NoteListActivity.class); startActivity(intent); } diff --git a/example/src/com/example/NewNote.java b/example/src/com/example/NewNote.java new file mode 100644 index 00000000..a80e2d4a --- /dev/null +++ b/example/src/com/example/NewNote.java @@ -0,0 +1,11 @@ +package com.example; + +import com.orm.dsl.Table; + +@Table(name = "new_note") +public class NewNote { + + public long id; + public String name; + +} diff --git a/example/src/com/example/Note.java b/example/src/com/example/Note.java index 0ede586c..9f1edaa4 100755 --- a/example/src/com/example/Note.java +++ b/example/src/com/example/Note.java @@ -1,11 +1,10 @@ package com.example; -import com.orm.SugarRecord; import com.orm.dsl.Column; import com.orm.dsl.Table; @Table(name = "Note") -public class Note extends SugarRecord{ +public class Note { @Column(name = "noteId", unique = true, notNull = true) private int noteId; @@ -62,7 +61,7 @@ public Tag getTag() { @Override public String toString() { - return title + "id: " + id + " - " + tag + " " + tag.getId(); + return title + "id: " + noteId; } } diff --git a/example/src/com/example/NoteListActivity.java b/example/src/com/example/NoteListActivity.java index a5d0399b..09877f2d 100755 --- a/example/src/com/example/NoteListActivity.java +++ b/example/src/com/example/NoteListActivity.java @@ -6,7 +6,7 @@ import android.util.Log; import android.view.View; import android.widget.ArrayAdapter; - +import com.orm.SugarRecord; import com.orm.query.Condition; import com.orm.query.Select; @@ -19,8 +19,9 @@ public void onCreate(Bundle savedInstanceState) { setContentView(R.layout.notelist); List notes = Select.from(Note.class).orderBy("title").list();//Note.listAll(Note.class); + List list = SugarRecord.listAll(NewNote.class); - setListAdapter(new ArrayAdapter(this,android.R.layout.simple_list_item_1, notes)); + setListAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_1, notes)); findViewById(R.id.Button01).setOnClickListener(new View.OnClickListener() { public void onClick(View view) { diff --git a/example/src/com/example/NoteRelation.java b/example/src/com/example/NoteRelation.java index 1a971d7d..a64d055a 100644 --- a/example/src/com/example/NoteRelation.java +++ b/example/src/com/example/NoteRelation.java @@ -1,9 +1,10 @@ package com.example; import android.content.Context; -import com.orm.SugarRecord; +import com.orm.dsl.Table; -public class NoteRelation extends SugarRecord { +@Table(name = "note_relation") +public class NoteRelation { String name; int noteId; diff --git a/example/src/com/example/SugarActivity.java b/example/src/com/example/SugarActivity.java index 88b0d29c..b7130f86 100755 --- a/example/src/com/example/SugarActivity.java +++ b/example/src/com/example/SugarActivity.java @@ -5,6 +5,8 @@ import android.os.Bundle; import com.orm.SugarRecord; +import static com.orm.SugarRecord.save; + public class SugarActivity extends Activity { /** Called when the activity is first created. */ @@ -16,6 +18,7 @@ public void onCreate(Bundle savedInstanceState) SugarRecord.deleteAll(Note.class); SugarRecord.deleteAll(TextNote.class); SugarRecord.deleteAll(Tag.class); + SugarRecord.deleteAll(NewNote.class); initDb(); Intent intent = new Intent(this, NoteListActivity.class); startActivity(intent); @@ -25,8 +28,8 @@ private void initDb() { Tag t1 = new Tag("tag1"); Tag t2 = new Tag("tag2"); - t1.save(); - t2.save(); + save(t1); + save(t2); Note n1 = new Note( 10, "note1", "description1", t1); Note n2 = new Note(11, "note12", "description2", t1); @@ -36,23 +39,25 @@ private void initDb() { TextNote textNote = new TextNote(); textNote.desc = "Test"; - textNote.save(); - n1.save(); - n2.save(); - n3.save(); - n4.save(); + save(textNote); + save(n1); + save(n2); + save(n3); + save(n4); n1.setDescription("matrix"); n1.setTitle("atrix"); - n1.save(); + save(n1); n2.setDescription("matrix"); n2.setTitle("satrix"); - n2.save(); + save(n2); n3.setDescription("matrix"); n3.setTitle("batrix"); - n3.save(); - - + save(n3); - } + NewNote newNote = new NewNote(); + newNote.name = "name"; + save(newNote); + + } } diff --git a/example/src/com/example/Tag.java b/example/src/com/example/Tag.java index 24d8c145..6600c14a 100755 --- a/example/src/com/example/Tag.java +++ b/example/src/com/example/Tag.java @@ -1,9 +1,9 @@ package com.example; -import android.content.Context; -import com.orm.SugarRecord; +import com.orm.dsl.Table; -public class Tag extends SugarRecord{ +@Table(name = "tag") +public class Tag { private String name; public Tag(String name) { diff --git a/example/src/com/example/TextNote.java b/example/src/com/example/TextNote.java index c4025774..91b7a33c 100644 --- a/example/src/com/example/TextNote.java +++ b/example/src/com/example/TextNote.java @@ -1,5 +1,8 @@ package com.example; +import com.orm.dsl.Table; + +@Table(name = "text_note") public class TextNote extends Note { public String desc; diff --git a/library/src/com/orm/Database.java b/library/src/com/orm/Database.java deleted file mode 100644 index 0896ee1f..00000000 --- a/library/src/com/orm/Database.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.orm; - -import android.content.Context; -import android.database.sqlite.SQLiteDatabase; - - -public class Database { - private SugarDb sugarDb; - private SQLiteDatabase sqLiteDatabase; - - public Database(Context context){ - this.sugarDb = new SugarDb(context); - } - - - public synchronized SQLiteDatabase getDB() { - if (this.sqLiteDatabase == null) { - this.sqLiteDatabase = this.sugarDb.getWritableDatabase(); - } - - return this.sqLiteDatabase; - } - -} diff --git a/library/src/com/orm/DatabaseCreator.java b/library/src/com/orm/DatabaseCreator.java new file mode 100644 index 00000000..616e742e --- /dev/null +++ b/library/src/com/orm/DatabaseCreator.java @@ -0,0 +1,173 @@ +package com.orm; + +import android.content.Context; +import android.database.SQLException; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; +import android.util.Log; +import com.orm.dsl.Column; +import com.orm.dsl.NotNull; +import com.orm.dsl.Unique; +import com.orm.util.NamingHelper; +import com.orm.util.NumberComparator; +import com.orm.util.QueryBuilder; +import com.orm.util.ReflectionUtil; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static com.orm.util.ReflectionUtil.getDomainClasses; + +public class DatabaseCreator { + + private Context context; + + public DatabaseCreator(Context context) { + this.context = context; + } + + public void createDatabase(SQLiteDatabase sqLiteDatabase) { + List domainClasses = getDomainClasses(context); + for (Class domain : domainClasses) { + createTable(domain, sqLiteDatabase); + } + } + + public void doUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) { + List domainClasses = getDomainClasses(context); + for (Class domain : domainClasses) { + try {// we try to do a select, if fails then (?) there isn't the table + sqLiteDatabase.query(NamingHelper.toSQLName(domain), null, null, null, null, null, null); + } catch (SQLiteException e) { + Log.i("Sugar", String.format("creating table on update (error was '%s')", e.getMessage())); + createTable(domain, sqLiteDatabase); + } + } + + executeSugarUpgrade(sqLiteDatabase, oldVersion, newVersion); + } + + public void deleteTables(SQLiteDatabase sqLiteDatabase) { + List tables = getDomainClasses(context); + for (Class table : tables) { + sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + NamingHelper.toSQLName(table)); + } + } + + private boolean executeSugarUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + + boolean isSuccess = false; + try { + List files = Arrays.asList(this.context.getAssets().list("sugar_upgrades")); + Collections.sort(files, new NumberComparator()); + + for (String file : files) { + Log.i("Sugar", "filename : " + file); + try { + int version = Integer.valueOf(file.replace(".sql", "")); + + if ((version > oldVersion) && (version <= newVersion)) { + executeScript(db, file); + isSuccess = true; + } + } catch (NumberFormatException e) { + Log.i("Sugar", "not a sugar script. ignored." + file); + } + } + } catch (IOException e) { + Log.e("Sugar", e.getMessage()); + } + + return isSuccess; + } + + private void executeScript(SQLiteDatabase db, String file) { + try { + InputStream is = this.context.getAssets().open("sugar_upgrades/" + file); + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + String line; + while ((line = reader.readLine()) != null) { + Log.i("Sugar script", line); + db.execSQL(line.toString()); + } + } catch (IOException e) { + Log.e("Sugar", e.getMessage()); + } + + Log.i("Sugar", "script executed"); + } + + private void createTable(Class table, SQLiteDatabase sqLiteDatabase) { + Log.i("Sugar", "create table"); + List fields = ReflectionUtil.getTableFields(table); + + String tableName = NamingHelper.toSQLName(table); + + StringBuilder sb = new StringBuilder("CREATE TABLE ").append(tableName).append( + " ( ID INTEGER PRIMARY KEY AUTOINCREMENT "); + + for (Field column : fields) { + String columnName = NamingHelper.toSQLName(column); + String columnType = QueryBuilder.getColumnType(column.getType()); + + if (columnType != null) { + + if (columnName.equalsIgnoreCase("Id")) { + continue; + } + + if (column.isAnnotationPresent(Column.class)) { + Column columnAnnotation = column.getAnnotation(Column.class); + columnName = columnAnnotation.name(); + + sb.append(", ").append(columnName).append(" ").append(columnType); + + if (columnAnnotation.notNull()) { + if (columnType.endsWith(" NULL")) { + sb.delete(sb.length() - 5, sb.length()); + } + sb.append(" NOT NULL"); + } + + if (columnAnnotation.unique()) { + sb.append(" UNIQUE"); + } + + } else { + + sb.append(", ").append(columnName).append(" ").append(columnType); + + if (column.isAnnotationPresent(NotNull.class)) { + if (columnType.endsWith(" NULL")) { + sb.delete(sb.length() - 5, sb.length()); + } + sb.append(" NOT NULL"); + } + + if (column.isAnnotationPresent(Unique.class)) { + sb.append(" UNIQUE"); + } + } + } + } + sb.append(" ) "); + + Log.i("Sugar", "creating table " + tableName); + + if (!"".equals(sb.toString())) { + try { + sqLiteDatabase.execSQL(sb.toString()); + } catch (SQLException e) { + e.printStackTrace(); + } + + } + } + +} diff --git a/library/src/com/orm/SugarApp.java b/library/src/com/orm/SugarApp.java index ac7da99d..e2638a0e 100644 --- a/library/src/com/orm/SugarApp.java +++ b/library/src/com/orm/SugarApp.java @@ -2,13 +2,17 @@ public class SugarApp extends android.app.Application{ - private Database database; private static SugarApp sugarContext; + private SugarDb sugarDb; + + public static SugarApp getSugarContext() { + return sugarContext; + } public void onCreate(){ super.onCreate(); SugarApp.sugarContext = this; - this.database = new Database(this); + this.sugarDb = new SugarDb(this); } /* @@ -18,17 +22,13 @@ public void onCreate(){ * Robolectric Android mock. */ public void onTerminate(){ - if (this.database != null) { - this.database.getDB().close(); + if (this.sugarDb != null) { + this.sugarDb.getDB().close(); } super.onTerminate(); } - public static SugarApp getSugarContext(){ - return sugarContext; - } - - protected Database getDatabase() { - return database; + protected SugarDb getSugarDb() { + return sugarDb; } } diff --git a/library/src/com/orm/SugarDb.java b/library/src/com/orm/SugarDb.java index 0b3db0de..7a097054 100644 --- a/library/src/com/orm/SugarDb.java +++ b/library/src/com/orm/SugarDb.java @@ -1,303 +1,39 @@ package com.orm; import android.content.Context; -import android.content.pm.PackageManager; import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteOpenHelper; -import android.util.Log; -import com.orm.dsl.*; -import dalvik.system.DexFile; +import com.orm.util.SugarConfig; +import com.orm.util.SugarCursorFactory; -import java.io.*; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.net.URL; -import java.util.*; - -import static com.orm.SugarConfig.getDatabaseVersion; -import static com.orm.SugarConfig.getDebugEnabled; +import static com.orm.util.SugarConfig.getDatabaseVersion; +import static com.orm.util.SugarConfig.getDebugEnabled; public class SugarDb extends SQLiteOpenHelper { - private Context context; + private final DatabaseCreator databaseCreator; + private SQLiteDatabase sqLiteDatabase; public SugarDb(Context context) { super(context, SugarConfig.getDatabaseName(context), new SugarCursorFactory(getDebugEnabled(context)), getDatabaseVersion(context)); - this.context = context; - - } - - public static List getTableFields(Class table) { - List fieldList = SugarConfig.getFields(table); - if (fieldList != null) return fieldList; - - Log.d("Sugar", "Fetching properties"); - List typeFields = new ArrayList(); - - getAllFields(typeFields, table); - - List toStore = new ArrayList(); - for (Field field : typeFields) { - if (!field.isAnnotationPresent(Ignore.class) && !Modifier.isStatic(field.getModifiers()) && !Modifier.isTransient(field.getModifiers())) { - toStore.add(field); - } - } - - SugarConfig.setFields(table, toStore); - return toStore; - } - - private static List getAllFields(List fields, Class type) { - Collections.addAll(fields, type.getDeclaredFields()); - - if (type.getSuperclass() != null) { - fields = getAllFields(fields, type.getSuperclass()); - } - - return fields; - } - - private List getDomainClasses(Context context) { - List domainClasses = new ArrayList(); - try { - for (String className : getAllClasses(context)) { - if (className.startsWith(SugarConfig.getDomainPackageName(context))) { - Class domainClass = getDomainClass(className, context); - if (domainClass != null) domainClasses.add(domainClass); - } - } - } catch (IOException e) { - Log.e("Sugar", e.getMessage()); - } catch (PackageManager.NameNotFoundException e) { - Log.e("Sugar", e.getMessage()); - } - - return domainClasses; - } - - private Class getDomainClass(String className, Context context) { - Class discoveredClass = null; - try { - discoveredClass = Class.forName(className, true, context.getClass().getClassLoader()); - } catch (ClassNotFoundException e) { - Log.e("Sugar", e.getMessage()); - } - - if ((discoveredClass != null) && - ((SugarRecord.class.isAssignableFrom(discoveredClass) && - !SugarRecord.class.equals(discoveredClass)) || - discoveredClass.isAnnotationPresent(Table.class)) && - !Modifier.isAbstract(discoveredClass.getModifiers())) { - - Log.i("Sugar", "domain class : " + discoveredClass.getSimpleName()); - return discoveredClass; - - } else { - return null; - } - } - - private List getAllClasses(Context context) throws PackageManager.NameNotFoundException, IOException { - String path = getSourcePath(context); - List classNames = new ArrayList(); - try { - DexFile dexfile = new DexFile(path); - Enumeration dexEntries = dexfile.entries(); - while (dexEntries.hasMoreElements()) { - classNames.add(dexEntries.nextElement()); - } - } catch (NullPointerException e) { - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - Enumeration urls = classLoader.getResources(""); - List fileNames = new ArrayList(); - while (urls.hasMoreElements()) { - String classDirectoryName = urls.nextElement().getFile(); - if (classDirectoryName.contains("bin") || classDirectoryName.contains("classes")) { - File classDirectory = new File(classDirectoryName); - for (File filePath : classDirectory.listFiles()) { - populateFiles(filePath, fileNames, ""); - } - classNames.addAll(fileNames); - } - } - } - return classNames; - } - - private void populateFiles(File path, List fileNames, String parent) { - if (path.isDirectory()) { - for (File newPath : path.listFiles()) { - if ("".equals(parent)) { - populateFiles(newPath, fileNames, path.getName()); - } else { - populateFiles(newPath, fileNames, parent + "." + path.getName()); - } - } - } else { - String pathName = path.getName(); - String classSuffix = ".class"; - pathName = pathName.endsWith(classSuffix) ? - pathName.substring(0, pathName.length() - classSuffix.length()) : pathName; - if ("".equals(parent)) { - fileNames.add(pathName); - } else { - fileNames.add(parent + "." + pathName); - } - } - } - - private String getSourcePath(Context context) throws PackageManager.NameNotFoundException { - return context.getPackageManager().getApplicationInfo(context.getPackageName(), 0).sourceDir; + databaseCreator = new DatabaseCreator(context); } @Override public void onCreate(SQLiteDatabase sqLiteDatabase) { - Log.i("Sugar", "on create"); - createDatabase(sqLiteDatabase); - } - - private void createDatabase(SQLiteDatabase sqLiteDatabase) { - List domainClasses = getDomainClasses(context); - for (Class domain : domainClasses) { - createTable(domain, sqLiteDatabase); - } - } - - private > void createTable(Class table, SQLiteDatabase sqLiteDatabase) { - Log.i("Sugar", "create table"); - List fields = getTableFields(table); - - String tableName = StringUtil.toSQLName(table); - - StringBuilder sb = new StringBuilder("CREATE TABLE ").append(tableName).append( - " ( ID INTEGER PRIMARY KEY AUTOINCREMENT "); - - for (Field column : fields) { - String columnName = StringUtil.toSQLName(column); - String columnType = QueryBuilder.getColumnType(column.getType()); - - if (columnType != null) { - - if (columnName.equalsIgnoreCase("Id")) { - continue; - } - - if(column.isAnnotationPresent(Column.class)){ - Column columnAnnotation = column.getAnnotation(Column.class); - columnName = columnAnnotation.name(); - - sb.append(", ").append(columnName).append(" ").append(columnType); - - if (columnAnnotation.notNull()) { - if (columnType.endsWith(" NULL")) { - sb.delete(sb.length() - 5, sb.length()); - } - sb.append(" NOT NULL"); - } - - if (columnAnnotation.unique()) { - sb.append(" UNIQUE"); - } - - }else { - - sb.append(", ").append(columnName).append(" ").append(columnType); - - if (column.isAnnotationPresent(NotNull.class)) { - if (columnType.endsWith(" NULL")) { - sb.delete(sb.length() - 5, sb.length()); - } - sb.append(" NOT NULL"); - } - - if (column.isAnnotationPresent(Unique.class)) { - sb.append(" UNIQUE"); - } - } - } - } - sb.append(" ) "); - - Log.i("Sugar", "creating table " + tableName); - - if (!"".equals(sb.toString())) - sqLiteDatabase.execSQL(sb.toString()); + databaseCreator.createDatabase(sqLiteDatabase); } @Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) { - Log.i("Sugar", "upgrading sugar"); - // check if some tables are to be created - doUpgrade(sqLiteDatabase); - - if (!executeSugarUpgrade(sqLiteDatabase, oldVersion, newVersion)) { - deleteTables(sqLiteDatabase); - onCreate(sqLiteDatabase); - } + databaseCreator.doUpgrade(sqLiteDatabase, oldVersion, newVersion); } - /** - * Create the tables that do not exist. - */ - private void doUpgrade(SQLiteDatabase sqLiteDatabase) { - List domainClasses = getDomainClasses(context); - for (Class domain : domainClasses) { - try {// we try to do a select, if fails then (?) there isn't the table - sqLiteDatabase.query(StringUtil.toSQLName(domain), null, null, null, null, null, null); - } catch (SQLiteException e) { - Log.i("Sugar", String.format("creating table on update (error was '%s')", e.getMessage())); - createTable(domain, sqLiteDatabase); - } + public synchronized SQLiteDatabase getDB() { + if (this.sqLiteDatabase == null) { + this.sqLiteDatabase = getWritableDatabase(); } - } - private void deleteTables(SQLiteDatabase sqLiteDatabase) { - List tables = getDomainClasses(this.context); - for (Class table : tables) { - sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + StringUtil.toSQLName(table)); - } + return this.sqLiteDatabase; } - private boolean executeSugarUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - - boolean isSuccess = false; - try { - List files = Arrays.asList(this.context.getAssets().list("sugar_upgrades")); - Collections.sort(files, new NumberComparator()); - - for (String file : files){ - Log.i("Sugar", "filename : " + file); - try { - int version = Integer.valueOf(file.replace(".sql", "")); - - if ((version > oldVersion) && (version <= newVersion)) { - executeScript(db, file); - isSuccess = true; - } - } catch (NumberFormatException e) { - Log.i("Sugar", "not a sugar script. ignored." + file); - } - } - } catch (IOException e) { - Log.e("Sugar", e.getMessage()); - } - - return isSuccess; - } - - private void executeScript(SQLiteDatabase db, String file) { - try { - InputStream is = this.context.getAssets().open("sugar_upgrades/" + file); - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - String line; - while ((line = reader.readLine()) != null) { - Log.i("Sugar script", line); - db.execSQL(line.toString()); - } - } catch (IOException e) { - Log.e("Sugar", e.getMessage()); - } - - Log.i("Sugar", "script executed"); - } } diff --git a/library/src/com/orm/SugarRecord.java b/library/src/com/orm/SugarRecord.java index dfc80158..efff7bd8 100644 --- a/library/src/com/orm/SugarRecord.java +++ b/library/src/com/orm/SugarRecord.java @@ -3,48 +3,48 @@ import android.content.ContentValues; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteStatement; import android.text.TextUtils; import android.util.Log; +import com.orm.dsl.Table; +import com.orm.util.NamingHelper; +import com.orm.util.ReflectionUtil; import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.sql.Timestamp; import java.util.*; import static com.orm.SugarApp.getSugarContext; -public class SugarRecord{ +public class SugarRecord { protected Long id = null; - public static > void deleteAll(Class type) { - Database db = getSugarContext().getDatabase(); + public static void deleteAll(Class type) { + SugarDb db = getSugarContext().getSugarDb(); SQLiteDatabase sqLiteDatabase = db.getDB(); - sqLiteDatabase.delete(StringUtil.toSQLName(type), null, null); + sqLiteDatabase.delete(NamingHelper.toSQLName(type), null, null); } - public static > void deleteAll(Class type, String whereClause, String... whereArgs ) { - Database db = getSugarContext().getDatabase(); + public static void deleteAll(Class type, String whereClause, String... whereArgs) { + SugarDb db = getSugarContext().getSugarDb(); SQLiteDatabase sqLiteDatabase = db.getDB(); - sqLiteDatabase.delete(StringUtil.toSQLName(type), whereClause, whereArgs); + sqLiteDatabase.delete(NamingHelper.toSQLName(type), whereClause, whereArgs); } @SuppressWarnings("deprecation") - public static > void saveInTx(T... objects ) { + public static void saveInTx(T... objects) { saveInTx(Arrays.asList(objects)); } @SuppressWarnings("deprecation") - public static > void saveInTx(Collection objects ) { - SQLiteDatabase sqLiteDatabase = getSugarContext().getDatabase().getDB(); + public static void saveInTx(Collection objects) { + SQLiteDatabase sqLiteDatabase = getSugarContext().getSugarDb().getDB(); try{ sqLiteDatabase.beginTransaction(); sqLiteDatabase.setLockingEnabled(false); for(T object: objects){ - object.save(sqLiteDatabase); + SugarRecord.save(object); } sqLiteDatabase.setTransactionSuccessful(); }catch (Exception e){ @@ -56,55 +56,55 @@ public static > void saveInTx(Collection objects ) { } - public static > List listAll(Class type) { + public static List listAll(Class type) { return find(type, null, null, null, null, null); } - public static > T findById(Class type, Long id) { + public static T findById(Class type, Long id) { List list = find(type, "id=?", new String[]{String.valueOf(id)}, null, null, "1"); if (list.isEmpty()) return null; return list.get(0); } - public static > T findById(Class type, Integer id) { + public static T findById(Class type, Integer id) { return findById(type, Long.valueOf(id)); } - public static > Iterator findAll(Class type) { + public static Iterator findAll(Class type) { return findAsIterator(type, null, null, null, null, null); } - public static > Iterator findAsIterator(Class type, + public static Iterator findAsIterator(Class type, String whereClause, String... whereArgs) { return findAsIterator(type, whereClause, whereArgs, null, null, null); } - public static > Iterator findWithQueryAsIterator(Class type, String query, String... arguments) { - Database db = getSugarContext().getDatabase(); + public static Iterator findWithQueryAsIterator(Class type, String query, String... arguments) { + SugarDb db = getSugarContext().getSugarDb(); SQLiteDatabase sqLiteDatabase = db.getDB(); Cursor c = sqLiteDatabase.rawQuery(query, arguments); return new CursorIterator(type, c); } - public static > Iterator findAsIterator(Class type, + public static Iterator findAsIterator(Class type, String whereClause, String[] whereArgs, String groupBy, String orderBy, String limit) { - Database db = getSugarContext().getDatabase(); + SugarDb db = getSugarContext().getSugarDb(); SQLiteDatabase sqLiteDatabase = db.getDB(); - Cursor c = sqLiteDatabase.query(StringUtil.toSQLName(type), null, + Cursor c = sqLiteDatabase.query(NamingHelper.toSQLName(type), null, whereClause, whereArgs, groupBy, null, orderBy, limit); return new CursorIterator(type, c); } - public static > List find(Class type, + public static List find(Class type, String whereClause, String... whereArgs) { return find(type, whereClause, whereArgs, null, null, null); } - public static > List findWithQuery(Class type, String query, String... arguments){ + public static List findWithQuery(Class type, String query, String... arguments) { - Database db = getSugarContext().getDatabase(); + SugarDb db = getSugarContext().getSugarDb(); SQLiteDatabase sqLiteDatabase = db.getDB(); T entity; List toRet = new ArrayList(); @@ -113,7 +113,7 @@ public static > List findWithQuery(Class type, St try { while (c.moveToNext()) { entity = type.getDeclaredConstructor().newInstance(); - entity.inflate(c); + SugarRecord.inflate(c, entity); toRet.add(entity); } } catch (Exception e) { @@ -125,22 +125,22 @@ public static > List findWithQuery(Class type, St } public static void executeQuery(String query, String... arguments){ - getSugarContext().getDatabase().getDB().execSQL(query, arguments); + getSugarContext().getSugarDb().getDB().execSQL(query, arguments); } - public static > List find(Class type, + public static List find(Class type, String whereClause, String[] whereArgs, String groupBy, String orderBy, String limit) { - Database db = getSugarContext().getDatabase(); + SugarDb db = getSugarContext().getSugarDb(); SQLiteDatabase sqLiteDatabase = db.getDB(); T entity; List toRet = new ArrayList(); - Cursor c = sqLiteDatabase.query(StringUtil.toSQLName(type), null, + Cursor c = sqLiteDatabase.query(NamingHelper.toSQLName(type), null, whereClause, whereArgs, groupBy, null, orderBy, limit); try { while (c.moveToNext()) { entity = type.getDeclaredConstructor().newInstance(); - entity.inflate(c); + SugarRecord.inflate(c, entity); toRet.add(entity); } } catch (Exception e) { @@ -151,25 +151,25 @@ public static > List find(Class type, return toRet; } - public static > long count(Class type) { + public static long count(Class type) { return count(type, null, null, null, null, null); } - public static > long count(Class type, + public static long count(Class type, String whereClause, String[] whereArgs) { return count(type, whereClause, whereArgs, null, null, null); } - public static > long count(Class type, + public static long count(Class type, String whereClause, String[] whereArgs, String groupBy, String orderBy, String limit) { - Database db = getSugarContext().getDatabase(); + SugarDb db = getSugarContext().getSugarDb(); SQLiteDatabase sqLiteDatabase = db.getDB(); long toRet = -1; String filter = (!TextUtils.isEmpty(whereClause)) ? " where " + whereClause : ""; - SQLiteStatement sqLiteStatament = sqLiteDatabase.compileStatement("SELECT count(*) FROM " + StringUtil.toSQLName(type) + filter); + SQLiteStatement sqLiteStatament = sqLiteDatabase.compileStatement("SELECT count(*) FROM " + NamingHelper.toSQLName(type) + filter); if (whereArgs != null) { for (int i = whereArgs.length; i != 0; i--) { @@ -188,163 +188,56 @@ public static > long count(Class type, return toRet; } - public void delete() { - SQLiteDatabase db = getSugarContext().getDatabase().getDB(); - db.delete(StringUtil.toSQLName(getClass()), "Id=?", new String[]{getId().toString()}); - } - - public long save() { - return save(getSugarContext().getDatabase().getDB()); + public static long save(Object object) { + return save(getSugarContext().getSugarDb().getDB(), object); } - long save(SQLiteDatabase db) { + static long save(SQLiteDatabase db, Object object) { - List columns = SugarDb.getTableFields(getClass()); + List columns = ReflectionUtil.getTableFields(object.getClass()); ContentValues values = new ContentValues(columns.size()); for (Field column : columns) { - column.setAccessible(true); - Class columnType = column.getType(); - try { - String columnName = StringUtil.toSQLName(column); - Object columnValue = column.get(this); - if (SugarRecord.class.isAssignableFrom(columnType)) { - values.put(columnName, - (columnValue != null) - ? String.valueOf(((SugarRecord) columnValue).id) - : "0"); - } else { - if (columnType.equals(Short.class) || columnType.equals(short.class)) { - values.put(columnName, (Short) columnValue); - } else if (columnType.equals(Integer.class) || columnType.equals(int.class)) { - values.put(columnName, (Integer) columnValue); - } else if (columnType.equals(Long.class) || columnType.equals(long.class)) { - values.put(columnName, (Long) columnValue); - } else if (columnType.equals(Float.class) || columnType.equals(float.class)) { - values.put(columnName, (Float) columnValue); - } else if (columnType.equals(Double.class) || columnType.equals(double.class)) { - values.put(columnName, (Double) columnValue); - } else if (columnType.equals(Boolean.class) || columnType.equals(boolean.class)) { - values.put(columnName, (Boolean) columnValue); - } else if (Date.class.equals(columnType)) { - try { - values.put(columnName, ((Date) column.get(this)).getTime()); - } catch (NullPointerException e) { - values.put(columnName, (Long) null); - } - } else if (Calendar.class.equals(columnType)) { - try { - values.put(columnName, ((Calendar) column.get(this)).getTimeInMillis()); - } catch (NullPointerException e) { - values.put(columnName, (Long) null); - } - } else { - if (columnValue == null) { - values.putNull(columnName); - } else { - values.put(columnName, String.valueOf(columnValue)); - } - } - } - - } catch (IllegalAccessException e) { - Log.e("Sugar", e.getMessage()); - } + ReflectionUtil.addFieldValueToColumn(values, column, object); } - id = db.insertWithOnConflict(StringUtil.toSQLName(getClass()), null, values, SQLiteDatabase.CONFLICT_REPLACE); + long id = db.insertWithOnConflict(NamingHelper.toSQLName(object.getClass()), null, values, SQLiteDatabase.CONFLICT_REPLACE); + + ReflectionUtil.setFieldValueForId(object, id); - Log.i("Sugar", getClass().getSimpleName() + " saved : " + id); + Log.i("Sugar", object.getClass().getSimpleName() + " saved : " + id); return id; } - @SuppressWarnings("unchecked") - void inflate(Cursor cursor) { - Map entities = new HashMap(); - List columns = SugarDb.getTableFields(getClass()); - for (Field field : columns) { - field.setAccessible(true); - try { - Class fieldType = field.getType(); - String colName = StringUtil.toSQLName(field); + private static void inflate(Cursor cursor, Object object) { - int columnIndex = cursor.getColumnIndex(colName); + List columns = ReflectionUtil.getTableFields(object.getClass()); - if (cursor.isNull(columnIndex)) { - continue; + for (Field field : columns) { + if (field.getClass().isAnnotationPresent(Table.class)) { + try { + long id = cursor.getLong(cursor.getColumnIndex(NamingHelper.toSQLName(field))); + field.set(object, (id > 0) ? findById(field.getType(), id) : null); + } catch (IllegalAccessException e) { + e.printStackTrace(); } - - if(colName.equalsIgnoreCase("id")){ - long cid = cursor.getLong(columnIndex); - field.set(this, Long.valueOf(cid)); - }else if (fieldType.equals(long.class) || fieldType.equals(Long.class)) { - field.set(this, - cursor.getLong(columnIndex)); - } else if (fieldType.equals(String.class)) { - String val = cursor.getString(columnIndex); - field.set(this, val != null && val.equals("null") ? null : val); - } else if (fieldType.equals(double.class) || fieldType.equals(Double.class)) { - field.set(this, - cursor.getDouble(columnIndex)); - } else if (fieldType.equals(boolean.class) || fieldType.equals(Boolean.class)) { - field.set(this, - cursor.getString(columnIndex).equals("1")); - } else if (field.getType().getName().equals("[B")) { - field.set(this, - cursor.getBlob(columnIndex)); - } else if (fieldType.equals(int.class) || fieldType.equals(Integer.class)) { - field.set(this, - cursor.getInt(columnIndex)); - } else if (fieldType.equals(float.class) || fieldType.equals(Float.class)) { - field.set(this, - cursor.getFloat(columnIndex)); - } else if (fieldType.equals(short.class) || fieldType.equals(Short.class)) { - field.set(this, - cursor.getShort(columnIndex)); - } else if (fieldType.equals(Timestamp.class)) { - long l = cursor.getLong(columnIndex); - field.set(this, new Timestamp(l)); - } else if (fieldType.equals(Date.class)) { - long l = cursor.getLong(columnIndex); - field.set(this, new Date(l)); - } else if (fieldType.equals(Calendar.class)) { - long l = cursor.getLong(columnIndex); - Calendar c = Calendar.getInstance(); - c.setTimeInMillis(l); - field.set(this, c); - } else if (Enum.class.isAssignableFrom(fieldType)) { - try { - Method valueOf = field.getType().getMethod("valueOf", String.class); - String strVal = cursor.getString(columnIndex); - Object enumVal = valueOf.invoke(field.getType(), strVal); - field.set(this, enumVal); - } catch (Exception e) { - Log.e("Sugar", "Enum cannot be read from Sqlite3 database. Please check the type of field " + field.getName()); - } - } else if (SugarRecord.class.isAssignableFrom(fieldType)) { - long id = cursor.getLong(columnIndex); - if (id > 0) - entities.put(field, id); - else - field.set(this, null); - } else - Log.e("Sugar", "Class cannot be read from Sqlite3 database. Please check the type of field " + field.getName() + "(" + field.getType().getName() + ")"); - } catch (IllegalArgumentException e) { - Log.e("field set error", e.getMessage()); - } catch (IllegalAccessException e) { - Log.e("field set error", e.getMessage()); + } else { + ReflectionUtil.setFieldValueFromCursor(cursor, field, object); } - } + } - for (Field f : entities.keySet()) { - try { - f.set(this, findById((Class>) f.getType(), - entities.get(f))); - } catch (SQLiteException e) { - } catch (IllegalArgumentException e) { - } catch (IllegalAccessException e) { - } - } + public void delete() { + SQLiteDatabase db = getSugarContext().getSugarDb().getDB(); + db.delete(NamingHelper.toSQLName(getClass()), "Id=?", new String[]{getId().toString()}); + } + + public long save() { + return save(getSugarContext().getSugarDb().getDB(), this); + } + + @SuppressWarnings("unchecked") + void inflate(Cursor cursor) { + inflate(cursor, this); } public Long getId() { @@ -355,7 +248,7 @@ public void setId(Long id) { this.id = id; } - static class CursorIterator> implements Iterator { + static class CursorIterator implements Iterator { Class type; Cursor cursor; @@ -382,7 +275,7 @@ public E next() { try { entity = type.getDeclaredConstructor().newInstance(); - entity.inflate(cursor); + SugarRecord.inflate(cursor, entity); } catch (Exception e) { e.printStackTrace(); } finally { diff --git a/library/src/com/orm/SugarTransactionHelper.java b/library/src/com/orm/SugarTransactionHelper.java index 505547df..ef3bf5fe 100644 --- a/library/src/com/orm/SugarTransactionHelper.java +++ b/library/src/com/orm/SugarTransactionHelper.java @@ -7,7 +7,7 @@ public class SugarTransactionHelper { public static void doInTansaction(SugarTransactionHelper.Callback callback) { - SQLiteDatabase database = SugarApp.getSugarContext().getDatabase().getDB(); + SQLiteDatabase database = SugarApp.getSugarContext().getSugarDb().getDB(); database.beginTransaction(); diff --git a/library/src/com/orm/query/Select.java b/library/src/com/orm/query/Select.java index 8c78bd43..0d148d46 100644 --- a/library/src/com/orm/query/Select.java +++ b/library/src/com/orm/query/Select.java @@ -1,13 +1,13 @@ package com.orm.query; -import com.orm.StringUtil; import com.orm.SugarRecord; +import com.orm.util.NamingHelper; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -public class Select> implements Iterable { +public class Select implements Iterable { private Class record; private String[] arguments; @@ -23,7 +23,7 @@ public Select(Class record) { this.record = record; } - public static > Select from(Class record) { + public static Select from(Class record) { return new Select(record); } @@ -107,7 +107,7 @@ public List list() { if(arguments == null) arguments = convertArgs(args); - return T.find(record, whereClause, arguments, groupBy, orderBy, limit); + return SugarRecord.find(record, whereClause, arguments, groupBy, orderBy, limit); } @@ -122,7 +122,7 @@ public T first() { if(arguments == null) arguments = convertArgs(args); - List list = T.find(record, whereClause, arguments, groupBy, orderBy, "1"); + List list = SugarRecord.find(record, whereClause, arguments, groupBy, orderBy, "1"); return list.size() > 0 ? list.get(0) : null; } @@ -131,7 +131,7 @@ String toSql() { sql.append("SELECT * FROM "); - sql.append(StringUtil.toSQLName(this.record) + " "); + sql.append(NamingHelper.toSQLName(this.record) + " "); if (whereClause != null) { sql.append("WHERE " + whereClause + " "); @@ -174,6 +174,6 @@ private String[] convertArgs(List argsList) { public Iterator iterator() { if(arguments == null) arguments = convertArgs(args); - return T.findAsIterator(record, whereClause, arguments, groupBy, orderBy, limit); + return SugarRecord.findAsIterator(record, whereClause, arguments, groupBy, orderBy, limit); } } diff --git a/library/src/com/orm/StringUtil.java b/library/src/com/orm/util/NamingHelper.java similarity index 93% rename from library/src/com/orm/StringUtil.java rename to library/src/com/orm/util/NamingHelper.java index 2c9767ff..5d8e7eaa 100644 --- a/library/src/com/orm/StringUtil.java +++ b/library/src/com/orm/util/NamingHelper.java @@ -1,11 +1,11 @@ -package com.orm; +package com.orm.util; import com.orm.dsl.Column; import com.orm.dsl.Table; import java.lang.reflect.Field; -public class StringUtil { +public class NamingHelper { public static String toSQLNameDefault(String javaNotation) { if (javaNotation.equalsIgnoreCase("_id")) return "_id"; @@ -54,6 +54,6 @@ public static String toSQLName(Class table) { Table annotation = table.getAnnotation(Table.class); return annotation.name(); } - return StringUtil.toSQLNameDefault(table.getSimpleName()); + return NamingHelper.toSQLNameDefault(table.getSimpleName()); } } diff --git a/library/src/com/orm/NumberComparator.java b/library/src/com/orm/util/NumberComparator.java similarity index 99% rename from library/src/com/orm/NumberComparator.java rename to library/src/com/orm/util/NumberComparator.java index 244d3281..c4284721 100644 --- a/library/src/com/orm/NumberComparator.java +++ b/library/src/com/orm/util/NumberComparator.java @@ -1,9 +1,17 @@ -package com.orm; +package com.orm.util; import java.util.Comparator; public class NumberComparator implements Comparator { + static char charAt(String s, int i) { + if (i >= s.length()) { + return '\000'; + } + + return s.charAt(i); + } + int compareRight(String a, String b) { int bias = 0; int ia = 0; @@ -89,12 +97,4 @@ public int compare(Object o1, Object o2) { ib++; } } - - static char charAt(String s, int i) { - if (i >= s.length()) { - return '\000'; - } - - return s.charAt(i); - } } diff --git a/library/src/com/orm/QueryBuilder.java b/library/src/com/orm/util/QueryBuilder.java similarity index 95% rename from library/src/com/orm/QueryBuilder.java rename to library/src/com/orm/util/QueryBuilder.java index 6ca26fc9..77d6247e 100644 --- a/library/src/com/orm/QueryBuilder.java +++ b/library/src/com/orm/util/QueryBuilder.java @@ -1,4 +1,6 @@ -package com.orm; +package com.orm.util; + +import com.orm.SugarRecord; public class QueryBuilder { diff --git a/library/src/com/orm/util/ReflectionUtil.java b/library/src/com/orm/util/ReflectionUtil.java new file mode 100644 index 00000000..7a213cae --- /dev/null +++ b/library/src/com/orm/util/ReflectionUtil.java @@ -0,0 +1,281 @@ +package com.orm.util; + +import android.content.ContentValues; +import android.content.Context; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.util.Log; +import com.orm.SugarRecord; +import com.orm.dsl.Ignore; +import com.orm.dsl.Table; +import dalvik.system.DexFile; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.sql.Timestamp; +import java.util.*; + +public class ReflectionUtil { + + public static List getTableFields(Class table) { + List fieldList = SugarConfig.getFields(table); + if (fieldList != null) return fieldList; + + Log.d("Sugar", "Fetching properties"); + List typeFields = new ArrayList(); + + getAllFields(typeFields, table); + + List toStore = new ArrayList(); + for (Field field : typeFields) { + if (!field.isAnnotationPresent(Ignore.class) && !Modifier.isStatic(field.getModifiers()) && !Modifier.isTransient(field.getModifiers())) { + toStore.add(field); + } + } + + SugarConfig.setFields(table, toStore); + return toStore; + } + + private static List getAllFields(List fields, Class type) { + Collections.addAll(fields, type.getDeclaredFields()); + + if (type.getSuperclass() != null) { + fields = getAllFields(fields, type.getSuperclass()); + } + + return fields; + } + + public static void addFieldValueToColumn(ContentValues values, Field column, Object object) { + column.setAccessible(true); + Class columnType = column.getType(); + try { + String columnName = NamingHelper.toSQLName(column); + Object columnValue = column.get(object); + + if (SugarRecord.class.isAssignableFrom(columnType)) { + values.put(columnName, + (columnValue != null) + ? String.valueOf(((SugarRecord) columnValue).getId()) + : "0"); + } else { + if (columnType.equals(Short.class) || columnType.equals(short.class)) { + values.put(columnName, (Short) columnValue); + } else if (columnType.equals(Integer.class) || columnType.equals(int.class)) { + values.put(columnName, (Integer) columnValue); + } else if (columnType.equals(Long.class) || columnType.equals(long.class)) { + values.put(columnName, (Long) columnValue); + } else if (columnType.equals(Float.class) || columnType.equals(float.class)) { + values.put(columnName, (Float) columnValue); + } else if (columnType.equals(Double.class) || columnType.equals(double.class)) { + values.put(columnName, (Double) columnValue); + } else if (columnType.equals(Boolean.class) || columnType.equals(boolean.class)) { + values.put(columnName, (Boolean) columnValue); + } else if (Date.class.equals(columnType)) { + try { + values.put(columnName, ((Date) column.get(object)).getTime()); + } catch (NullPointerException e) { + values.put(columnName, (Long) null); + } + } else if (Calendar.class.equals(columnType)) { + try { + values.put(columnName, ((Calendar) column.get(object)).getTimeInMillis()); + } catch (NullPointerException e) { + values.put(columnName, (Long) null); + } + } else { + if (columnValue == null) { + values.putNull(columnName); + } else { + values.put(columnName, String.valueOf(columnValue)); + } + } + } + + } catch (IllegalAccessException e) { + Log.e("Sugar", e.getMessage()); + } + } + + public static void setFieldValueFromCursor(Cursor cursor, Field field, Object object) { + field.setAccessible(true); + try { + Class fieldType = field.getType(); + String colName = NamingHelper.toSQLName(field); + + int columnIndex = cursor.getColumnIndex(colName); + + if (cursor.isNull(columnIndex)) { + return; + } + + if (colName.equalsIgnoreCase("id")) { + long cid = cursor.getLong(columnIndex); + field.set(object, Long.valueOf(cid)); + } else if (fieldType.equals(long.class) || fieldType.equals(Long.class)) { + field.set(object, + cursor.getLong(columnIndex)); + } else if (fieldType.equals(String.class)) { + String val = cursor.getString(columnIndex); + field.set(object, val != null && val.equals("null") ? null : val); + } else if (fieldType.equals(double.class) || fieldType.equals(Double.class)) { + field.set(object, + cursor.getDouble(columnIndex)); + } else if (fieldType.equals(boolean.class) || fieldType.equals(Boolean.class)) { + field.set(object, + cursor.getString(columnIndex).equals("1")); + } else if (field.getType().getName().equals("[B")) { + field.set(object, + cursor.getBlob(columnIndex)); + } else if (fieldType.equals(int.class) || fieldType.equals(Integer.class)) { + field.set(object, + cursor.getInt(columnIndex)); + } else if (fieldType.equals(float.class) || fieldType.equals(Float.class)) { + field.set(object, + cursor.getFloat(columnIndex)); + } else if (fieldType.equals(short.class) || fieldType.equals(Short.class)) { + field.set(object, + cursor.getShort(columnIndex)); + } else if (fieldType.equals(Timestamp.class)) { + long l = cursor.getLong(columnIndex); + field.set(object, new Timestamp(l)); + } else if (fieldType.equals(Date.class)) { + long l = cursor.getLong(columnIndex); + field.set(object, new Date(l)); + } else if (fieldType.equals(Calendar.class)) { + long l = cursor.getLong(columnIndex); + Calendar c = Calendar.getInstance(); + c.setTimeInMillis(l); + field.set(object, c); + } else if (Enum.class.isAssignableFrom(fieldType)) { + try { + Method valueOf = field.getType().getMethod("valueOf", String.class); + String strVal = cursor.getString(columnIndex); + Object enumVal = valueOf.invoke(field.getType(), strVal); + field.set(object, enumVal); + } catch (Exception e) { + Log.e("Sugar", "Enum cannot be read from Sqlite3 database. Please check the type of field " + field.getName()); + } + } else + Log.e("Sugar", "Class cannot be read from Sqlite3 database. Please check the type of field " + field.getName() + "(" + field.getType().getName() + ")"); + } catch (IllegalArgumentException e) { + Log.e("field set error", e.getMessage()); + } catch (IllegalAccessException e) { + Log.e("field set error", e.getMessage()); + } + } + + public static void setFieldValueForId(Object object, Long value) { + + try { + Field field = object.getClass().getField("id"); + + field.setAccessible(true); + field.set(object, value); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } + } + + public static List getDomainClasses(Context context) { + List domainClasses = new ArrayList(); + try { + for (String className : getAllClasses(context)) { + if (className.startsWith(SugarConfig.getDomainPackageName(context))) { + Class domainClass = getDomainClass(className, context); + if (domainClass != null) domainClasses.add(domainClass); + } + } + } catch (IOException e) { + Log.e("Sugar", e.getMessage()); + } catch (PackageManager.NameNotFoundException e) { + Log.e("Sugar", e.getMessage()); + } + + return domainClasses; + } + + + private static Class getDomainClass(String className, Context context) { + Class discoveredClass = null; + try { + discoveredClass = Class.forName(className, true, context.getClass().getClassLoader()); + } catch (ClassNotFoundException e) { + Log.e("Sugar", e.getMessage()); + } + + if ((discoveredClass != null) && + ((SugarRecord.class.isAssignableFrom(discoveredClass) && + !SugarRecord.class.equals(discoveredClass)) || + discoveredClass.isAnnotationPresent(Table.class)) && + !Modifier.isAbstract(discoveredClass.getModifiers())) { + + Log.i("Sugar", "domain class : " + discoveredClass.getSimpleName()); + return discoveredClass; + + } else { + return null; + } + } + + + private static List getAllClasses(Context context) throws PackageManager.NameNotFoundException, IOException { + String path = getSourcePath(context); + List classNames = new ArrayList(); + try { + DexFile dexfile = new DexFile(path); + Enumeration dexEntries = dexfile.entries(); + while (dexEntries.hasMoreElements()) { + classNames.add(dexEntries.nextElement()); + } + } catch (NullPointerException e) { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + Enumeration urls = classLoader.getResources(""); + List fileNames = new ArrayList(); + while (urls.hasMoreElements()) { + String classDirectoryName = urls.nextElement().getFile(); + if (classDirectoryName.contains("bin") || classDirectoryName.contains("classes")) { + File classDirectory = new File(classDirectoryName); + for (File filePath : classDirectory.listFiles()) { + populateFiles(filePath, fileNames, ""); + } + classNames.addAll(fileNames); + } + } + } + return classNames; + } + + private static void populateFiles(File path, List fileNames, String parent) { + if (path.isDirectory()) { + for (File newPath : path.listFiles()) { + if ("".equals(parent)) { + populateFiles(newPath, fileNames, path.getName()); + } else { + populateFiles(newPath, fileNames, parent + "." + path.getName()); + } + } + } else { + String pathName = path.getName(); + String classSuffix = ".class"; + pathName = pathName.endsWith(classSuffix) ? + pathName.substring(0, pathName.length() - classSuffix.length()) : pathName; + if ("".equals(parent)) { + fileNames.add(pathName); + } else { + fileNames.add(parent + "." + pathName); + } + } + } + + private static String getSourcePath(Context context) throws PackageManager.NameNotFoundException { + return context.getPackageManager().getApplicationInfo(context.getPackageName(), 0).sourceDir; + } +} diff --git a/library/src/com/orm/SugarConfig.java b/library/src/com/orm/util/SugarConfig.java similarity index 99% rename from library/src/com/orm/SugarConfig.java rename to library/src/com/orm/util/SugarConfig.java index aaeb79b6..524adf29 100644 --- a/library/src/com/orm/SugarConfig.java +++ b/library/src/com/orm/util/SugarConfig.java @@ -1,4 +1,4 @@ -package com.orm; +package com.orm.util; import android.content.Context; import android.content.pm.ApplicationInfo; diff --git a/library/src/com/orm/SugarCursorFactory.java b/library/src/com/orm/util/SugarCursorFactory.java similarity index 97% rename from library/src/com/orm/SugarCursorFactory.java rename to library/src/com/orm/util/SugarCursorFactory.java index 17c60c59..538c8a9c 100644 --- a/library/src/com/orm/SugarCursorFactory.java +++ b/library/src/com/orm/util/SugarCursorFactory.java @@ -1,4 +1,4 @@ -package com.orm; +package com.orm.util; import android.database.Cursor; import android.database.sqlite.SQLiteCursor; diff --git a/library/test/com/orm/NamingHelperTest.java b/library/test/com/orm/NamingHelperTest.java new file mode 100644 index 00000000..d7c2e30e --- /dev/null +++ b/library/test/com/orm/NamingHelperTest.java @@ -0,0 +1,25 @@ +package com.orm; + +import com.orm.util.NamingHelper; +import org.junit.Test; + +import static junit.framework.Assert.assertEquals; + +public class NamingHelperTest { + @Test + public void testToSQLNameCaseConversion() throws Exception { + assertEquals("TESTLOWERCASE", NamingHelper.toSQLNameDefault("testlowercase")); + assertEquals("TESTUPPERCASE", NamingHelper.toSQLNameDefault("TESTUPPERCASE")); + } + + @Test + public void testToSQLNameUnderscore() { + assertEquals("TEST_UNDERSCORE", NamingHelper.toSQLNameDefault("testUnderscore")); + assertEquals("AB_CD", NamingHelper.toSQLNameDefault("AbCd")); + assertEquals("AB_CD", NamingHelper.toSQLNameDefault("ABCd")); + assertEquals("AB_CD", NamingHelper.toSQLNameDefault("AbCD")); + assertEquals("SOME_DETAILS_OBJECT", NamingHelper.toSQLNameDefault("SomeDetailsObject")); + } + + +} diff --git a/library/test/com/orm/StringUtilTest.java b/library/test/com/orm/StringUtilTest.java deleted file mode 100644 index 8f2b8786..00000000 --- a/library/test/com/orm/StringUtilTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.orm; - -import org.junit.Test; - -import static junit.framework.Assert.assertEquals; - -public class StringUtilTest { - @Test - public void testToSQLNameCaseConversion() throws Exception { - assertEquals("TESTLOWERCASE", StringUtil.toSQLNameDefault("testlowercase")); - assertEquals("TESTUPPERCASE", StringUtil.toSQLNameDefault("TESTUPPERCASE")); - } - - @Test - public void testToSQLNameUnderscore(){ - assertEquals("TEST_UNDERSCORE", StringUtil.toSQLNameDefault("testUnderscore")); - assertEquals("AB_CD", StringUtil.toSQLNameDefault("AbCd")); - assertEquals("AB_CD", StringUtil.toSQLNameDefault("ABCd")); - assertEquals("AB_CD", StringUtil.toSQLNameDefault("AbCD")); - assertEquals("SOME_DETAILS_OBJECT", StringUtil.toSQLNameDefault("SomeDetailsObject")); - } - - -} diff --git a/library/test/com/orm/query/TestRecord.java b/library/test/com/orm/query/TestRecord.java index 0751a936..50997205 100644 --- a/library/test/com/orm/query/TestRecord.java +++ b/library/test/com/orm/query/TestRecord.java @@ -1,10 +1,9 @@ package com.orm.query; import android.content.Context; -import com.orm.SugarApp; import com.orm.SugarRecord; -public class TestRecord extends SugarRecord{ +public class TestRecord extends SugarRecord { private String name; From 169e4755da6441b2de1e74206933ef3ee0b40d59 Mon Sep 17 00:00:00 2001 From: Satya Narayan Date: Tue, 22 Jul 2014 19:15:57 -0700 Subject: [PATCH 028/305] table name is optional. That way simplifying the annotation. --- example/src/com/example/Note.java | 2 ++ .../orm/{DatabaseCreator.java => SchemaGenerator.java} | 4 ++-- library/src/com/orm/SugarDb.java | 8 ++++---- library/src/com/orm/dsl/Table.java | 2 +- library/src/com/orm/{dsl => util}/Collection.java | 2 +- library/src/com/orm/util/NamingHelper.java | 3 +++ 6 files changed, 13 insertions(+), 8 deletions(-) rename library/src/com/orm/{DatabaseCreator.java => SchemaGenerator.java} (98%) rename library/src/com/orm/{dsl => util}/Collection.java (97%) diff --git a/example/src/com/example/Note.java b/example/src/com/example/Note.java index 9f1edaa4..b83f730f 100755 --- a/example/src/com/example/Note.java +++ b/example/src/com/example/Note.java @@ -6,6 +6,8 @@ @Table(name = "Note") public class Note { + private long id; + @Column(name = "noteId", unique = true, notNull = true) private int noteId; diff --git a/library/src/com/orm/DatabaseCreator.java b/library/src/com/orm/SchemaGenerator.java similarity index 98% rename from library/src/com/orm/DatabaseCreator.java rename to library/src/com/orm/SchemaGenerator.java index 616e742e..3dc1f72b 100644 --- a/library/src/com/orm/DatabaseCreator.java +++ b/library/src/com/orm/SchemaGenerator.java @@ -24,11 +24,11 @@ import static com.orm.util.ReflectionUtil.getDomainClasses; -public class DatabaseCreator { +public class SchemaGenerator { private Context context; - public DatabaseCreator(Context context) { + public SchemaGenerator(Context context) { this.context = context; } diff --git a/library/src/com/orm/SugarDb.java b/library/src/com/orm/SugarDb.java index 7a097054..2d4dbfd3 100644 --- a/library/src/com/orm/SugarDb.java +++ b/library/src/com/orm/SugarDb.java @@ -10,22 +10,22 @@ import static com.orm.util.SugarConfig.getDebugEnabled; public class SugarDb extends SQLiteOpenHelper { - private final DatabaseCreator databaseCreator; + private final SchemaGenerator schemaGenerator; private SQLiteDatabase sqLiteDatabase; public SugarDb(Context context) { super(context, SugarConfig.getDatabaseName(context), new SugarCursorFactory(getDebugEnabled(context)), getDatabaseVersion(context)); - databaseCreator = new DatabaseCreator(context); + schemaGenerator = new SchemaGenerator(context); } @Override public void onCreate(SQLiteDatabase sqLiteDatabase) { - databaseCreator.createDatabase(sqLiteDatabase); + schemaGenerator.createDatabase(sqLiteDatabase); } @Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) { - databaseCreator.doUpgrade(sqLiteDatabase, oldVersion, newVersion); + schemaGenerator.doUpgrade(sqLiteDatabase, oldVersion, newVersion); } public synchronized SQLiteDatabase getDB() { diff --git a/library/src/com/orm/dsl/Table.java b/library/src/com/orm/dsl/Table.java index a801d9d7..0dfaa18b 100644 --- a/library/src/com/orm/dsl/Table.java +++ b/library/src/com/orm/dsl/Table.java @@ -5,5 +5,5 @@ @Retention(RetentionPolicy.RUNTIME) public @interface Table { - String name(); + String name() default ""; } diff --git a/library/src/com/orm/dsl/Collection.java b/library/src/com/orm/util/Collection.java similarity index 97% rename from library/src/com/orm/dsl/Collection.java rename to library/src/com/orm/util/Collection.java index 6f1b5075..596a3e22 100644 --- a/library/src/com/orm/dsl/Collection.java +++ b/library/src/com/orm/util/Collection.java @@ -1,4 +1,4 @@ -package com.orm.dsl; +package com.orm.util; import java.util.*; public class Collection { diff --git a/library/src/com/orm/util/NamingHelper.java b/library/src/com/orm/util/NamingHelper.java index 5d8e7eaa..116ab25d 100644 --- a/library/src/com/orm/util/NamingHelper.java +++ b/library/src/com/orm/util/NamingHelper.java @@ -52,6 +52,9 @@ public static String toSQLName(Class table) { if (table.isAnnotationPresent(Table.class)) { Table annotation = table.getAnnotation(Table.class); + if ("".equals(annotation.name())) { + return NamingHelper.toSQLNameDefault(table.getSimpleName()); + } return annotation.name(); } return NamingHelper.toSQLNameDefault(table.getSimpleName()); From 5ab12699c6dacb311d72b1df4499381a50d008f0 Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Wed, 23 Jul 2014 22:47:00 -0700 Subject: [PATCH 029/305] Update id only when the model extends SugarRecord --- library/src/com/orm/SugarRecord.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/src/com/orm/SugarRecord.java b/library/src/com/orm/SugarRecord.java index efff7bd8..07a64f5a 100644 --- a/library/src/com/orm/SugarRecord.java +++ b/library/src/com/orm/SugarRecord.java @@ -202,7 +202,9 @@ static long save(SQLiteDatabase db, Object object) { long id = db.insertWithOnConflict(NamingHelper.toSQLName(object.getClass()), null, values, SQLiteDatabase.CONFLICT_REPLACE); - ReflectionUtil.setFieldValueForId(object, id); + if (SugarRecord.class.isAssignableFrom(object.getClass())) { + ReflectionUtil.setFieldValueForId(object, id); + } Log.i("Sugar", object.getClass().getSimpleName() + " saved : " + id); return id; From 2a310ad018f83298df55c46593c85d3d67c7bcc9 Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Sun, 10 Aug 2014 09:08:51 -0700 Subject: [PATCH 030/305] Mostly nonfunctional formatting changes --- library/src/com/orm/SchemaGenerator.java | 28 ++++---- library/src/com/orm/SugarApp.java | 7 +- library/src/com/orm/SugarDb.java | 4 +- library/src/com/orm/SugarRecord.java | 55 ++++++--------- .../src/com/orm/SugarTransactionHelper.java | 13 ++-- library/src/com/orm/query/Condition.java | 31 ++++++--- library/src/com/orm/query/Select.java | 69 ++++++++++--------- library/src/com/orm/util/Collection.java | 1 + library/src/com/orm/util/NamingHelper.java | 7 +- .../src/com/orm/util/NumberComparator.java | 5 +- library/src/com/orm/util/QueryBuilder.java | 1 + library/src/com/orm/util/SugarConfig.java | 25 +++---- .../src/com/orm/util/SugarCursorFactory.java | 10 ++- 13 files changed, 132 insertions(+), 124 deletions(-) diff --git a/library/src/com/orm/SchemaGenerator.java b/library/src/com/orm/SchemaGenerator.java index 3dc1f72b..a7f25b51 100644 --- a/library/src/com/orm/SchemaGenerator.java +++ b/library/src/com/orm/SchemaGenerator.java @@ -42,14 +42,14 @@ public void createDatabase(SQLiteDatabase sqLiteDatabase) { public void doUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) { List domainClasses = getDomainClasses(context); for (Class domain : domainClasses) { - try {// we try to do a select, if fails then (?) there isn't the table + try { // we try to do a select, if fails then (?) there isn't the table sqLiteDatabase.query(NamingHelper.toSQLName(domain), null, null, null, null, null, null); } catch (SQLiteException e) { - Log.i("Sugar", String.format("creating table on update (error was '%s')", e.getMessage())); + Log.i("Sugar", String.format("Creating table on update (error was '%s')", + e.getMessage())); createTable(domain, sqLiteDatabase); } } - executeSugarUpgrade(sqLiteDatabase, oldVersion, newVersion); } @@ -61,14 +61,14 @@ public void deleteTables(SQLiteDatabase sqLiteDatabase) { } private boolean executeSugarUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - boolean isSuccess = false; + try { List files = Arrays.asList(this.context.getAssets().list("sugar_upgrades")); Collections.sort(files, new NumberComparator()); - for (String file : files) { Log.i("Sugar", "filename : " + file); + try { int version = Integer.valueOf(file.replace(".sql", "")); @@ -79,6 +79,7 @@ private boolean executeSugarUpgrade(SQLiteDatabase db, int oldVersion, int newVe } catch (NumberFormatException e) { Log.i("Sugar", "not a sugar script. ignored." + file); } + } } catch (IOException e) { Log.e("Sugar", e.getMessage()); @@ -100,24 +101,21 @@ private void executeScript(SQLiteDatabase db, String file) { Log.e("Sugar", e.getMessage()); } - Log.i("Sugar", "script executed"); + Log.i("Sugar", "Script executed"); } private void createTable(Class table, SQLiteDatabase sqLiteDatabase) { - Log.i("Sugar", "create table"); + Log.i("Sugar", "Create table"); List fields = ReflectionUtil.getTableFields(table); - String tableName = NamingHelper.toSQLName(table); - - StringBuilder sb = new StringBuilder("CREATE TABLE ").append(tableName).append( - " ( ID INTEGER PRIMARY KEY AUTOINCREMENT "); + StringBuilder sb = new StringBuilder("CREATE TABLE "); + sb.append(tableName).append(" ( ID INTEGER PRIMARY KEY AUTOINCREMENT "); for (Field column : fields) { String columnName = NamingHelper.toSQLName(column); String columnType = QueryBuilder.getColumnType(column.getType()); if (columnType != null) { - if (columnName.equalsIgnoreCase("Id")) { continue; } @@ -140,7 +138,6 @@ private void createTable(Class table, SQLiteDatabase sqLiteDatabase) { } } else { - sb.append(", ").append(columnName).append(" ").append(columnType); if (column.isAnnotationPresent(NotNull.class)) { @@ -156,9 +153,9 @@ private void createTable(Class table, SQLiteDatabase sqLiteDatabase) { } } } - sb.append(" ) "); - Log.i("Sugar", "creating table " + tableName); + sb.append(" ) "); + Log.i("Sugar", "Creating table " + tableName); if (!"".equals(sb.toString())) { try { @@ -166,7 +163,6 @@ private void createTable(Class table, SQLiteDatabase sqLiteDatabase) { } catch (SQLException e) { e.printStackTrace(); } - } } diff --git a/library/src/com/orm/SugarApp.java b/library/src/com/orm/SugarApp.java index e2638a0e..18142acb 100644 --- a/library/src/com/orm/SugarApp.java +++ b/library/src/com/orm/SugarApp.java @@ -1,6 +1,6 @@ package com.orm; -public class SugarApp extends android.app.Application{ +public class SugarApp extends android.app.Application { private static SugarApp sugarContext; private SugarDb sugarDb; @@ -9,7 +9,7 @@ public static SugarApp getSugarContext() { return sugarContext; } - public void onCreate(){ + public void onCreate() { super.onCreate(); SugarApp.sugarContext = this; this.sugarDb = new SugarDb(this); @@ -21,7 +21,7 @@ public void onCreate(){ * emulated process environments such as an emulator or * Robolectric Android mock. */ - public void onTerminate(){ + public void onTerminate() { if (this.sugarDb != null) { this.sugarDb.getDB().close(); } @@ -31,4 +31,5 @@ public void onTerminate(){ protected SugarDb getSugarDb() { return sugarDb; } + } diff --git a/library/src/com/orm/SugarDb.java b/library/src/com/orm/SugarDb.java index 2d4dbfd3..21895f6b 100644 --- a/library/src/com/orm/SugarDb.java +++ b/library/src/com/orm/SugarDb.java @@ -10,11 +10,13 @@ import static com.orm.util.SugarConfig.getDebugEnabled; public class SugarDb extends SQLiteOpenHelper { + private final SchemaGenerator schemaGenerator; private SQLiteDatabase sqLiteDatabase; public SugarDb(Context context) { - super(context, SugarConfig.getDatabaseName(context), new SugarCursorFactory(getDebugEnabled(context)), getDatabaseVersion(context)); + super(context, SugarConfig.getDatabaseName(context), + new SugarCursorFactory(getDebugEnabled(context)), getDatabaseVersion(context)); schemaGenerator = new SchemaGenerator(context); } diff --git a/library/src/com/orm/SugarRecord.java b/library/src/com/orm/SugarRecord.java index 07a64f5a..a6a05f51 100644 --- a/library/src/com/orm/SugarRecord.java +++ b/library/src/com/orm/SugarRecord.java @@ -39,21 +39,19 @@ public static void saveInTx(T... objects) { @SuppressWarnings("deprecation") public static void saveInTx(Collection objects) { SQLiteDatabase sqLiteDatabase = getSugarContext().getSugarDb().getDB(); - - try{ + try { sqLiteDatabase.beginTransaction(); sqLiteDatabase.setLockingEnabled(false); - for(T object: objects){ + for (T object: objects) { SugarRecord.save(object); } sqLiteDatabase.setTransactionSuccessful(); - }catch (Exception e){ + } catch (Exception e) { Log.i("Sugar", "Error in saving in transaction " + e.getMessage()); - }finally { + } finally { sqLiteDatabase.endTransaction(); sqLiteDatabase.setLockingEnabled(true); } - } public static List listAll(Class type) { @@ -74,8 +72,7 @@ public static Iterator findAll(Class type) { return findAsIterator(type, null, null, null, null, null); } - public static Iterator findAsIterator(Class type, - String whereClause, String... whereArgs) { + public static Iterator findAsIterator(Class type, String whereClause, String... whereArgs) { return findAsIterator(type, whereClause, whereArgs, null, null, null); } @@ -86,24 +83,19 @@ public static Iterator findWithQueryAsIterator(Class type, String quer return new CursorIterator(type, c); } - public static Iterator findAsIterator(Class type, - String whereClause, String[] whereArgs, - String groupBy, String orderBy, String limit) { - + public static Iterator findAsIterator(Class type, String whereClause, String[] whereArgs, String groupBy, String orderBy, String limit) { SugarDb db = getSugarContext().getSugarDb(); SQLiteDatabase sqLiteDatabase = db.getDB(); - Cursor c = sqLiteDatabase.query(NamingHelper.toSQLName(type), null, - whereClause, whereArgs, groupBy, null, orderBy, limit); + Cursor c = sqLiteDatabase.query(NamingHelper.toSQLName(type), null, whereClause, whereArgs, + groupBy, null, orderBy, limit); return new CursorIterator(type, c); } - public static List find(Class type, - String whereClause, String... whereArgs) { + public static List find(Class type, String whereClause, String... whereArgs) { return find(type, whereClause, whereArgs, null, null, null); } public static List findWithQuery(Class type, String query, String... arguments) { - SugarDb db = getSugarContext().getSugarDb(); SQLiteDatabase sqLiteDatabase = db.getDB(); T entity; @@ -121,22 +113,21 @@ public static List findWithQuery(Class type, String query, String... a } finally { c.close(); } + return toRet; } - public static void executeQuery(String query, String... arguments){ + public static void executeQuery(String query, String... arguments) { getSugarContext().getSugarDb().getDB().execSQL(query, arguments); } - public static List find(Class type, - String whereClause, String[] whereArgs, - String groupBy, String orderBy, String limit) { + public static List find(Class type, String whereClause, String[] whereArgs, String groupBy, String orderBy, String limit) { SugarDb db = getSugarContext().getSugarDb(); SQLiteDatabase sqLiteDatabase = db.getDB(); T entity; List toRet = new ArrayList(); - Cursor c = sqLiteDatabase.query(NamingHelper.toSQLName(type), null, - whereClause, whereArgs, groupBy, null, orderBy, limit); + Cursor c = sqLiteDatabase.query(NamingHelper.toSQLName(type), null, whereClause, whereArgs, + groupBy, null, orderBy, limit); try { while (c.moveToNext()) { entity = type.getDeclaredConstructor().newInstance(); @@ -155,15 +146,11 @@ public static long count(Class type) { return count(type, null, null, null, null, null); } - public static long count(Class type, - String whereClause, String[] whereArgs) { + public static long count(Class type, String whereClause, String[] whereArgs) { return count(type, whereClause, whereArgs, null, null, null); } - public static long count(Class type, - String whereClause, String[] whereArgs, - String groupBy, String orderBy, String limit) { - + public static long count(Class type, String whereClause, String[] whereArgs, String groupBy, String orderBy, String limit) { SugarDb db = getSugarContext().getSugarDb(); SQLiteDatabase sqLiteDatabase = db.getDB(); @@ -193,25 +180,24 @@ public static long save(Object object) { } static long save(SQLiteDatabase db, Object object) { - List columns = ReflectionUtil.getTableFields(object.getClass()); ContentValues values = new ContentValues(columns.size()); for (Field column : columns) { ReflectionUtil.addFieldValueToColumn(values, column, object); } - long id = db.insertWithOnConflict(NamingHelper.toSQLName(object.getClass()), null, values, SQLiteDatabase.CONFLICT_REPLACE); + long id = db.insertWithOnConflict(NamingHelper.toSQLName(object.getClass()), null, values, + SQLiteDatabase.CONFLICT_REPLACE); if (SugarRecord.class.isAssignableFrom(object.getClass())) { ReflectionUtil.setFieldValueForId(object, id); } - Log.i("Sugar", object.getClass().getSimpleName() + " saved : " + id); + return id; } private static void inflate(Cursor cursor, Object object) { - List columns = ReflectionUtil.getTableFields(object.getClass()); for (Field field : columns) { @@ -286,6 +272,7 @@ public E next() { cursor.close(); } } + return entity; } @@ -293,8 +280,6 @@ public E next() { public void remove() { throw new UnsupportedOperationException(); } - - } } diff --git a/library/src/com/orm/SugarTransactionHelper.java b/library/src/com/orm/SugarTransactionHelper.java index ef3bf5fe..d5758718 100644 --- a/library/src/com/orm/SugarTransactionHelper.java +++ b/library/src/com/orm/SugarTransactionHelper.java @@ -6,24 +6,27 @@ public class SugarTransactionHelper { public static void doInTansaction(SugarTransactionHelper.Callback callback) { - SQLiteDatabase database = SugarApp.getSugarContext().getSugarDb().getDB(); - database.beginTransaction(); try { - Log.d(SugarTransactionHelper.class.getSimpleName(), "callback executing within transaction"); + Log.d(SugarTransactionHelper.class.getSimpleName(), + "Callback executing within transaction"); callback.manipulateInTransaction(); database.setTransactionSuccessful(); - Log.d(SugarTransactionHelper.class.getSimpleName(), "callback successfully executed within transaction"); + Log.d(SugarTransactionHelper.class.getSimpleName(), + "Callback successfully executed within transaction"); } catch (Throwable e) { - Log.d(SugarTransactionHelper.class.getSimpleName(), "could execute callback within transaction", e); + Log.d(SugarTransactionHelper.class.getSimpleName(), + "Could execute callback within transaction", e); } finally { database.endTransaction(); } + } public static interface Callback { void manipulateInTransaction(); } + } diff --git a/library/src/com/orm/query/Condition.java b/library/src/com/orm/query/Condition.java index 6838b843..989bd458 100644 --- a/library/src/com/orm/query/Condition.java +++ b/library/src/com/orm/query/Condition.java @@ -1,16 +1,22 @@ package com.orm.query; public class Condition { + private String property; private Object value; private Check check; enum Check { - EQUALS(" = "), GREATER_THAN(" > "), LESSER_THAN(" < "), NOT_EQUALS (" != "), LIKE(" LIKE "), NOT_LIKE(" NOT LIKE "); + EQUALS(" = "), + GREATER_THAN(" > "), + LESSER_THAN(" < "), + NOT_EQUALS (" != "), + LIKE(" LIKE "), + NOT_LIKE(" NOT LIKE "); + private String symbol; Check(String symbol) { - this.symbol = symbol; } @@ -20,48 +26,50 @@ public String getSymbol() { } enum Type { - AND, OR, NOT + AND, + OR, + NOT } public Condition(String property) { this.property = property; } - public static Condition prop(String property){ + public static Condition prop(String property) { return new Condition(property); } - public Condition eq(Object value){ + public Condition eq(Object value) { this.value = value; check = Check.EQUALS; return this; } - public Condition like(Object value){ + public Condition like(Object value) { this.value = value; check = Check.LIKE; return this; } - public Condition notLike(Object value){ + public Condition notLike(Object value) { this.value = value; check = Check.NOT_LIKE; return this; } - public Condition notEq(Object value){ + public Condition notEq(Object value) { this.value = value; check = Check.NOT_EQUALS; return this; } - public Condition gt(Object value){ + public Condition gt(Object value) { this.value = value; check = Check.GREATER_THAN; return this; } - public Condition lt(Object value){ + public Condition lt(Object value) { this.value = value; check = Check.LESSER_THAN; return this; @@ -79,7 +87,8 @@ public Check getCheck() { return check; } - public String getCheckSymbol(){ + public String getCheckSymbol() { return check.getSymbol(); } + } diff --git a/library/src/com/orm/query/Select.java b/library/src/com/orm/query/Select.java index 0d148d46..9e91543c 100644 --- a/library/src/com/orm/query/Select.java +++ b/library/src/com/orm/query/Select.java @@ -16,7 +16,6 @@ public class Select implements Iterable { private String groupBy; private String limit; private String offset; - private List args = new ArrayList(); public Select(Class record) { @@ -42,8 +41,6 @@ public Select limit(String limit) { return this; } - - public Select where(String whereClause) { this.whereClause = whereClause; return this; @@ -59,26 +56,32 @@ public Select where(Condition... condition) { private void mergeConditions(Condition[] conditions, Condition.Type type) { StringBuilder toAppend = new StringBuilder(""); for (Condition condition : conditions) { - if (toAppend.length() != 0) { - toAppend.append(" " + type.name() + " "); + toAppend.append(" ").append(type.name()).append(" "); } - if(Condition.Check.LIKE.equals(condition.getCheck()) || Condition.Check.NOT_LIKE.equals(condition.getCheck())){ - - toAppend.append(condition.getProperty() + condition.getCheckSymbol() + "'" + condition.getValue().toString() + "'"); - - }else{ - - toAppend.append(condition.getProperty() + condition.getCheckSymbol() + "? "); + if (Condition.Check.LIKE.equals(condition.getCheck()) || + Condition.Check.NOT_LIKE.equals(condition.getCheck())) { + toAppend + .append(condition.getProperty()) + .append(condition.getCheckSymbol()) + .append("'") + .append(condition.getValue().toString()) + .append("'"); + + } else { + toAppend + .append(condition.getProperty()) + .append(condition.getCheckSymbol()) + .append("? "); args.add(condition.getValue()); } - } if (!"".equals(whereClause)) { whereClause += " " + type.name() + " "; } + whereClause += "(" + toAppend + ")"; } @@ -104,23 +107,25 @@ public Select where(String whereClause, String[] args) { } public List list() { - - if(arguments == null) arguments = convertArgs(args); + if (arguments == null) { + arguments = convertArgs(args); + } return SugarRecord.find(record, whereClause, arguments, groupBy, orderBy, limit); - } public long count() { + if (arguments == null) { + arguments = convertArgs(args); + } - if(arguments == null) arguments = convertArgs(args); - - return SugarRecord.count(record, whereClause, arguments, groupBy, orderBy, limit); + return SugarRecord.count(record, whereClause, arguments, groupBy, orderBy, limit); } public T first() { - - if(arguments == null) arguments = convertArgs(args); + if (arguments == null) { + arguments = convertArgs(args); + } List list = SugarRecord.find(record, whereClause, arguments, groupBy, orderBy, "1"); return list.size() > 0 ? list.get(0) : null; @@ -128,25 +133,22 @@ public T first() { String toSql() { StringBuilder sql = new StringBuilder(); - - sql.append("SELECT * FROM "); - - sql.append(NamingHelper.toSQLName(this.record) + " "); + sql.append("SELECT * FROM ").append(NamingHelper.toSQLName(this.record)).append(" "); if (whereClause != null) { - sql.append("WHERE " + whereClause + " "); + sql.append("WHERE ").append(whereClause).append(" "); } if (orderBy != null) { - sql.append("ORDER BY " + orderBy + " "); + sql.append("ORDER BY ").append(orderBy).append(" "); } if (limit != null) { - sql.append("LIMIT " + limit + " "); + sql.append("LIMIT ").append(limit).append(" "); } if (offset != null) { - sql.append("OFFSET " + offset + " "); + sql.append("OFFSET ").append(offset).append(" "); } return sql.toString(); @@ -156,14 +158,14 @@ String getWhereCond() { return whereClause; } - String[] getArgs(){ + String[] getArgs() { return convertArgs(args); } private String[] convertArgs(List argsList) { String[] argsArray = new String[argsList.size()]; - for(int i=0; i< argsList.size();i++){ + for (int i = 0; i < argsList.size(); i++) { argsArray[i] = argsList.get(i).toString(); } @@ -172,8 +174,11 @@ private String[] convertArgs(List argsList) { @Override public Iterator iterator() { - if(arguments == null) arguments = convertArgs(args); + if (arguments == null) { + arguments = convertArgs(args); + } return SugarRecord.findAsIterator(record, whereClause, arguments, groupBy, orderBy, limit); } + } diff --git a/library/src/com/orm/util/Collection.java b/library/src/com/orm/util/Collection.java index 596a3e22..fe7a7e63 100644 --- a/library/src/com/orm/util/Collection.java +++ b/library/src/com/orm/util/Collection.java @@ -36,4 +36,5 @@ public Entry(K key, V value) { this.value = value; } } + } diff --git a/library/src/com/orm/util/NamingHelper.java b/library/src/com/orm/util/NamingHelper.java index 116ab25d..3d1622aa 100644 --- a/library/src/com/orm/util/NamingHelper.java +++ b/library/src/com/orm/util/NamingHelper.java @@ -6,9 +6,11 @@ import java.lang.reflect.Field; public class NamingHelper { + public static String toSQLNameDefault(String javaNotation) { - if (javaNotation.equalsIgnoreCase("_id")) + if (javaNotation.equalsIgnoreCase("_id")) { return "_id"; + } StringBuilder sb = new StringBuilder(); char[] buf = javaNotation.toCharArray(); @@ -49,7 +51,6 @@ public static String toSQLName(Field field) { } public static String toSQLName(Class table) { - if (table.isAnnotationPresent(Table.class)) { Table annotation = table.getAnnotation(Table.class); if ("".equals(annotation.name())) { @@ -57,6 +58,8 @@ public static String toSQLName(Class table) { } return annotation.name(); } + return NamingHelper.toSQLNameDefault(table.getSimpleName()); } + } diff --git a/library/src/com/orm/util/NumberComparator.java b/library/src/com/orm/util/NumberComparator.java index c4284721..b39ca9c9 100644 --- a/library/src/com/orm/util/NumberComparator.java +++ b/library/src/com/orm/util/NumberComparator.java @@ -4,7 +4,7 @@ public class NumberComparator implements Comparator { - static char charAt(String s, int i) { + private static char charAt(String s, int i) { if (i >= s.length()) { return '\000'; } @@ -12,7 +12,7 @@ static char charAt(String s, int i) { return s.charAt(i); } - int compareRight(String a, String b) { + private int compareRight(String a, String b) { int bias = 0; int ia = 0; int ib = 0; @@ -97,4 +97,5 @@ public int compare(Object o1, Object o2) { ib++; } } + } diff --git a/library/src/com/orm/util/QueryBuilder.java b/library/src/com/orm/util/QueryBuilder.java index 77d6247e..6de9e8ca 100644 --- a/library/src/com/orm/util/QueryBuilder.java +++ b/library/src/com/orm/util/QueryBuilder.java @@ -37,4 +37,5 @@ public static String getColumnType(Class type) { return ""; } + } diff --git a/library/src/com/orm/util/SugarConfig.java b/library/src/com/orm/util/SugarConfig.java index 524adf29..9568fa67 100644 --- a/library/src/com/orm/util/SugarConfig.java +++ b/library/src/com/orm/util/SugarConfig.java @@ -25,13 +25,12 @@ public static String getDatabaseName(Context context) { return databaseName; } - public static void setFields(Class clazz, List fieldz){ + public static void setFields(Class clazz, List fieldz) { fields.put(clazz, fieldz); } - public static List getFields(Class clazz){ - - if(fields.containsKey(clazz)){ + public static List getFields(Class clazz) { + if (fields.containsKey(clazz)) { List list = fields.get(clazz); return Collections.synchronizedList(list); } @@ -39,14 +38,13 @@ public static List getFields(Class clazz){ return null; } - public static void clearCache(){ + public static void clearCache() { fields.clear(); fields = new HashMap, List>(); } public static int getDatabaseVersion(Context context) { Integer databaseVersion = getMetaDataInteger(context, "VERSION"); - if ((databaseVersion == null) || (databaseVersion == 0)) { databaseVersion = 1; } @@ -54,9 +52,8 @@ public static int getDatabaseVersion(Context context) { return databaseVersion; } - public static String getDomainPackageName(Context context){ + public static String getDomainPackageName(Context context) { String domainPackageName = getMetaDataString(context, "DOMAIN_PACKAGE_NAME"); - if (domainPackageName == null) { domainPackageName = ""; } @@ -70,13 +67,13 @@ public static boolean getDebugEnabled(Context context) { public static String getMetaDataString(Context context, String name) { String value = null; - PackageManager pm = context.getPackageManager(); + try { ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), 128); value = ai.metaData.getString(name); } catch (Exception e) { - Log.d("sugar", "Couldn't find config value: " + name); + Log.d("Sugar", "Couldn't find config value: " + name); } return value; @@ -84,13 +81,13 @@ public static String getMetaDataString(Context context, String name) { public static Integer getMetaDataInteger(Context context, String name) { Integer value = null; - PackageManager pm = context.getPackageManager(); + try { ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), 128); value = ai.metaData.getInt(name); } catch (Exception e) { - Log.d("sugar", "Couldn't find config value: " + name); + Log.d("Sugar", "Couldn't find config value: " + name); } return value; @@ -98,13 +95,13 @@ public static Integer getMetaDataInteger(Context context, String name) { public static Boolean getMetaDataBoolean(Context context, String name) { Boolean value = false; - PackageManager pm = context.getPackageManager(); + try { ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), 128); value = ai.metaData.getBoolean(name); } catch (Exception e) { - Log.d("sugar", "Couldn't find config value: " + name); + Log.d("Sugar", "Couldn't find config value: " + name); } return value; diff --git a/library/src/com/orm/util/SugarCursorFactory.java b/library/src/com/orm/util/SugarCursorFactory.java index 538c8a9c..12706516 100644 --- a/library/src/com/orm/util/SugarCursorFactory.java +++ b/library/src/com/orm/util/SugarCursorFactory.java @@ -15,18 +15,22 @@ public SugarCursorFactory() { this.debugEnabled = false; } - public SugarCursorFactory(boolean debugEnabled){ + public SugarCursorFactory(boolean debugEnabled) { this.debugEnabled = debugEnabled; } @SuppressWarnings("deprecation") - public Cursor newCursor(SQLiteDatabase sqLiteDatabase, SQLiteCursorDriver sqLiteCursorDriver, String editTable, SQLiteQuery sqLiteQuery) { + public Cursor newCursor(SQLiteDatabase sqLiteDatabase, + SQLiteCursorDriver sqLiteCursorDriver, + String editTable, + SQLiteQuery sqLiteQuery) { - if(debugEnabled){ + if (debugEnabled) { Log.d("SQL Log", sqLiteQuery.toString()); } return new SQLiteCursor(sqLiteDatabase, sqLiteCursorDriver, editTable, sqLiteQuery); } + } From bcb9badc79aa3149f847e72efbe9da3d09923947 Mon Sep 17 00:00:00 2001 From: Jan Paul Imhoff Date: Thu, 20 Feb 2014 10:20:28 +0100 Subject: [PATCH 031/305] Refactoring to a new startup routine. It is not needed to extend SugarApp in the client application anymore, instead a call to SugarContext.init(Context) has to be made in the Client application. To close the database we have to call SugarContext.terminate(Context) in Application.onTerminate() --- library/src/com/orm/SugarApp.java | 35 ------------- library/src/com/orm/SugarContext.java | 50 +++++++++++++++++++ library/src/com/orm/SugarRecord.java | 2 +- .../src/com/orm/SugarTransactionHelper.java | 2 +- 4 files changed, 52 insertions(+), 37 deletions(-) delete mode 100644 library/src/com/orm/SugarApp.java create mode 100644 library/src/com/orm/SugarContext.java diff --git a/library/src/com/orm/SugarApp.java b/library/src/com/orm/SugarApp.java deleted file mode 100644 index 18142acb..00000000 --- a/library/src/com/orm/SugarApp.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.orm; - -public class SugarApp extends android.app.Application { - - private static SugarApp sugarContext; - private SugarDb sugarDb; - - public static SugarApp getSugarContext() { - return sugarContext; - } - - public void onCreate() { - super.onCreate(); - SugarApp.sugarContext = this; - this.sugarDb = new SugarDb(this); - } - - /* - * Per issue #106 on Github, this method won't be called in - * any real Android device. This method is used purely in - * emulated process environments such as an emulator or - * Robolectric Android mock. - */ - public void onTerminate() { - if (this.sugarDb != null) { - this.sugarDb.getDB().close(); - } - super.onTerminate(); - } - - protected SugarDb getSugarDb() { - return sugarDb; - } - -} diff --git a/library/src/com/orm/SugarContext.java b/library/src/com/orm/SugarContext.java new file mode 100644 index 00000000..ea853766 --- /dev/null +++ b/library/src/com/orm/SugarContext.java @@ -0,0 +1,50 @@ +package com.orm; + +import android.content.Context; + +public class SugarContext { + + private static SugarContext instance = null; + private SugarDb sugarDb; + private Context context; + + private SugarContext(Context context) { + this.context = context; + this.sugarDb = new SugarDb(context); + } + + public static SugarContext getSugarContext() { + if (instance == null) { + throw new NullPointerException("SugarContext has not been initialized properly. Call SugarContext.init(Context) in your Application.onCreate() method and SugarContext.terminate() in your Application.onTerminate() method."); + } + return instance; + } + + public static void init(Context context) { + instance = new SugarContext(context); + } + + public static void terminate() { + if (instance == null) { + return; + } + instance.doTerminate(); + } + + /* + * Per issue #106 on Github, this method won't be called in + * any real Android device. This method is used purely in + * emulated process environments such as an emulator or + * Robolectric Android mock. + */ + private void doTerminate() { + if (this.sugarDb != null) { + this.sugarDb.getDB().close(); + } + } + + protected SugarDb getSugarDb() { + return sugarDb; + } + +} diff --git a/library/src/com/orm/SugarRecord.java b/library/src/com/orm/SugarRecord.java index a6a05f51..2d854b64 100644 --- a/library/src/com/orm/SugarRecord.java +++ b/library/src/com/orm/SugarRecord.java @@ -13,7 +13,7 @@ import java.lang.reflect.Field; import java.util.*; -import static com.orm.SugarApp.getSugarContext; +import static com.orm.SugarContext.getSugarContext; public class SugarRecord { diff --git a/library/src/com/orm/SugarTransactionHelper.java b/library/src/com/orm/SugarTransactionHelper.java index d5758718..0f2683f2 100644 --- a/library/src/com/orm/SugarTransactionHelper.java +++ b/library/src/com/orm/SugarTransactionHelper.java @@ -6,7 +6,7 @@ public class SugarTransactionHelper { public static void doInTansaction(SugarTransactionHelper.Callback callback) { - SQLiteDatabase database = SugarApp.getSugarContext().getSugarDb().getDB(); + SQLiteDatabase database = SugarContext.getSugarContext().getSugarDb().getDB(); database.beginTransaction(); try { From ea18cc8e84e8838f2ee928e431b73de85e0b494f Mon Sep 17 00:00:00 2001 From: Jan Paul Imhoff Date: Thu, 20 Feb 2014 10:35:09 +0100 Subject: [PATCH 032/305] Modified the Example to be in line with the new startup system --- example/AndroidManifest.xml | 2 +- example/src/com/example/ClientApp.java | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 example/src/com/example/ClientApp.java diff --git a/example/AndroidManifest.xml b/example/AndroidManifest.xml index e647f387..e03f036a 100755 --- a/example/AndroidManifest.xml +++ b/example/AndroidManifest.xml @@ -3,7 +3,7 @@ package="com.example" android:versionCode="1" android:versionName="1.0"> - + diff --git a/example/src/com/example/ClientApp.java b/example/src/com/example/ClientApp.java new file mode 100644 index 00000000..6e241a9d --- /dev/null +++ b/example/src/com/example/ClientApp.java @@ -0,0 +1,21 @@ +package com.example; + +import com.orm.SugarContext; + +import android.app.Application; + +public class ClientApp extends Application { + + @Override + public void onCreate() { + super.onCreate(); + SugarContext.init(this); + } + + @Override + public void onTerminate() { + super.onTerminate(); + SugarContext.terminate(); + } + +} From 5e893664594c8675d4bf99ed81a6b5c078efa7c8 Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Mon, 18 Aug 2014 12:04:36 -0700 Subject: [PATCH 033/305] Preserve original behavior to allow existing applications to specify SugarApp in the manifest. --- library/src/com/orm/SugarApp.java | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 library/src/com/orm/SugarApp.java diff --git a/library/src/com/orm/SugarApp.java b/library/src/com/orm/SugarApp.java new file mode 100644 index 00000000..5140e1ee --- /dev/null +++ b/library/src/com/orm/SugarApp.java @@ -0,0 +1,21 @@ +package com.orm; + +import com.orm.SugarContext; + +import android.app.Application; + +public class SugarApp extends Application { + + @Override + public void onCreate() { + super.onCreate(); + SugarContext.init(this); + } + + @Override + public void onTerminate() { + super.onTerminate(); + SugarContext.terminate(); + } + +} From 53b8a9c95b0d958200b303299ef8f2cfd856a17a Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Mon, 18 Aug 2014 15:22:18 -0700 Subject: [PATCH 034/305] Add .gradle to ignore file to usher in support for Android Studio --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2adde673..76806924 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,6 @@ library/gen library/.project library/.classpath library/library.iml +library/.gradle *.class .DS_Store From 02c88053c999eeb9260b484c02536228580e0c7f Mon Sep 17 00:00:00 2001 From: fhur Date: Thu, 7 Aug 2014 23:20:43 -0500 Subject: [PATCH 035/305] Improved documentation and cleaned up NamingHelper 1. Added documentation to toSQLNameDefault, toSQLName(Field) and toSQLName(Class) 2. Rewrote toSQLNameDefault as implementation was hard to understand (refactored to make it more readable) 3. Added additional test cases to NamingHelper and created helper method assertToSqlNameEquals(String,String) this helps make the test case more readable. --- library/src/com/orm/util/NamingHelper.java | 67 +++++++++++++--------- library/test/com/orm/NamingHelperTest.java | 25 +++++--- 2 files changed, 58 insertions(+), 34 deletions(-) diff --git a/library/src/com/orm/util/NamingHelper.java b/library/src/com/orm/util/NamingHelper.java index 3d1622aa..e19d14bb 100644 --- a/library/src/com/orm/util/NamingHelper.java +++ b/library/src/com/orm/util/NamingHelper.java @@ -1,46 +1,51 @@ package com.orm.util; +import android.text.TextUtils; + import com.orm.dsl.Column; import com.orm.dsl.Table; import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; public class NamingHelper { - public static String toSQLNameDefault(String javaNotation) { - if (javaNotation.equalsIgnoreCase("_id")) { + /** + * Converts a given CamelCasedString to UPPER_CASE_UNDER_SCORE. + * + * @param camelCased a non empty camelCased string + * @return the equivalent string converted to UPPER_CASE_UNDER_SCORE unless camelCased equals + * "_id" (not case sensitive) in which case "_id" is returned + */ + public static String toSQLNameDefault(String camelCased) { + if (camelCased.equalsIgnoreCase("_id")) { return "_id"; } - StringBuilder sb = new StringBuilder(); - char[] buf = javaNotation.toCharArray(); - - for (int i = 0; i < buf.length; i++) { - char prevChar = (i > 0) ? buf[i - 1] : 0; - char c = buf[i]; - char nextChar = (i < buf.length - 1) ? buf[i + 1] : 0; - boolean isFirstChar = (i == 0); - - if (isFirstChar || Character.isLowerCase(c) || Character.isDigit(c)) { - sb.append(Character.toUpperCase(c)); - } else if (Character.isUpperCase(c)) { - if (Character.isLetterOrDigit(prevChar)) { - if (Character.isLowerCase(prevChar)) { - sb.append('_').append(Character.toUpperCase(c)); - } else if (nextChar > 0 && Character.isLowerCase(nextChar)) { - sb.append('_').append(Character.toUpperCase(c)); - } else { - sb.append(c); - } - } else { - sb.append(c); - } + List words = new LinkedList(); + StringBuilder currentWord = new StringBuilder(); + for(int i=0; i< camelCased.length(); i++){ + char c = camelCased.charAt(i); + if(Character.isUpperCase(c)){ + if(currentWord.length() == 0) words.add(currentWord.toString()); + currentWord = new StringBuilder(); } + currentWord.append(Character.toUpperCase(c)); } - - return sb.toString(); + return TextUtils.join("_", words); } + /** + * Maps a Java Field object to the database's column name. + * + * @param field the {@link java.lang.reflect.Field} that will be mapped + * @return the name of the given Field as represented in the database. If the Field is annotated + * with {@link com.orm.dsl.Column} then the {@link com.orm.dsl.Column#name()} will be + * returned. Else, the Field's {@link java.lang.reflect.Field#getName()} will be + * converted from CamelCase to UNDER_SCORE notation + */ public static String toSQLName(Field field) { if (field.isAnnotationPresent(Column.class)) { Column annotation = field.getAnnotation(Column.class); @@ -50,6 +55,14 @@ public static String toSQLName(Field field) { return toSQLNameDefault(field.getName()); } + /** + * Maps a Java Class to the name of the class. + * + * @param table the generic {@link java.lang.Class} that defines a database table + * @return if the given class is annotated with {@link com.orm.dsl.Table} then the value for + * {@link com.orm.dsl.Table#name()} will be returned. Else, the class' simple name will + * be converted from CamelCase to UNDER_SCORE notation + */ public static String toSQLName(Class table) { if (table.isAnnotationPresent(Table.class)) { Table annotation = table.getAnnotation(Table.class); diff --git a/library/test/com/orm/NamingHelperTest.java b/library/test/com/orm/NamingHelperTest.java index d7c2e30e..28c705a0 100644 --- a/library/test/com/orm/NamingHelperTest.java +++ b/library/test/com/orm/NamingHelperTest.java @@ -8,18 +8,29 @@ public class NamingHelperTest { @Test public void testToSQLNameCaseConversion() throws Exception { - assertEquals("TESTLOWERCASE", NamingHelper.toSQLNameDefault("testlowercase")); - assertEquals("TESTUPPERCASE", NamingHelper.toSQLNameDefault("TESTUPPERCASE")); + assertToSqlNameEquals("TESTLOWERCASE", "testlowercase"); + assertToSqlNameEquals("TESTUPPERCASE", "TESTUPPERCASE"); } @Test public void testToSQLNameUnderscore() { - assertEquals("TEST_UNDERSCORE", NamingHelper.toSQLNameDefault("testUnderscore")); - assertEquals("AB_CD", NamingHelper.toSQLNameDefault("AbCd")); - assertEquals("AB_CD", NamingHelper.toSQLNameDefault("ABCd")); - assertEquals("AB_CD", NamingHelper.toSQLNameDefault("AbCD")); - assertEquals("SOME_DETAILS_OBJECT", NamingHelper.toSQLNameDefault("SomeDetailsObject")); + assertToSqlNameEquals("TEST_UNDERSCORE", "testUnderscore"); + assertToSqlNameEquals("AB_CD", "AbCd"); + assertToSqlNameEquals("AB_CD", "ABCd"); + assertToSqlNameEquals("AB_CD", "AbCD"); + assertToSqlNameEquals("SOME_DETAILS_OBJECT", "SomeDetailsObject"); + assertToSqlNameEquals("H_OL_A","hOlA"); + assertToSqlNameEquals("A","a"); } + /** + * Helper method that asserts a CamelCaseString is converted to UPPER_CASE_UNDER_SCORE. + * + * @param expected a CamelCaseString + * @param actual the expected UPPER_CASE_UNDER_SCORE string + */ + private static void assertToSqlNameEquals(String expected, String actual) { + assertEquals(expected, NamingHelper.toSQLNameDefault(actual)); + } } From 060b3d567bde35fe54b71b6f13f9ef411013dc11 Mon Sep 17 00:00:00 2001 From: fhur Date: Thu, 7 Aug 2014 23:52:35 -0500 Subject: [PATCH 036/305] rolled back changes, apparently this is not exacttly your typical CamelCase to UPPER_CASE_UNDER_SCORE algorithm I thought ABC should have been converted to A_B_C (I was wrong). I have reverted to previous algorithm. --- library/src/com/orm/util/NamingHelper.java | 34 ++++++++++++++++------ 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/library/src/com/orm/util/NamingHelper.java b/library/src/com/orm/util/NamingHelper.java index e19d14bb..dd86f8d1 100644 --- a/library/src/com/orm/util/NamingHelper.java +++ b/library/src/com/orm/util/NamingHelper.java @@ -24,17 +24,33 @@ public static String toSQLNameDefault(String camelCased) { return "_id"; } - List words = new LinkedList(); - StringBuilder currentWord = new StringBuilder(); - for(int i=0; i< camelCased.length(); i++){ - char c = camelCased.charAt(i); - if(Character.isUpperCase(c)){ - if(currentWord.length() == 0) words.add(currentWord.toString()); - currentWord = new StringBuilder(); + StringBuilder sb = new StringBuilder(); + char[] buf = camelCased.toCharArray(); + + for (int i = 0; i < buf.length; i++) { + char prevChar = (i > 0) ? buf[i - 1] : 0; + char c = buf[i]; + char nextChar = (i < buf.length - 1) ? buf[i + 1] : 0; + boolean isFirstChar = (i == 0); + + if (isFirstChar || Character.isLowerCase(c) || Character.isDigit(c)) { + sb.append(Character.toUpperCase(c)); + } else if (Character.isUpperCase(c)) { + if (Character.isLetterOrDigit(prevChar)) { + if (Character.isLowerCase(prevChar)) { + sb.append('_').append(Character.toUpperCase(c)); + } else if (nextChar > 0 && Character.isLowerCase(nextChar)) { + sb.append('_').append(Character.toUpperCase(c)); + } else { + sb.append(c); + } + } else { + sb.append(c); + } } - currentWord.append(Character.toUpperCase(c)); } - return TextUtils.join("_", words); + + return sb.toString(); } /** From 90ded2d242279c8e1dff6e4edaff28a983fce874 Mon Sep 17 00:00:00 2001 From: fhur Date: Fri, 8 Aug 2014 19:31:19 -0500 Subject: [PATCH 037/305] Created ManifestHelper and moved AndroidManifest metadata read methods from SugarConfig to ManifestHelper Reason: SugarConfig seemed to have 2 different responsibilities (holding the field cache and retrieving data from the AndroidManifest), this separates those concerns into 2 different classes. Changes 1. Created ManifestHelper ManifestHelper provides methods for accessing the AndroidManifest's metadata (i.e. the database version, database name, etc.) 2. Added documentation to ManifestHelper 3. Refactored references that used SugarConfig to use ManifestHelper Note: There was an integer Flag with value 128, I'm not sure what that means so I added a TODO. --- library/src/com/orm/SugarDb.java | 9 +- library/src/com/orm/util/ManifestHelper.java | 134 +++++++++++++++++++ library/src/com/orm/util/ReflectionUtil.java | 2 +- library/src/com/orm/util/SugarConfig.java | 74 +--------- 4 files changed, 141 insertions(+), 78 deletions(-) create mode 100644 library/src/com/orm/util/ManifestHelper.java diff --git a/library/src/com/orm/SugarDb.java b/library/src/com/orm/SugarDb.java index 21895f6b..a15110a3 100644 --- a/library/src/com/orm/SugarDb.java +++ b/library/src/com/orm/SugarDb.java @@ -3,11 +3,12 @@ import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; -import com.orm.util.SugarConfig; + +import com.orm.util.ManifestHelper; import com.orm.util.SugarCursorFactory; -import static com.orm.util.SugarConfig.getDatabaseVersion; -import static com.orm.util.SugarConfig.getDebugEnabled; +import static com.orm.util.ManifestHelper.getDatabaseVersion; +import static com.orm.util.ManifestHelper.getDebugEnabled; public class SugarDb extends SQLiteOpenHelper { @@ -15,7 +16,7 @@ public class SugarDb extends SQLiteOpenHelper { private SQLiteDatabase sqLiteDatabase; public SugarDb(Context context) { - super(context, SugarConfig.getDatabaseName(context), + super(context, ManifestHelper.getDatabaseName(context), new SugarCursorFactory(getDebugEnabled(context)), getDatabaseVersion(context)); schemaGenerator = new SchemaGenerator(context); } diff --git a/library/src/com/orm/util/ManifestHelper.java b/library/src/com/orm/util/ManifestHelper.java new file mode 100644 index 00000000..f98f790e --- /dev/null +++ b/library/src/com/orm/util/ManifestHelper.java @@ -0,0 +1,134 @@ +package com.orm.util; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.util.Log; + +/** + * Helper class for accessing properties in the AndroidManifest + */ +public class ManifestHelper { + + /** + * TODO document the meaning of this 128 flag. + */ + private final static int FLAG = 128; + /** + * Key for the database name meta data. + */ + public final static String METADATA_DATABASE = "DATABASE"; + /** + * Key for the database verison meta data. + */ + public final static String METADATA_VERSION = "VERSION"; + public final static String METADATA_DOMAIN_PACKAGE_NAME = "DOMAIN_PACKAGE_NAME"; + public final static String METADATA_QUERY_LOG = "QUERY_LOG"; + /** + * The default name for the database unless specified in the AndroidManifest. + */ + public final static String DATABASE_DEFAULT_NAME = "Sugar.db"; + + /** + * Grabs the database version from the manifest. + * + * @param context the {@link android.content.Context} of the Android application + * @return the database version as specified by the {@link #METADATA_VERSION} version or 1 of + * not present + */ + public static int getDatabaseVersion(Context context) { + Integer databaseVersion = getMetaDataInteger(context, METADATA_VERSION); + + if ((databaseVersion == null) || (databaseVersion == 0)) { + databaseVersion = 1; + } + + return databaseVersion; + } + + /** + * Grabs the domain name of the model classes from the manifest. + * + * @param context the {@link android.content.Context} of the Android application + * @return the package String that Sugar uses to search for model classes + */ + public static String getDomainPackageName(Context context){ + String domainPackageName = getMetaDataString(context, METADATA_DOMAIN_PACKAGE_NAME); + + if (domainPackageName == null) { + domainPackageName = ""; + } + + return domainPackageName; + } + + /** + * Grabs the name of the database file specified in the manifest. + * + * @param context the {@link android.content.Context} of the Android application + * @return the value for the {@value #METADATA_DATABASE} meta data in the AndroidManifest or + * {@link #DATABASE_DEFAULT_NAME} if not present + */ + public static String getDatabaseName(Context context) { + String databaseName = getMetaDataString(context, METADATA_DATABASE); + + if (databaseName == null) { + databaseName = DATABASE_DEFAULT_NAME; + } + + return databaseName; + } + + /** + * Grabs the debug flag from the manifest. + * + * @param context the {@link android.content.Context} of the Android application + * @return true if the debug flag is enabled + */ + public static boolean getDebugEnabled(Context context) { + return getMetaDataBoolean(context, METADATA_QUERY_LOG); + } + + private static String getMetaDataString(Context context, String name) { + String value = null; + + PackageManager pm = context.getPackageManager(); + try { + ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), FLAG); + value = ai.metaData.getString(name); + } catch (Exception e) { + Log.d("sugar", "Couldn't find config value: " + name); + } + + return value; + } + + private static Integer getMetaDataInteger(Context context, String name) { + Integer value = null; + + PackageManager pm = context.getPackageManager(); + try { + ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), FLAG); + value = ai.metaData.getInt(name); + } catch (Exception e) { + Log.d("sugar", "Couldn't find config value: " + name); + } + + return value; + } + + private static Boolean getMetaDataBoolean(Context context, String name) { + Boolean value = false; + + PackageManager pm = context.getPackageManager(); + try { + ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), FLAG); + value = ai.metaData.getBoolean(name); + } catch (Exception e) { + Log.d("sugar", "Couldn't find config value: " + name); + } + + return value; + } + +} diff --git a/library/src/com/orm/util/ReflectionUtil.java b/library/src/com/orm/util/ReflectionUtil.java index 7a213cae..b676d183 100644 --- a/library/src/com/orm/util/ReflectionUtil.java +++ b/library/src/com/orm/util/ReflectionUtil.java @@ -188,7 +188,7 @@ public static List getDomainClasses(Context context) { List domainClasses = new ArrayList(); try { for (String className : getAllClasses(context)) { - if (className.startsWith(SugarConfig.getDomainPackageName(context))) { + if (className.startsWith(ManifestHelper.getDomainPackageName(context))) { Class domainClass = getDomainClass(className, context); if (domainClass != null) domainClasses.add(domainClass); } diff --git a/library/src/com/orm/util/SugarConfig.java b/library/src/com/orm/util/SugarConfig.java index 9568fa67..b93da25e 100644 --- a/library/src/com/orm/util/SugarConfig.java +++ b/library/src/com/orm/util/SugarConfig.java @@ -11,20 +11,11 @@ import java.util.List; import java.util.Map; + public class SugarConfig { static Map, List> fields = new HashMap, List>(); - public static String getDatabaseName(Context context) { - String databaseName = getMetaDataString(context, "DATABASE"); - - if (databaseName == null) { - databaseName = "Sugar.db"; - } - - return databaseName; - } - public static void setFields(Class clazz, List fieldz) { fields.put(clazz, fieldz); } @@ -43,67 +34,4 @@ public static void clearCache() { fields = new HashMap, List>(); } - public static int getDatabaseVersion(Context context) { - Integer databaseVersion = getMetaDataInteger(context, "VERSION"); - if ((databaseVersion == null) || (databaseVersion == 0)) { - databaseVersion = 1; - } - - return databaseVersion; - } - - public static String getDomainPackageName(Context context) { - String domainPackageName = getMetaDataString(context, "DOMAIN_PACKAGE_NAME"); - if (domainPackageName == null) { - domainPackageName = ""; - } - - return domainPackageName; - } - - public static boolean getDebugEnabled(Context context) { - return getMetaDataBoolean(context, "QUERY_LOG"); - } - - public static String getMetaDataString(Context context, String name) { - String value = null; - PackageManager pm = context.getPackageManager(); - - try { - ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), 128); - value = ai.metaData.getString(name); - } catch (Exception e) { - Log.d("Sugar", "Couldn't find config value: " + name); - } - - return value; - } - - public static Integer getMetaDataInteger(Context context, String name) { - Integer value = null; - PackageManager pm = context.getPackageManager(); - - try { - ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), 128); - value = ai.metaData.getInt(name); - } catch (Exception e) { - Log.d("Sugar", "Couldn't find config value: " + name); - } - - return value; - } - - public static Boolean getMetaDataBoolean(Context context, String name) { - Boolean value = false; - PackageManager pm = context.getPackageManager(); - - try { - ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), 128); - value = ai.metaData.getBoolean(name); - } catch (Exception e) { - Log.d("Sugar", "Couldn't find config value: " + name); - } - - return value; - } } From 64444466e4a062dcd02e2e5cc4b7fe5014050b2c Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Mon, 18 Aug 2014 16:54:26 -0700 Subject: [PATCH 038/305] Use self-documenting flag --- library/src/com/orm/util/ManifestHelper.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/library/src/com/orm/util/ManifestHelper.java b/library/src/com/orm/util/ManifestHelper.java index f98f790e..fb65724c 100644 --- a/library/src/com/orm/util/ManifestHelper.java +++ b/library/src/com/orm/util/ManifestHelper.java @@ -10,10 +10,6 @@ */ public class ManifestHelper { - /** - * TODO document the meaning of this 128 flag. - */ - private final static int FLAG = 128; /** * Key for the database name meta data. */ @@ -94,7 +90,8 @@ private static String getMetaDataString(Context context, String name) { PackageManager pm = context.getPackageManager(); try { - ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), FLAG); + ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), + PackageManager.GET_META_DATA); value = ai.metaData.getString(name); } catch (Exception e) { Log.d("sugar", "Couldn't find config value: " + name); @@ -108,7 +105,8 @@ private static Integer getMetaDataInteger(Context context, String name) { PackageManager pm = context.getPackageManager(); try { - ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), FLAG); + ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), + PackageManager.GET_META_DATA); value = ai.metaData.getInt(name); } catch (Exception e) { Log.d("sugar", "Couldn't find config value: " + name); @@ -122,7 +120,8 @@ private static Boolean getMetaDataBoolean(Context context, String name) { PackageManager pm = context.getPackageManager(); try { - ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), FLAG); + ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), + PackageManager.GET_META_DATA); value = ai.metaData.getBoolean(name); } catch (Exception e) { Log.d("sugar", "Couldn't find config value: " + name); From 9c04bdc16d00695571477c4af954ea6b2a7a2423 Mon Sep 17 00:00:00 2001 From: Xudong Shen Date: Fri, 25 Jul 2014 14:19:51 +0800 Subject: [PATCH 039/305] add Timestamp convert on save() --- library/src/com/orm/util/ReflectionUtil.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/src/com/orm/util/ReflectionUtil.java b/library/src/com/orm/util/ReflectionUtil.java index b676d183..3dc2b59d 100644 --- a/library/src/com/orm/util/ReflectionUtil.java +++ b/library/src/com/orm/util/ReflectionUtil.java @@ -76,6 +76,12 @@ public static void addFieldValueToColumn(ContentValues values, Field column, Obj values.put(columnName, (Double) columnValue); } else if (columnType.equals(Boolean.class) || columnType.equals(boolean.class)) { values.put(columnName, (Boolean) columnValue); + } else if (Timestamp.class.equals(columnType)) { + try { + values.put(columnName, ((Timestamp) column.get(object)).getTime()); + } catch (NullPointerException e) { + values.put(columnName, (Long) null); + } } else if (Date.class.equals(columnType)) { try { values.put(columnName, ((Date) column.get(object)).getTime()); From d6d36abf6a46d0c227ed63f976c9fa5235041008 Mon Sep 17 00:00:00 2001 From: Caleb Barde Date: Thu, 21 Aug 2014 12:21:15 -0500 Subject: [PATCH 040/305] add Log on delete() --- library/src/com/orm/SugarRecord.java | 1 + 1 file changed, 1 insertion(+) diff --git a/library/src/com/orm/SugarRecord.java b/library/src/com/orm/SugarRecord.java index 2d854b64..8448cad9 100644 --- a/library/src/com/orm/SugarRecord.java +++ b/library/src/com/orm/SugarRecord.java @@ -217,6 +217,7 @@ private static void inflate(Cursor cursor, Object object) { public void delete() { SQLiteDatabase db = getSugarContext().getSugarDb().getDB(); db.delete(NamingHelper.toSQLName(getClass()), "Id=?", new String[]{getId().toString()}); + Log.i("Sugar", getClass().getSimpleName() + " deleted : " + getId().toString()}); } public long save() { From 58e5d98f22291d62b65dfe580afda96d44e01f13 Mon Sep 17 00:00:00 2001 From: Caleb Barde Date: Fri, 22 Aug 2014 09:18:34 -0500 Subject: [PATCH 041/305] initial add of .travis.yml --- .travis.yml | 13 +++++++++++++ library/build.properties | 5 +++-- library/build.xml | 27 ++++++++++++++++++++++----- library/src/com/orm/SugarRecord.java | 2 +- 4 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..ab91b91e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +language: android +android: + components: + - sys-img-x86-android-17 + - build-tools-19.1.0 + - android-17 + - android-19 + +before_script: + - cd library + +script: + - ant travis-all diff --git a/library/build.properties b/library/build.properties index ff5c1fe8..b5b95df0 100644 --- a/library/build.properties +++ b/library/build.properties @@ -1,2 +1,3 @@ -jdk.home.1.6=/Library/Java/Home -android.home=/Users/ADMIN/Downloads/android-sdk-macosx/platforms/android-17/android.jar \ No newline at end of file +jdk.home.1.6=$JAVA_HOME +android.home=/Users/ADMIN/Downloads/android-sdk-macosx/platforms/android-17/android.jar +android.travis.home=/usr/local/android-sdk/platforms/android-17/android.jar diff --git a/library/build.xml b/library/build.xml index ef746a37..927b3904 100644 --- a/library/build.xml +++ b/library/build.xml @@ -20,6 +20,10 @@ + + + + @@ -29,10 +33,10 @@ - + - + @@ -41,12 +45,25 @@ - + + + + + + + + + + + + + - + + - \ No newline at end of file + diff --git a/library/src/com/orm/SugarRecord.java b/library/src/com/orm/SugarRecord.java index 8448cad9..d3a5e8fc 100644 --- a/library/src/com/orm/SugarRecord.java +++ b/library/src/com/orm/SugarRecord.java @@ -217,7 +217,7 @@ private static void inflate(Cursor cursor, Object object) { public void delete() { SQLiteDatabase db = getSugarContext().getSugarDb().getDB(); db.delete(NamingHelper.toSQLName(getClass()), "Id=?", new String[]{getId().toString()}); - Log.i("Sugar", getClass().getSimpleName() + " deleted : " + getId().toString()}); + Log.i("Sugar", getClass().getSimpleName() + " deleted : " + getId().toString()); } public long save() { From eb7fe26b4a22976667168898879cfe3d3aaa3f3f Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Sat, 23 Aug 2014 00:12:57 -0700 Subject: [PATCH 042/305] Add travis build status to README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2cea7303..5495ab9a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Sugar ORM +# Sugar ORM ![travis status](https://travis-ci.org/satyan/sugar.svg?branch=master) Insanely easy way to work with Android databases. From 17694f505350ad062279dbce0f14eb3f4521c10b Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Sat, 23 Aug 2014 00:14:35 -0700 Subject: [PATCH 043/305] Better travis status link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5495ab9a..4480e154 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Sugar ORM ![travis status](https://travis-ci.org/satyan/sugar.svg?branch=master) +# Sugar ORM [![Build Status](https://travis-ci.org/satyan/sugar.svg?branch=master)](https://travis-ci.org/satyan/sugar) Insanely easy way to work with Android databases. From 88b372ae6ba6acda685d0293e9d1ed5a48b796df Mon Sep 17 00:00:00 2001 From: Peter McAtominey Date: Fri, 26 Sep 2014 19:48:54 +0100 Subject: [PATCH 044/305] Updated build.gradle meet minimum build requirements --- library/build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/build.gradle b/library/build.gradle index 43157b3c..5aa31cce 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -4,19 +4,19 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:0.9.+' + classpath 'com.android.tools.build:gradle:0.12.2' } } - apply plugin: 'android-library' + apply plugin: 'com.android.library' dependencies { - compile 'com.android.support:support-v4:19.0.1' + compile 'com.android.support:support-v4:20.0.0' } android { compileSdkVersion 19 - buildToolsVersion "19.0.3" + buildToolsVersion "19.1.0" sourceSets { main { manifest.srcFile 'AndroidManifest.xml' From ba2b6b40fdc1aadbeea7b7d3fc94a172ffb9b4c2 Mon Sep 17 00:00:00 2001 From: MSchwarzer-Haverbier Date: Wed, 15 Oct 2014 15:03:50 +0200 Subject: [PATCH 045/305] Update NamingHelper.java Removed redundant call to UpperCase, as the given character already is in UpperCase (see line 38). --- library/src/com/orm/util/NamingHelper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/src/com/orm/util/NamingHelper.java b/library/src/com/orm/util/NamingHelper.java index dd86f8d1..734b4a56 100644 --- a/library/src/com/orm/util/NamingHelper.java +++ b/library/src/com/orm/util/NamingHelper.java @@ -38,9 +38,9 @@ public static String toSQLNameDefault(String camelCased) { } else if (Character.isUpperCase(c)) { if (Character.isLetterOrDigit(prevChar)) { if (Character.isLowerCase(prevChar)) { - sb.append('_').append(Character.toUpperCase(c)); + sb.append('_').append(c); } else if (nextChar > 0 && Character.isLowerCase(nextChar)) { - sb.append('_').append(Character.toUpperCase(c)); + sb.append('_').append(c); } else { sb.append(c); } From 8324aa324e88fca3e00190bd868c1a59518458b7 Mon Sep 17 00:00:00 2001 From: Neil Wells Date: Thu, 8 Jan 2015 13:00:44 +0000 Subject: [PATCH 046/305] Queries can now take SugarRecord objects as foreign keys --- library/src/com/orm/query/Condition.java | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/library/src/com/orm/query/Condition.java b/library/src/com/orm/query/Condition.java index 989bd458..9e9a08d1 100644 --- a/library/src/com/orm/query/Condition.java +++ b/library/src/com/orm/query/Condition.java @@ -1,5 +1,7 @@ package com.orm.query; +import com.orm.SugarRecord; + public class Condition { private String property; @@ -40,37 +42,37 @@ public static Condition prop(String property) { } public Condition eq(Object value) { - this.value = value; + setValue(value); check = Check.EQUALS; return this; } public Condition like(Object value) { - this.value = value; + setValue(value); check = Check.LIKE; return this; } public Condition notLike(Object value) { - this.value = value; + setValue(value); check = Check.NOT_LIKE; return this; } public Condition notEq(Object value) { - this.value = value; + setValue(value); check = Check.NOT_EQUALS; return this; } public Condition gt(Object value) { - this.value = value; + setValue(value); check = Check.GREATER_THAN; return this; } public Condition lt(Object value) { - this.value = value; + setValue(value); check = Check.LESSER_THAN; return this; } @@ -91,4 +93,12 @@ public String getCheckSymbol() { return check.getSymbol(); } + private void setValue(Object value) { + if (value instanceof SugarRecord) { + this.value = ((SugarRecord)value).getId(); + } else { + this.value = value; + } + } + } From cedafd7366aa69392a2680d5598c486f83c2dcfe Mon Sep 17 00:00:00 2001 From: sashas Date: Sat, 10 Jan 2015 20:25:10 +0200 Subject: [PATCH 047/305] robolectric-sugarorm test failure on "table already exists" IF you run tobolectric test from command line via gradle tests fails with "already exists" error, this error is casued by the fact that when robolectric tests are run there are 2 java pathes on context app/build/intermediates/classes/debug/ and app/build/test-classes/debug/ This bug caused all clases that are found under first director yto appear twice, which caused sugarorm to try and create the same tables twice which lead to failure. --- library/src/com/orm/util/ReflectionUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/com/orm/util/ReflectionUtil.java b/library/src/com/orm/util/ReflectionUtil.java index 3dc2b59d..8fc3a242 100644 --- a/library/src/com/orm/util/ReflectionUtil.java +++ b/library/src/com/orm/util/ReflectionUtil.java @@ -244,8 +244,8 @@ private static List getAllClasses(Context context) throws PackageManager } catch (NullPointerException e) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); Enumeration urls = classLoader.getResources(""); - List fileNames = new ArrayList(); while (urls.hasMoreElements()) { + List fileNames = new ArrayList(); String classDirectoryName = urls.nextElement().getFile(); if (classDirectoryName.contains("bin") || classDirectoryName.contains("classes")) { File classDirectory = new File(classDirectoryName); From 9753600377babff606c0b36ee343eaacce663c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hoffmann=20Andr=C3=A1s?= Date: Thu, 15 Jan 2015 14:46:45 +0100 Subject: [PATCH 048/305] doInTransaction typo --- library/src/com/orm/SugarTransactionHelper.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/library/src/com/orm/SugarTransactionHelper.java b/library/src/com/orm/SugarTransactionHelper.java index 0f2683f2..73440635 100644 --- a/library/src/com/orm/SugarTransactionHelper.java +++ b/library/src/com/orm/SugarTransactionHelper.java @@ -5,7 +5,7 @@ public class SugarTransactionHelper { - public static void doInTansaction(SugarTransactionHelper.Callback callback) { + public static void doInTransaction(SugarTransactionHelper.Callback callback) { SQLiteDatabase database = SugarContext.getSugarContext().getSugarDb().getDB(); database.beginTransaction(); @@ -22,11 +22,9 @@ public static void doInTansaction(SugarTransactionHelper.Callback callback) { } finally { database.endTransaction(); } - } public static interface Callback { void manipulateInTransaction(); } - } From ed634b9a948ac94a8624276cbef913150b825a40 Mon Sep 17 00:00:00 2001 From: Ishan Khanna Date: Tue, 5 Aug 2014 20:32:03 +0530 Subject: [PATCH 049/305] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4480e154..55d1a606 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Declare the dependency in Maven: Download the source code and import it as a library project in Eclipse. The project is available in the folder **library**. For more information on how to do this, read [here](http://developer.android.com/tools/projects/index.html#LibraryProjects). -#### Use a jar +#### As a jar Visit the [releases](https://github.com/satyan/sugar/releases) page to download jars directly. You can drop them into your `libs` folder and configure the Java build path to include the library. See this [tutorial](http://www.vogella.com/tutorials/AndroidLibraryProjects/article.html) for an excellent guide on how to do this. From 623b31d6e1f1bb34eee7033cb3d468ddde75c9e0 Mon Sep 17 00:00:00 2001 From: Ishan Khanna Date: Fri, 5 Sep 2014 13:08:00 -0800 Subject: [PATCH 050/305] Migrated to Gradle Build closes #160 --- .gitignore | 3 +++ example/example.iml | 18 ++++++++++++++++++ sugar.iml | 12 ++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 example/example.iml create mode 100644 sugar.iml diff --git a/.gitignore b/.gitignore index 76806924..7278b5fa 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,6 @@ library/library.iml library/.gradle *.class .DS_Store +*.idea +example/gen +*/local.properties diff --git a/example/example.iml b/example/example.iml new file mode 100644 index 00000000..024684bf --- /dev/null +++ b/example/example.iml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/sugar.iml b/sugar.iml new file mode 100644 index 00000000..e7239020 --- /dev/null +++ b/sugar.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + From eadd351dc7ce471d248701739a277692b8f23995 Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Tue, 27 Jan 2015 22:19:38 -0800 Subject: [PATCH 051/305] Update tools and libraries to support official Android Studio 1.0 release --- library/build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/build.gradle b/library/build.gradle index 5aa31cce..8d8edc7e 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -4,19 +4,19 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:0.12.2' + classpath 'com.android.tools.build:gradle:1.0.0' } } apply plugin: 'com.android.library' dependencies { - compile 'com.android.support:support-v4:20.0.0' + compile 'com.android.support:support-v4:21.0.3' } android { - compileSdkVersion 19 - buildToolsVersion "19.1.0" + compileSdkVersion 21 + buildToolsVersion "21.1.2" sourceSets { main { manifest.srcFile 'AndroidManifest.xml' From 2bcfacae0b7556a26a704371db71b8f661f85398 Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Thu, 26 Mar 2015 23:28:57 -0700 Subject: [PATCH 052/305] Use more standard project directory for the library --- library/{ => src/main}/AndroidManifest.xml | 3 +-- library/src/{ => main/java}/com/orm/SchemaGenerator.java | 0 library/src/{ => main/java}/com/orm/SugarApp.java | 0 library/src/{ => main/java}/com/orm/SugarContext.java | 0 library/src/{ => main/java}/com/orm/SugarDb.java | 0 library/src/{ => main/java}/com/orm/SugarRecord.java | 0 .../src/{ => main/java}/com/orm/SugarTransactionHelper.java | 0 library/src/{ => main/java}/com/orm/dsl/Column.java | 0 library/src/{ => main/java}/com/orm/dsl/Ignore.java | 0 library/src/{ => main/java}/com/orm/dsl/NotNull.java | 0 library/src/{ => main/java}/com/orm/dsl/Table.java | 0 library/src/{ => main/java}/com/orm/dsl/Unique.java | 0 library/src/{ => main/java}/com/orm/query/Condition.java | 0 library/src/{ => main/java}/com/orm/query/Select.java | 0 library/src/{ => main/java}/com/orm/util/Collection.java | 0 library/src/{ => main/java}/com/orm/util/ManifestHelper.java | 0 library/src/{ => main/java}/com/orm/util/NamingHelper.java | 0 library/src/{ => main/java}/com/orm/util/NumberComparator.java | 0 library/src/{ => main/java}/com/orm/util/QueryBuilder.java | 0 library/src/{ => main/java}/com/orm/util/ReflectionUtil.java | 0 library/src/{ => main/java}/com/orm/util/SugarConfig.java | 0 .../src/{ => main/java}/com/orm/util/SugarCursorFactory.java | 0 22 files changed, 1 insertion(+), 2 deletions(-) rename library/{ => src/main}/AndroidManifest.xml (69%) rename library/src/{ => main/java}/com/orm/SchemaGenerator.java (100%) rename library/src/{ => main/java}/com/orm/SugarApp.java (100%) rename library/src/{ => main/java}/com/orm/SugarContext.java (100%) rename library/src/{ => main/java}/com/orm/SugarDb.java (100%) rename library/src/{ => main/java}/com/orm/SugarRecord.java (100%) rename library/src/{ => main/java}/com/orm/SugarTransactionHelper.java (100%) rename library/src/{ => main/java}/com/orm/dsl/Column.java (100%) rename library/src/{ => main/java}/com/orm/dsl/Ignore.java (100%) rename library/src/{ => main/java}/com/orm/dsl/NotNull.java (100%) rename library/src/{ => main/java}/com/orm/dsl/Table.java (100%) rename library/src/{ => main/java}/com/orm/dsl/Unique.java (100%) rename library/src/{ => main/java}/com/orm/query/Condition.java (100%) rename library/src/{ => main/java}/com/orm/query/Select.java (100%) rename library/src/{ => main/java}/com/orm/util/Collection.java (100%) rename library/src/{ => main/java}/com/orm/util/ManifestHelper.java (100%) rename library/src/{ => main/java}/com/orm/util/NamingHelper.java (100%) rename library/src/{ => main/java}/com/orm/util/NumberComparator.java (100%) rename library/src/{ => main/java}/com/orm/util/QueryBuilder.java (100%) rename library/src/{ => main/java}/com/orm/util/ReflectionUtil.java (100%) rename library/src/{ => main/java}/com/orm/util/SugarConfig.java (100%) rename library/src/{ => main/java}/com/orm/util/SugarCursorFactory.java (100%) diff --git a/library/AndroidManifest.xml b/library/src/main/AndroidManifest.xml similarity index 69% rename from library/AndroidManifest.xml rename to library/src/main/AndroidManifest.xml index a48c294b..849de626 100644 --- a/library/AndroidManifest.xml +++ b/library/src/main/AndroidManifest.xml @@ -2,7 +2,6 @@ - + android:versionName="1.3.1"> \ No newline at end of file diff --git a/library/src/com/orm/SchemaGenerator.java b/library/src/main/java/com/orm/SchemaGenerator.java similarity index 100% rename from library/src/com/orm/SchemaGenerator.java rename to library/src/main/java/com/orm/SchemaGenerator.java diff --git a/library/src/com/orm/SugarApp.java b/library/src/main/java/com/orm/SugarApp.java similarity index 100% rename from library/src/com/orm/SugarApp.java rename to library/src/main/java/com/orm/SugarApp.java diff --git a/library/src/com/orm/SugarContext.java b/library/src/main/java/com/orm/SugarContext.java similarity index 100% rename from library/src/com/orm/SugarContext.java rename to library/src/main/java/com/orm/SugarContext.java diff --git a/library/src/com/orm/SugarDb.java b/library/src/main/java/com/orm/SugarDb.java similarity index 100% rename from library/src/com/orm/SugarDb.java rename to library/src/main/java/com/orm/SugarDb.java diff --git a/library/src/com/orm/SugarRecord.java b/library/src/main/java/com/orm/SugarRecord.java similarity index 100% rename from library/src/com/orm/SugarRecord.java rename to library/src/main/java/com/orm/SugarRecord.java diff --git a/library/src/com/orm/SugarTransactionHelper.java b/library/src/main/java/com/orm/SugarTransactionHelper.java similarity index 100% rename from library/src/com/orm/SugarTransactionHelper.java rename to library/src/main/java/com/orm/SugarTransactionHelper.java diff --git a/library/src/com/orm/dsl/Column.java b/library/src/main/java/com/orm/dsl/Column.java similarity index 100% rename from library/src/com/orm/dsl/Column.java rename to library/src/main/java/com/orm/dsl/Column.java diff --git a/library/src/com/orm/dsl/Ignore.java b/library/src/main/java/com/orm/dsl/Ignore.java similarity index 100% rename from library/src/com/orm/dsl/Ignore.java rename to library/src/main/java/com/orm/dsl/Ignore.java diff --git a/library/src/com/orm/dsl/NotNull.java b/library/src/main/java/com/orm/dsl/NotNull.java similarity index 100% rename from library/src/com/orm/dsl/NotNull.java rename to library/src/main/java/com/orm/dsl/NotNull.java diff --git a/library/src/com/orm/dsl/Table.java b/library/src/main/java/com/orm/dsl/Table.java similarity index 100% rename from library/src/com/orm/dsl/Table.java rename to library/src/main/java/com/orm/dsl/Table.java diff --git a/library/src/com/orm/dsl/Unique.java b/library/src/main/java/com/orm/dsl/Unique.java similarity index 100% rename from library/src/com/orm/dsl/Unique.java rename to library/src/main/java/com/orm/dsl/Unique.java diff --git a/library/src/com/orm/query/Condition.java b/library/src/main/java/com/orm/query/Condition.java similarity index 100% rename from library/src/com/orm/query/Condition.java rename to library/src/main/java/com/orm/query/Condition.java diff --git a/library/src/com/orm/query/Select.java b/library/src/main/java/com/orm/query/Select.java similarity index 100% rename from library/src/com/orm/query/Select.java rename to library/src/main/java/com/orm/query/Select.java diff --git a/library/src/com/orm/util/Collection.java b/library/src/main/java/com/orm/util/Collection.java similarity index 100% rename from library/src/com/orm/util/Collection.java rename to library/src/main/java/com/orm/util/Collection.java diff --git a/library/src/com/orm/util/ManifestHelper.java b/library/src/main/java/com/orm/util/ManifestHelper.java similarity index 100% rename from library/src/com/orm/util/ManifestHelper.java rename to library/src/main/java/com/orm/util/ManifestHelper.java diff --git a/library/src/com/orm/util/NamingHelper.java b/library/src/main/java/com/orm/util/NamingHelper.java similarity index 100% rename from library/src/com/orm/util/NamingHelper.java rename to library/src/main/java/com/orm/util/NamingHelper.java diff --git a/library/src/com/orm/util/NumberComparator.java b/library/src/main/java/com/orm/util/NumberComparator.java similarity index 100% rename from library/src/com/orm/util/NumberComparator.java rename to library/src/main/java/com/orm/util/NumberComparator.java diff --git a/library/src/com/orm/util/QueryBuilder.java b/library/src/main/java/com/orm/util/QueryBuilder.java similarity index 100% rename from library/src/com/orm/util/QueryBuilder.java rename to library/src/main/java/com/orm/util/QueryBuilder.java diff --git a/library/src/com/orm/util/ReflectionUtil.java b/library/src/main/java/com/orm/util/ReflectionUtil.java similarity index 100% rename from library/src/com/orm/util/ReflectionUtil.java rename to library/src/main/java/com/orm/util/ReflectionUtil.java diff --git a/library/src/com/orm/util/SugarConfig.java b/library/src/main/java/com/orm/util/SugarConfig.java similarity index 100% rename from library/src/com/orm/util/SugarConfig.java rename to library/src/main/java/com/orm/util/SugarConfig.java diff --git a/library/src/com/orm/util/SugarCursorFactory.java b/library/src/main/java/com/orm/util/SugarCursorFactory.java similarity index 100% rename from library/src/com/orm/util/SugarCursorFactory.java rename to library/src/main/java/com/orm/util/SugarCursorFactory.java From 0c0500a65775d5a8763903d11e0f2a648d742ee7 Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Thu, 26 Mar 2015 23:29:22 -0700 Subject: [PATCH 053/305] Remove unnecessary files --- library/project.properties | 3 --- library/res/values/strings.xml | 4 ---- 2 files changed, 7 deletions(-) delete mode 100644 library/project.properties delete mode 100644 library/res/values/strings.xml diff --git a/library/project.properties b/library/project.properties deleted file mode 100644 index 05d02d38..00000000 --- a/library/project.properties +++ /dev/null @@ -1,3 +0,0 @@ -android.library=true -# Project target. -target=android-15 diff --git a/library/res/values/strings.xml b/library/res/values/strings.xml deleted file mode 100644 index 0d2c4cc4..00000000 --- a/library/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file From c726b345d31fd10a1f4899be9a49c525e29afdc5 Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Thu, 26 Mar 2015 23:30:24 -0700 Subject: [PATCH 054/305] Upgrade .gitignore --- .gitignore | 52 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 7278b5fa..9ea91260 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,44 @@ -library/bin -library/build -library/gen -library/.project -library/.classpath -library/library.iml -library/.gradle +.DS_Store + +# built application files +*.apk +*.ap_ + +# files for the dex VM +*.dex + +# Java class files *.class + +# generated files +bin/ +gen/ + +# Local configuration file (sdk path, etc) +local.properties + +# Eclipse project files +.classpath +.project + +*.a +*.dylib +*.log +*.o +*.pot +*.pyc +*.pydevproject +*.so +*.suo +*.xcworkspace +*_ReSharper* + .DS_Store -*.idea -example/gen -*/local.properties +._.DS_Store + +project.properties + +.settings +build/ +.gradle/ +.idea From 68164a89de299e419ddcc0731e17860cabea1489 Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Thu, 26 Mar 2015 23:31:34 -0700 Subject: [PATCH 055/305] Update gradle build process --- build.gradle | 16 +++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 164 +++++++++++++++++++++++ gradlew.bat | 90 +++++++++++++ library/build.gradle | 61 ++++++--- library/library.iml | 97 ++++++++++++++ settings.gradle | 1 + sugar.iml | 11 +- 9 files changed, 423 insertions(+), 23 deletions(-) create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 library/library.iml create mode 100644 settings.gradle diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..358b5504 --- /dev/null +++ b/build.gradle @@ -0,0 +1,16 @@ +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.1.0' + } +} + +allprojects { + repositories { + mavenCentral() + mavenLocal() + jcenter() + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..8c0fb64a8698b08ecc4158d828ca593c4928e9dd GIT binary patch literal 49896 zcmagFb986H(k`5d^NVfUwr$(C?M#x1ZQHiZiEVpg+jrjgoQrerx!>1o_ul)D>ebz~ zs=Mmxr&>W81QY-S1PKWQ%N-;H^tS;2*XwVA`dej1RRn1z<;3VgfE4~kaG`A%QSPsR z#ovnZe+tS9%1MfeDyz`RirvdjPRK~p(#^q2(^5@O&NM19EHdvN-A&StN>0g6QA^VN z0Gx%Gq#PD$QMRFzmK+utjS^Y1F0e8&u&^=w5K<;4Rz|i3A=o|IKLY+g`iK6vfr9?+ z-`>gmU&i?FGSL5&F?TXFu`&Js6h;15QFkXp2M1H9|Eq~bpov-GU(uz%mH0n55wUl- zv#~ccAz`F5wlQ>e_KlJS3@{)B?^v*EQM=IxLa&76^y51a((wq|2-`qON>+4dLc{Oo z51}}o^Zen(oAjxDK7b++9_Yg`67p$bPo3~BCpGM7uAWmvIhWc5Gi+gQZ|Pwa-Gll@<1xmcPy z|NZmu6m)g5Ftu~BG&Xdxclw7Cij{xbBMBn-LMII#Slp`AElb&2^Hw+w>(3crLH!;I zN+Vk$D+wP1#^!MDCiad@vM>H#6+`Ct#~6VHL4lzmy;lSdk>`z6)=>Wh15Q2)dQtGqvn0vJU@+(B5{MUc*qs4!T+V=q=wy)<6$~ z!G>e_4dN@lGeF_$q9`Ju6Ncb*x?O7=l{anm7Eahuj_6lA{*#Gv*TaJclevPVbbVYu z(NY?5q+xxbO6%g1xF0r@Ix8fJ~u)VRUp`S%&rN$&e!Od`~s+64J z5*)*WSi*i{k%JjMSIN#X;jC{HG$-^iX+5f5BGOIHWAl*%15Z#!xntpk($-EGKCzKa zT7{siZ9;4TICsWQ$pu&wKZQTCvpI$Xvzwxoi+XkkpeE&&kFb!B?h2hi%^YlXt|-@5 zHJ~%AN!g_^tmn1?HSm^|gCE#!GRtK2(L{9pL#hp0xh zME}|DB>(5)`iE7CM)&_+S}-Bslc#@B5W4_+k4Cp$l>iVyg$KP>CN?SVGZ(&02>iZK zB<^HP$g$Lq*L$BWd?2(F?-MUbNWTJVQdW7$#8a|k_30#vHAD1Z{c#p;bETk0VnU5A zBgLe2HFJ3032$G<`m*OB!KM$*sdM20jm)It5OSru@tXpK5LT>#8)N!*skNu1$TpIw zufjjdp#lyH5bZ%|Iuo|iu9vG1HrIVWLH>278xo>aVBkPN3V$~!=KnlXQ4eDqS7%E% zQ!z^$Q$b^6Q)g#cLpwur(|<0gWHo6A6jc;n`t(V9T;LzTAU{IAu*uEQ%Ort1k+Kn+f_N`9|bxYC+~Z1 zCC1UCWv*Orx$_@ydv9mIe(liLfOr7mhbV@tKw{6)q^1DH1nmvZ0cj215R<~&I<4S| zgnr;9Cdjqpz#o8i0CQjtl`}{c*P)aSdH|abxGdrR)-3z+02-eX(k*B)Uqv6~^nh** z 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..aec99730 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/library/build.gradle b/library/build.gradle index 8d8edc7e..555061e8 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -1,29 +1,48 @@ -buildscript { - repositories { - mavenCentral() - } +apply plugin: 'com.android.library' +apply plugin: 'maven-publish' + +group 'com.github.satyan' +version '1.3.1' + +android { + compileSdkVersion 21 + buildToolsVersion "21.1.2" + + defaultConfig { + minSdkVersion 8 + targetSdkVersion 21 + } - dependencies { - classpath 'com.android.tools.build:gradle:1.0.0' + buildTypes { + release { + minifyEnabled false } } +} - apply plugin: 'com.android.library' +dependencies { + compile 'com.android.support:support-v4:21.0.3' +} - dependencies { - compile 'com.android.support:support-v4:21.0.3' - } +task libraryJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + baseName 'sugar' +} - android { - compileSdkVersion 21 - buildToolsVersion "21.1.2" - sourceSets { - main { - manifest.srcFile 'AndroidManifest.xml' - java.srcDirs = ['src'] - resources.srcDirs = ['src'] - res.srcDirs = ['res'] - assets.srcDirs = ['assets'] - } +publishing { + publications { + artifactJar(MavenPublication) { + artifact libraryJar + artifactId 'sugar' + groupId project.group } + artifactAar(MavenPublication) { + artifact bundleRelease + artifactId 'sugar' + groupId project.group + } + } + repositories { + mavenCentral() } +} diff --git a/library/library.iml b/library/library.iml new file mode 100644 index 00000000..4c42fe43 --- /dev/null +++ b/library/library.iml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..d8f14a13 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':library' diff --git a/sugar.iml b/sugar.iml index e7239020..00bc43cf 100644 --- a/sugar.iml +++ b/sugar.iml @@ -1,12 +1,19 @@ + + + + + + - - + + From e5669d2568439ba2eace1a19a76aaec8b6b580c3 Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Thu, 26 Mar 2015 23:47:24 -0700 Subject: [PATCH 056/305] Use maven local first --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 358b5504..fc79749e 100644 --- a/build.gradle +++ b/build.gradle @@ -9,8 +9,8 @@ buildscript { allprojects { repositories { - mavenCentral() mavenLocal() + mavenCentral() jcenter() } } From 18b69a1cb43dc1a026569b9be92cce3f6ab112ba Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Thu, 26 Mar 2015 23:48:04 -0700 Subject: [PATCH 057/305] Move example files into more standard structure --- example/{ => src/main}/AndroidManifest.xml | 0 example/{ => src/main}/assets/sugar_upgrades/3.sql | 0 .../java}/com/example/AddNoteActivity.java | 0 .../src/{ => main/java}/com/example/ClientApp.java | 0 .../src/{ => main/java}/com/example/NewNote.java | 0 example/src/{ => main/java}/com/example/Note.java | 0 .../java}/com/example/NoteListActivity.java | 0 .../{ => main/java}/com/example/NoteRelation.java | 0 .../{ => main/java}/com/example/SugarActivity.java | 0 example/src/{ => main/java}/com/example/Tag.java | 0 .../src/{ => main/java}/com/example/TextNote.java | 0 example/{ => src/main}/res/drawable-hdpi/icon.png | Bin example/{ => src/main}/res/drawable-ldpi/icon.png | Bin example/{ => src/main}/res/drawable-mdpi/icon.png | Bin example/{ => src/main}/res/layout/main.xml | 0 example/{ => src/main}/res/layout/notelist.xml | 0 example/{ => src/main}/res/values/strings.xml | 0 17 files changed, 0 insertions(+), 0 deletions(-) rename example/{ => src/main}/AndroidManifest.xml (100%) rename example/{ => src/main}/assets/sugar_upgrades/3.sql (100%) rename example/src/{ => main/java}/com/example/AddNoteActivity.java (100%) rename example/src/{ => main/java}/com/example/ClientApp.java (100%) rename example/src/{ => main/java}/com/example/NewNote.java (100%) rename example/src/{ => main/java}/com/example/Note.java (100%) rename example/src/{ => main/java}/com/example/NoteListActivity.java (100%) rename example/src/{ => main/java}/com/example/NoteRelation.java (100%) rename example/src/{ => main/java}/com/example/SugarActivity.java (100%) rename example/src/{ => main/java}/com/example/Tag.java (100%) rename example/src/{ => main/java}/com/example/TextNote.java (100%) rename example/{ => src/main}/res/drawable-hdpi/icon.png (100%) rename example/{ => src/main}/res/drawable-ldpi/icon.png (100%) rename example/{ => src/main}/res/drawable-mdpi/icon.png (100%) rename example/{ => src/main}/res/layout/main.xml (100%) rename example/{ => src/main}/res/layout/notelist.xml (100%) rename example/{ => src/main}/res/values/strings.xml (100%) diff --git a/example/AndroidManifest.xml b/example/src/main/AndroidManifest.xml similarity index 100% rename from example/AndroidManifest.xml rename to example/src/main/AndroidManifest.xml diff --git a/example/assets/sugar_upgrades/3.sql b/example/src/main/assets/sugar_upgrades/3.sql similarity index 100% rename from example/assets/sugar_upgrades/3.sql rename to example/src/main/assets/sugar_upgrades/3.sql diff --git a/example/src/com/example/AddNoteActivity.java b/example/src/main/java/com/example/AddNoteActivity.java similarity index 100% rename from example/src/com/example/AddNoteActivity.java rename to example/src/main/java/com/example/AddNoteActivity.java diff --git a/example/src/com/example/ClientApp.java b/example/src/main/java/com/example/ClientApp.java similarity index 100% rename from example/src/com/example/ClientApp.java rename to example/src/main/java/com/example/ClientApp.java diff --git a/example/src/com/example/NewNote.java b/example/src/main/java/com/example/NewNote.java similarity index 100% rename from example/src/com/example/NewNote.java rename to example/src/main/java/com/example/NewNote.java diff --git a/example/src/com/example/Note.java b/example/src/main/java/com/example/Note.java similarity index 100% rename from example/src/com/example/Note.java rename to example/src/main/java/com/example/Note.java diff --git a/example/src/com/example/NoteListActivity.java b/example/src/main/java/com/example/NoteListActivity.java similarity index 100% rename from example/src/com/example/NoteListActivity.java rename to example/src/main/java/com/example/NoteListActivity.java diff --git a/example/src/com/example/NoteRelation.java b/example/src/main/java/com/example/NoteRelation.java similarity index 100% rename from example/src/com/example/NoteRelation.java rename to example/src/main/java/com/example/NoteRelation.java diff --git a/example/src/com/example/SugarActivity.java b/example/src/main/java/com/example/SugarActivity.java similarity index 100% rename from example/src/com/example/SugarActivity.java rename to example/src/main/java/com/example/SugarActivity.java diff --git a/example/src/com/example/Tag.java b/example/src/main/java/com/example/Tag.java similarity index 100% rename from example/src/com/example/Tag.java rename to example/src/main/java/com/example/Tag.java diff --git a/example/src/com/example/TextNote.java b/example/src/main/java/com/example/TextNote.java similarity index 100% rename from example/src/com/example/TextNote.java rename to example/src/main/java/com/example/TextNote.java diff --git a/example/res/drawable-hdpi/icon.png b/example/src/main/res/drawable-hdpi/icon.png similarity index 100% rename from example/res/drawable-hdpi/icon.png rename to example/src/main/res/drawable-hdpi/icon.png diff --git a/example/res/drawable-ldpi/icon.png b/example/src/main/res/drawable-ldpi/icon.png similarity index 100% rename from example/res/drawable-ldpi/icon.png rename to example/src/main/res/drawable-ldpi/icon.png diff --git a/example/res/drawable-mdpi/icon.png b/example/src/main/res/drawable-mdpi/icon.png similarity index 100% rename from example/res/drawable-mdpi/icon.png rename to example/src/main/res/drawable-mdpi/icon.png diff --git a/example/res/layout/main.xml b/example/src/main/res/layout/main.xml similarity index 100% rename from example/res/layout/main.xml rename to example/src/main/res/layout/main.xml diff --git a/example/res/layout/notelist.xml b/example/src/main/res/layout/notelist.xml similarity index 100% rename from example/res/layout/notelist.xml rename to example/src/main/res/layout/notelist.xml diff --git a/example/res/values/strings.xml b/example/src/main/res/values/strings.xml similarity index 100% rename from example/res/values/strings.xml rename to example/src/main/res/values/strings.xml From 71a3e901ca69da8e0c7d79e3de308deb9b02317a Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Thu, 26 Mar 2015 23:48:21 -0700 Subject: [PATCH 058/305] Remove unused files --- example/example/project.properties | 15 ------------- example/local.properties | 10 --------- example/proguard.cfg | 36 ------------------------------ 3 files changed, 61 deletions(-) delete mode 100644 example/example/project.properties delete mode 100755 example/local.properties delete mode 100755 example/proguard.cfg diff --git a/example/example/project.properties b/example/example/project.properties deleted file mode 100644 index debfe960..00000000 --- a/example/example/project.properties +++ /dev/null @@ -1,15 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-3 -android.library.reference.1=../library diff --git a/example/local.properties b/example/local.properties deleted file mode 100755 index 2543c8bd..00000000 --- a/example/local.properties +++ /dev/null @@ -1,10 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must *NOT* be checked in Version Control Systems, -# as it contains information specific to your local configuration. - -# location of the SDK. This is only used by Ant -# For customization when using a Version Control System, please read the -# header note. -sdk.dir=/home/satya/software/android-sdk-linux_x86 diff --git a/example/proguard.cfg b/example/proguard.cfg deleted file mode 100755 index 12dd0392..00000000 --- a/example/proguard.cfg +++ /dev/null @@ -1,36 +0,0 @@ --optimizationpasses 5 --dontusemixedcaseclassnames --dontskipnonpubliclibraryclasses --dontpreverify --verbose --optimizations !code/simplification/arithmetic,!field/*,!class/merging/* - --keep public class * extends android.app.Activity --keep public class * extends android.app.Application --keep public class * extends android.app.Service --keep public class * extends android.content.BroadcastReceiver --keep public class * extends android.content.ContentProvider --keep public class * extends android.app.backup.BackupAgentHelper --keep public class * extends android.preference.Preference --keep public class com.android.vending.licensing.ILicensingService - --keepclasseswithmembernames class * { - native ; -} - --keepclasseswithmembernames class * { - public (android.content.Context, android.util.AttributeSet); -} - --keepclasseswithmembernames class * { - public (android.content.Context, android.util.AttributeSet, int); -} - --keepclassmembers enum * { - public static **[] values(); - public static ** valueOf(java.lang.String); -} - --keep class * implements android.os.Parcelable { - public static final android.os.Parcelable$Creator *; -} From 0c7a7e07453daa49dfc222d4d0862eaf4aadb94c Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Thu, 26 Mar 2015 23:48:43 -0700 Subject: [PATCH 059/305] Update minimum sdk for library --- library/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/build.gradle b/library/build.gradle index 555061e8..5e2e71ef 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -9,7 +9,7 @@ android { buildToolsVersion "21.1.2" defaultConfig { - minSdkVersion 8 + minSdkVersion 9 targetSdkVersion 21 } From bb44e5bb209d50cf77a92b6dce7f718bc3f362eb Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Thu, 26 Mar 2015 23:49:03 -0700 Subject: [PATCH 060/305] Enable gradle for the example project --- example/build.gradle | 23 ++++++++++++ example/example.iml | 89 ++++++++++++++++++++++++++++++++++++++++---- settings.gradle | 1 + 3 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 example/build.gradle diff --git a/example/build.gradle b/example/build.gradle new file mode 100644 index 00000000..7576eb25 --- /dev/null +++ b/example/build.gradle @@ -0,0 +1,23 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 21 + buildToolsVersion "21.1.2" + + defaultConfig { + applicationId "com.example" + minSdkVersion 9 + targetSdkVersion 21 + } + + buildTypes { + release { + minifyEnabled false + } + } +} + +dependencies { + compile 'com.android.support:support-v4:21.0.3' + compile 'com.github.satyan:sugar:1.3.1' +} \ No newline at end of file diff --git a/example/example.iml b/example/example.iml index 024684bf..e1507057 100644 --- a/example/example.iml +++ b/example/example.iml @@ -1,18 +1,93 @@ - + + + + + - + + - + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + diff --git a/settings.gradle b/settings.gradle index d8f14a13..507c9f4a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,2 @@ include ':library' +include ':example' From 1d183d098f9f563b6dd45532baaf58f64d56297d Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Fri, 27 Mar 2015 19:52:57 -0700 Subject: [PATCH 061/305] Add robolectric gradle dependency to support more unit testing in the example project --- build.gradle | 1 + example/build.gradle | 32 +++++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index fc79749e..35875814 100644 --- a/build.gradle +++ b/build.gradle @@ -4,6 +4,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:1.1.0' + classpath 'org.robolectric:robolectric-gradle-plugin:1.0.1' } } diff --git a/example/build.gradle b/example/build.gradle index 7576eb25..d93d68bd 100644 --- a/example/build.gradle +++ b/example/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.application' +apply plugin: 'org.robolectric' android { compileSdkVersion 21 @@ -20,4 +21,33 @@ android { dependencies { compile 'com.android.support:support-v4:21.0.3' compile 'com.github.satyan:sugar:1.3.1' -} \ No newline at end of file + testCompile 'org.robolectric:robolectric:2.4' + testCompile 'junit:junit:4.12' +} + +robolectric { + // Configure includes / excludes + include '**/*Test.class' + exclude '**/espresso/**/*.class' + + // Configure max heap size of the test JVM + maxHeapSize = '2048m' + + // Configure the test JVM arguments - Does not apply to Java 8 + jvmArgs '-XX:MaxPermSize=512m', '-XX:-UseSplitVerifier' + + // Specify max number of processes (default is 1) + maxParallelForks = 4 + + // Specify max number of test classes to execute in a test process + // before restarting the process (default is unlimited) + forkEvery = 150 + + // configure whether failing tests should fail the build + ignoreFailures true + + // use afterTest to listen to the test execution results + afterTest { descriptor, result -> + println "Executing test for ${descriptor.name} with result: ${result.resultType}" + } +} From b28e2c35b6a08874e55a4f29635123cedee04f58 Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Fri, 27 Mar 2015 19:53:04 -0700 Subject: [PATCH 062/305] Restructure the example project --- example/example.iml | 54 ++++++++++++------- example/src/main/AndroidManifest.xml | 7 ++- .../{ => activities}/AddNoteActivity.java | 8 ++- .../{ => activities}/NoteListActivity.java | 8 ++- .../{ => activities}/SugarActivity.java | 8 ++- .../com/example/{ => models}/NewNote.java | 2 +- .../java/com/example/{ => models}/Note.java | 2 +- .../example/{ => models}/NoteRelation.java | 2 +- .../java/com/example/{ => models}/Tag.java | 2 +- .../com/example/{ => models}/TextNote.java | 2 +- 10 files changed, 63 insertions(+), 32 deletions(-) rename example/src/main/java/com/example/{ => activities}/AddNoteActivity.java (93%) mode change 100755 => 100644 rename example/src/main/java/com/example/{ => activities}/NoteListActivity.java (91%) mode change 100755 => 100644 rename example/src/main/java/com/example/{ => activities}/SugarActivity.java (89%) mode change 100755 => 100644 rename example/src/main/java/com/example/{ => models}/NewNote.java (81%) rename example/src/main/java/com/example/{ => models}/Note.java (98%) mode change 100755 => 100644 rename example/src/main/java/com/example/{ => models}/NoteRelation.java (95%) rename example/src/main/java/com/example/{ => models}/Tag.java (91%) mode change 100755 => 100644 rename example/src/main/java/com/example/{ => models}/TextNote.java (84%) diff --git a/example/example.iml b/example/example.iml index e1507057..227821f1 100644 --- a/example/example.iml +++ b/example/example.iml @@ -9,12 +9,11 @@ diff --git a/example/src/main/AndroidManifest.xml b/example/src/main/AndroidManifest.xml index e03f036a..ca949ff1 100755 --- a/example/src/main/AndroidManifest.xml +++ b/example/src/main/AndroidManifest.xml @@ -4,15 +4,14 @@ android:versionCode="1" android:versionName="1.0"> - + - - + + diff --git a/example/src/main/java/com/example/AddNoteActivity.java b/example/src/main/java/com/example/activities/AddNoteActivity.java old mode 100755 new mode 100644 similarity index 93% rename from example/src/main/java/com/example/AddNoteActivity.java rename to example/src/main/java/com/example/activities/AddNoteActivity.java index aba5e236..cbb54a20 --- a/example/src/main/java/com/example/AddNoteActivity.java +++ b/example/src/main/java/com/example/activities/AddNoteActivity.java @@ -1,4 +1,4 @@ -package com.example; +package com.example.activities; import android.app.Activity; import android.content.Intent; @@ -9,6 +9,10 @@ import android.widget.LinearLayout; import android.widget.TextView; +import com.example.R; +import com.example.models.Note; +import com.example.models.Tag; + import static com.orm.SugarRecord.save; @@ -49,4 +53,4 @@ public void onClick(View view) { }); } -} \ No newline at end of file +} diff --git a/example/src/main/java/com/example/NoteListActivity.java b/example/src/main/java/com/example/activities/NoteListActivity.java old mode 100755 new mode 100644 similarity index 91% rename from example/src/main/java/com/example/NoteListActivity.java rename to example/src/main/java/com/example/activities/NoteListActivity.java index 09877f2d..7b91201b --- a/example/src/main/java/com/example/NoteListActivity.java +++ b/example/src/main/java/com/example/activities/NoteListActivity.java @@ -1,4 +1,4 @@ -package com.example; +package com.example.activities; import android.app.ListActivity; import android.content.Intent; @@ -6,6 +6,10 @@ import android.util.Log; import android.view.View; import android.widget.ArrayAdapter; + +import com.example.R; +import com.example.models.NewNote; +import com.example.models.Note; import com.orm.SugarRecord; import com.orm.query.Condition; import com.orm.query.Select; @@ -35,4 +39,4 @@ public void onClick(View view) { Log.d("COUNT", "Count: " + Select.from(Note.class).where(new Condition[]{new Condition("title").eq("note")}).count() + "/" + notes.size()); } -} \ No newline at end of file +} diff --git a/example/src/main/java/com/example/SugarActivity.java b/example/src/main/java/com/example/activities/SugarActivity.java old mode 100755 new mode 100644 similarity index 89% rename from example/src/main/java/com/example/SugarActivity.java rename to example/src/main/java/com/example/activities/SugarActivity.java index b7130f86..68ceca8e --- a/example/src/main/java/com/example/SugarActivity.java +++ b/example/src/main/java/com/example/activities/SugarActivity.java @@ -1,8 +1,14 @@ -package com.example; +package com.example.activities; import android.app.Activity; import android.content.Intent; import android.os.Bundle; + +import com.example.R; +import com.example.models.NewNote; +import com.example.models.Note; +import com.example.models.Tag; +import com.example.models.TextNote; import com.orm.SugarRecord; import static com.orm.SugarRecord.save; diff --git a/example/src/main/java/com/example/NewNote.java b/example/src/main/java/com/example/models/NewNote.java similarity index 81% rename from example/src/main/java/com/example/NewNote.java rename to example/src/main/java/com/example/models/NewNote.java index a80e2d4a..45806744 100644 --- a/example/src/main/java/com/example/NewNote.java +++ b/example/src/main/java/com/example/models/NewNote.java @@ -1,4 +1,4 @@ -package com.example; +package com.example.models; import com.orm.dsl.Table; diff --git a/example/src/main/java/com/example/Note.java b/example/src/main/java/com/example/models/Note.java old mode 100755 new mode 100644 similarity index 98% rename from example/src/main/java/com/example/Note.java rename to example/src/main/java/com/example/models/Note.java index b83f730f..79151644 --- a/example/src/main/java/com/example/Note.java +++ b/example/src/main/java/com/example/models/Note.java @@ -1,4 +1,4 @@ -package com.example; +package com.example.models; import com.orm.dsl.Column; import com.orm.dsl.Table; diff --git a/example/src/main/java/com/example/NoteRelation.java b/example/src/main/java/com/example/models/NoteRelation.java similarity index 95% rename from example/src/main/java/com/example/NoteRelation.java rename to example/src/main/java/com/example/models/NoteRelation.java index a64d055a..282f0a43 100644 --- a/example/src/main/java/com/example/NoteRelation.java +++ b/example/src/main/java/com/example/models/NoteRelation.java @@ -1,4 +1,4 @@ -package com.example; +package com.example.models; import android.content.Context; import com.orm.dsl.Table; diff --git a/example/src/main/java/com/example/Tag.java b/example/src/main/java/com/example/models/Tag.java old mode 100755 new mode 100644 similarity index 91% rename from example/src/main/java/com/example/Tag.java rename to example/src/main/java/com/example/models/Tag.java index 6600c14a..bcb7aaf2 --- a/example/src/main/java/com/example/Tag.java +++ b/example/src/main/java/com/example/models/Tag.java @@ -1,4 +1,4 @@ -package com.example; +package com.example.models; import com.orm.dsl.Table; diff --git a/example/src/main/java/com/example/TextNote.java b/example/src/main/java/com/example/models/TextNote.java similarity index 84% rename from example/src/main/java/com/example/TextNote.java rename to example/src/main/java/com/example/models/TextNote.java index 91b7a33c..bd044bfa 100644 --- a/example/src/main/java/com/example/TextNote.java +++ b/example/src/main/java/com/example/models/TextNote.java @@ -1,4 +1,4 @@ -package com.example; +package com.example.models; import com.orm.dsl.Table; From 1b49132e8a9c4c3afd84d6dce53a9eef6725e0a6 Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Fri, 27 Mar 2015 19:53:10 -0700 Subject: [PATCH 063/305] Restructure test directory of library project --- library/build.gradle | 1 + library/library.iml | 39 ++++++++++--------- .../test/java}/com/orm/NamingHelperTest.java | 1 + .../java}/com/orm/query/DummyContext.java | 32 ++++++++++++++- .../test/java}/com/orm/query/SelectTest.java | 3 +- .../test/java}/com/orm/query/TestRecord.java | 0 6 files changed, 54 insertions(+), 22 deletions(-) rename library/{test => src/test/java}/com/orm/NamingHelperTest.java (99%) rename library/{test => src/test/java}/com/orm/query/DummyContext.java (96%) rename library/{test => src/test/java}/com/orm/query/SelectTest.java (97%) rename library/{test => src/test/java}/com/orm/query/TestRecord.java (100%) diff --git a/library/build.gradle b/library/build.gradle index 5e2e71ef..e37db9dd 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -22,6 +22,7 @@ android { dependencies { compile 'com.android.support:support-v4:21.0.3' + testCompile 'junit:junit:4.12' } task libraryJar(type: Jar) { diff --git a/library/library.iml b/library/library.iml index 4c42fe43..33525dbc 100644 --- a/library/library.iml +++ b/library/library.iml @@ -9,12 +9,11 @@ - - - diff --git a/example/src/main/java/com/example/activities/AddNoteActivity.java b/example/src/main/java/com/example/activities/AddNoteActivity.java deleted file mode 100644 index 02ed08ce..00000000 --- a/example/src/main/java/com/example/activities/AddNoteActivity.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.example.activities; - -import android.app.Activity; -import android.content.Intent; -import android.os.Bundle; -import android.view.View; -import android.widget.Button; -import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.TextView; - -import static com.orm.SugarRecord.save; - -import com.example.models.Note; -import com.example.models.Tag; -import com.example.R; - - -public class AddNoteActivity extends Activity { - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.main); - - LinearLayout view = (LinearLayout) findViewById(R.id.layout); - TextView titleText = new TextView(this); - titleText.setText("Title"); - TextView descText = new TextView(this); - descText.setText("Description"); - TextView tagText = new TextView(this); - tagText.setText("Tag"); - final EditText titleBox = new EditText(this); - final EditText descBox = new EditText(this); - final EditText tagBox = new EditText(this); - - Button save = new Button(this); - save.setText("Save"); - view.addView(titleText); - view.addView(titleBox); - view.addView(descText); - view.addView(descBox); - view.addView(tagText); - view.addView(tagBox); - view.addView(save); - - save.setOnClickListener(new View.OnClickListener() { - public void onClick(View view) { - Tag tag = new Tag(tagBox.getText().toString()); - save(tag); - save(new Note(10 + (int) (10 * Math.random()), titleBox.getText().toString(), descBox.getText().toString(), tag)); - Intent intent = new Intent(AddNoteActivity.this, NoteListActivity.class); - startActivity(intent); - } - }); - } -} diff --git a/example/src/main/java/com/example/activities/NoteListActivity.java b/example/src/main/java/com/example/activities/NoteListActivity.java deleted file mode 100644 index 9b85b90e..00000000 --- a/example/src/main/java/com/example/activities/NoteListActivity.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.example.activities; - -import android.app.ListActivity; -import android.content.Intent; -import android.os.Bundle; -import android.util.Log; -import android.view.View; -import android.widget.ArrayAdapter; - -import java.util.List; - -import com.orm.query.Condition; -import com.orm.query.Select; -import com.orm.SugarRecord; - -import com.example.models.NewNote; -import com.example.models.Note; -import com.example.R; - - -public class NoteListActivity extends ListActivity { - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.notelist); - - List notes = Select.from(Note.class).orderBy("title").list(); - List list = SugarRecord.listAll(NewNote.class); - - setListAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_1, notes)); - - findViewById(R.id.Button01).setOnClickListener(new View.OnClickListener() { - public void onClick(View view) { - Intent intent = new Intent(NoteListActivity.this, AddNoteActivity.class); - startActivity(intent); - } - }); - - Log.d("COUNT", "Count: " + Select.from(Note.class).where(new Condition[]{new Condition("title").eq("note")}).count() + "/" + notes.size()); - } -} diff --git a/example/src/main/java/com/example/activities/SugarActivity.java b/example/src/main/java/com/example/activities/SugarActivity.java index bf5a7110..83670252 100644 --- a/example/src/main/java/com/example/activities/SugarActivity.java +++ b/example/src/main/java/com/example/activities/SugarActivity.java @@ -28,8 +28,6 @@ public void onCreate(Bundle savedInstanceState) SugarRecord.deleteAll(Tag.class); SugarRecord.deleteAll(NewNote.class); initDb(); - Intent intent = new Intent(this, NoteListActivity.class); - startActivity(intent); } private void initDb() { diff --git a/example/src/main/res/layout/notelist.xml b/example/src/main/res/layout/notelist.xml deleted file mode 100755 index b07f003f..00000000 --- a/example/src/main/res/layout/notelist.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - -